From d4e366cd5f35de358f83bb58c8211fa8f26701b9 Mon Sep 17 00:00:00 2001 From: chrisgitiota Date: Thu, 24 Apr 2025 15:59:31 +0200 Subject: [PATCH 01/10] Remove all resources that will be provided by product-core --- Cargo.toml | 2 - bindings/wasm/README.md | 4 - bindings/wasm/iota_interaction_ts/Cargo.toml | 57 - bindings/wasm/iota_interaction_ts/LICENSE | 177 --- bindings/wasm/iota_interaction_ts/README.md | 3 - .../wasm/iota_interaction_ts/lib/index.ts | 6 - .../lib/iota_client_helpers.ts | 181 --- .../lib/move_calls/asset/create.ts | 27 - .../lib/move_calls/asset/delete.ts | 21 - .../lib/move_calls/asset/index.ts | 7 - .../lib/move_calls/asset/transfer.ts | 80 -- .../lib/move_calls/asset/update.ts | 23 - .../lib/move_calls/identity/borrow_asset.ts | 140 --- .../lib/move_calls/identity/config.ts | 73 -- .../identity/controller_execution.ts | 112 -- .../lib/move_calls/identity/create.ts | 57 - .../lib/move_calls/identity/index.ts | 11 - .../lib/move_calls/identity/proposal.ts | 30 - .../lib/move_calls/identity/send_asset.ts | 69 -- .../lib/move_calls/identity/update.ts | 55 - .../lib/move_calls/identity/upgrade.ts | 43 - .../lib/move_calls/index.ts | 6 - .../lib/move_calls/migration.ts | 34 - .../lib/move_calls/utils.ts | 50 - .../iota_interaction_ts/lib/tsconfig.json | 17 - .../iota_interaction_ts/lib/tsconfig.web.json | 19 - .../iota_interaction_ts/package-lock.json | 1068 ----------------- .../wasm/iota_interaction_ts/package.json | 48 - .../src/asset_move_calls.rs | 193 --- .../src/bindings/keytool/mod.rs | 8 - .../src/bindings/keytool/signer.rs | 110 -- .../src/bindings/keytool/storage.rs | 147 --- .../iota_interaction_ts/src/bindings/mod.rs | 11 - .../iota_interaction_ts/src/bindings/types.rs | 12 - .../src/bindings/wasm_iota_client.rs | 437 ------- .../src/bindings/wasm_types.rs | 581 --------- .../iota_interaction_ts/src/common/macros.rs | 62 - .../iota_interaction_ts/src/common/mod.rs | 11 - .../iota_interaction_ts/src/common/types.rs | 122 -- .../iota_interaction_ts/src/common/utils.rs | 26 - .../wasm/iota_interaction_ts/src/error.rs | 223 ---- .../src/identity_move_calls.rs | 673 ----------- .../src/iota_client_ts_sdk.rs | 427 ------- bindings/wasm/iota_interaction_ts/src/lib.rs | 58 - .../src/migration_move_calls.rs | 54 - .../src/transaction_builder.rs | 63 - .../wasm/iota_interaction_ts/tsconfig.json | 15 - .../iota_interaction_ts/tsconfig.node.json | 8 - .../iota_interaction_rust/asset_move_calls.rs | 203 ---- .../identity_move_calls.rs | 851 ------------- .../iota_client_rust_sdk.rs | 535 --------- .../migration_move_calls.rs | 56 - .../src/iota_interaction_rust/mod.rs | 41 - .../transaction_builder.rs | 53 - .../src/iota_interaction_rust/utils.rs | 122 -- identity_iota_core/src/lib.rs | 3 - identity_iota_interaction/Cargo.toml | 72 -- identity_iota_interaction/README.md | 51 - .../src/effects_mut_api.rs | 94 -- .../src/iota_client_trait.rs | 257 ---- .../src/iota_verifiable_credential.rs | 39 - .../src/keytool/internal.rs | 126 -- identity_iota_interaction/src/keytool/mod.rs | 9 - .../src/keytool/signer.rs | 131 -- .../src/keytool/storage.rs | 145 --- identity_iota_interaction/src/lib.rs | 132 -- .../src/move_call_traits.rs | 251 ---- identity_iota_interaction/src/move_type.rs | 75 -- .../src/sdk_types/error.rs | 37 - .../src/sdk_types/generated_types.rs | 270 ----- .../iota_json_rpc_types/iota_coin.rs | 36 - .../iota_json_rpc_types/iota_event.rs | 195 --- .../iota_json_rpc_types/iota_move.rs | 346 ------ .../iota_json_rpc_types/iota_object.rs | 808 ------------- .../iota_json_rpc_types/iota_transaction.rs | 407 ------- .../src/sdk_types/iota_json_rpc_types/mod.rs | 27 - .../src/sdk_types/iota_types/balance.rs | 95 -- .../src/sdk_types/iota_types/base_types.rs | 814 ------------- .../src/sdk_types/iota_types/coin.rs | 186 --- .../sdk_types/iota_types/collection_types.rs | 103 -- .../src/sdk_types/iota_types/crypto.rs | 685 ----------- .../src/sdk_types/iota_types/digests.rs | 470 -------- .../src/sdk_types/iota_types/dynamic_field.rs | 156 --- .../src/sdk_types/iota_types/error.rs | 943 --------------- .../src/sdk_types/iota_types/event.rs | 55 - .../sdk_types/iota_types/execution_status.rs | 368 ------ .../src/sdk_types/iota_types/gas.rs | 63 - .../src/sdk_types/iota_types/gas_coin.rs | 147 --- .../src/sdk_types/iota_types/governance.rs | 98 -- .../src/sdk_types/iota_types/id.rs | 108 -- .../src/sdk_types/iota_types/iota_serde.rs | 409 ------- .../sdk_types/iota_types/iota_types_lib.rs | 125 -- .../src/sdk_types/iota_types/mod.rs | 29 - .../src/sdk_types/iota_types/move_package.rs | 84 -- .../src/sdk_types/iota_types/object.rs | 110 -- .../iota_types/quorum_driver_types.rs | 12 - .../src/sdk_types/iota_types/stardust/mod.rs | 4 - .../iota_types/stardust/output/mod.rs | 6 - .../iota_types/stardust/output/nft.rs | 33 - .../src/sdk_types/iota_types/storage.rs | 28 - .../src/sdk_types/iota_types/timelock/mod.rs | 5 - .../sdk_types/iota_types/timelock/timelock.rs | 124 -- .../timelock/timelocked_staked_iota.rs | 85 -- .../src/sdk_types/iota_types/transaction.rs | 463 ------- .../src/sdk_types/mod.rs | 17 - .../move_core_types/account_address.rs | 337 ------ .../move_core_types/annotated_value.rs | 774 ------------ .../move_core_types/annotated_visitor.rs | 814 ------------- .../sdk_types/move_core_types/identifier.rs | 243 ---- .../move_core_types/language_storage.rs | 350 ------ .../src/sdk_types/move_core_types/mod.rs | 35 - .../move_core_types/parsing/address.rs | 202 ---- .../sdk_types/move_core_types/parsing/mod.rs | 11 - .../move_core_types/parsing/parser.rs | 470 -------- .../move_core_types/parsing/types.rs | 195 --- .../move_core_types/parsing/values.rs | 320 ----- .../move_core_types/runtime_value.rs | 584 --------- .../src/sdk_types/move_core_types/u256.rs | 391 ------ .../src/sdk_types/shared_crypto/intent.rs | 204 ---- .../src/sdk_types/shared_crypto/mod.rs | 4 - .../src/transaction_builder_trait.rs | 15 - 121 files changed, 21807 deletions(-) delete mode 100644 bindings/wasm/iota_interaction_ts/Cargo.toml delete mode 100644 bindings/wasm/iota_interaction_ts/LICENSE delete mode 100644 bindings/wasm/iota_interaction_ts/README.md delete mode 100644 bindings/wasm/iota_interaction_ts/lib/index.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/asset/index.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/index.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/index.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts delete mode 100644 bindings/wasm/iota_interaction_ts/lib/tsconfig.json delete mode 100644 bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json delete mode 100644 bindings/wasm/iota_interaction_ts/package-lock.json delete mode 100644 bindings/wasm/iota_interaction_ts/package.json delete mode 100644 bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/keytool/mod.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/keytool/signer.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/keytool/storage.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/mod.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/types.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/common/macros.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/common/mod.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/common/types.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/common/utils.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/error.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/lib.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs delete mode 100644 bindings/wasm/iota_interaction_ts/src/transaction_builder.rs delete mode 100644 bindings/wasm/iota_interaction_ts/tsconfig.json delete mode 100644 bindings/wasm/iota_interaction_ts/tsconfig.node.json delete mode 100644 identity_iota_core/src/iota_interaction_rust/asset_move_calls.rs delete mode 100644 identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs delete mode 100644 identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs delete mode 100644 identity_iota_core/src/iota_interaction_rust/migration_move_calls.rs delete mode 100644 identity_iota_core/src/iota_interaction_rust/mod.rs delete mode 100644 identity_iota_core/src/iota_interaction_rust/transaction_builder.rs delete mode 100644 identity_iota_core/src/iota_interaction_rust/utils.rs delete mode 100644 identity_iota_interaction/Cargo.toml delete mode 100644 identity_iota_interaction/README.md delete mode 100644 identity_iota_interaction/src/effects_mut_api.rs delete mode 100644 identity_iota_interaction/src/iota_client_trait.rs delete mode 100644 identity_iota_interaction/src/iota_verifiable_credential.rs delete mode 100644 identity_iota_interaction/src/keytool/internal.rs delete mode 100644 identity_iota_interaction/src/keytool/mod.rs delete mode 100644 identity_iota_interaction/src/keytool/signer.rs delete mode 100644 identity_iota_interaction/src/keytool/storage.rs delete mode 100644 identity_iota_interaction/src/lib.rs delete mode 100644 identity_iota_interaction/src/move_call_traits.rs delete mode 100644 identity_iota_interaction/src/move_type.rs delete mode 100644 identity_iota_interaction/src/sdk_types/error.rs delete mode 100644 identity_iota_interaction/src/sdk_types/generated_types.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_coin.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_event.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_move.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_object.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_transaction.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/balance.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/base_types.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/coin.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/collection_types.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/crypto.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/digests.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/dynamic_field.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/error.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/event.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/execution_status.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/gas.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/gas_coin.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/governance.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/id.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/move_package.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/object.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/quorum_driver_types.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/stardust/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/storage.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/timelock/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/timelock/timelocked_staked_iota.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/transaction.rs delete mode 100644 identity_iota_interaction/src/sdk_types/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/annotated_value.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/annotated_visitor.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/identifier.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/language_storage.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/runtime_value.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/u256.rs delete mode 100644 identity_iota_interaction/src/sdk_types/shared_crypto/intent.rs delete mode 100644 identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs delete mode 100644 identity_iota_interaction/src/transaction_builder_trait.rs diff --git a/Cargo.toml b/Cargo.toml index e9870fa07..fa5001a50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,6 @@ members = [ "identity_ecdsa_verifier", "identity_eddsa_verifier", "examples", - "identity_iota_interaction", - "bindings/wasm/iota_interaction_ts", ] exclude = ["bindings/wasm/identity_wasm", "bindings/grpc"] diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 4b2507674..970f2dede 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -10,10 +10,6 @@ Here is an overview of the existing artifacts: * `identity_wasm`
Exports the IdentityClient to TypeScript using wasm-bindgen generated wasm bindings -* `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. - ## Building an Artifact For build instructions please have a look into the artifact README file. diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml deleted file mode 100644 index 5e6ad0892..000000000 --- a/bindings/wasm/iota_interaction_ts/Cargo.toml +++ /dev/null @@ -1,57 +0,0 @@ -[package] -name = "iota_interaction_ts" -version = "1.6.0-alpha" -authors = ["IOTA Stiftung"] -edition = "2021" -homepage = "https://www.iota.org" -keywords = ["iota", "tangle", "identity", "wasm"] -license = "Apache-2.0" -publish = false -readme = "README.md" -resolver = "2" -description = "identity_iota_interaction Adapters using Web Assembly bindings." - -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -anyhow = { version = "1.0.94", features = ["std"] } -async-trait = { version = "0.1", default-features = false } -bcs = "0.1.6" -bls12_381_plus = "0.8.17" -cfg-if = "1.0.0" -console_error_panic_hook = { version = "0.1" } -eyre = "0.6.12" -fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", package = "fastcrypto" } -futures = { version = "0.3" } -identity_core = { version = "=1.6.0-alpha", path = "../../../identity_core" } -identity_iota_interaction = { version = "=1.6.0-alpha", path = "../../../identity_iota_interaction", default-features = false } -js-sys = { version = "0.3.61" } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } -serde = { version = "1.0", features = ["derive"] } -serde-wasm-bindgen = "0.6.5" -serde_json.workspace = true -serde_repr = { version = "0.1", default-features = false } -thiserror.workspace = true -# Want to use the nice API of tokio::sync::RwLock for now even though we can't use threads. -tokio = { version = "1.43", default-features = false, features = ["sync"] } -tsify = "0.4.5" -wasm-bindgen = { version = "0.2.100", features = ["serde-serialize"] } -wasm-bindgen-futures = { version = "0.4", default-features = false } -zkryptium = "0.2.2" - -[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" - -[features] -default = [] -keytool = ["identity_iota_interaction/keytool"] diff --git a/bindings/wasm/iota_interaction_ts/LICENSE b/bindings/wasm/iota_interaction_ts/LICENSE deleted file mode 100644 index 4947287f7..000000000 --- a/bindings/wasm/iota_interaction_ts/LICENSE +++ /dev/null @@ -1,177 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/bindings/wasm/iota_interaction_ts/README.md b/bindings/wasm/iota_interaction_ts/README.md deleted file mode 100644 index 6f7839c27..000000000 --- a/bindings/wasm/iota_interaction_ts/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# 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/index.ts b/bindings/wasm/iota_interaction_ts/lib/index.ts deleted file mode 100644 index 1a201cc25..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -export * from "~iota_interaction_ts"; -export * as iota_client_helpers from "./iota_client_helpers"; -export * as move_calls from "./move_calls"; diff --git a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts deleted file mode 100644 index 1eafe9e97..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { CoinStruct, IotaClient, IotaTransactionBlockResponse, TransactionEffects } from "@iota/iota-sdk/client"; -import { GasData, TransactionDataBuilder } from "@iota/iota-sdk/transactions"; - -export type Signer = { sign(data: Uint8Array): Promise }; - -const MINIMUM_BALANCE_FOR_COIN = BigInt(1_000_000_000); - -export class WasmIotaTransactionBlockResponseWrapper { - response: IotaTransactionBlockResponse; - - constructor(response: IotaTransactionBlockResponse) { - this.response = response; - } - - to_string(): string { - return JSON.stringify(this.response); - } - - get_effects(): TransactionEffects | null | undefined { - return this.response.effects; - } - - get_response(): IotaTransactionBlockResponse { - return this.response; - } - - get_digest(): string { - return this.response.digest; - } -} - -function byHighestBalance({ balance: a }: T, { balance: b }: T) { - if (a > b) { - return -1; - } - if (a < b) { - return 1; - } - return 0; -} - -async function getCoinForTransaction(iotaClient: IotaClient, senderAddress: string): Promise { - let cursor: string | null | undefined = undefined; - do { - const response = await iotaClient.getCoins({ owner: senderAddress, cursor }); - if (response.data.length === 0) { - throw new Error( - `no coin found with minimum required balance of ${MINIMUM_BALANCE_FOR_COIN} for address ${senderAddress}"`, - ); - } - - let sortedValidCoins = response.data - .map((coin) => ({ coin, balance: BigInt(coin.balance) })) - .filter(({ balance }) => balance >= MINIMUM_BALANCE_FOR_COIN) - .sort(byHighestBalance); - - if (sortedValidCoins.length >= 1) { - return sortedValidCoins[0].coin; - } - - cursor = response.nextCursor; - } while (cursor); - - throw new Error( - `no coin found with minimum required balance of ${MINIMUM_BALANCE_FOR_COIN} for address ${senderAddress}"`, - ); -} - -/** - * Inserts these values into the transaction and replaces placeholder values. - * - * - sender (overwritten as we assume a placeholder to be used in prepared transaction) - * - gas budget (value determined automatically if not provided) - * - gas price (value determined automatically) - * - gas coin / payment object (fetched automatically) - * - gas owner (equals sender) - * - * @param iotaClient client instance - * @param senderAddress transaction sender (and the one paying for it) - * @param txBcs transaction data serialized to bcs, most probably having placeholder values - * @param gasBudget optional fixed gas budget, determined automatically with a dry run if not provided - * @returns updated transaction data - */ -export 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: GasData = { - budget: gasBudget ? gasBudget.toString() : "50000000", // 50_000_000 - owner: senderAddress, - payment: [{ - objectId: gasCoin.coinObjectId, - version: gasCoin.version, - digest: gasCoin.digest, - }], - price: gasPrice.toString(), - }; - const overrides = { - gasData, - sender: senderAddress, - }; - // TODO: check why `.build` with `overrides` doesn't override these values - txData.sender = overrides.sender; - txData.gasData = overrides.gasData; - let builtTx = txData.build({ overrides }); - - 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; - - overrides.gasData.budget = budget.toString(); - txData.gasData.budget = budget.toString(); - - builtTx = txData.build({ overrides }); - } - - return builtTx; -} - -// estimate gas, get coin, execute tx here -export async function executeTransaction( - iotaClient: IotaClient, - senderAddress: string, - txBcs: Uint8Array, - signer: Signer, - gasBudget?: bigint, -): Promise { - const txWithGasData = await addGasDataToTransaction(iotaClient, senderAddress, txBcs, gasBudget); - const signature = await signer.sign(txWithGasData); - - const response = await iotaClient.executeTransactionBlock({ - transactionBlock: txWithGasData, - signature, - options: { // equivalent of `IotaTransactionBlockResponseOptions::full_content()` - showEffects: true, - showInput: true, - showRawInput: true, - showEvents: true, - showObjectChanges: true, - showBalanceChanges: true, - showRawEffects: false, - }, - }); - - if (response?.effects?.status.status === "failure") { - throw new Error(`transaction returned an unexpected response; ${response?.effects?.status.error}`); - } - - return new WasmIotaTransactionBlockResponseWrapper(response); -} - -/** - * Helper function to pause execution. - * - * @param durationMs time to sleep in ms - */ -export function sleep(durationMs: number) { - return new Promise(resolve => setTimeout(resolve, durationMs)); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts deleted file mode 100644 index 9aee87fa2..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { Transaction } from "@iota/iota-sdk/transactions"; - -export function create( - inner_bytes: Uint8Array, - inner_type: string, - mutable: boolean, - transferable: boolean, - deletable: boolean, - packageId: string, -): Promise { - const tx = new Transaction(); - const inner_arg = tx.pure(inner_bytes); - const mutableArg = tx.pure.bool(mutable); - const transferableArg = tx.pure.bool(transferable); - const deletableArg = tx.pure.bool(deletable); - - tx.moveCall({ - target: `${packageId}::asset::new_with_config`, - typeArguments: [inner_type], - arguments: [inner_arg, mutableArg, transferableArg, deletableArg], - }); - - return tx.build(); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts deleted file mode 100644 index 04b4a10f9..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; - -export function remove( - asset: ObjectRef, - asset_type: string, - packageId: string, -): Promise { - const tx = new Transaction(); - const asset_arg = tx.objectRef(asset); - - tx.moveCall({ - target: `${packageId}::asset::delete`, - typeArguments: [asset_type], - arguments: [asset_arg], - }); - - return tx.build(); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/index.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/index.ts deleted file mode 100644 index feaf3adfc..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -export * from "./create"; -export * from "./delete"; -export * from "./transfer"; -export * from "./update"; diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts deleted file mode 100644 index 5ab2b7ad7..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; - -export function transfer( - asset: ObjectRef, - assetType: string, - recipient: string, - packageId: string, -): Promise { - const tx = new Transaction(); - const assetArg = tx.objectRef(asset); - const recipientArg = tx.pure.address(recipient); - - tx.moveCall({ - target: `${packageId}::asset::transfer`, - typeArguments: [assetType], - arguments: [assetArg, recipientArg], - }); - - return tx.build(); -} - -function makeTx( - proposal: SharedObjectRef, - cap: ObjectRef, - asset: ObjectRef, - assetType: string, - packageId: string, - functionName: string, -): Promise { - const tx = new Transaction(); - const proposalArg = tx.sharedObjectRef(proposal); - const capArg = tx.objectRef(cap); - const assetArg = tx.objectRef(asset); - - tx.moveCall({ - target: `${packageId}::asset::${functionName}`, - typeArguments: [assetType], - arguments: [proposalArg, capArg, assetArg], - }); - - return tx.build(); -} - -export function acceptProposal( - proposal: SharedObjectRef, - recipientCap: ObjectRef, - asset: ObjectRef, - assetType: string, - packageId: string, -): Promise { - return makeTx( - proposal, - recipientCap, - asset, - assetType, - packageId, - "accept", - ); -} - -export function concludeOrCancel( - proposal: SharedObjectRef, - senderCap: ObjectRef, - asset: ObjectRef, - assetType: string, - packageId: string, -): Promise { - return makeTx( - proposal, - senderCap, - asset, - assetType, - packageId, - "conclude_or_cancel", - ); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts deleted file mode 100644 index c80f52f9b..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; - -export function update( - asset: ObjectRef, - content: Uint8Array, - contentType: string, - packageId: string, -): Promise { - const tx = new Transaction(); - const contentArg = tx.pure(content); - const assetArg = tx.objectRef(asset); - - tx.moveCall({ - target: `${packageId}::asset::update`, - typeArguments: [contentType], - arguments: [assetArg, contentArg], - }); - - return tx.build(); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts deleted file mode 100644 index 774eb6eee..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; -import { IotaObjectData } from "@iota/iota-sdk/dist/cjs/client"; -import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota-sdk/transactions"; -import { getControllerDelegation, putBackDelegationToken } from "../utils"; - -export function proposeBorrow( - identity: SharedObjectRef, - capability: ObjectRef, - objects: string[], - packageId: string, - expiration?: number, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const identityArg = tx.sharedObjectRef(identity); - const exp = tx.pure.option("u64", expiration); - const objectsArg = tx.pure.vector("id", objects); - - tx.moveCall({ - target: `${packageId}::identity::propose_borrow`, - arguments: [identityArg, delegationToken, exp, objectsArg], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - return tx.build(); -} - -export function executeBorrow( - identity: SharedObjectRef, - capability: ObjectRef, - proposalId: string, - objects: IotaObjectData[], - intentFn: (arg0: Transaction, arg1: Map) => void, - packageId: string, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const proposal = tx.pure.id(proposalId); - const identityArg = tx.sharedObjectRef(identity); - - let action = tx.moveCall({ - target: `${packageId}::identity::execute_proposal`, - typeArguments: [`${packageId}::borrow_proposal::Borrow`], - arguments: [identityArg, delegationToken, proposal], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - const objectArgMap = new Map(); - for (const obj of objects) { - const recvObj = tx.receivingRef(obj); - const objArg = tx.moveCall({ - target: `${packageId}::identity::execute_borrow`, - typeArguments: [obj.type!], - arguments: [identityArg, action, recvObj], - }); - - objectArgMap.set(obj.objectId, [objArg, obj]); - } - - intentFn(tx, objectArgMap); - - for (const [obj, objData] of objectArgMap.values()) { - tx.moveCall({ - target: `${packageId}::borrow_proposal::put_back`, - typeArguments: [objData.type!], - arguments: [action, obj], - }); - } - - tx.moveCall({ - target: `${packageId}::transfer_proposal::conclude_borrow`, - arguments: [action], - }); - - return tx.build(); -} - -export function createAndExecuteBorrow( - identity: SharedObjectRef, - capability: ObjectRef, - objects: IotaObjectData[], - intentFn: (arg0: Transaction, arg1: Map) => void, - packageId: string, - expiration?: number, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const identityArg = tx.sharedObjectRef(identity); - const exp = tx.pure.option("u64", expiration); - const objectsArg = tx.pure.vector("id", objects.map(obj => obj.objectId)); - - const proposal = tx.moveCall({ - target: `${packageId}::identity::propose_borrow`, - arguments: [identityArg, delegationToken, exp, objectsArg], - }); - - let action = tx.moveCall({ - target: `${packageId}::identity::execute_proposal`, - typeArguments: [`${packageId}::borrow_proposal::Borrow`], - arguments: [identityArg, delegationToken, proposal], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - const objectArgMap = new Map(); - for (const obj of objects) { - const recvObj = tx.receivingRef(obj); - const objArg = tx.moveCall({ - target: `${packageId}::identity::execute_borrow`, - typeArguments: [obj.type!], - arguments: [identityArg, action, recvObj], - }); - - objectArgMap.set(obj.objectId, [objArg, obj]); - } - - intentFn(tx, objectArgMap); - - for (const [obj, objData] of objectArgMap.values()) { - tx.moveCall({ - target: `${packageId}::borrow_proposal::put_back`, - typeArguments: [objData.type!], - arguments: [action, obj], - }); - } - - tx.moveCall({ - target: `${packageId}::transfer_proposal::conclude_borrow`, - arguments: [action], - }); - return tx.build(); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts deleted file mode 100644 index 666b0022e..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; -import { getControllerDelegation, putBackDelegationToken } from "../utils"; - -export function proposeConfigChange( - identity: SharedObjectRef, - controllerCap: ObjectRef, - controllersToAdd: [string, number][], - controllersToRemove: string[], - controllersToUpdate: [string, number][], - packageId: string, - expiration?: number, - threshold?: number, -): Promise { - const tx = new Transaction(); - const addressesToAdd = tx.pure.vector("address", controllersToAdd.map(c => c[0])); - const vpsToAdd = tx.pure.vector("u64", controllersToAdd.map(c => c[1])); - const controllersToAddArg = tx.moveCall({ - target: `${packageId}::utils::vec_map_from_keys_values`, - typeArguments: ["address", "u64"], - arguments: [addressesToAdd, vpsToAdd], - }); - - const idsToUpdate = tx.pure.vector("id", controllersToUpdate.map(c => c[0])); - const vpsToUpdate = tx.pure.vector("u64", controllersToUpdate.map(c => c[1])); - const controllersToUpdateArg = tx.moveCall({ - target: `${packageId}::utils::vec_map_from_keys_values`, - typeArguments: ["id", "u64"], - arguments: [idsToUpdate, vpsToUpdate], - }); - - const identityArg = tx.sharedObjectRef(identity); - const cap = tx.objectRef(controllerCap); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const thresholdArg = tx.pure.option("u64", threshold); - const exp = tx.pure.option("u64", expiration); - const controllersToRemoveArg = tx.pure.vector("id", controllersToRemove); - - tx.moveCall({ - target: `${packageId}::identity::propose_config_change`, - arguments: [identityArg, delegationToken, exp, thresholdArg, controllersToAddArg, controllersToRemoveArg, - controllersToUpdateArg], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - return tx.build(); -} - -export function executeConfigChange( - identity: SharedObjectRef, - capability: ObjectRef, - proposalId: string, - packageId: string, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const proposal = tx.pure.id(proposalId); - const identityArg = tx.sharedObjectRef(identity); - - tx.moveCall({ - target: `${packageId}::identity::execute_config_change`, - arguments: [identityArg, delegationToken, proposal], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - return tx.build(); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts deleted file mode 100644 index 332daa33a..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; -import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota-sdk/transactions"; -import { getControllerDelegation, putBackDelegationToken } from "../utils"; - -export function proposeControllerExecution( - identity: SharedObjectRef, - capability: ObjectRef, - controllerCapId: string, - packageId: string, - expiration?: number, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const identityArg = tx.sharedObjectRef(identity); - const exp = tx.pure.option("u64", expiration); - const controllerCapIdArg = tx.pure.id(controllerCapId); - - tx.moveCall({ - target: `${packageId}::identity::propose_controller_execution`, - arguments: [identityArg, delegationToken, controllerCapIdArg, exp], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - return tx.build(); -} - -export function executeControllerExecution( - identity: SharedObjectRef, - capability: ObjectRef, - proposalId: string, - controllerCapRef: ObjectRef, - intentFn: (arg0: Transaction, arg1: TransactionArgument) => void, - packageId: string, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const proposal = tx.pure.id(proposalId); - const identityArg = tx.sharedObjectRef(identity); - - let action = tx.moveCall({ - target: `${packageId}::identity::execute_proposal`, - typeArguments: [`${packageId}::borrow_proposal::Borrow`], - arguments: [identityArg, delegationToken, proposal], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - const receiving = tx.receivingRef(controllerCapRef); - const BorrowedControllerCap = tx.moveCall({ - target: `${packageId}::identity::borrow_controller_cap`, - arguments: [identityArg, action, receiving], - }); - - intentFn(tx, BorrowedControllerCap); - - tx.moveCall({ - target: `${packageId}::controller_proposal::put_back`, - arguments: [action, BorrowedControllerCap], - }); - - return tx.build(); -} - -export function createAndExecuteControllerExecution( - identity: SharedObjectRef, - capability: ObjectRef, - controllerCapRef: ObjectRef, - intentFn: (arg0: Transaction, arg1: TransactionArgument) => void, - packageId: string, - expiration?: number, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const identityArg = tx.sharedObjectRef(identity); - const exp = tx.pure.option("u64", expiration); - const controller_cap_id = tx.pure.id(controllerCapRef.objectId); - - const proposal = tx.moveCall({ - target: `${packageId}::identity::propose_controller_execution`, - arguments: [identityArg, delegationToken, controller_cap_id, exp], - }); - - let action = tx.moveCall({ - target: `${packageId}::identity::execute_proposal`, - typeArguments: [`${packageId}::borrow_proposal::Borrow`], - arguments: [identityArg, delegationToken, proposal], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - const receiving = tx.receivingRef(controllerCapRef); - const borrowedControllerCap = tx.moveCall({ - target: `${packageId}::identity::borrow_controller_cap`, - arguments: [identityArg, action, receiving], - }); - - intentFn(tx, borrowedControllerCap); - - tx.moveCall({ - target: `${packageId}::controller_proposal::put_back`, - arguments: [action, borrowedControllerCap], - }); - - return tx.build(); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts deleted file mode 100644 index 498dd7013..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { bcs } from "@iota/iota-sdk/bcs"; -import { Transaction } from "@iota/iota-sdk/transactions"; -import { getClockRef } from "../utils"; - -export async function create(didDoc: Uint8Array | undefined, packageId: string): Promise { - const tx = new Transaction(); - const didDocArg = tx.pure(bcs.option(bcs.vector(bcs.U8)).serialize(didDoc)); - const clock = getClockRef(tx); - - tx.moveCall({ - target: `${packageId}::identity::new`, - arguments: [didDocArg, clock], - }); - - return tx.build({ onlyTransactionKind: true }); -} - -export async function newWithControllers( - didDoc: Uint8Array | undefined, - controllers: [string, number][], - threshold: number, - packageId: string, -): Promise { - const tx = new Transaction(); - const ids = tx.pure.vector("address", controllers.map(controller => controller[0])); - const vps = tx.pure.vector("u64", controllers.map(controller => controller[1])); - const controllersArg = tx.moveCall({ - target: `${packageId}::utils::vec_map_from_keys_values`, - typeArguments: ["address", "u64"], - arguments: [ids, vps], - }); - const controllersThatCanDelegate = tx.moveCall({ - target: "0x2::vec_map::empty", - typeArguments: ["address", "u64"], - arguments: [], - }); - const didDocArg = tx.pure(bcs.option(bcs.vector(bcs.U8)).serialize(didDoc)); - const clock = getClockRef(tx); - const thresholdArg = tx.pure.u64(threshold); - - tx.moveCall({ - target: `${packageId}::identity::new_with_controllers`, - arguments: [didDocArg, controllersArg, controllersThatCanDelegate, thresholdArg, clock], - }); - - const tx_kind_bcs = await tx.build({ onlyTransactionKind: true }); - try { - const tx_kind = bcs.TransactionKind.parse(tx_kind_bcs); - } catch (e) { - console.error(`failed to deserialize tx kind: ${e}`); - } finally { - return tx_kind_bcs; - } -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/index.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/index.ts deleted file mode 100644 index 8d50ae018..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -export * from "./borrow_asset"; -export * from "./config"; -export * from "./controller_execution"; -export * from "./create"; -export * from "./proposal"; -export * from "./send_asset"; -export * from "./update"; -export * from "./upgrade"; diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts deleted file mode 100644 index 6a683ced3..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; -import { getControllerDelegation, putBackDelegationToken } from "../utils"; - -export function approve( - identity: SharedObjectRef, - capability: ObjectRef, - proposalId: string, - proposalType: string, - packageId: string, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const identityArg = tx.sharedObjectRef(identity); - const proposal = tx.pure.id(proposalId); - - tx.moveCall({ - target: `${packageId}::identity::approve_proposal`, - typeArguments: [proposalType], - arguments: [identityArg, delegationToken, proposal], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - return tx.build(); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts deleted file mode 100644 index 2688a0d4f..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; -import { getControllerDelegation, putBackDelegationToken } from "../utils"; - -export function proposeSend( - identity: SharedObjectRef, - capability: ObjectRef, - transferMap: [string, string][], - packageId: string, - expiration?: number, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const identityArg = tx.sharedObjectRef(identity); - const exp = tx.pure.option("u64", expiration); - const objects = tx.pure.vector("id", transferMap.map(t => t[0])); - const recipients = tx.pure.vector("address", transferMap.map(t => t[1])); - - tx.moveCall({ - target: `${packageId}::identity::propose_send`, - arguments: [identityArg, delegationToken, exp, objects, recipients], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - return tx.build(); -} - -export function executeSend( - identity: SharedObjectRef, - capability: ObjectRef, - proposalId: string, - objects: [ObjectRef, string][], - packageId: string, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const proposal = tx.pure.id(proposalId); - const identityArg = tx.sharedObjectRef(identity); - - let action = tx.moveCall({ - target: `${packageId}::identity::execute_proposal`, - typeArguments: [`${packageId}::transfer_proposal::Send`], - arguments: [identityArg, delegationToken, proposal], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - for (const [obj, objType] of objects) { - const recv_obj = tx.receivingRef(obj); - tx.moveCall({ - target: `${packageId}::identity::execute_send`, - typeArguments: [objType], - arguments: [identityArg, action, recv_obj], - }); - } - - tx.moveCall({ - target: `${packageId}::transfer_proposal::complete_send`, - arguments: [action], - }); - - return tx.build(); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts deleted file mode 100644 index b503fe512..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { bcs } from "@iota/iota-sdk/bcs"; -import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; -import { getClockRef, getControllerDelegation, putBackDelegationToken } from "../utils"; - -export function proposeUpdate( - identity: SharedObjectRef, - capability: ObjectRef, - didDoc: Uint8Array | undefined, - packageId: string, - expiration?: number, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const identityArg = tx.sharedObjectRef(identity); - const exp = tx.pure.option("u64", expiration); - const doc = tx.pure(bcs.option(bcs.vector(bcs.U8)).serialize(didDoc)); - const clock = getClockRef(tx); - - tx.moveCall({ - target: `${packageId}::identity::propose_update`, - arguments: [identityArg, delegationToken, doc, exp, clock], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - return tx.build({ onlyTransactionKind: true }); -} - -export function executeUpdate( - identity: SharedObjectRef, - capability: ObjectRef, - proposalId: string, - packageId: string, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); - const proposal = tx.pure.id(proposalId); - const identityArg = tx.sharedObjectRef(identity); - const clock = getClockRef(tx); - - tx.moveCall({ - target: `${packageId}::identity::execute_update`, - arguments: [identityArg, delegationToken, proposal, clock], - }); - - putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); - - return tx.build({ onlyTransactionKind: true }); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts deleted file mode 100644 index cc8622bfe..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; - -export function proposeUpgrade( - identity: SharedObjectRef, - capability: ObjectRef, - packageId: string, - expiration?: number, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const identityArg = tx.sharedObjectRef(identity); - const exp = tx.pure.option("u64", expiration); - - tx.moveCall({ - target: `${packageId}::identity::propose_upgrade`, - arguments: [identityArg, cap, exp], - }); - - return tx.build(); -} - -export function executeUpgrade( - identity: SharedObjectRef, - capability: ObjectRef, - proposalId: string, - packageId: string, -): Promise { - const tx = new Transaction(); - const cap = tx.objectRef(capability); - const proposal = tx.pure.id(proposalId); - const identityArg = tx.sharedObjectRef(identity); - - tx.moveCall({ - target: `${packageId}::identity::execute_upgrade`, - arguments: [identityArg, cap, proposal], - }); - - return tx.build(); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/index.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/index.ts deleted file mode 100644 index 7c8b6e125..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -export * as asset from "./asset"; -export * as identity from "./identity"; -export * from "./migration"; diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts deleted file mode 100644 index e29cd620f..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; -import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; -import { getClockRef } from "./utils"; - -export function migrateDidOutput( - didOutput: ObjectRef, - migrationRegistry: SharedObjectRef, - packageId: string, - creationTimestamp?: number, -): Promise { - const tx = new Transaction(); - const did_output = tx.objectRef(didOutput); - const migration_registry = tx.sharedObjectRef(migrationRegistry); - const clock = getClockRef(tx); - let timestamp; - if (creationTimestamp) { - timestamp = tx.pure.u64(creationTimestamp); - } else { - timestamp = tx.moveCall({ - target: "0x2::clock::timestamp_ms", - arguments: [clock], - }); - } - - tx.moveCall({ - target: `${packageId}::migration::migrate_alias_output`, - arguments: [did_output, migration_registry, timestamp, clock], - }); - - return tx.build(); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts deleted file mode 100644 index 80c49b462..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota-sdk/transactions"; -import { IOTA_CLOCK_OBJECT_ID } from "@iota/iota-sdk/utils"; - -const PLACEHOLDER_SENDER = "0x00000000000000090807060504030201"; -const PLACEHOLDER_GAS_BUDGET = 9; -const PLACEHOLDER_GAS_PRICE = 8; -const PLACEHOLDER_GAS_PAYMENT: ObjectRef[] = []; - -export function getClockRef(tx: Transaction): TransactionArgument { - return tx.sharedObjectRef({ objectId: IOTA_CLOCK_OBJECT_ID, initialSharedVersion: 1, mutable: false }); -} - -export function getControllerDelegation( - tx: Transaction, - controllerCap: TransactionArgument, - packageId: string, -): [TransactionArgument, TransactionArgument] { - const [token, borrow] = tx.moveCall({ - target: `${packageId}::controller::borrow`, - arguments: [controllerCap], - }); - return [token, borrow]; -} - -export function putBackDelegationToken( - tx: Transaction, - controllerCap: TransactionArgument, - delegationToken: TransactionArgument, - borrow: TransactionArgument, - packageId: string, -) { - tx.moveCall({ - target: `${packageId}::controller::put_back`, - arguments: [controllerCap, delegationToken, borrow], - }); -} - -/** - * Inserts placeholders related to sender and payment into transaction. - * - * This is required if wanting to call `tx.build`, as this will check if these values have been set. - * - * @param tx transaction to update - */ -export function insertPlaceholders(tx: Transaction) { - tx.setGasPrice(PLACEHOLDER_GAS_PRICE); - tx.setGasBudget(PLACEHOLDER_GAS_BUDGET); - tx.setGasPayment([...PLACEHOLDER_GAS_PAYMENT]); // make sure, we're not sharing the array between tx - tx.setSender(PLACEHOLDER_SENDER); -} diff --git a/bindings/wasm/iota_interaction_ts/lib/tsconfig.json b/bindings/wasm/iota_interaction_ts/lib/tsconfig.json deleted file mode 100644 index 9bee396af..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../tsconfig.node.json", - "compilerOptions": { - "baseUrl": "./", - "paths": { - "../lib": [ - "." - ], - "~iota_interaction_ts": [ - "../node/iota_interaction_ts", - "./iota_interaction_ts.js" - ] - }, - "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 deleted file mode 100644 index b1937c9cb..000000000 --- a/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "target": "ES2020", - "baseUrl": "./", - "paths": { - "../lib": [ - "." - ], - "~iota_interaction_ts": [ - "../web/iota_interaction_ts", - "./iota_interaction_ts.js" - ] - }, - "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 deleted file mode 100644 index ff0c6f9bd..000000000 --- a/bindings/wasm/iota_interaction_ts/package-lock.json +++ /dev/null @@ -1,1068 +0,0 @@ -{ - "name": "@iota/iota-interaction-ts", - "version": "0.3.3", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@iota/iota-interaction-ts", - "version": "0.3.3", - "license": "Apache-2.0", - "devDependencies": { - "@types/node": "^22.0.0", - "dprint": "^0.33.0", - "rimraf": "^6.0.1", - "tsconfig-paths": "^4.1.0", - "typescript": "^5.7.3", - "wasm-opt": "^1.4.0" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@iota/iota-sdk": "^0.6.0" - } - }, - "node_modules/@0no-co/graphql.web": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.13.tgz", - "integrity": "sha512-jqYxOevheVTU1S36ZdzAkJIdvRp2m3OYIG5SEoKDw5NI8eVwkoI0D/Q3DYNGmXCxkA6CQuoa7zvMiDPTLqUNuw==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - }, - "peerDependenciesMeta": { - "graphql": { - "optional": true - } - } - }, - "node_modules/@0no-co/graphqlsp": { - "version": "1.12.16", - "resolved": "https://registry.npmjs.org/@0no-co/graphqlsp/-/graphqlsp-1.12.16.tgz", - "integrity": "sha512-B5pyYVH93Etv7xjT6IfB7QtMBdaaC07yjbhN6v8H7KgFStMkPvi+oWYBTibMFRMY89qwc9H8YixXg8SXDVgYWw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@gql.tada/internal": "^1.0.0", - "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" - }, - "peerDependencies": { - "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", - "typescript": "^5.0.0" - } - }, - "node_modules/@gql.tada/cli-utils": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@gql.tada/cli-utils/-/cli-utils-1.6.3.tgz", - "integrity": "sha512-jFFSY8OxYeBxdKi58UzeMXG1tdm4FVjXa8WHIi66Gzu9JWtCE6mqom3a8xkmSw+mVaybFW5EN2WXf1WztJVNyQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@0no-co/graphqlsp": "^1.12.13", - "@gql.tada/internal": "1.0.8", - "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" - }, - "peerDependencies": { - "@0no-co/graphqlsp": "^1.12.13", - "@gql.tada/svelte-support": "1.0.1", - "@gql.tada/vue-support": "1.0.1", - "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "@gql.tada/svelte-support": { - "optional": true - }, - "@gql.tada/vue-support": { - "optional": true - } - } - }, - "node_modules/@gql.tada/internal": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@gql.tada/internal/-/internal-1.0.8.tgz", - "integrity": "sha512-XYdxJhtHC5WtZfdDqtKjcQ4d7R1s0d1rnlSs3OcBEUbYiPoJJfZU7tWsVXuv047Z6msvmr4ompJ7eLSK5Km57g==", - "license": "MIT", - "peer": true, - "dependencies": { - "@0no-co/graphql.web": "^1.0.5" - }, - "peerDependencies": { - "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", - "typescript": "^5.0.0" - } - }, - "node_modules/@graphql-typed-document-node/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", - "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, - "node_modules/@iota/bcs": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@iota/bcs/-/bcs-0.2.1.tgz", - "integrity": "sha512-T+iv5gZhUZP7BiDY7+Ir4MA2rYmyGNZA2b+nxjv219Fp8klFt+l38OWA+1RgJXrCmzuZ+M4hbMAeHhHziURX6Q==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "bs58": "^6.0.0" - } - }, - "node_modules/@iota/iota-sdk": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@iota/iota-sdk/-/iota-sdk-0.6.0.tgz", - "integrity": "sha512-NEYiE7bdWw2DA3vLV7dO2EnoLDljN9NPhYrjfDGefTbIS9XpqX0JZTHMi//Q/K0aO4NwSHR5gu7n/ywoOTzTKg==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@graphql-typed-document-node/core": "^3.2.0", - "@iota/bcs": "0.2.1", - "@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" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@iota/iota-sdk/node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "license": "Unlicense", - "peer": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@noble/curves": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", - "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@noble/hashes": "1.7.1" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", - "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/base": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz", - "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", - "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@noble/curves": "~1.8.1", - "@noble/hashes": "~1.7.1", - "@scure/base": "~1.2.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip39": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", - "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@noble/hashes": "~1.7.1", - "@scure/base": "~1.2.4" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@suchipi/femver": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@suchipi/femver/-/femver-1.0.0.tgz", - "integrity": "sha512-bprE8+K5V+DPX7q2e2K57ImqNBdfGHDIWaGI5xHxZoxbKOuQZn4wzPiUxOAHnsUr3w3xHrWXwN7gnG/iIuEMIg==", - "license": "MIT", - "peer": true - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "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 - }, - "node_modules/base-x": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", - "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==", - "license": "MIT", - "peer": true - }, - "node_modules/bech32": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", - "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==", - "license": "MIT", - "peer": true - }, - "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, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/bs58": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", - "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", - "license": "MIT", - "peer": true, - "dependencies": { - "base-x": "^5.0.0" - } - }, - "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/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "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, - "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 - }, - "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, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "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/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "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/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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, - "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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/gql.tada": { - "version": "1.8.10", - "resolved": "https://registry.npmjs.org/gql.tada/-/gql.tada-1.8.10.tgz", - "integrity": "sha512-FrvSxgz838FYVPgZHGOSgbpOjhR+yq44rCzww3oOPJYi0OvBJjAgCiP6LEokZIYND2fUTXzQAyLgcvgw1yNP5A==", - "license": "MIT", - "peer": true, - "dependencies": { - "@0no-co/graphql.web": "^1.0.5", - "@0no-co/graphqlsp": "^1.12.13", - "@gql.tada/cli-utils": "1.6.3", - "@gql.tada/internal": "1.0.8" - }, - "bin": { - "gql-tada": "bin/cli.js", - "gql.tada": "bin/cli.js" - }, - "peerDependencies": { - "typescript": "^5.0.0" - } - }, - "node_modules/graphql": { - "version": "16.10.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", - "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.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, - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jackspeak": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", - "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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/lru-cache": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", - "dev": true, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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, - "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, - "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, - "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, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "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==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "dev": true, - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "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/rimraf": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", - "dev": true, - "dependencies": { - "glob": "^11.0.0", - "package-json-from-dist": "^1.0.0" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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 - }, - "node_modules/string-width-cjs/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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/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, - "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/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, - "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/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, - "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/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/valibot": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.36.0.tgz", - "integrity": "sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ==", - "license": "MIT", - "peer": true - }, - "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, - "dependencies": { - "node-fetch": "^2.6.9", - "tar": "^6.1.13" - }, - "bin": { - "wasm-opt": "bin/wasm-opt.js" - } - }, - "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==", - "dev": true - }, - "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==", - "dev": true, - "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, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "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, - "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/wrap-ansi-cjs/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/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 - }, - "node_modules/wrap-ansi-cjs/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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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 - }, - "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" - } - } - } -} diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json deleted file mode 100644 index 3f4eadcb6..000000000 --- a/bindings/wasm/iota_interaction_ts/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "@iota/iota-interaction-ts", - "author": "IOTA Foundation ", - "description": "WASM bindings importing types from the IOTA Client typescript SDK to be used in Rust", - "homepage": "https://www.iota.org", - "version": "0.3.3", - "license": "Apache-2.0", - "repository": { - "type": "git", - "url": "git+https://github.com/iotaledger/identity.rs.git" - }, - "scripts": { - "build:src": "cargo build --lib --release --target wasm32-unknown-unknown --target-dir ../target", - "build:src:node": "cargo build --lib --release --target wasm32-unknown-unknown --features keytool --target-dir ../target", - "prebundle:nodejs": "rimraf node", - "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", - "prebundle:web": "rimraf web", - "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:node && 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": "npm run build:web && npm run build:nodejs", - "fmt": "dprint fmt" - }, - "bugs": { - "url": "https://github.com/iotaledger/identity.rs/issues" - }, - "publishConfig": { - "access": "public" - }, - "files": [ - "web/*", - "node/*" - ], - "devDependencies": { - "@types/node": "^22.0.0", - "dprint": "^0.33.0", - "rimraf": "^6.0.1", - "tsconfig-paths": "^4.1.0", - "typescript": "^5.7.3", - "wasm-opt": "^1.4.0" - }, - "peerDependencies": { - "@iota/iota-sdk": "^0.6.0" - }, - "engines": { - "node": ">=20" - } -} diff --git a/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs deleted file mode 100644 index be0b7a087..000000000 --- a/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_iota_interaction::types::execution_status::CommandArgumentError; -use js_sys::Uint8Array; -use serde::Serialize; -use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsValue; - -use crate::bindings::WasmObjectRef; -use crate::bindings::WasmSharedObjectRef; -use crate::error::TsSdkError; -use crate::error::WasmError; -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; - -#[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls/asset")] -extern "C" { - #[wasm_bindgen(js_name = "create", catch)] - pub(crate) async fn new_asset( - inner_bytes: &[u8], - inner_type: &str, - mutable: bool, - transferable: bool, - deletable: bool, - package: &str, - ) -> Result; - - #[wasm_bindgen(catch, js_name = "remove")] - pub(crate) async fn delete(asset: WasmObjectRef, asset_type: &str, package: &str) -> Result; - - #[wasm_bindgen(catch)] - pub(crate) async fn update( - asset: WasmObjectRef, - content: &[u8], - content_type: &str, - package: &str, - ) -> Result; - - #[wasm_bindgen(catch)] - pub(crate) async fn transfer( - asset: WasmObjectRef, - asset_type: &str, - recipient: &str, - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "acceptProposal", catch)] - pub(crate) async fn accept_proposal( - proposal: WasmSharedObjectRef, - recipient_cap: WasmObjectRef, - asset: WasmObjectRef, - asset_type: &str, - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "concludeOrCancel", catch)] - pub(crate) async fn conclude_or_cancel( - proposal: WasmSharedObjectRef, - sender_cap: WasmObjectRef, - asset: WasmObjectRef, - asset_type: &str, - package: &str, - ) -> Result; -} - -pub struct AssetMoveCallsTsSdk {} - -impl AssetMoveCalls for AssetMoveCallsTsSdk { - type Error = TsSdkError; - - fn new_asset( - inner: &T, - mutable: bool, - transferable: bool, - deletable: bool, - package: ObjectID, - ) -> Result { - let inner_bytes = bcs::to_bytes(inner).map_err(|_| CommandArgumentError::InvalidBCSBytes)?; - let inner_type = T::move_type(package).to_string(); - let package = package.to_string(); - - futures::executor::block_on(new_asset( - &inner_bytes, - &inner_type, - mutable, - transferable, - deletable, - &package, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn delete(asset: ObjectRef, package: ObjectID) -> Result { - let asset = asset.into(); - let asset_type = T::move_type(package).to_string(); - let package = package.to_string(); - - futures::executor::block_on(delete(asset, &asset_type, &package)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn transfer( - asset: ObjectRef, - recipient: IotaAddress, - package: ObjectID, - ) -> Result { - let asset = asset.into(); - let asset_type = T::move_type(package).to_string(); - let recipient = recipient.to_string(); - let package = package.to_string(); - - futures::executor::block_on(transfer(asset, &asset_type, &recipient, &package)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - 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: TypeTag, - package: ObjectID, - ) -> Result { - let proposal = (proposal.0, proposal.1, true).into(); - let asset = asset.into(); - let asset_type = asset_type.to_canonical_string(true); - let recipient = recipient_cap.into(); - let package = package.to_string(); - - futures::executor::block_on(accept_proposal(proposal, recipient, asset, &asset_type, &package)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn conclude_or_cancel( - proposal: (ObjectID, SequenceNumber), - sender_cap: ObjectRef, - asset: ObjectRef, - asset_type: TypeTag, - package: ObjectID, - ) -> Result { - let proposal = (proposal.0, proposal.1, true).into(); - let asset = asset.into(); - let asset_type = asset_type.to_canonical_string(true); - let sender = sender_cap.into(); - let package = package.to_string(); - - futures::executor::block_on(conclude_or_cancel(proposal, sender, asset, &asset_type, &package)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn update( - asset: ObjectRef, - new_content: &T, - package: ObjectID, - ) -> Result { - let asset = asset.into(); - let content_type = T::move_type(package).to_string(); - let content = bcs::to_bytes(new_content).map_err(|_| CommandArgumentError::InvalidBCSBytes)?; - let package = package.to_string(); - - futures::executor::block_on(update(asset, &content, &content_type, &package)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } -} diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/mod.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool/mod.rs deleted file mode 100644 index 4bc1a321a..000000000 --- a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod signer; -mod storage; - -pub use signer::*; -pub use storage::*; diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/signer.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool/signer.rs deleted file mode 100644 index a5d77cc28..000000000 --- a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/signer.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::str::FromStr; - -use crate::error::Result; -use crate::error::WasmResult; -use fastcrypto::traits::EncodeDecodeBase64 as _; -use identity_iota_interaction::types::base_types::IotaAddress; -use identity_iota_interaction::KeytoolSigner; -use identity_iota_interaction::KeytoolSignerBuilder; -use secret_storage::Signer; -use serde_json::Value; -use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsError; - -use crate::WasmPublicKey; - -#[wasm_bindgen(module = buffer)] -extern "C" { - #[wasm_bindgen(typescript_type = Buffer)] - type NodeBuffer; - #[wasm_bindgen(method, js_name = toString)] - fn to_string(this: &NodeBuffer) -> String; -} - -#[wasm_bindgen(module = child_process)] -extern "C" { - #[wasm_bindgen(js_name = execSync, catch)] - fn exec_cli_cmd(cmd: &str) -> Result; -} - -/// An implementation of the Signer interface that relies on -/// IOTA Keytool. -#[wasm_bindgen(js_name = KeytoolSigner)] -pub struct WasmKeytoolSigner(pub(crate) KeytoolSigner); - -#[wasm_bindgen(js_class = KeytoolSigner)] -impl WasmKeytoolSigner { - /// Returns a new {@link KeytoolSigner}. The optional parameters respectively - /// allow to set the signing address and the `iota` binary to use. Defaults - /// to use the current active address and the binary found in $PATH. - #[wasm_bindgen(constructor)] - pub fn new(address: Option, iota_bin_location: Option) -> Result { - let address = address - .as_deref() - .map(IotaAddress::from_str) - .transpose() - .wasm_result()?; - - let builder = address - .map(|address| KeytoolSignerBuilder::new().with_address(address)) - .unwrap_or_default(); - let builder = if let Some(iota_bin_location) = iota_bin_location { - builder.iota_bin_location(iota_bin_location) - } else { - builder - }; - - Ok(WasmKeytoolSigner(builder.build().wasm_result()?)) - } - - /// Returns the signing address. - #[wasm_bindgen] - pub fn address(&self) -> String { - self.0.address().to_string() - } - - // These method definition are needed to make sure `KeytoolSigner` - // implements `Signer` interface. - - #[wasm_bindgen(js_name = keyId)] - pub fn key_id(&self) -> String { - self.address() - } - - #[wasm_bindgen(js_name = publicKey)] - pub async fn public_key(&self) -> Result { - self.0.public_key().try_into() - } - - #[wasm_bindgen] - pub async fn sign(&self, tx_data_bcs: &[u8]) -> Result { - let tx_data = bcs::from_bytes(tx_data_bcs).map_err(|e| JsError::new(&e.to_string()))?; - self - .0 - .sign(&tx_data) - .await - .map(|sig| sig.encode_base64()) - .map_err(|e| JsError::new(&e.to_string()).into()) - } - - #[wasm_bindgen(js_name = iotaPublicKeyBytes)] - pub async fn iota_public_key_bytes(&self) -> Vec { - let pk = self.0.public_key(); - let mut bytes = vec![pk.flag()]; - bytes.extend_from_slice(pk.as_ref()); - - bytes - } -} - -// This is used in KeytoolSigner implementation to issue CLI commands. -#[no_mangle] -pub extern "Rust" fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result { - let output = exec_cli_cmd(cmd) - .map_err(|e| anyhow::anyhow!("exec failed: {e:?}"))? - .to_string(); - serde_json::from_str(&output).map_err(|_| anyhow::anyhow!("failed to deserialize JSON object from command output")) -} diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/storage.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool/storage.rs deleted file mode 100644 index a78951a46..000000000 --- a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/storage.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::str::FromStr; - -use identity_iota_interaction::types::base_types::IotaAddress; -use identity_iota_interaction::types::crypto::IotaKeyPair; -use identity_iota_interaction::types::crypto::SignatureScheme; -use identity_iota_interaction::KeytoolStorage; -use js_sys::Array; -use js_sys::JsString; -use wasm_bindgen::prelude::*; - -use crate::error::Result; -use crate::error::WasmResult; -use crate::WasmPublicKey; - -use super::signer::WasmKeytoolSigner; - -const __TS_IMPORTS: &str = r#" -import { PublicKey } from "@iota/iota-sdk/cryptography"; -"#; - -fn make_pk_alias_tuple(pk: &WasmPublicKey, alias: &str) -> Array { - let arr = Array::new(); - arr.push(pk.as_ref()); - arr.push(JsString::from(alias).as_ref()); - - arr -} - -/// IOTA Keytool CLI wrapper. -#[derive(Default, Clone)] -#[wasm_bindgen(js_name = KeytoolStorage)] -pub struct WasmKeytoolStorage(pub(crate) KeytoolStorage); - -impl AsRef for WasmKeytoolStorage { - fn as_ref(&self) -> &KeytoolStorage { - &self.0 - } -} - -#[wasm_bindgen(js_class = KeytoolStorage)] -impl WasmKeytoolStorage { - /// Creates a new {@link KeytoolStorage} that wraps the given iota binary. - /// Attempts to use the one in PATH if none is provided. - #[wasm_bindgen(constructor)] - pub fn new(iota_bin: Option) -> Self { - iota_bin - .as_deref() - .map(KeytoolStorage::new_with_custom_bin) - .map(Self) - .unwrap_or_default() - } - - /// Returns a {@link KeytoolSigner} that will use the provided `address` - /// to sign transactions. If no address is provided the current active - /// one will be used. - pub fn signer(&self, address: Option) -> Result { - let address = address.map(|s| IotaAddress::from_str(&s)).transpose().wasm_result()?; - let mut signer_builder = self.0.signer(); - if let Some(address) = address { - signer_builder = signer_builder.with_address(address); - } - - signer_builder.build().map(WasmKeytoolSigner).wasm_result() - } - - /// Creates a new key of type `key_scheme`. - /// Returns the tuple ([`PublicKey`](https://docs.iota.org/ts-sdk/api/cryptography/classes/PublicKey), alias). - #[wasm_bindgen( - js_name = generateKey, - unchecked_return_type = "[PublicKey, string]", - )] - pub fn generate_key( - &self, - #[wasm_bindgen(unchecked_param_type = "'ed25519' | 'secp256r1' | 'secp256k1'")] key_scheme: &str, - ) -> Result { - let key_scheme = match key_scheme { - "ed25519" => SignatureScheme::ED25519, - "secp256r1" => SignatureScheme::Secp256r1, - "secp256k1" => SignatureScheme::Secp256k1, - _ => return Err(JsError::new("invalid key type").into()), - }; - - self - .0 - .generate_key(key_scheme) - .wasm_result() - .map(|(pk, alias)| make_pk_alias_tuple(&WasmPublicKey::try_from(&pk).unwrap(), &alias)) - } - - /// Inserts a Bech32-encoded private key in the keystore. - /// The key must use the prefix `iotaprivkey`. - /// - /// Returns the key's alias. - #[wasm_bindgen(js_name = insertKey)] - pub fn insert_key(&self, bech32_secret_key: &str) -> Result { - let key = IotaKeyPair::decode(bech32_secret_key) - .map_err(|e| anyhow::anyhow!("{e:?}")) - .wasm_result()?; - self.0.insert_key(key).wasm_result() - } - - /// Signs `data` with `address`'s secret key. - #[wasm_bindgen(js_name = signRaw)] - pub fn sign_raw(&self, address: &str, data: &[u8]) -> Result> { - let address = address.parse().wasm_result()?; - self.0.sign_raw(address, data).wasm_result() - } - - /// Updates an alias from `old_alias` to `new_alias` - /// If no value for `new_alias` is provided, a randomly generated one will be used. - #[wasm_bindgen(js_name = updateAlias)] - pub fn update_alias(&self, old_alias: &str, new_alias: Option) -> Result<()> { - let new_alias = new_alias.as_deref(); - self.0.update_alias(old_alias, new_alias).wasm_result() - } - - /// Returns the [`PublicKey`](https://docs.iota.org/ts-sdk/api/cryptography/classes/PublicKey) for the given address together with its alias. - #[wasm_bindgen( - js_name = getKey, - unchecked_return_type = "[PublicKey, string]", - )] - pub fn get_key(&self, address: &str) -> Result { - let address = address.parse().wasm_result()?; - self - .0 - .get_key(address) - .wasm_result()? - .map(|(pk, alias)| make_pk_alias_tuple(&WasmPublicKey::try_from(&pk).unwrap(), &alias)) - .ok_or_else(|| anyhow::anyhow!("the requested address is not in the keystore")) - .wasm_result() - } - - /// Returns the [`PublicKey`](https://docs.iota.org/ts-sdk/api/cryptography/classes/PublicKey) that has the given alias. - #[wasm_bindgen(js_name = getKeyByAlias)] - pub fn get_key_by_alias(&self, alias: &str) -> Result { - self - .0 - .get_key_by_alias(alias) - .wasm_result()? - .map(|pk| WasmPublicKey::try_from(&pk).unwrap()) - .ok_or_else(|| anyhow::anyhow!("the requested alias is not in the keystore")) - .wasm_result() - } -} diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs b/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs deleted file mode 100644 index 095591007..000000000 --- a/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(feature = "keytool")] -pub mod keytool; -mod types; -mod wasm_iota_client; -mod wasm_types; - -pub use types::*; -pub use wasm_iota_client::*; diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/types.rs deleted file mode 100644 index fe0645f61..000000000 --- a/bindings/wasm/iota_interaction_ts/src/bindings/types.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use wasm_bindgen::prelude::*; - -pub use super::wasm_types::*; - -#[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 deleted file mode 100644 index 0bf674cfe..000000000 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs +++ /dev/null @@ -1,437 +0,0 @@ -// 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::WaitForTransactionParams; -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::crypto::Signature; -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::TransactionData; -use js_sys::Promise; -use serde::Serialize; -use wasm_bindgen::prelude::*; -use wasm_bindgen_futures::JsFuture; - -use super::wasm_types::PromiseIotaTransactionBlockResponse; -use super::wasm_types::WasmExecuteTransactionBlockParams; -use super::wasm_types::WasmIotaTransactionBlockResponseWrapper; -use super::PromiseDryRunTransactionBlockResponse; -use super::WasmDryRunTransactionBlockParams; -use super::WasmWaitForTransactionParams; - -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 used as arguments for rust functions via the typescript_type annotation. -// In other words: The typescript_type "IotaClient" is imported here to be bound -// to the WasmIotaClient functions below. -// TODO: check why this isn't done by `module` macro attribute for `WasmIotaClient` -#[wasm_bindgen(typescript_custom_section)] -const IOTA_CLIENT_TYPE: &'static str = r#" - import { IotaClient } from "@iota/iota-sdk/client"; -"#; - -#[wasm_bindgen(module = "@iota/iota-sdk/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 = dryRunTransactionBlock)] - pub fn dry_run_transaction_block( - this: &WasmIotaClient, - params: &WasmDryRunTransactionBlockParams, - ) -> PromiseDryRunTransactionBlockResponse; - - #[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; - - #[wasm_bindgen(method, js_name = waitForTransaction)] - pub fn wait_for_transaction( - this: &WasmIotaClient, - input: &WasmWaitForTransactionParams, - ) -> PromiseIotaTransactionBlockResponse; -} - -// Helper struct used to convert TYPESCRIPT types to RUST types -#[derive(Clone)] -pub struct ManagedWasmIotaClient(pub(crate) 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: TransactionData, - signatures: Vec, - options: Option, - request_type: Option, - ) -> IotaRpcResult { - let params = ExecuteTransactionBlockParams::new(tx_data, signatures, options, request_type); - let wasm_params = params - .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .map_err(|e| { - IotaRpcError::FfiError(format!( - "failed to convert ExecuteTransactionBlockParams to JS value: {e}" - )) - })? - .into(); - - let promise: Promise = Promise::resolve(&WasmIotaClient::execute_transaction_block(&self.0, &wasm_params)); - let result: JsValue = JsFuture::from(promise).await.map_err(|e| { - console_log!("Error executing JsFuture::from(promise): {:?}", e); - IotaRpcError::FfiError(format!("{:?}", e)) - })?; - Ok(WasmIotaTransactionBlockResponseWrapper::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)) - })?; - - #[allow(deprecated)] // will be refactored - 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)) - })?; - - #[allow(deprecated)] // will be refactored - 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)) - })?; - - #[allow(deprecated)] // will be refactored - 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(WasmIotaTransactionBlockResponseWrapper::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)) - })?; - - serde_wasm_bindgen::from_value(result) - .map_err(|e| IotaRpcError::FfiError(format!("failed to deserialize gas price from JS value: {e}"))) - } - - 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)) - })?; - - #[allow(deprecated)] // will be refactored - 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)) - })?; - - #[allow(deprecated)] // will be refactored - Ok(result.into_serde()?) - } - - /// Wait for a transaction block result to be available over the API. - /// This can be used in conjunction with `execute_transaction_block` to wait for the transaction to - /// be available via the API. - /// This currently polls the `getTransactionBlock` API to check for the transaction. - /// - /// # Arguments - /// - /// * `digest` - The digest of the queried transaction. - /// * `options` - Options for specifying the content to be returned. - /// * `timeout` - The amount of time to wait for a transaction block. Defaults to one minute. - /// * `poll_interval` - The amount of time to wait between checks for the transaction block. Defaults to 2 seconds. - pub async fn wait_for_transaction( - &self, - digest: TransactionDigest, - options: Option, - timeout: Option, - poll_interval: Option, - ) -> IotaRpcResult { - let params_object = WaitForTransactionParams::new(digest.to_string(), options, timeout, poll_interval); - let params: WasmWaitForTransactionParams = serde_json::to_value(¶ms_object) - .map_err(|e| { - console_log!("Error serializing WaitForTransactionParams to Value: {:?}", e); - IotaRpcError::FfiError(format!("{:?}", e)) - }) - .and_then(|v| { - v.serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .map_err(|e| { - console_log!("Error serializing Value to WasmWaitForTransactionParams: {:?}", e); - IotaRpcError::FfiError(format!("{:?}", e)) - }) - })? - .into(); - - let promise: Promise = Promise::resolve(&WasmIotaClient::wait_for_transaction(&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(WasmIotaTransactionBlockResponseWrapper::new(result.into())) - } -} diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs deleted file mode 100644 index 7e7abda81..000000000 --- a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 -use std::str::FromStr; - -use fastcrypto::encoding::Base64; -use fastcrypto::encoding::Encoding; -use fastcrypto::traits::EncodeDecodeBase64 as _; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -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::crypto::PublicKey; -use identity_iota_interaction::types::crypto::Signature; -use identity_iota_interaction::types::digests::TransactionDigest; -use identity_iota_interaction::types::execution_status::CommandArgumentError; -use identity_iota_interaction::types::object::Owner; -use identity_iota_interaction::types::transaction::TransactionData; -use identity_iota_interaction::ProgrammableTransactionBcs; -use js_sys::Promise; -use js_sys::Uint8Array; -use serde::Deserialize; -use serde::Serialize; -use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsCast; -use wasm_bindgen::JsError; -use wasm_bindgen::JsValue; -use wasm_bindgen_futures::JsFuture; - -use crate::bindings::WasmIotaClient; -use crate::console_log; -use crate::error::TsSdkError; -use crate::error::WasmError; - -// TODO: fix/add signer or remove functions relying on it -type WasmStorageSigner = (); - -#[wasm_bindgen(typescript_custom_section)] -const TS_SDK_TYPES: &str = r#" - import { - Balance, - ExecuteTransactionBlockParams, - GetCoinsParams, - GetDynamicFieldObjectParams, - GetObjectParams, - GetOwnedObjectsParams, - GetTransactionBlockParams, - IotaClient, - IotaObjectData, - IotaObjectResponse, - IotaTransactionBlockResponse, - IotaTransactionBlockResponseOptions, - ObjectRead, - PaginatedCoins, - PaginatedEvents, - PaginatedObjectsResponse, - QueryEventsParams, - TryGetPastObjectParams, - } from "@iota/iota-sdk/client"; - import { bcs } from "@iota/iota-sdk/bcs"; - import { - executeTransaction, - WasmIotaTransactionBlockResponseWrapper, - } from "./iota_client_helpers" -"#; - -#[wasm_bindgen(module = "@iota/iota-sdk/client")] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseBalance; - - #[wasm_bindgen(typescript_type = "TransactionArgument")] - pub type WasmTransactionArgument; - - #[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 = "Promise")] - pub type PromiseDryRunTransactionBlockResponse; - - #[wasm_bindgen(typescript_type = "DryRunTransactionBlockResponse")] - #[derive(Clone)] - pub type WasmDryRunTransactionBlockResponse; - - #[wasm_bindgen(typescript_type = "DryRunTransactionBlockParams")] - #[derive(Clone)] - pub type WasmDryRunTransactionBlockParams; - - #[derive(Clone)] - #[wasm_bindgen( - typescript_type = "TransactionEffects", - extends = js_sys::Object, - )] - pub type WasmIotaTransactionBlockEffects; - - #[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 = "IotaObjectRef")] - #[derive(Clone)] - pub type WasmObjectRef; - - #[wasm_bindgen(method, getter, js_name = objectId)] - pub fn object_id(this: &WasmObjectRef) -> String; - - #[wasm_bindgen(method, getter, js_name = digest)] - pub fn digest(this: &WasmObjectRef) -> String; - - #[wasm_bindgen(method, getter, js_name = version)] - pub fn version(this: &WasmObjectRef) -> String; - - #[wasm_bindgen(typescript_type = "SharedObjectRef")] - #[derive(Clone)] - pub type WasmSharedObjectRef; - - #[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 PromiseIotaTransactionBlockResponseWrapper; - - #[wasm_bindgen(typescript_type = "Signature")] - pub type WasmIotaSignature; - - #[wasm_bindgen(typescript_type = "Parameters")] - #[derive(Clone, Debug)] - pub type WasmWaitForTransactionParams; -} - -impl From for IotaTransactionBlockEffects { - fn from(value: WasmIotaTransactionBlockEffects) -> Self { - serde_wasm_bindgen::from_value(value.into()).expect("have the same repr") - } -} - -impl From<&'_ IotaTransactionBlockEffects> for WasmIotaTransactionBlockEffects { - fn from(value: &'_ IotaTransactionBlockEffects) -> Self { - value - .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .expect("same representation") - .unchecked_into() - } -} - -#[derive(Serialize, Deserialize)] -enum IotaSignatureHelper { - Ed25519IotaSignature(String), - Secp256k1IotaSignature(String), - Secp256r1IotaSignature(String), -} - -impl TryFrom for WasmIotaSignature { - type Error = JsValue; - fn try_from(sig: Signature) -> Result { - let base64sig = Base64::encode(&sig); - let json_signature = match sig { - Signature::Ed25519IotaSignature(_) => IotaSignatureHelper::Ed25519IotaSignature(base64sig), - Signature::Secp256r1IotaSignature(_) => IotaSignatureHelper::Secp256r1IotaSignature(base64sig), - Signature::Secp256k1IotaSignature(_) => IotaSignatureHelper::Secp256k1IotaSignature(base64sig), - }; - - json_signature - .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .map(JsCast::unchecked_into) - .map_err(|e| e.into()) - } -} - -impl TryFrom for Signature { - type Error = JsValue; - fn try_from(sig: WasmIotaSignature) -> Result { - let sig_helper = serde_wasm_bindgen::from_value(sig.into())?; - let base64sig = match sig_helper { - IotaSignatureHelper::Ed25519IotaSignature(s) => s, - IotaSignatureHelper::Secp256k1IotaSignature(s) => s, - IotaSignatureHelper::Secp256r1IotaSignature(s) => s, - }; - - base64sig - .parse() - .map_err(|e: eyre::Report| JsError::new(&e.to_string()).into()) - } -} - -#[wasm_bindgen(module = "@iota/iota-sdk/transactions")] -extern "C" { - #[derive(Clone)] - #[wasm_bindgen(typescript_type = "Transaction")] - pub type WasmTransaction; - - #[wasm_bindgen(js_name = "from", js_class = "Transaction", static_method_of = WasmTransaction, catch)] - pub fn from_bcs_bytes(bytes: Uint8Array) -> Result; - - #[wasm_bindgen(method, structural, catch)] - pub async fn build( - this: &WasmTransaction, - options: Option, - ) -> Result; - - #[wasm_bindgen(typescript_type = BuildTransactionOptions)] - pub type WasmBuildTransactionOptions; - - #[derive(Clone)] - #[wasm_bindgen(typescript_type = "TransactionData")] - pub type WasmTransactionData; - - #[wasm_bindgen(typescript_type = "Transaction")] - pub type WasmTransactionBuilder; - - #[wasm_bindgen(js_name = "from", js_class = "Transaction", static_method_of = WasmTransactionBuilder, catch)] - pub fn from_bcs_bytes(bytes: Uint8Array) -> Result; - - #[wasm_bindgen(method, structural, catch)] - pub async fn build(this: &WasmTransactionBuilder) -> Result; - - #[derive(Clone)] - #[wasm_bindgen(typescript_type = TransactionDataBuilder)] - pub type WasmTransactionDataBuilder; - - #[wasm_bindgen( - js_name = fromBytes, - js_class = TransactionDataBuilder, - static_method_of = WasmTransactionDataBuilder, - catch - )] - pub fn from_bcs_bytes(bytes: Uint8Array) -> Result; - - #[wasm_bindgen( - js_name = fromKindBytes, - js_class = TransactionDataBuilder, - static_method_of = WasmTransactionDataBuilder, - catch - )] - pub fn from_kind_bcs_bytes(bytes: Uint8Array) -> Result; - - #[wasm_bindgen(method, catch)] - pub fn build(this: &WasmTransactionDataBuilder) -> Result; - // TODO: decide if we need the following functions: "yagni" or not? - - // #[wasm_bindgen(js_name = "setSender", method, catch)] - // pub fn set_sender(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; - - // #[wasm_bindgen(js_name = "setGasOwner", method, catch)] - // pub fn set_gas_owner(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; - - // #[wasm_bindgen(js_name = "setGasPrice", method, catch)] - // pub fn set_gas_price(this: &WasmTransactionBuilder, price: u64) -> Result<(), JsValue>; - - // #[wasm_bindgen(js_name = "setGasPayment", method, catch)] - // pub fn set_gas_payment(this: &WasmTransactionBuilder, payments: Vec) -> Result<(), JsValue>; - - // #[wasm_bindgen(js_name = "setGasBudget", method, catch)] - // pub fn set_gas_budget(this: &WasmTransactionBuilder, budget: u64) -> Result<(), JsValue>; - - // #[wasm_bindgen(js_name = "getData", method, catch)] - // pub fn get_data(this: &WasmTransactionBuilder) -> Result; -} - -impl TryFrom for WasmTransactionData { - type Error = serde_wasm_bindgen::Error; - fn try_from(value: TransactionData) -> Result { - let js_value = serde_wasm_bindgen::to_value(&value)?; - Ok(js_value.unchecked_into()) - } -} - -impl TryFrom for TransactionData { - type Error = serde_wasm_bindgen::Error; - fn try_from(value: WasmTransactionData) -> Result { - serde_wasm_bindgen::from_value(value.into()) - } -} - -#[wasm_bindgen(module = "@iota/iota-sdk/cryptography")] -extern "C" { - #[derive(Clone)] - #[wasm_bindgen(typescript_type = PublicKey)] - pub type WasmPublicKey; - - #[wasm_bindgen(js_name = toIotaPublicKey, method)] - pub fn to_iota_public_key(this: &WasmPublicKey) -> String; - - #[wasm_bindgen(js_name = toRawBytes, method)] - pub fn to_raw_bytes(this: &WasmPublicKey) -> Vec; - - #[wasm_bindgen(js_name = toIotaAddress, method)] - pub fn to_iota_address(this: &WasmPublicKey) -> String; - - #[wasm_bindgen(method)] - pub fn flag(this: &WasmPublicKey) -> u8; -} - -#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/ed25519")] -extern "C" { - #[wasm_bindgen(extends = WasmPublicKey)] - pub type Ed25519PublicKey; - - #[wasm_bindgen(constructor, catch)] - pub fn new_ed25519_pk(bytes: &[u8]) -> Result; -} - -#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/secp256r1")] -extern "C" { - #[wasm_bindgen(extends = WasmPublicKey)] - pub type Secp256r1PublicKey; - - #[wasm_bindgen(constructor, catch)] - pub fn new_secp256r1_pk(bytes: &[u8]) -> Result; -} - -#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/secp256k1")] -extern "C" { - #[wasm_bindgen(extends = WasmPublicKey)] - pub type Secp256k1PublicKey; - - #[wasm_bindgen(constructor, catch)] - pub fn new_secp256k1_pk(bytes: &[u8]) -> Result; -} - -impl TryFrom<&'_ PublicKey> for WasmPublicKey { - type Error = JsValue; - fn try_from(pk: &PublicKey) -> Result { - let pk_bytes = pk.as_ref(); - let wasm_pk: WasmPublicKey = match pk { - PublicKey::Ed25519(_) => Ed25519PublicKey::new_ed25519_pk(pk_bytes)?.into(), - PublicKey::Secp256r1(_) => Secp256r1PublicKey::new_secp256r1_pk(pk_bytes)?.into(), - PublicKey::Secp256k1(_) => Secp256k1PublicKey::new_secp256k1_pk(pk_bytes)?.into(), - _ => return Err(JsError::new("unsupported PublicKey type").into()), - }; - - assert_eq!(pk_bytes, &wasm_pk.to_raw_bytes()); - assert_eq!( - IotaAddress::from(pk), - wasm_pk.to_iota_address().parse().expect("valid iota address") - ); - - Ok(wasm_pk) - } -} - -impl TryFrom for PublicKey { - type Error = JsValue; - fn try_from(wasm_pk: WasmPublicKey) -> Result { - let pk = PublicKey::decode_base64(&wasm_pk.to_iota_public_key()) - .map_err(|_| JsError::new("failed to decode base64 JS PublicKey"))?; - - assert_eq!(&wasm_pk.to_raw_bytes(), pk.as_ref()); - assert_eq!( - IotaAddress::from(&pk), - wasm_pk.to_iota_address().parse().expect("valid iota address") - ); - - Ok(pk) - } -} - -impl TryFrom for ObjectRef { - type Error = anyhow::Error; - fn try_from(value: WasmObjectRef) -> Result { - let digest = serde_json::from_value(serde_json::Value::String(value.digest()))?; - let version = { - let version_number = serde_json::Number::from_str(&value.version())?; - serde_json::from_value(serde_json::Value::Number(version_number))? - }; - let object_id = value.object_id().parse()?; - - Ok((object_id, version, digest)) - } -} - -impl From for WasmObjectRef { - fn from(value: ObjectRef) -> Self { - let json_obj = serde_json::json!({ - "objectId": value.0, - "version": value.1, - "digest": value.2, - }); - - json_obj - .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .expect("a JSON object is a JS value") - // safety: `json_obj` was constructed following TS ObjectRef's interface. - .unchecked_into() - } -} - -impl From<(ObjectID, SequenceNumber, bool)> for WasmSharedObjectRef { - fn from(value: (ObjectID, SequenceNumber, bool)) -> Self { - let json_obj = serde_json::json!({ - "objectId": value.0, - "initialSharedVersion": value.1, - "mutable": value.2, - }); - - json_obj - .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .expect("a JSON object is a JS value") - // safety: `json_obj` was constructed following TS SharedObjectRef's interface. - .unchecked_into() - } -} - -impl TryFrom for WasmSharedObjectRef { - type Error = TsSdkError; - fn try_from(value: OwnedObjectRef) -> Result { - let Owner::Shared { initial_shared_version } = value.owner else { - return Err(TsSdkError::CommandArgumentError(CommandArgumentError::TypeMismatch)); - }; - let obj_id = value.object_id(); - - Ok((obj_id, initial_shared_version, true).into()) - } -} - -impl WasmSharedObjectRef { - #[allow(dead_code)] - pub(crate) fn immutable(self) -> Self { - const JS_FALSE: JsValue = JsValue::from_bool(false); - - let _ = js_sys::Reflect::set(&self, &JsValue::from_str("mutable"), &JS_FALSE); - self - } -} - -#[wasm_bindgen(module = "@iota/iota-interaction-ts/iota_client_helpers")] -extern "C" { - // Please note: For unclear reasons the `typescript_type` name and the `pub type` name defined - // in wasm_bindgen extern "C" scopes must be equal. Otherwise, the JS constructor will not be - // found in the generated js code. - #[wasm_bindgen(typescript_type = "WasmIotaTransactionBlockResponseWrapper")] - #[derive(Clone)] - pub type WasmIotaTransactionBlockResponseWrapper; - - #[wasm_bindgen(constructor)] - pub fn new(response: WasmIotaTransactionBlockResponse) -> WasmIotaTransactionBlockResponseWrapper; - - #[wasm_bindgen(method, js_name = get_effects)] - pub fn effects(this: &WasmIotaTransactionBlockResponseWrapper) -> Option; - - #[wasm_bindgen(method)] - pub fn to_string(this: &WasmIotaTransactionBlockResponseWrapper) -> String; - - #[wasm_bindgen(method, js_name = "get_digest")] - fn digest_inner(this: &WasmIotaTransactionBlockResponseWrapper) -> String; - - #[wasm_bindgen(method, js_name = "get_response")] - pub fn response(this: &WasmIotaTransactionBlockResponseWrapper) -> WasmIotaTransactionBlockResponse; - - #[wasm_bindgen(js_name = executeTransaction)] - fn execute_transaction_inner( - iota_client: &WasmIotaClient, // --> TypeScript: IotaClient - sender_address: String, // --> TypeScript: string - tx_bcs: Vec, // --> TypeScript: Uint8Array, - signer: WasmStorageSigner, // --> TypeScript: Signer (iota_client_helpers module) - gas_budget: Option, // --> TypeScript: optional bigint - ) -> PromiseIotaTransactionBlockResponseWrapper; - - #[wasm_bindgen(js_name = "sleep")] - fn sleep_inner(ms: i32) -> Promise; -} - -/// Helper function to pause execution. -pub async fn sleep(duration_ms: i32) -> Result<(), JsValue> { - let promise = sleep_inner(duration_ms); - let js_fut = JsFuture::from(promise); - js_fut.await?; - Ok(()) -} - -impl WasmIotaTransactionBlockResponseWrapper { - pub fn digest(&self) -> Result { - TransactionDigest::from_str(&self.digest_inner()) - .map_err(|err| TsSdkError::WasmError("Failed to parse transaction block digest".to_string(), err.to_string())) - } -} - -pub async fn execute_transaction( - iota_client: &WasmIotaClient, // --> Binding: WasmIotaClient - sender_address: IotaAddress, // --> Binding: String - 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(), - tx_bcs, - signer, - gas_budget, - )); - let result: JsValue = JsFuture::from(promise).await.map_err(|e| { - let message = "Error executing JsFuture::from(promise) for `execute_transaction`"; - let details = format!("{e:?}"); - console_log!("{message}; {details}"); - TsSdkError::WasmError(message.to_string(), details.to_string()) - })?; - - Ok(WasmIotaTransactionBlockResponseWrapper::new(result.into())) -} - -#[derive(Deserialize)] -#[serde(try_from = "Vec")] -pub struct ProgrammableTransaction(#[allow(dead_code)] pub(crate) WasmTransactionBuilder); -impl TryFrom> for ProgrammableTransaction { - type Error = TsSdkError; - fn try_from(value: Vec) -> Result { - let uint8array: Uint8Array = value.as_slice().into(); - WasmTransactionBuilder::from_bcs_bytes(uint8array) - .map(Self) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } -} diff --git a/bindings/wasm/iota_interaction_ts/src/common/macros.rs b/bindings/wasm/iota_interaction_ts/src/common/macros.rs deleted file mode 100644 index 259fc5417..000000000 --- a/bindings/wasm/iota_interaction_ts/src/common/macros.rs +++ /dev/null @@ -1,62 +0,0 @@ -// 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 deleted file mode 100644 index f764977e2..000000000 --- a/bindings/wasm/iota_interaction_ts/src/common/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -// 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 deleted file mode 100644 index d95f6dcfa..000000000 --- a/bindings/wasm/iota_interaction_ts/src/common/types.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_core::common::Object; -use identity_iota_interaction::types::transaction::TransactionKind; -use identity_iota_interaction::ProgrammableTransactionBcs; -use js_sys::Promise; -use js_sys::Uint8Array; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::JsFuture; - -use crate::error::TsSdkError; -use crate::error::TsSdkResult; -use crate::error::WasmError; -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()), - #[allow(deprecated)] // will be refactored - &JsValue::from_serde(&value).wasm_result()?, - ); - } - Ok(map.unchecked_into::()) - } -} - -impl Default for MapStringAny { - fn default() -> Self { - js_sys::Map::new().unchecked_into() - } -} - -impl PromiseUint8Array { - /// Helper function to convert Uint8 arrays from contract calls to the internal `ProgrammableTransactionBcs` type. - pub async fn to_programmable_transaction_bcs(&self) -> TsSdkResult { - let promise: Promise = Promise::resolve(self); - let tx_kind_bcs = JsFuture::from(promise) - .await - .map(|v| Uint8Array::from(v).to_vec()) - .map_err(WasmError::from)?; - - let tx_kind = bcs::from_bytes(&tx_kind_bcs).map_err(|e| { - TsSdkError::TransactionSerializationError(format!("failed to deserialize BCS TransactionKind: {e}")) - })?; - - #[allow(irrefutable_let_patterns)] - let TransactionKind::ProgrammableTransaction(pt) = tx_kind - else { - return Err(TsSdkError::WasmError( - "TransactionKind variant".to_owned(), - "only programmable transactions can be used within this library".to_owned(), - )); - }; - - bcs::to_bytes(&pt).map_err(|e| { - TsSdkError::TransactionSerializationError(format!("failed to BCS serialize programmable transaction: {e}")) - }) - } -} diff --git a/bindings/wasm/iota_interaction_ts/src/common/utils.rs b/bindings/wasm/iota_interaction_ts/src/common/utils.rs deleted file mode 100644 index 3ed8447a1..000000000 --- a/bindings/wasm/iota_interaction_ts/src/common/utils.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::de::DeserializeOwned; -use wasm_bindgen::prelude::*; - -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 deleted file mode 100644 index e29a09837..000000000 --- a/bindings/wasm/iota_interaction_ts/src/error.rs +++ /dev/null @@ -1,223 +0,0 @@ -// 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()), - } - } -} - -impl From for WasmError<'_> { - fn from(error: anyhow::Error) -> Self { - Self { - name: Cow::Borrowed("anyhow::Error"), - message: Cow::Owned(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(#[from] PackageUpgradeError), - #[error("[TsSdkError] CommandArgumentError: {0}")] - CommandArgumentError(#[from] CommandArgumentError), - #[error("[TsSdkError] ExecutionFailureStatus: {0}")] - ExecutionFailureStatus(#[from] ExecutionFailureStatus), - #[error("[TsSdkError] TypeArgumentError: {0}")] - TypeArgumentError(#[from] TypeArgumentError), - #[error("[TsSdkError] WasmError:{{\n name: {0},\n message: {1}\n}}")] - WasmError(String, String), - #[error("[TsSdkError] JsSysError: {0}")] - JsSysError(String), - #[error("[TsSdkError] TransactionSerializationError: {0}")] - TransactionSerializationError(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 deleted file mode 100644 index a26c1ff09..000000000 --- a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs +++ /dev/null @@ -1,673 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use async_trait::async_trait; -use js_sys::Uint8Array; -use std::cell::Cell; -use std::collections::HashSet; -use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsCast; -use wasm_bindgen::JsValue; - -use crate::bindings::WasmIotaObjectData; -use crate::bindings::WasmObjectRef; -use crate::bindings::WasmSharedObjectRef; -use crate::bindings::WasmTransactionArgument; -use crate::bindings::WasmTransactionBuilder; -use crate::common::PromiseUint8Array; -use crate::error::TsSdkError; -use crate::error::WasmError; -use crate::transaction_builder::TransactionBuilderTsSdk; -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::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; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "[string, number]")] - pub(crate) type WasmControllerCouple; - - #[wasm_bindgen(typescript_type = "[string, string]")] - pub(crate) type WasmTransferCouple; - - #[wasm_bindgen(typescript_type = "[ObjectRef, string]")] - pub(crate) type WasmObjectRefAndType; - - #[wasm_bindgen(typescript_type = "Map")] - pub(crate) type WasmTxArgumentMap; -} - -impl From<(IotaAddress, u64)> for WasmControllerCouple { - fn from((address, vp): (IotaAddress, u64)) -> Self { - let address = JsValue::from_str(&address.to_string()); - let vp = JsValue::bigint_from_str(&vp.to_string()); - - let arr = js_sys::Array::new(); - arr.push(&address); - arr.push(&vp); - - arr.unchecked_into() - } -} - -#[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls/identity")] -extern "C" { - #[wasm_bindgen(js_name = "create", catch)] - fn identity_new(did: Option<&[u8]>, package: &str) -> Result; - - #[wasm_bindgen(js_name = "newWithControllers", catch)] - fn identity_new_with_controllers( - did: Option<&[u8]>, - controllers: Vec, - threshold: u64, - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "approve", catch)] - async fn approve_proposal( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - proposal: &str, - proposal_type: &str, - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "proposeDeactivation", catch)] - fn propose_deactivation( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - package: &str, - expiration: Option, - ) -> Result; - - #[wasm_bindgen(js_name = "executeDeactivation", catch)] - async fn execute_deactivation( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - proposal: &str, - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "proposeUpgrade", catch)] - async fn propose_upgrade( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - package: &str, - expiration: Option, - ) -> Result; - - #[wasm_bindgen(js_name = "executeUpgrade", catch)] - async fn execute_upgrade( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - proposal: &str, - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "proposeSend", catch)] - async fn propose_send( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - assets: Vec, - package: &str, - expiration: Option, - ) -> Result; - - #[wasm_bindgen(js_name = "executeSend", catch)] - async fn execute_send( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - proposal: &str, - assets: Vec, - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "proposeUpdate", catch)] - fn propose_update( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - did_doc: Option<&[u8]>, - package: &str, - expiration: Option, - ) -> Result; - - #[wasm_bindgen(js_name = "executeUpdate", catch)] - fn execute_update( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - proposal: &str, - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "proposeBorrow", catch)] - async fn propose_borrow( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - objects: Vec, - package: &str, - expiration: Option, - ) -> Result; - - #[wasm_bindgen(js_name = "executeBorrow", catch)] - async fn execute_borrow( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - proposal: &str, - objects: Vec, - intent_fn: &dyn Fn(WasmTransactionBuilder, WasmTxArgumentMap), - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "createAndExecuteBorrow", catch)] - async fn create_and_execute_borrow( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - objects: Vec, - intent_fn: &dyn Fn(WasmTransactionBuilder, WasmTxArgumentMap), - package: &str, - expiration: Option, - ) -> Result; - - #[wasm_bindgen(js_name = "proposeConfigChange", catch)] - async fn propose_config_change( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - controllers_to_add: Vec, - controllers_to_remove: Vec, - controllers_to_update: Vec, - package: &str, - expiration: Option, - threshold: Option, - ) -> Result; - - #[wasm_bindgen(js_name = "executeConfigChange", catch)] - async fn execute_config_change( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - proposal: &str, - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "proposeControllerExecution", catch)] - async fn propose_controller_execution( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - controller_cap_id: &str, - package: &str, - expiration: Option, - ) -> Result; - - #[wasm_bindgen(js_name = "executeControllerExecution", catch)] - async fn execute_controller_execution( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - proposal: &str, - controller_cap_ref: WasmObjectRef, - intent_fn: &dyn Fn(WasmTransactionBuilder, WasmTransactionArgument), - package: &str, - ) -> Result; - - #[wasm_bindgen(js_name = "createAndExecuteControllerExecution", catch)] - async fn create_and_execute_controller_execution( - identity: WasmSharedObjectRef, - capability: WasmObjectRef, - controller_cap_ref: WasmObjectRef, - intent_fn: &dyn Fn(WasmTransactionBuilder, WasmTransactionArgument), - package: &str, - expiration: Option, - ) -> Result; -} - -pub struct IdentityMoveCallsTsSdk {} - -#[async_trait(?Send)] -impl IdentityMoveCalls for IdentityMoveCallsTsSdk { - type Error = TsSdkError; - type NativeTxBuilder = WasmTransactionBuilder; - - fn propose_borrow( - identity: OwnedObjectRef, - capability: ObjectRef, - objects: Vec, - expiration: Option, - package_id: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let controller_cap = capability.into(); - let package_id = package_id.to_string(); - let objects = objects.into_iter().map(|obj| obj.to_string()).collect(); - - futures::executor::block_on(propose_borrow( - identity, - controller_cap, - objects, - &package_id, - expiration, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn execute_borrow>( - identity: OwnedObjectRef, - capability: ObjectRef, - proposal_id: ObjectID, - objects: Vec, - intent_fn: F, - package: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let capability = capability.into(); - let proposal = proposal_id.to_string(); - let package = package.to_string(); - let objects = objects - .into_iter() - .map(|obj| serde_wasm_bindgen::to_value(&obj).map(WasmIotaObjectData::from)) - .collect::, _>>() - .map_err(WasmError::from)?; - - // Use cell to move `intent_fn` inside `closure` without actually moving it. - // This ensures that `closure` is an `impl Fn(..)` instead of `impl FnOnce(..)` like `intent_fn`. - let wrapped_intent_fn = Cell::new(Some(intent_fn)); - let closure = |tx_builder: WasmTransactionBuilder, args: WasmTxArgumentMap| { - let mut builder = TransactionBuilderTsSdk::new(tx_builder); - let args = serde_wasm_bindgen::from_value(args.into()).expect("failed to convert JS argument map"); - wrapped_intent_fn.take().unwrap()(&mut builder, &args); - }; - - futures::executor::block_on(execute_borrow( - identity, capability, &proposal, objects, &closure, &package, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn create_and_execute_borrow>( - identity: OwnedObjectRef, - capability: ObjectRef, - objects: Vec, - intent_fn: F, - expiration: Option, - package_id: ObjectID, - ) -> anyhow::Result { - let identity = identity.try_into()?; - let capability = capability.into(); - let package = package_id.to_string(); - let objects = objects - .into_iter() - .map(|obj| serde_wasm_bindgen::to_value(&obj).map(WasmIotaObjectData::from)) - .collect::, _>>() - .map_err(WasmError::from)?; - - // Use cell to move `intent_fn` inside `closure` without actually moving it. - // This ensures that `closure` is an `impl Fn(..)` instead of `impl FnOnce(..)` like `intent_fn`. - let wrapped_intent_fn = Cell::new(Some(intent_fn)); - let closure = |tx_builder: WasmTransactionBuilder, args: WasmTxArgumentMap| { - let mut builder = TransactionBuilderTsSdk::new(tx_builder); - let args = serde_wasm_bindgen::from_value(args.into()).expect("failed to convert JS argument map"); - wrapped_intent_fn.take().unwrap()(&mut builder, &args); - }; - - futures::executor::block_on(create_and_execute_borrow( - identity, capability, objects, &closure, &package, expiration, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - 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 identity = identity.try_into()?; - let capability = controller_cap.into(); - let package = package.to_string(); - - let controllers_to_add = controllers_to_add - .into_iter() - .map(|controller| serde_wasm_bindgen::to_value(&controller).map(WasmControllerCouple::from)) - .collect::, _>>() - .map_err(WasmError::from)?; - let controllers_to_remove = controllers_to_remove - .into_iter() - .map(|controller| controller.to_string()) - .collect(); - let controllers_to_update = controllers_to_update - .into_iter() - .map(|controller| serde_wasm_bindgen::to_value(&controller).map(WasmControllerCouple::from)) - .collect::, _>>() - .map_err(WasmError::from)?; - - futures::executor::block_on(propose_config_change( - identity, - capability, - controllers_to_add, - controllers_to_remove, - controllers_to_update, - &package, - expiration, - threshold, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn execute_config_change( - identity: OwnedObjectRef, - controller_cap: ObjectRef, - proposal_id: ObjectID, - package: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let capability = controller_cap.into(); - let proposal = proposal_id.to_string(); - let package = package.to_string(); - - futures::executor::block_on(execute_config_change(identity, capability, &proposal, &package)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn propose_controller_execution( - identity: OwnedObjectRef, - capability: ObjectRef, - controller_cap_id: ObjectID, - expiration: Option, - package_id: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let controller_cap = capability.into(); - let package_id = package_id.to_string(); - let borrowed_cap = controller_cap_id.to_string(); - - futures::executor::block_on(propose_controller_execution( - identity, - controller_cap, - &borrowed_cap, - &package_id, - expiration, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn execute_controller_execution>( - identity: OwnedObjectRef, - capability: ObjectRef, - proposal_id: ObjectID, - borrowing_controller_cap_ref: ObjectRef, - intent_fn: F, - package: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let capability = capability.into(); - let proposal = proposal_id.to_string(); - let package = package.to_string(); - let borrowing_cap = borrowing_controller_cap_ref.into(); - - // Use cell to move `intent_fn` inside `closure` without actually moving it. - // This ensures that `closure` is an `impl Fn(..)` instead of `impl FnOnce(..)` like `intent_fn`. - let wrapped_intent_fn = Cell::new(Some(intent_fn)); - let closure = |tx_builder: WasmTransactionBuilder, args: WasmTransactionArgument| { - let mut builder = TransactionBuilderTsSdk::new(tx_builder); - let args = serde_wasm_bindgen::from_value(args.into()).expect("failed to convert JS argument map"); - wrapped_intent_fn.take().unwrap()(&mut builder, &args); - }; - - futures::executor::block_on(execute_controller_execution( - identity, - capability, - &proposal, - borrowing_cap, - &closure, - &package, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - 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, - { - let identity = identity.try_into()?; - let capability = capability.into(); - let package = package_id.to_string(); - let borrowing_cap = borrowing_controller_cap_ref.into(); - - // Use cell to move `intent_fn` inside `closure` without actually moving it. - // This ensures that `closure` is an `impl Fn(..)` instead of `impl FnOnce(..)` like `intent_fn`. - let wrapped_intent_fn = Cell::new(Some(intent_fn)); - let closure = |tx_builder: WasmTransactionBuilder, args: WasmTransactionArgument| { - let mut builder = TransactionBuilderTsSdk::new(tx_builder); - let args = serde_wasm_bindgen::from_value(args.into()).expect("failed to convert JS argument map"); - wrapped_intent_fn.take().unwrap()(&mut builder, &args); - }; - - futures::executor::block_on(create_and_execute_controller_execution( - identity, - capability, - borrowing_cap, - &closure, - &package, - expiration, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - async fn new_identity( - did_doc: Option<&[u8]>, - package_id: ObjectID, - ) -> Result { - let package = package_id.to_string(); - - identity_new(did_doc, &package) - .map_err(WasmError::from)? - .to_programmable_transaction_bcs() - .await - } - - async fn new_with_controllers( - did_doc: Option<&[u8]>, - controllers: C, - threshold: u64, - package_id: ObjectID, - ) -> Result - where - C: IntoIterator, - { - let package = package_id.to_string(); - let controllers = controllers.into_iter().map(Into::into).collect(); - - identity_new_with_controllers(did_doc, controllers, threshold, &package) - .map_err(WasmError::from)? - .to_programmable_transaction_bcs() - .await - } - - fn approve_proposal( - identity: OwnedObjectRef, - controller_cap: ObjectRef, - proposal_id: ObjectID, - package: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let controller_cap = controller_cap.into(); - let proposal_id = proposal_id.to_string(); - let package_id = package.to_string(); - - futures::executor::block_on(approve_proposal( - identity, - controller_cap, - &proposal_id, - &T::move_type(package).to_canonical_string(true), - &package_id, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn propose_send( - identity: OwnedObjectRef, - capability: ObjectRef, - transfer_map: Vec<(ObjectID, IotaAddress)>, - expiration: Option, - package_id: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let controller_cap = capability.into(); - let package_id = package_id.to_string(); - let transfer_map = transfer_map - .into_iter() - .map(|tx| serde_wasm_bindgen::to_value(&tx).map(JsValue::into)) - .collect::, _>>() - .map_err(|e| WasmError::from(e))?; - - futures::executor::block_on(propose_send( - identity, - controller_cap, - transfer_map, - &package_id, - expiration, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - 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 { - let identity = identity.try_into()?; - let controller_cap = capability.into(); - let proposal = proposal_id.to_string(); - let package_id = package.to_string(); - let objects = objects - .into_iter() - .map(|tx| serde_wasm_bindgen::to_value(&tx).map(JsValue::into)) - .collect::, _>>() - .map_err(|e| WasmError::from(e))?; - - futures::executor::block_on(execute_send(identity, controller_cap, &proposal, objects, &package_id)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - async fn propose_update( - identity: OwnedObjectRef, - capability: ObjectRef, - did_doc: Option<&[u8]>, - expiration: Option, - package_id: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let controller_cap = capability.into(); - let package_id = package_id.to_string(); - - propose_update(identity, controller_cap, did_doc, &package_id, expiration) - .map_err(WasmError::from)? - .to_programmable_transaction_bcs() - .await - } - - async fn execute_update( - identity: OwnedObjectRef, - capability: ObjectRef, - proposal_id: ObjectID, - package_id: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let controller_cap = capability.into(); - let proposal = proposal_id.to_string(); - let package_id = package_id.to_string(); - - execute_update(identity, controller_cap, &proposal, &package_id) - .map_err(WasmError::from)? - .to_programmable_transaction_bcs() - .await - } - - fn propose_upgrade( - identity: OwnedObjectRef, - capability: ObjectRef, - expiration: Option, - package_id: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let capability = capability.into(); - let package = package_id.to_string(); - - futures::executor::block_on(propose_upgrade(identity, capability, &package, expiration)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } - - fn execute_upgrade( - identity: OwnedObjectRef, - capability: ObjectRef, - proposal_id: ObjectID, - package_id: ObjectID, - ) -> Result { - let identity = identity.try_into()?; - let capability = capability.into(); - let proposal = proposal_id.to_string(); - let package = package_id.to_string(); - - futures::executor::block_on(execute_upgrade(identity, capability, &proposal, &package)) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } -} 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 deleted file mode 100644 index db6f00e00..000000000 --- a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs +++ /dev/null @@ -1,427 +0,0 @@ -// 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::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::types::crypto::Signature; -use identity_iota_interaction::types::digests::TransactionDigest; -use identity_iota_interaction::types::dynamic_field::DynamicFieldName; -use identity_iota_interaction::types::transaction::TransactionData; -use secret_storage::Signer; - -use identity_iota_interaction::error::Error as IotaRpcError; -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::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::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::types::transaction::ProgrammableTransaction as ProgrammableTransactionSdk; -use identity_iota_interaction::types::transaction::TransactionDataAPI as _; -use identity_iota_interaction::CoinReadTrait; -use identity_iota_interaction::EventTrait; -use identity_iota_interaction::IotaClientTrait; -use identity_iota_interaction::IotaKeySignature; -use identity_iota_interaction::IotaTransactionBlockResponseT; -use identity_iota_interaction::QuorumDriverTrait; -use identity_iota_interaction::ReadTrait; - -use crate::bindings::ManagedWasmIotaClient; -use crate::bindings::WasmIotaClient; -use crate::bindings::WasmIotaTransactionBlockResponseWrapper; -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: WasmIotaTransactionBlockResponseWrapper, - effects: Option, -} - -impl IotaTransactionBlockResponseProvider { - pub fn new(response: WasmIotaTransactionBlockResponseWrapper) -> Self { - let effects = response.effects().map(Into::into); - IotaTransactionBlockResponseProvider { response, effects } - } -} - -#[async_trait::async_trait(?Send)] -impl IotaTransactionBlockResponseT for IotaTransactionBlockResponseProvider { - type Error = TsSdkError; - type NativeResponse = WasmIotaTransactionBlockResponseWrapper; - - fn to_string(&self) -> String { - format!("{:?}", self.response.to_string()) - } - - fn effects(&self) -> Option<&IotaTransactionBlockEffects> { - self.effects.as_ref() - } - - 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() - } - - fn digest(&self) -> Result { - self.response.digest() - } -} - -pub struct ReadAdapter { - client: ManagedWasmIotaClient, -} - -#[async_trait::async_trait(?Send)] -impl ReadTrait for ReadAdapter { - type Error = TsSdkError; - type NativeResponse = WasmIotaTransactionBlockResponseWrapper; - - 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 = WasmIotaTransactionBlockResponseWrapper; - - async fn execute_transaction_block( - &self, - tx_data: TransactionData, - signatures: Vec, - options: Option, - request_type: Option, - ) -> IotaRpcResult { - let wasm_response = self - .client - .execute_transaction_block(tx_data, signatures, options, request_type) - .await?; - - let digest = wasm_response - .digest() - .map_err(|e| IotaRpcError::FfiError(e.to_string()))?; - - self - .client - .wait_for_transaction(digest, Some(IotaTransactionBlockResponseOptions::new()), None, None) - .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 = WasmIotaTransactionBlockResponseWrapper; - - 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, - tx_data: TransactionData, - signer: &S, - ) -> Result< - Box>, - Self::Error, - > { - let response = self.sdk_execute_transaction(tx_data, signer).await?; - - // wait until new transaction block is available - self - .iota_client - .wait_for_transaction( - response.digest()?, - Some(IotaTransactionBlockResponseOptions::new()), - None, - None, - ) - .await - .unwrap(); - - Ok(Box::new(response)) - } - - async fn default_gas_budget( - &self, - _sender_address: IotaAddress, - _tx: &ProgrammableTransactionSdk, - ) -> Result { - Ok(50_000_000) - } - - 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), - }) - } - - pub fn into_inner(self) -> WasmIotaClient { - self.iota_client.clone().0 - } - - // Submit tx to IOTA client, also: - // - signs tx - // - calls execute_transaction_block to submit tx (with signatures created here) - async fn sdk_execute_transaction>( - &self, - tx: TransactionData, - signer: &S, - ) -> Result { - let sender_public_key = signer - .public_key() - .await - .map_err(|e| TsSdkError::WasmError(String::from("SecretStorage"), e.to_string()))?; - let sender_address = IotaAddress::from(&sender_public_key); - if sender_address != tx.sender() { - return Err(TsSdkError::WasmError("SDK".to_owned(), format!("transaction data needs to be signed by address {}, but client can only provide signature for address {sender_address}", tx.sender()))); - } - let signature = signer - .sign(&tx) - .await - .map_err(|e| TsSdkError::WasmError("SecretStorage".to_owned(), e.to_string()))?; - - let wasm_response = self - .quorum_driver_api() - .execute_transaction_block( - tx, - vec![signature], - Some(IotaTransactionBlockResponseOptions::full_content()), - Some(ExecuteTransactionRequestType::WaitForLocalExecution), - ) - .await - .unwrap(); - let native = wasm_response.clone_native_response(); - - Ok(IotaTransactionBlockResponseProvider::new(native)) - } -} diff --git a/bindings/wasm/iota_interaction_ts/src/lib.rs b/bindings/wasm/iota_interaction_ts/src/lib.rs deleted file mode 100644 index 218525657..000000000 --- a/bindings/wasm/iota_interaction_ts/src/lib.rs +++ /dev/null @@ -1,58 +0,0 @@ -// 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_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - #[allow(unused_imports)] pub use error::TsSdkError as AdapterError; - #[allow(unused_imports)] pub use asset_move_calls::AssetMoveCallsTsSdk as AssetMoveCallsAdapter; - #[allow(unused_imports)] pub use identity_move_calls::IdentityMoveCallsTsSdk as IdentityMoveCallsAdapter; - #[allow(unused_imports)] pub use iota_client_ts_sdk::IotaClientTsSdk as IotaClientAdapter; - #[allow(unused_imports)] pub use iota_client_ts_sdk::IotaTransactionBlockResponseProvider as IotaTransactionBlockResponseAdapter; - #[allow(unused_imports)] pub use bindings::WasmIotaTransactionBlockResponseWrapper as NativeTransactionBlockResponse; - #[allow(unused_imports)] pub use migration_move_calls::MigrationMoveCallsTsSdk as MigrationMoveCallsAdapter; - #[allow(unused_imports)] pub use transaction_builder::TransactionBuilderTsSdk as TransactionBuilderAdapter; - - #[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 bindings::ProgrammableTransaction; - #[allow(unused_imports)] pub use bindings::WasmPublicKey; - #[allow(unused_imports)] pub use bindings::Ed25519PublicKey as WasmEd25519PublicKey; - #[allow(unused_imports)] pub use bindings::Secp256r1PublicKey as WasmSecp256r1PublicKey; - #[allow(unused_imports)] pub use bindings::Secp256k1PublicKey as WasmSecp256k1PublicKey; - #[allow(unused_imports)] pub use bindings::WasmIotaSignature; - #[cfg(feature = "keytool")] - #[allow(unused_imports)] - pub use bindings::keytool::*; - - #[allow(unused_imports)] pub use transaction_builder::NativeTsTransactionBuilderBindingWrapper; - } -} diff --git a/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs b/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs deleted file mode 100644 index 5000374fb..000000000 --- a/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs +++ /dev/null @@ -1,54 +0,0 @@ -// 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::ObjectID; -use identity_iota_interaction::types::base_types::ObjectRef; -use identity_iota_interaction::MigrationMoveCalls; -use identity_iota_interaction::ProgrammableTransactionBcs; -use js_sys::Uint8Array; -use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsValue; - -use crate::bindings::WasmObjectRef; -use crate::bindings::WasmSharedObjectRef; -use crate::error::TsSdkError; -use crate::error::WasmError; - -#[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls")] -extern "C" { - #[wasm_bindgen(js_name = "migrateDidOutput", catch)] - async fn migrate_did_output_impl( - did_output: WasmObjectRef, - migration_registry: WasmSharedObjectRef, - package: &str, - creation_timestamp: Option, - ) -> Result; -} - -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 { - let did_output = did_output.into(); - let package = package.to_string(); - let migration_registry = migration_registry.try_into()?; - - futures::executor::block_on(migrate_did_output_impl( - did_output, - migration_registry, - &package, - creation_timestamp, - )) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(Self::Error::from) - } -} diff --git a/bindings/wasm/iota_interaction_ts/src/transaction_builder.rs b/bindings/wasm/iota_interaction_ts/src/transaction_builder.rs deleted file mode 100644 index 0d95bb740..000000000 --- a/bindings/wasm/iota_interaction_ts/src/transaction_builder.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::ops::Deref; -use std::ops::DerefMut; - -use crate::bindings::WasmTransactionBuilder; -use crate::error::TsSdkError; -use crate::error::WasmError; -use identity_iota_interaction::ProgrammableTransactionBcs; -use identity_iota_interaction::TransactionBuilderT; - -pub type NativeTsTransactionBuilderBindingWrapper = WasmTransactionBuilder; - -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 { - futures::executor::block_on(self.builder.build()) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(Self::Error::from) - } - - fn as_native_tx_builder(&mut self) -> &mut Self::NativeTxBuilder { - &mut self.builder - } - - fn into_native_tx_builder(self) -> Self::NativeTxBuilder { - self.builder - } -} - -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 deleted file mode 100644 index e8a42d2a3..000000000 --- a/bindings/wasm/iota_interaction_ts/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "extends": "../tsconfig.json", - "entryPoints": [ - "./node/" - ], - "out": "./docs/wasm", - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@iota/iota-interaction-ts/*": [ - "./*" - ], - } - } -} diff --git a/bindings/wasm/iota_interaction_ts/tsconfig.node.json b/bindings/wasm/iota_interaction_ts/tsconfig.node.json deleted file mode 100644 index c75065fb2..000000000 --- a/bindings/wasm/iota_interaction_ts/tsconfig.node.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "target": "ES2020", - "esModuleInterop": true, - "module": "commonjs" - } -} \ No newline at end of file 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 deleted file mode 100644 index f5122c566..000000000 --- a/identity_iota_core/src/iota_interaction_rust/asset_move_calls.rs +++ /dev/null @@ -1,203 +0,0 @@ -// 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 deleted file mode 100644 index 2e5dc788f..000000000 --- a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs +++ /dev/null @@ -1,851 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use async_trait::async_trait; -use identity_iota_interaction::OptionalSend; -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 {} - -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -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())?) - } - - async fn new_identity( - did_doc: Option<&[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())?) - } - - async fn new_with_controllers( - did_doc: Option<&[u8]>, - controllers: C, - threshold: u64, - package_id: ObjectID, - ) -> Result - where - C: IntoIterator + OptionalSend, - { - 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 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())?) - } - - async fn propose_update( - identity: OwnedObjectRef, - capability: ObjectRef, - did_doc: Option<&[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).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())?) - } - - async 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 deleted file mode 100644 index 115b07d06..000000000 --- a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs +++ /dev/null @@ -1,535 +0,0 @@ -// 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 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::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::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::types::transaction::TransactionDataAPI as _; -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::OptionalSync; -use identity_iota_interaction::QuorumDriverTrait; -use identity_iota_interaction::ReadTrait; - -/// 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(crate) 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(&self) -> Option<&IotaTransactionBlockEffects> { - self.response.effects.as_ref() - } - - fn to_string(&self) -> String { - format!("{:?}", self.response) - } - - 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() - } - - fn digest(&self) -> Result { - Ok(self.response.digest) - } -} - -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: TransactionData, - signatures: Vec, - options: Option, - request_type: Option, - ) -> IotaRpcResult { - let tx = Transaction::from_data(tx_data, signatures); - 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( - &self, - tx_data: TransactionData, - signer: &S, - ) -> Result - where - S: Signer + OptionalSync, - { - let response = self.sdk_execute_transaction(tx_data, signer).await?; - Ok(Box::new(IotaTransactionBlockResponseProvider::new(response))) - } - - async fn default_gas_budget(&self, sender_address: IotaAddress, tx: &ProgrammableTransaction) -> Result { - 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, - tx: TransactionData, - signer: &S, - ) -> Result { - let public_key = signer - .public_key() - .await - .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; - let sender_address = IotaAddress::from(&public_key); - - if sender_address != tx.sender() { - return Err(Error::TransactionSigningFailed(format!("transaction data needs to be signed by address {}, but client can only provide signature for address {sender_address}", tx.sender()))); - } - - let signature = signer - .sign(&tx) - .await - .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; - - // execute tx - let response = self - .iota_client - .quorum_driver_api() - .execute_transaction_block( - Transaction::from_data(tx, 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_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 coin found with minimum required balance of {} for address {}", - MINIMUM_BALANCE, 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 deleted file mode 100644 index eaeb087ce..000000000 --- a/identity_iota_core/src/iota_interaction_rust/migration_move_calls.rs +++ /dev/null @@ -1,56 +0,0 @@ -// 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 deleted file mode 100644 index ab6ed93f5..000000000 --- a/identity_iota_core/src/iota_interaction_rust/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -// 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 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; diff --git a/identity_iota_core/src/iota_interaction_rust/transaction_builder.rs b/identity_iota_core/src/iota_interaction_rust/transaction_builder.rs deleted file mode 100644 index 9854e1893..000000000 --- a/identity_iota_core/src/iota_interaction_rust/transaction_builder.rs +++ /dev/null @@ -1,53 +0,0 @@ -// 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 deleted file mode 100644 index 357d3647a..000000000 --- a/identity_iota_core/src/iota_interaction_rust/utils.rs +++ /dev/null @@ -1,122 +0,0 @@ -// 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 012fe35d9..f0a272b74 100644 --- a/identity_iota_core/src/lib.rs +++ b/identity_iota_core/src/lib.rs @@ -34,9 +34,6 @@ mod state_metadata; 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; diff --git a/identity_iota_interaction/Cargo.toml b/identity_iota_interaction/Cargo.toml deleted file mode 100644 index a580760c6..000000000 --- a/identity_iota_interaction/Cargo.toml +++ /dev/null @@ -1,72 +0,0 @@ -[package] -name = "identity_iota_interaction" -version = "1.6.0-alpha" -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 } -bcs = "0.1.4" -cfg-if = "1.0.0" -fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", package = "fastcrypto", features = ["copy_key"] } -jsonpath-rust = { version = "0.5.1", optional = true } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } -serde.workspace = true -serde_json.workspace = true - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.12.0-rc" } -move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.12.0-rc" } -tokio = { version = "1", optional = true, default-features = false, features = ["process"] } - -shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "shared-crypto", tag = "v0.12.0-rc" } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -eyre = { version = "0.6" } -fastcrypto-zkp = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", 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_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" } -derive_more = "0.99.18" -enum_dispatch = "0.3.13" -schemars = "0.8.21" -tap = "1" -nonempty = "0.11" - -[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/send-sync-storage"] -send-sync-transaction = ["secret-storage/send-sync-storage"] -keytool = ["dep:tokio", "dep:jsonpath-rust"] - -[lints.clippy] -result_large_err = "allow" - -[lints.rust] -# from local sdk types -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(msim)'] } diff --git a/identity_iota_interaction/README.md b/identity_iota_interaction/README.md deleted file mode 100644 index abee8ad9e..000000000 --- a/identity_iota_interaction/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# 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/effects_mut_api.rs b/identity_iota_interaction/src/effects_mut_api.rs deleted file mode 100644 index 83b1879be..000000000 --- a/identity_iota_interaction/src/effects_mut_api.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::rpc_types::IotaObjectRef; -use crate::rpc_types::IotaTransactionBlockEffects; -use crate::rpc_types::IotaTransactionBlockEffectsAPI; -use crate::rpc_types::IotaTransactionBlockEffectsV1; -use crate::rpc_types::OwnedObjectRef; - -/// A mutable version of [IotaTransactionBlockEffectsAPI] that allows the -/// in-place mutation of [IotaTransactionBlockEffects] -pub trait IotaTransactionBlockEffectsMutAPI: IotaTransactionBlockEffectsAPI { - fn shared_objects_mut(&mut self) -> &mut Vec; - fn created_mut(&mut self) -> &mut Vec; - fn mutated_mut(&mut self) -> &mut Vec; - fn unwrapped_mut(&mut self) -> &mut Vec; - fn deleted_mut(&mut self) -> &mut Vec; - fn unwrapped_then_deleted_mut(&mut self) -> &mut Vec; - fn wrapped_mut(&mut self) -> &mut Vec; -} - -impl IotaTransactionBlockEffectsMutAPI for IotaTransactionBlockEffectsV1 { - fn shared_objects_mut(&mut self) -> &mut Vec { - &mut self.shared_objects - } - - fn created_mut(&mut self) -> &mut Vec { - &mut self.created - } - - fn mutated_mut(&mut self) -> &mut Vec { - &mut self.mutated - } - - fn unwrapped_mut(&mut self) -> &mut Vec { - &mut self.unwrapped - } - - fn deleted_mut(&mut self) -> &mut Vec { - &mut self.deleted - } - - fn unwrapped_then_deleted_mut(&mut self) -> &mut Vec { - &mut self.unwrapped_then_deleted - } - - fn wrapped_mut(&mut self) -> &mut Vec { - &mut self.wrapped - } -} - -impl IotaTransactionBlockEffectsMutAPI for IotaTransactionBlockEffects { - fn shared_objects_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.shared_objects, - } - } - - fn created_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.created, - } - } - - fn mutated_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.mutated, - } - } - - fn unwrapped_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.unwrapped, - } - } - - fn deleted_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.deleted, - } - } - - fn unwrapped_then_deleted_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.unwrapped_then_deleted, - } - } - - fn wrapped_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.wrapped, - } - } -} diff --git a/identity_iota_interaction/src/iota_client_trait.rs b/identity_iota_interaction/src/iota_client_trait.rs deleted file mode 100644 index acec57a1c..000000000 --- a/identity_iota_interaction/src/iota_client_trait.rs +++ /dev/null @@ -1,257 +0,0 @@ -// 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::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::IotaTransactionBlockEffects; -use crate::rpc_types::IotaTransactionBlockResponseOptions; -use crate::rpc_types::ObjectsPage; -use crate::types::base_types::IotaAddress; -use crate::types::base_types::ObjectID; -use crate::types::base_types::SequenceNumber; -use crate::types::crypto::PublicKey; -use crate::types::crypto::Signature; -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::types::transaction::ProgrammableTransaction; -use crate::types::transaction::TransactionData; -use crate::OptionalSend; -use async_trait::async_trait; -use secret_storage::SignatureScheme as SignatureSchemeSecretStorage; -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; - -#[cfg(feature = "send-sync-transaction")] -use crate::OptionalSync; - -pub struct IotaKeySignature { - pub public_key: PublicKey, - pub signature: Signature, -} - -impl SignatureSchemeSecretStorage for IotaKeySignature { - type PublicKey = PublicKey; - type Signature = Signature; - type Input = TransactionData; -} - -//******************************************************************** -// 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; - - /// Returns Debug representation of the IotaTransactionBlockResponse - fn to_string(&self) -> String; - - /// Returns the effects of this transaction - fn effects(&self) -> Option<&IotaTransactionBlockEffects>; - - /// 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; - - // Returns digest for transaction block. - fn digest(&self) -> Result; -} - -#[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: TransactionData, - signatures: Vec, - 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, - tx_data: TransactionData, - signer: &S, - ) -> Result< - Box>, - Self::Error, - >; - #[cfg(feature = "send-sync-transaction")] - async fn execute_transaction + OptionalSync>( - &self, - tx_data: TransactionData, - signer: &S, - ) -> Result< - Box>, - Self::Error, - >; - - async fn default_gas_budget( - &self, - sender_address: IotaAddress, - tx: &ProgrammableTransaction, - ) -> 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 deleted file mode 100644 index 7f2ab9dcd..000000000 --- a/identity_iota_interaction/src/iota_verifiable_credential.rs +++ /dev/null @@ -1,39 +0,0 @@ -// 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, PartialEq, Eq)] -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/keytool/internal.rs b/identity_iota_interaction/src/keytool/internal.rs deleted file mode 100644 index e09c206c3..000000000 --- a/identity_iota_interaction/src/keytool/internal.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::path::Path; -use std::path::PathBuf; -use std::str::FromStr as _; - -use anyhow::anyhow; -use anyhow::Context as _; -use fastcrypto::traits::EncodeDecodeBase64 as _; -use jsonpath_rust::JsonPathQuery as _; -use serde::Deserialize; -use serde_json::Value; - -use crate::types::base_types::IotaAddress; -use crate::types::crypto::PublicKey; - -#[derive(Debug, Clone)] -pub(super) struct IotaCliWrapper { - iota_bin: PathBuf, -} - -impl Default for IotaCliWrapper { - fn default() -> Self { - Self { - iota_bin: PathBuf::from_str("iota").expect("infallible"), - } - } -} - -impl IotaCliWrapper { - /// Creates a new [IotaCliWrapper] that will use the iota binary found at - /// the provided path. - pub fn new_with_custom_bin(iota_bin: impl AsRef) -> Self { - Self { - iota_bin: iota_bin.as_ref().to_owned(), - } - } - - /// Returns the location of the iota binary used. - pub fn iota_bin(&self) -> &Path { - &self.iota_bin - } - - /// Executes a given "iota" command with the provided string-encoded args. - /// Returns the parsed JSON output. - pub fn run_command(&self, args: &str) -> anyhow::Result { - cfg_if::cfg_if! { - if #[cfg(not(target_arch = "wasm32"))] { - let output = std::process::Command::new(&self.iota_bin) - .args(args.split_ascii_whitespace()) - .arg("--json") - .output() - .map_err(|e| anyhow!("failed to run command: {e}"))?; - - if !output.status.success() { - let err_msg = - String::from_utf8(output.stderr).map_err(|e| anyhow!("command failed with non-utf8 error message: {e}"))?; - return Err(anyhow!("failed to run keytool cmd: {err_msg}")); - } - - let trimmed_output = { - let start_of_json = output.stdout.iter().enumerate().find_map(|(i, b)| matches!(*b, b'[' | b'{' | b'\"').then_some(i)).context("no JSON in command output")?; - &output.stdout[start_of_json..] - }; - - serde_json::from_slice(trimmed_output).context("invalid JSON object in command output") - } else { - extern "Rust" { - fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result; - } - let iota_bin = self.iota_bin.to_str().context("invalid IOTA bin path")?; - let cmd = format!("{iota_bin} {args} --json"); - unsafe { __wasm_exec_iota_cmd(&cmd) } - } - } - } - - /// Returns the current active address. - pub fn get_active_address(&self) -> anyhow::Result { - self - .run_command("client active-address") - .and_then(|value| serde_json::from_value(value).context("failed to parse IotaAddress from output")) - } - - fn get_key_impl(&self, json_path_query: &str) -> anyhow::Result> { - let Some(pk_json_data) = self - .run_command("keytool list")? - .path(json_path_query) - .map_err(|e| anyhow!("failed to query JSON output: {e}"))? - .get_mut(0) - .map(Value::take) - else { - return Ok(None); - }; - - let KeytoolPublicKeyHelper { - public_base64_key_with_flag, - alias, - .. - } = serde_json::from_value(pk_json_data)?; - - let pk = PublicKey::decode_base64(&public_base64_key_with_flag).map_err(|e| anyhow!("{e:?}"))?; - - Ok(Some((pk, alias))) - } - - /// Returns the public key of a given address, if any. - pub fn get_key(&self, address: IotaAddress) -> anyhow::Result> { - let query = format!("$[?(@.iotaAddress==\"{}\")]", address); - self.get_key_impl(&query) - } - - /// Returns the public key with the given alias, if any. - pub fn get_key_by_alias(&self, alias: &str) -> anyhow::Result> { - let query = format!("$[?(@.alias==\"{}\")]", alias); - Ok(self.get_key_impl(&query)?.map(|(pk, _)| pk)) - } -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct KeytoolPublicKeyHelper { - alias: String, - public_base64_key_with_flag: String, -} diff --git a/identity_iota_interaction/src/keytool/mod.rs b/identity_iota_interaction/src/keytool/mod.rs deleted file mode 100644 index 2b1b517e3..000000000 --- a/identity_iota_interaction/src/keytool/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod internal; -mod signer; -mod storage; - -pub use signer::*; -pub use storage::*; diff --git a/identity_iota_interaction/src/keytool/signer.rs b/identity_iota_interaction/src/keytool/signer.rs deleted file mode 100644 index dc307e514..000000000 --- a/identity_iota_interaction/src/keytool/signer.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::path::Path; -use std::path::PathBuf; - -use crate::types::base_types::IotaAddress; -use crate::types::crypto::PublicKey; -use crate::types::crypto::Signature; -use crate::types::transaction::TransactionData; -use crate::IotaKeySignature; -use anyhow::anyhow; -use anyhow::Context as _; -use async_trait::async_trait; -use fastcrypto::encoding::Base64; -use fastcrypto::encoding::Encoding; -use secret_storage::Error as SecretStorageError; -use secret_storage::Signer; - -use super::internal::IotaCliWrapper; - -/// Builder structure to ease the creation of a [KeytoolSigner]. -#[derive(Debug, Default)] -pub struct KeytoolSignerBuilder { - address: Option, - iota_bin: Option, -} - -impl KeytoolSignerBuilder { - /// Returns a new [KeytoolSignerBuilder] with default configuration: - /// - use current active address; - /// - assumes `iota` binary to be in PATH; - pub fn new() -> Self { - Self::default() - } - - /// Sets the address the signer will use. - /// Defaults to current active address if no address is provided. - pub fn with_address(mut self, address: IotaAddress) -> Self { - self.address = Some(address); - self - } - - /// Sets the path to the `iota` binary to use. - /// Assumes `iota` to be in PATH if no value is provided. - pub fn iota_bin_location(mut self, path: impl AsRef) -> Self { - let path = path.as_ref().to_path_buf(); - self.iota_bin = Some(path); - - self - } - - /// Builds a new [KeytoolSigner] using the provided configuration. - pub fn build(self) -> anyhow::Result { - let KeytoolSignerBuilder { address, iota_bin } = self; - let iota_cli_wrapper = iota_bin.map(IotaCliWrapper::new_with_custom_bin).unwrap_or_default(); - let address = if let Some(address) = address { - address - } else { - iota_cli_wrapper.get_active_address()? - }; - - let public_key = iota_cli_wrapper.get_key(address)?.context("key doens't exist")?.0; - - Ok(KeytoolSigner { - public_key, - iota_cli_wrapper, - address, - }) - } -} - -/// IOTA Keytool [Signer] implementation. -#[derive(Debug)] -pub struct KeytoolSigner { - public_key: PublicKey, - iota_cli_wrapper: IotaCliWrapper, - address: IotaAddress, -} - -impl KeytoolSigner { - /// Returns a [KeytoolSignerBuilder]. - pub fn builder() -> KeytoolSignerBuilder { - KeytoolSignerBuilder::default() - } - - /// Returns the [IotaAddress] used by this [KeytoolSigner]. - pub fn address(&self) -> IotaAddress { - self.address - } - - /// Returns the [PublicKey] used by this [KeytoolSigner]. - pub fn public_key(&self) -> &PublicKey { - &self.public_key - } -} - -#[cfg_attr(feature = "send-sync-transaction", async_trait)] -#[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] -impl Signer for KeytoolSigner { - type KeyId = IotaAddress; - - fn key_id(&self) -> Self::KeyId { - self.address - } - - async fn public_key(&self) -> Result { - Ok(self.public_key.clone()) - } - - async fn sign(&self, data: &TransactionData) -> Result { - let tx_data_bcs = - bcs::to_bytes(data).map_err(|e| SecretStorageError::Other(anyhow!("bcs serialization failed: {e}")))?; - let base64_data = Base64::encode(&tx_data_bcs); - let command = format!("keytool sign --address {} --data {base64_data}", self.address); - - self - .iota_cli_wrapper - .run_command(&command) - .and_then(|json| { - json - .get("iotaSignature") - .context("invalid JSON output: missing iotaSignature")? - .as_str() - .context("not a JSON string")? - .parse() - .map_err(|e| anyhow!("invalid IOTA signature: {e}")) - }) - .map_err(SecretStorageError::Other) - } -} diff --git a/identity_iota_interaction/src/keytool/storage.rs b/identity_iota_interaction/src/keytool/storage.rs deleted file mode 100644 index 6dc0f3c01..000000000 --- a/identity_iota_interaction/src/keytool/storage.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::path::Path; - -use anyhow::anyhow; -use anyhow::Context as _; -use fastcrypto::ed25519::Ed25519Signature; -use fastcrypto::secp256k1::Secp256k1Signature; -use fastcrypto::secp256r1::Secp256r1Signature; -use fastcrypto::traits::Signer; -use serde::Deserialize; - -use crate::types::base_types::IotaAddress; -use crate::types::crypto::IotaKeyPair; -use crate::types::crypto::PublicKey; -use crate::types::crypto::SignatureScheme as IotaSignatureScheme; - -use super::internal::IotaCliWrapper; -use super::KeytoolSignerBuilder; - -#[derive(Clone, Default)] -pub struct KeytoolStorage { - iota_cli_wrapper: IotaCliWrapper, -} - -impl KeytoolStorage { - /// Returns a new [KeytoolStorage] that will use the IOTA binary in PATH. - pub fn new() -> Self { - Self::default() - } - - /// Returns a new [KeytoolStorage] that will use the provided IOTA binary. - pub fn new_with_custom_bin(iota_bin: impl AsRef) -> Self { - Self { - iota_cli_wrapper: IotaCliWrapper::new_with_custom_bin(iota_bin), - } - } - - /// Returns a [KeytoolSignerBuilder] to construct a [super::KeytoolSigner] after - /// selecting an address. - pub fn signer(&self) -> KeytoolSignerBuilder { - KeytoolSignerBuilder::new().iota_bin_location(self.iota_cli_wrapper.iota_bin()) - } - - /// Generates a new keypair of type `key_scheme`. - /// Returns the resulting [PublicKey] together with its alias. - pub fn generate_key(&self, key_scheme: IotaSignatureScheme) -> anyhow::Result<(PublicKey, String)> { - if !matches!( - &key_scheme, - IotaSignatureScheme::ED25519 | IotaSignatureScheme::Secp256k1 | IotaSignatureScheme::Secp256r1 - ) { - anyhow::bail!("key scheme {key_scheme} is not supported by the keytool"); - } - - let cmd = format!("client new-address --key-scheme {key_scheme}"); - let KeyGenOutput { alias, address } = { - let json_output = self.iota_cli_wrapper.run_command(&cmd)?; - serde_json::from_value(json_output)? - }; - - let pk = self - .iota_cli_wrapper - .get_key(address)? - .ok_or_else(|| anyhow!("key for address {address} wasn't found"))? - .0; - - Ok((pk, alias)) - } - - /// Inserts a new key in this keytool. - /// Returns the alias assigned to the inserted key. - pub fn insert_key(&self, key: IotaKeyPair) -> anyhow::Result { - let bech32_encoded_key = key.encode().map_err(|e| anyhow!("{e:?}"))?; - let key_scheme = key.public().scheme().to_string(); - let cmd = format!("keytool import {bech32_encoded_key} {key_scheme}"); - - let json_output = self.iota_cli_wrapper.run_command(&cmd)?; - let KeyGenOutput { alias, .. } = serde_json::from_value(json_output)?; - - Ok(alias) - } - - /// Uses the private key corresponding to [IotaAddress] `address` to sign `data`. - /// ## Notes - /// - SHA-512 is used to produce signatures when the key is ed25519. - /// - SHA-256 is used otherwise. - pub fn sign_raw(&self, address: IotaAddress, data: impl AsRef<[u8]>) -> anyhow::Result> { - let cmd = format!("keytool export {address}"); - let keypair = { - let json_output = self.iota_cli_wrapper.run_command(&cmd)?; - let KeyExportOutput { - exported_private_key: bech32_encoded_sk, - } = serde_json::from_value(json_output)?; - - IotaKeyPair::decode(&bech32_encoded_sk).map_err(|e| anyhow!("failed to decode private key: {e:?}"))? - }; - let data = data.as_ref(); - - let sig = match keypair { - IotaKeyPair::Ed25519(sk) => Signer::::sign(&sk, data).sig.to_bytes().to_vec(), - IotaKeyPair::Secp256r1(sk) => Signer::::sign(&sk, data).sig.to_vec(), - IotaKeyPair::Secp256k1(sk) => { - let sig = Signer::::sign(&sk, data); - sig.as_ref().to_vec() - } - }; - - Ok(sig) - } - - /// Updates an alias from `old_alias` to `new_alias` - /// If no value for `new_alias` is provided, a randomly generated one will be used. - pub fn update_alias(&self, old_alias: &str, new_alias: Option<&str>) -> anyhow::Result<()> { - let cmd = format!("keytool update-alias {old_alias} {}", new_alias.unwrap_or_default()); - self - .iota_cli_wrapper - .run_command(&cmd) - .context("failed to update alias")?; - - Ok(()) - } - - /// Returns the [PublicKey] for the given [IotaAddress] together with its alias. - pub fn get_key(&self, address: IotaAddress) -> anyhow::Result> { - self.iota_cli_wrapper.get_key(address) - } - - /// Returns the [PublicKey] that has the given alias, if any. - pub fn get_key_by_alias(&self, alias: &str) -> anyhow::Result> { - self.iota_cli_wrapper.get_key_by_alias(alias) - } -} - -#[derive(Deserialize)] -struct KeyGenOutput { - alias: String, - address: IotaAddress, -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct KeyExportOutput { - exported_private_key: String, -} diff --git a/identity_iota_interaction/src/lib.rs b/identity_iota_interaction/src/lib.rs deleted file mode 100644 index 64ea7b62d..000000000 --- a/identity_iota_interaction/src/lib.rs +++ /dev/null @@ -1,132 +0,0 @@ -// 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 effects_mut_api; -mod iota_client_trait; -mod iota_verifiable_credential; -#[cfg(feature = "keytool")] -pub mod keytool; -mod move_call_traits; -mod move_type; -mod transaction_builder_trait; - -pub use effects_mut_api::*; -pub use iota_client_trait::*; -pub use iota_verifiable_credential::*; -#[cfg(feature = "keytool")] -pub use keytool::*; -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 deleted file mode 100644 index d1d9e8e41..000000000 --- a/identity_iota_interaction/src/move_call_traits.rs +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::collections::HashMap; -use std::collections::HashSet; -use std::iter::IntoIterator; - -use async_trait::async_trait; -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::OptionalSend; -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) {} - -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -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; - - async fn new_identity( - did_doc: Option<&[u8]>, - package_id: ObjectID, - ) -> Result; - - async fn new_with_controllers + OptionalSend>( - did_doc: Option<&[u8]>, - controllers: C, - threshold: u64, - 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; - - async fn propose_update( - identity: OwnedObjectRef, - capability: ObjectRef, - did_doc: Option<&[u8]>, - expiration: Option, - package_id: ObjectID, - ) -> Result; - - async 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 deleted file mode 100644 index ac49e6ee4..000000000 --- a/identity_iota_interaction/src/move_type.rs +++ /dev/null @@ -1,75 +0,0 @@ -// 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 deleted file mode 100644 index 1448df9a3..000000000 --- a/identity_iota_interaction/src/sdk_types/error.rs +++ /dev/null @@ -1,37 +0,0 @@ -// 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 funds for address [{address}], requested amount: {amount}")] - InsufficientFunds { address: IotaAddress, amount: u128 }, - #[error(transparent)] - Json(#[from] serde_json::Error), - #[error("Error caused by a foreign function interface call: {0}")] - FfiError(String), // Added for IOTA interaction -} \ 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 deleted file mode 100644 index 03319749d..000000000 --- a/identity_iota_interaction/src/sdk_types/generated_types.rs +++ /dev/null @@ -1,270 +0,0 @@ -// 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 super::types::crypto::Signature; -use super::types::transaction::TransactionData; - -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; - -// 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_data: TransactionData, - signatures: Vec, - options: Option, - request_type: Option, - ) -> Self { - let tx_data_bcs = bcs::to_bytes(&tx_data).expect("this serialization cannot fail"); - let signatures_b64 = signatures - .into_iter() - .map(|sig| Base64::from_bytes(sig.as_ref())) - .collect(); - ExecuteTransactionBlockParams { - transaction_block: Base64::from_bytes(&tx_data_bcs), - signature: signatures_b64, - 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 - #[serde(skip_serializing_if = "Option::is_none")] - 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, - } - } -} - -/// Params for `wait_for_transaction` / `wait_for_transaction`. -/// -/// Be careful when serializing with `serde_wasm_bindgen::to_value`, as `#[serde(flatten)]` -/// will turn the object into a `Map` instead of a plain object in Js. -/// Prefer serializing with `serde_wasm_bindgen::Serializer::json_compatible` or perform custom serialization. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct WaitForTransactionParams { - /// Block digest and options for content that should be returned. - #[serde(flatten)] - get_transaction_block_params: GetTransactionBlockParams, - /// The amount of time to wait for a transaction block. Defaults to one minute. - #[serde(skip_serializing_if = "Option::is_none")] - timeout: Option, - /// The amount of time to wait between checks for the transaction block. Defaults to 2 seconds. - #[serde(skip_serializing_if = "Option::is_none")] - poll_interval: Option, -} - -impl WaitForTransactionParams { - pub fn new( - digest: String, - options: Option, - timeout: Option, - poll_interval: Option, - ) -> Self { - WaitForTransactionParams { - get_transaction_block_params: GetTransactionBlockParams::new(digest, options), - timeout, - poll_interval, - } - } -} 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 deleted file mode 100644 index 818dbb4fd..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_coin.rs +++ /dev/null @@ -1,36 +0,0 @@ -// 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 deleted file mode 100644 index 339386eb0..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_event.rs +++ /dev/null @@ -1,195 +0,0 @@ -// 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, Base64}; - -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, - /// Base64 encoded bcs bytes of the move event - #[serde(flatten)] - pub bcs: BcsEvent, - /// 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(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", tag = "bcsEncoding")] -#[serde(from = "MaybeTaggedBcsEvent")] -pub enum BcsEvent { - Base64 { - #[serde_as(as = "Base64")] - bcs: Vec, - }, - Base58 { - #[serde_as(as = "Base58")] - bcs: Vec, - }, -} - -impl BcsEvent { - pub fn new(bytes: Vec) -> Self { - Self::Base64 { bcs: bytes } - } - - pub fn bytes(&self) -> &[u8] { - match self { - BcsEvent::Base64 { bcs } => bcs.as_ref(), - BcsEvent::Base58 { bcs } => bcs.as_ref(), - } - } - - pub fn into_bytes(self) -> Vec { - match self { - BcsEvent::Base64 { bcs } => bcs, - BcsEvent::Base58 { bcs } => bcs, - } - } -} - -#[allow(unused)] -#[serde_as] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase", untagged)] -enum MaybeTaggedBcsEvent { - Tagged(TaggedBcsEvent), - Base58 { - #[serde_as(as = "Base58")] - bcs: Vec, - }, -} - -#[serde_as] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase", tag = "bcsEncoding")] -enum TaggedBcsEvent { - Base64 { - #[serde_as(as = "Base64")] - bcs: Vec, - }, - Base58 { - #[serde_as(as = "Base58")] - bcs: Vec, - }, -} - -impl From for BcsEvent { - fn from(event: MaybeTaggedBcsEvent) -> BcsEvent { - let bcs = match event { - MaybeTaggedBcsEvent::Tagged(TaggedBcsEvent::Base58 { bcs }) - | MaybeTaggedBcsEvent::Base58 { bcs } => bcs, - MaybeTaggedBcsEvent::Tagged(TaggedBcsEvent::Base64 { bcs }) => bcs, - }; - - // Bytes are already decoded, force into Base64 variant to avoid serializing to - // base58 - Self::Base64 { bcs } - } -} - -#[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 deleted file mode 100644 index fa5e6b996..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_move.rs +++ /dev/null @@ -1,346 +0,0 @@ -// 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, MoveVariant}, - 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>), - Variant(IotaMoveVariant), -} - -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), - IotaMoveValue::Variant(v) => v.to_json_value(), - } - } -} - -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") - )?; - } - IotaMoveValue::Variant(value) => write!(writer, "{value}")?, - } - 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))) - } - MoveValue::Variant(MoveVariant { - type_, - variant_name, - tag: _, - fields, - }) => IotaMoveValue::Variant(IotaMoveVariant { - type_: type_.clone(), - variant: variant_name.to_string(), - fields: fields - .into_iter() - .map(|(id, value)| (id.into_string(), value.into())) - .collect::>(), - }), - } - } -} - -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(rename = "MoveVariant")] -pub struct IotaMoveVariant { - #[serde(rename = "type")] - #[serde_as(as = "IotaStructTag")] - pub type_: StructTag, - pub variant: String, - pub fields: BTreeMap, -} - -impl IotaMoveVariant { - pub fn to_json_value(self) -> Value { - // We only care about values here, assuming type information is known at the - // client side. - let fields = self - .fields - .into_iter() - .map(|(key, value)| (key, value.to_json_value())) - .collect::>(); - json!({ - "variant": self.variant, - "fields": fields, - }) - } -} - -impl Display for IotaMoveVariant { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut writer = String::new(); - let IotaMoveVariant { - type_, - variant, - fields, - } = self; - writeln!(writer)?; - writeln!(writer, " {}: {type_}", "type")?; - writeln!(writer, " {}: {variant}", "variant")?; - 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')) - } -} - -#[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(), - } - } -} 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 deleted file mode 100644 index 41e84546a..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_object.rs +++ /dev/null @@ -1,808 +0,0 @@ -// 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 schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use serde_with::{DisplayFromStr, serde_as}; -use serde_json::Value; - -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 fastcrypto::encoding::Base64; - -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 - #[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", - )?; - } - if let Some(content) = self.content.as_ref() { - writeln!(writer, "{}", "----- Data -----")?; - write!(writer, "{content}")?; - } - - 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, JsonSchema)] -#[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 dependencies - // 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; - - // try_from_object() and try_from_package() are not defined here because - // MoveObject and MoveStructLayout introduce too many dependencies - - 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; - - // try_from_object() and try_from_package() are not defined here because - // MoveObject and MoveStructLayout introduce too many dependencies - - 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 fields: IotaMoveStruct, -} - -impl IotaParsedMoveObject { - // try_from_object_read()is not defined here because - // MoveObject introduces too many dependencies - - 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 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), - } - } -} 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 deleted file mode 100644 index cc2ffcf40..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_transaction.rs +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - vec::Vec, - fmt::{self, Display, Formatter}, -}; - -use enum_dispatch::enum_dispatch; -use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use serde_with::serde_as; - -use crate::{iota_types::{base_types::EpochId, digests::{TransactionDigest, TransactionEventsDigest}, gas::GasCostSummary, storage::{DeleteKind, WriteKind}}, types::{ - base_types::{ObjectID, SequenceNumber}, execution_status::ExecutionStatus, object::Owner, quorum_driver_types::ExecuteTransactionRequestType, - iota_serde::{BigInt, SequenceNumber as AsSequenceNumber}, -}}; - -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_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, JsonSchema)] -#[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 Display for IotaExecutionStatus { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Self::Success => write!(f, "success"), - Self::Failure { error } => write!(f, "failure due to {error}"), - } - } -} - -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, JsonSchema)] -#[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 - } -} - -#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)] -#[enum_dispatch(IotaTransactionBlockEffectsAPI)] -#[serde( - rename = "TransactionBlockEffects", - rename_all = "camelCase", - tag = "messageVersion" -)] -pub enum IotaTransactionBlockEffects { - V1(IotaTransactionBlockEffectsV1), -} - -#[enum_dispatch] -pub trait IotaTransactionBlockEffectsAPI { - fn status(&self) -> &IotaExecutionStatus; - fn into_status(self) -> IotaExecutionStatus; - fn shared_objects(&self) -> &[IotaObjectRef]; - fn created(&self) -> &[OwnedObjectRef]; - fn mutated(&self) -> &[OwnedObjectRef]; - fn unwrapped(&self) -> &[OwnedObjectRef]; - fn deleted(&self) -> &[IotaObjectRef]; - fn unwrapped_then_deleted(&self) -> &[IotaObjectRef]; - fn wrapped(&self) -> &[IotaObjectRef]; - fn gas_object(&self) -> &OwnedObjectRef; - fn events_digest(&self) -> Option<&TransactionEventsDigest>; - fn dependencies(&self) -> &[TransactionDigest]; - fn executed_epoch(&self) -> EpochId; - fn transaction_digest(&self) -> &TransactionDigest; - fn gas_cost_summary(&self) -> &GasCostSummary; - - /// Return an iterator of mutated objects, but excluding the gas object. - fn mutated_excluding_gas(&self) -> Vec; - fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>; - fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)>; - fn all_deleted_objects(&self) -> Vec<(&IotaObjectRef, DeleteKind)>; -} - -#[serde_as] -#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)] -#[serde( - rename = "TransactionBlockEffectsModifiedAtVersions", - rename_all = "camelCase" -)] -pub struct IotaTransactionBlockEffectsModifiedAtVersions { - object_id: ObjectID, - #[schemars(with = "AsSequenceNumber")] - #[serde_as(as = "AsSequenceNumber")] - sequence_number: SequenceNumber, -} - -/// The response from processing a transaction or a certified transaction -#[serde_as] -#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)] -#[serde(rename = "TransactionBlockEffectsV1", rename_all = "camelCase")] -pub struct IotaTransactionBlockEffectsV1 { - /// The status of the execution - pub status: IotaExecutionStatus, - /// The epoch when this transaction was executed. - #[schemars(with = "BigInt")] - #[serde_as(as = "BigInt")] - pub executed_epoch: EpochId, - pub gas_used: GasCostSummary, - /// The version that every modified (mutated or deleted) object had before - /// it was modified by this transaction. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub modified_at_versions: Vec, - /// The object references of the shared objects used in this transaction. - /// Empty if no shared objects were used. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub shared_objects: Vec, - /// The transaction digest - pub transaction_digest: TransactionDigest, - /// ObjectRef and owner of new objects created. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub created: Vec, - /// ObjectRef and owner of mutated objects, including gas object. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub mutated: Vec, - /// ObjectRef and owner of objects that are unwrapped in this transaction. - /// Unwrapped objects are objects that were wrapped into other objects in - /// the past, and just got extracted out. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub unwrapped: Vec, - /// Object Refs of objects now deleted (the old refs). - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub deleted: Vec, - /// Object refs of objects previously wrapped in other objects but now - /// deleted. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub unwrapped_then_deleted: Vec, - /// Object refs of objects now wrapped in other objects. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub wrapped: Vec, - /// The updated gas object reference. Have a dedicated field for convenient - /// access. It's also included in mutated. - pub gas_object: OwnedObjectRef, - /// The digest of the events emitted during execution, - /// can be None if the transaction does not emit any event. - #[serde(skip_serializing_if = "Option::is_none")] - pub events_digest: Option, - /// The set of transaction digests this transaction depends on. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub dependencies: Vec, -} - -impl IotaTransactionBlockEffectsAPI for IotaTransactionBlockEffectsV1 { - fn status(&self) -> &IotaExecutionStatus { - &self.status - } - fn into_status(self) -> IotaExecutionStatus { - self.status - } - fn shared_objects(&self) -> &[IotaObjectRef] { - &self.shared_objects - } - fn created(&self) -> &[OwnedObjectRef] { - &self.created - } - fn mutated(&self) -> &[OwnedObjectRef] { - &self.mutated - } - fn unwrapped(&self) -> &[OwnedObjectRef] { - &self.unwrapped - } - fn deleted(&self) -> &[IotaObjectRef] { - &self.deleted - } - fn unwrapped_then_deleted(&self) -> &[IotaObjectRef] { - &self.unwrapped_then_deleted - } - fn wrapped(&self) -> &[IotaObjectRef] { - &self.wrapped - } - fn gas_object(&self) -> &OwnedObjectRef { - &self.gas_object - } - fn events_digest(&self) -> Option<&TransactionEventsDigest> { - self.events_digest.as_ref() - } - fn dependencies(&self) -> &[TransactionDigest] { - &self.dependencies - } - - fn executed_epoch(&self) -> EpochId { - self.executed_epoch - } - - fn transaction_digest(&self) -> &TransactionDigest { - &self.transaction_digest - } - - fn gas_cost_summary(&self) -> &GasCostSummary { - &self.gas_used - } - - fn mutated_excluding_gas(&self) -> Vec { - self.mutated - .iter() - .filter(|o| *o != &self.gas_object) - .cloned() - .collect() - } - - fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> { - self.modified_at_versions - .iter() - .map(|v| (v.object_id, v.sequence_number)) - .collect::>() - } - - fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)> { - self.mutated - .iter() - .map(|owner_ref| (owner_ref, WriteKind::Mutate)) - .chain( - self.created - .iter() - .map(|owner_ref| (owner_ref, WriteKind::Create)), - ) - .chain( - self.unwrapped - .iter() - .map(|owner_ref| (owner_ref, WriteKind::Unwrap)), - ) - .collect() - } - - fn all_deleted_objects(&self) -> Vec<(&IotaObjectRef, DeleteKind)> { - self.deleted - .iter() - .map(|r| (r, DeleteKind::Normal)) - .chain( - self.unwrapped_then_deleted - .iter() - .map(|r| (r, DeleteKind::UnwrapThenDelete)), - ) - .chain(self.wrapped.iter().map(|r| (r, DeleteKind::Wrap))) - .collect() - } -} \ No newline at end of file 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 deleted file mode 100644 index 47aa47d99..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -// 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 deleted file mode 100644 index c4867a4a1..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/balance.rs +++ /dev/null @@ -1,95 +0,0 @@ -// 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 deleted file mode 100644 index cfb71ba85..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/base_types.rs +++ /dev/null @@ -1,814 +0,0 @@ -// 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 schemars::JsonSchema; -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, to_iota_struct_tag_string}; -use super::timelock::timelock::{self, TimeLock}; -use super::timelock::timelocked_staked_iota::TimelockedStakedIota; -use super::stardust::output::Nft; -use super::gas_coin::GasCoin; -use super::object::{Owner}; -use super::parse_iota_struct_tag; - -pub use super::digests::{ObjectDigest, TransactionDigest}; - -// ----------------------------------------------------------------- -// Originally defined in crates/iota-types/src/committee.rs -// ----------------------------------------------------------------- -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; -// ----------------------------------------------------------------- -// Originally defined in crates/iota-types/src/execution_status.rs -// ----------------------------------------------------------------- -pub type CommandIndex = usize; -// ----------------------------------------------------------------- -// Originally defined in external-crates/move/crates/move-binary-format/src/file_format.rs -// ----------------------------------------------------------------- -/// Index into the code stream for a jump. The offset is relative to the -/// beginning of the instruction stream. -pub type CodeOffset = u16; -/// 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; -// ----------------------------------------------------------------- - -#[derive( - Eq, - PartialEq, - Ord, - PartialOrd, - Copy, - Clone, - Hash, - Default, - Debug, - Serialize, - Deserialize, - JsonSchema, -)] -pub struct SequenceNumber(u64); - -impl fmt::Display for SequenceNumber { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:#x}", self.0) - } -} - -pub type AuthorityName = AuthorityPublicKeyBytes; - -#[serde_as] -#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] -pub struct ObjectID( - #[schemars(with = "Hex")] - #[serde_as(as = "Readable")] - AccountAddress, -); - -pub type ObjectRef = (ObjectID, SequenceNumber, ObjectDigest); - -/// 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), - /// An 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_v1(&self) -> bool { - self.address() == IOTA_FRAMEWORK_ADDRESS - && self.module().as_str() == "coin" - && self.name().as_str() == "DenyCapV1" - } - - 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 an 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, JsonSchema)] -pub struct IotaAddress( - #[schemars(with = "Hex")] - #[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)) - } -} - -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, -); - -// 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) - } -} - -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::TryFromSlice) - .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 - } -} - -#[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")] - TryFromSlice, -} - -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) - } -} - -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), - } - } -} \ 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 deleted file mode 100644 index e1cd2a27c..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/coin.rs +++ /dev/null @@ -1,186 +0,0 @@ -// 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 COIN_JOIN_FUNC_NAME: &IdentStr = ident_str!("join"); - -pub const PAY_MODULE_NAME: &IdentStr = ident_str!("pay"); -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(Box::new(UID::layout())), - ), - MoveFieldLayout::new( - ident_str!("balance").to_owned(), - MoveTypeLayout::Struct(Box::new(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 deleted file mode 100644 index 28d851db4..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/collection_types.rs +++ /dev/null @@ -1,103 +0,0 @@ -// 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 deleted file mode 100644 index 0ad555fe6..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs +++ /dev/null @@ -1,685 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 -use std::hash::Hash; -use std::str::FromStr; - -use enum_dispatch::enum_dispatch; -use strum::EnumString; -use schemars::JsonSchema; -use derive_more::{AsRef, AsMut, From}; -use eyre::{eyre, Report}; - -use fastcrypto::{ - bls12381::min_sig::{ - BLS12381AggregateSignature, BLS12381AggregateSignatureAsBytes, BLS12381KeyPair, - BLS12381PrivateKey, BLS12381PublicKey, BLS12381Signature, - }, ed25519::{ - Ed25519KeyPair, Ed25519PrivateKey, Ed25519PublicKey, Ed25519PublicKeyAsBytes, - Ed25519Signature - }, encoding::{Base64, Bech32, Encoding}, error::{FastCryptoError, FastCryptoResult}, hash::{Blake2b256, HashFunction}, secp256k1::{Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, Secp256k1Signature}, secp256r1::{Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1PublicKeyAsBytes, Secp256r1Signature}, traits::{Authenticator, EncodeDecodeBase64, KeyPair as KeypairTraits, Signer, ToFromBytes, VerifyingKey} -}; -use fastcrypto_zkp::zk_login_utils::Bn254FrElement; - -use serde::{Deserialize, Deserializer, Serializer}; -use serde::Serialize; -use serde_with::{serde_as, Bytes}; - -use crate::shared_crypto::intent::IntentMessage; - -use super::{ - base_types::IotaAddress, error::{IotaError, IotaResult}, iota_serde::Readable -}; - -const IOTA_PRIV_KEY_PREFIX: &str = "iotaprivkey"; - -// 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; - -// Account Keys -// -// * The following section defines the keypairs that are used by -// * accounts to interact with Iota. -// * Currently we support eddsa and ecdsa on Iota. - -#[expect(clippy::large_enum_variant)] -#[derive(Debug, From, PartialEq, Eq)] -pub enum IotaKeyPair { - Ed25519(Ed25519KeyPair), - Secp256k1(Secp256k1KeyPair), - Secp256r1(Secp256r1KeyPair), -} - -impl IotaKeyPair { - pub fn public(&self) -> PublicKey { - match self { - IotaKeyPair::Ed25519(kp) => PublicKey::Ed25519(kp.public().into()), - IotaKeyPair::Secp256k1(kp) => PublicKey::Secp256k1(kp.public().into()), - IotaKeyPair::Secp256r1(kp) => PublicKey::Secp256r1(kp.public().into()), - } - } - - pub fn copy(&self) -> Self { - match self { - IotaKeyPair::Ed25519(kp) => kp.copy().into(), - IotaKeyPair::Secp256k1(kp) => kp.copy().into(), - IotaKeyPair::Secp256r1(kp) => kp.copy().into(), - } - } -} - -impl EncodeDecodeBase64 for IotaKeyPair { - fn encode_base64(&self) -> String { - Base64::encode(self.to_bytes()) - } - - fn decode_base64(value: &str) -> FastCryptoResult { - let bytes = Base64::decode(value)?; - Self::from_bytes(&bytes).map_err(|_| FastCryptoError::InvalidInput) - } -} -impl IotaKeyPair { - pub fn to_bytes(&self) -> Vec { - let mut bytes: Vec = Vec::new(); - bytes.push(self.public().flag()); - - match self { - IotaKeyPair::Ed25519(kp) => { - bytes.extend_from_slice(kp.as_bytes()); - } - IotaKeyPair::Secp256k1(kp) => { - bytes.extend_from_slice(kp.as_bytes()); - } - IotaKeyPair::Secp256r1(kp) => { - bytes.extend_from_slice(kp.as_bytes()); - } - } - bytes - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - match SignatureScheme::from_flag_byte(bytes.first().ok_or_else(|| eyre!("Invalid length"))?) - { - Ok(x) => match x { - SignatureScheme::ED25519 => Ok(IotaKeyPair::Ed25519(Ed25519KeyPair::from_bytes( - bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, - )?)), - SignatureScheme::Secp256k1 => { - Ok(IotaKeyPair::Secp256k1(Secp256k1KeyPair::from_bytes( - bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, - )?)) - } - SignatureScheme::Secp256r1 => { - Ok(IotaKeyPair::Secp256r1(Secp256r1KeyPair::from_bytes( - bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, - )?)) - } - _ => Err(eyre!("Invalid flag byte")), - }, - _ => Err(eyre!("Invalid bytes")), - } - } - - pub fn to_bytes_no_flag(&self) -> Vec { - match self { - IotaKeyPair::Ed25519(kp) => kp.as_bytes().to_vec(), - IotaKeyPair::Secp256k1(kp) => kp.as_bytes().to_vec(), - IotaKeyPair::Secp256r1(kp) => kp.as_bytes().to_vec(), - } - } - - /// Encode a IotaKeyPair as `flag || privkey` in Bech32 starting with - /// "iotaprivkey" to a string. Note that the pubkey is not encoded. - pub fn encode(&self) -> Result { - Bech32::encode(self.to_bytes(), IOTA_PRIV_KEY_PREFIX).map_err(|e| eyre!(e)) - } - - /// Decode a IotaKeyPair from `flag || privkey` in Bech32 starting with - /// "iotaprivkey" to IotaKeyPair. The public key is computed directly from - /// the private key bytes. - pub fn decode(value: &str) -> Result { - let bytes = Bech32::decode(value, IOTA_PRIV_KEY_PREFIX)?; - Self::from_bytes(&bytes) - } -} - -impl Serialize for IotaKeyPair { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let s = self.encode_base64(); - serializer.serialize_str(&s) - } -} - -impl<'de> Deserialize<'de> for IotaKeyPair { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - use serde::de::Error; - let s = String::deserialize(deserializer)?; - IotaKeyPair::decode_base64(&s).map_err(|e| Error::custom(e.to_string())) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum PublicKey { - Ed25519(Ed25519PublicKeyAsBytes), - Secp256k1(Secp256k1PublicKeyAsBytes), - Secp256r1(Secp256r1PublicKeyAsBytes), - ZkLogin(ZkLoginPublicIdentifier), - Passkey(Secp256r1PublicKeyAsBytes), -} - -/// 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, - PublicKey::Passkey(pk) => &pk.0, - } - } -} - -impl EncodeDecodeBase64 for PublicKey { - fn encode_base64(&self) -> String { - let mut bytes: Vec = Vec::new(); - bytes.extend_from_slice(&[self.flag()]); - bytes.extend_from_slice(self.as_ref()); - Base64::encode(&bytes[..]) - } - - fn decode_base64(value: &str) -> FastCryptoResult { - let bytes = Base64::decode(value)?; - match bytes.first() { - Some(x) => { - if x == &SignatureScheme::ED25519.flag() { - let pk: Ed25519PublicKey = - Ed25519PublicKey::from_bytes(bytes.get(1..).ok_or( - FastCryptoError::InputLengthWrong(Ed25519PublicKey::LENGTH + 1), - )?)?; - Ok(PublicKey::Ed25519((&pk).into())) - } else if x == &SignatureScheme::Secp256k1.flag() { - let pk = Secp256k1PublicKey::from_bytes(bytes.get(1..).ok_or( - FastCryptoError::InputLengthWrong(Secp256k1PublicKey::LENGTH + 1), - )?)?; - Ok(PublicKey::Secp256k1((&pk).into())) - } else if x == &SignatureScheme::Secp256r1.flag() { - let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or( - FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1), - )?)?; - Ok(PublicKey::Secp256r1((&pk).into())) - } else if x == &SignatureScheme::PasskeyAuthenticator.flag() { - let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or( - FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1), - )?)?; - Ok(PublicKey::Passkey((&pk).into())) - } else { - Err(FastCryptoError::InvalidInput) - } - } - _ => Err(FastCryptoError::InvalidInput), - } - } -} - -impl PublicKey { - pub fn flag(&self) -> u8 { - self.scheme().flag() - } - - pub fn try_from_bytes( - curve: SignatureScheme, - key_bytes: &[u8], - ) -> Result { - match curve { - SignatureScheme::ED25519 => Ok(PublicKey::Ed25519( - (&Ed25519PublicKey::from_bytes(key_bytes)?).into(), - )), - SignatureScheme::Secp256k1 => Ok(PublicKey::Secp256k1( - (&Secp256k1PublicKey::from_bytes(key_bytes)?).into(), - )), - SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1( - (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), - )), - SignatureScheme::PasskeyAuthenticator => Ok(PublicKey::Passkey( - (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), - )), - _ => Err(eyre!("Unsupported curve")), - } - } - - pub fn scheme(&self) -> SignatureScheme { - match self { - PublicKey::Ed25519(_) => SignatureScheme::ED25519, // Equals Ed25519IotaSignature::SCHEME - PublicKey::Secp256k1(_) => SignatureScheme::Secp256k1, // Equals Secp256k1IotaSignature::SCHEME - PublicKey::Secp256r1(_) => SignatureScheme::Secp256r1, // Equals Secp256r1IotaSignature::SCHEME - PublicKey::ZkLogin(_) => SignatureScheme::ZkLoginAuthenticator, - PublicKey::Passkey(_) => SignatureScheme::PasskeyAuthenticator, - } - } -} - -/// 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 // schemars::JsonSchema and AsRef are omitted here, having Debug instead -)] -pub struct AuthorityPublicKeyBytes( - #[serde_as(as = "Readable")] - pub [u8; AuthorityPublicKey::LENGTH], -); - -// Enums for signature scheme signatures -#[enum_dispatch] -#[derive(Clone, JsonSchema, Debug, PartialEq, Eq, Hash)] -pub enum Signature { - Ed25519IotaSignature, - Secp256k1IotaSignature, - Secp256r1IotaSignature, -} - -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let bytes = self.as_ref(); - - if serializer.is_human_readable() { - let s = Base64::encode(bytes); - serializer.serialize_str(&s) - } else { - serializer.serialize_bytes(bytes) - } - } -} - -impl<'de> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - use serde::de::Error; - - let bytes = if deserializer.is_human_readable() { - let s = String::deserialize(deserializer)?; - Base64::decode(&s).map_err(|e| Error::custom(e.to_string()))? - } else { - let data: Vec = Vec::deserialize(deserializer)?; - data - }; - - Self::from_bytes(&bytes).map_err(|e| Error::custom(e.to_string())) - } -} - -impl AsRef<[u8]> for Signature { - fn as_ref(&self) -> &[u8] { - match self { - Signature::Ed25519IotaSignature(sig) => sig.as_ref(), - Signature::Secp256k1IotaSignature(sig) => sig.as_ref(), - Signature::Secp256r1IotaSignature(sig) => sig.as_ref(), - } - } -} -impl AsMut<[u8]> for Signature { - fn as_mut(&mut self) -> &mut [u8] { - match self { - Signature::Ed25519IotaSignature(sig) => sig.as_mut(), - Signature::Secp256k1IotaSignature(sig) => sig.as_mut(), - Signature::Secp256r1IotaSignature(sig) => sig.as_mut(), - } - } -} - -impl ToFromBytes for Signature { - fn from_bytes(bytes: &[u8]) -> Result { - match bytes.first() { - Some(x) => { - if x == &Ed25519IotaSignature::SCHEME.flag() { - Ok(::from_bytes(bytes)?.into()) - } else if x == &Secp256k1IotaSignature::SCHEME.flag() { - Ok(::from_bytes(bytes)?.into()) - } else if x == &Secp256r1IotaSignature::SCHEME.flag() { - Ok(::from_bytes(bytes)?.into()) - } else { - Err(FastCryptoError::InvalidInput) - } - } - _ => Err(FastCryptoError::InvalidInput), - } - } -} - -// Ed25519 Iota Signature port -// - -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] -#[as_ref(forward)] -#[as_mut(forward)] -pub struct Ed25519IotaSignature( - #[schemars(with = "Base64")] - #[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 IotaSignatureInner for Ed25519IotaSignature { - type Sig = Ed25519Signature; - type PubKey = Ed25519PublicKey; - type KeyPair = Ed25519KeyPair; - const LENGTH: usize = Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1; -} - -impl IotaPublicKey for Ed25519PublicKey { - const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::ED25519; -} - -impl ToFromBytes for Ed25519IotaSignature { - fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != Self::LENGTH { - return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); - } - let mut sig_bytes = [0; Self::LENGTH]; - sig_bytes.copy_from_slice(bytes); - Ok(Self(sig_bytes)) - } -} - -// Secp256k1 Iota Signature port -// -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] -#[as_ref(forward)] -#[as_mut(forward)] -pub struct Secp256k1IotaSignature( - #[schemars(with = "Base64")] - #[serde_as(as = "Readable")] - [u8; Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1], -); - -impl IotaSignatureInner for Secp256k1IotaSignature { - type Sig = Secp256k1Signature; - type PubKey = Secp256k1PublicKey; - type KeyPair = Secp256k1KeyPair; - const LENGTH: usize = Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1; -} - -impl IotaPublicKey for Secp256k1PublicKey { - const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256k1; -} - -impl ToFromBytes for Secp256k1IotaSignature { - fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != Self::LENGTH { - return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); - } - let mut sig_bytes = [0; Self::LENGTH]; - sig_bytes.copy_from_slice(bytes); - Ok(Self(sig_bytes)) - } -} - -// Secp256r1 Iota Signature port -// -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] -#[as_ref(forward)] -#[as_mut(forward)] -pub struct Secp256r1IotaSignature( - #[schemars(with = "Base64")] - #[serde_as(as = "Readable")] - [u8; Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1], -); - -impl IotaSignatureInner for Secp256r1IotaSignature { - type Sig = Secp256r1Signature; - type PubKey = Secp256r1PublicKey; - type KeyPair = Secp256r1KeyPair; - const LENGTH: usize = Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1; -} - -impl IotaPublicKey for Secp256r1PublicKey { - const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256r1; -} - -impl ToFromBytes for Secp256r1IotaSignature { - fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != Self::LENGTH { - return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); - } - let mut sig_bytes = [0; Self::LENGTH]; - sig_bytes.copy_from_slice(bytes); - Ok(Self(sig_bytes)) - } -} - -// This struct exists due to the limitations of the `enum_dispatch` library. -// -pub trait IotaSignatureInner: Sized + ToFromBytes + PartialEq + Eq + Hash { - type Sig: Authenticator; - type PubKey: VerifyingKey + IotaPublicKey; - type KeyPair: KeypairTraits; - - const LENGTH: usize = Self::Sig::LENGTH + Self::PubKey::LENGTH + 1; - const SCHEME: SignatureScheme = Self::PubKey::SIGNATURE_SCHEME; - - /// Returns the deserialized signature and deserialized pubkey. - fn get_verification_inputs(&self) -> IotaResult<(Self::Sig, Self::PubKey)> { - let pk = Self::PubKey::from_bytes(self.public_key_bytes()) - .map_err(|_| IotaError::KeyConversion("Invalid public key".to_string()))?; - - // deserialize the signature - let signature = Self::Sig::from_bytes(self.signature_bytes()).map_err(|_| { - IotaError::InvalidSignature { - error: "Fail to get pubkey and sig".to_string(), - } - })?; - - Ok((signature, pk)) - } - - fn new(kp: &Self::KeyPair, message: &[u8]) -> Self { - let sig = Signer::sign(kp, message); - - let mut signature_bytes: Vec = Vec::new(); - signature_bytes - .extend_from_slice(&[::SIGNATURE_SCHEME.flag()]); - signature_bytes.extend_from_slice(sig.as_ref()); - signature_bytes.extend_from_slice(kp.public().as_ref()); - Self::from_bytes(&signature_bytes[..]) - .expect("Serialized signature did not have expected size") - } -} - -pub trait IotaPublicKey: VerifyingKey { - const SIGNATURE_SCHEME: SignatureScheme; -} - -#[enum_dispatch(Signature)] -pub trait IotaSignature: Sized + ToFromBytes { - fn signature_bytes(&self) -> &[u8]; - fn public_key_bytes(&self) -> &[u8]; - fn scheme(&self) -> SignatureScheme; - - fn verify_secure( - &self, - value: &IntentMessage, - author: IotaAddress, - scheme: SignatureScheme, - ) -> IotaResult<()> - where - T: Serialize; -} - -impl IotaSignature for S { - fn signature_bytes(&self) -> &[u8] { - // Access array slice is safe because the array bytes is initialized as - // flag || signature || pubkey with its defined length. - &self.as_ref()[1..1 + S::Sig::LENGTH] - } - - fn public_key_bytes(&self) -> &[u8] { - // Access array slice is safe because the array bytes is initialized as - // flag || signature || pubkey with its defined length. - &self.as_ref()[S::Sig::LENGTH + 1..] - } - - fn scheme(&self) -> SignatureScheme { - S::PubKey::SIGNATURE_SCHEME - } - - fn verify_secure( - &self, - value: &IntentMessage, - author: IotaAddress, - scheme: SignatureScheme, - ) -> Result<(), IotaError> - where - T: Serialize, - { - let mut hasher = DefaultHash::default(); - hasher.update(bcs::to_bytes(&value).expect("Message serialization should not fail")); - let digest = hasher.finalize().digest; - - let (sig, pk) = &self.get_verification_inputs()?; - match scheme { - SignatureScheme::ZkLoginAuthenticator => {} // Pass this check because zk login does - // not derive address from pubkey. - _ => { - let address = IotaAddress::from(pk); - if author != address { - return Err(IotaError::IncorrectSigner { - error: format!( - "Incorrect signer, expected {:?}, got {:?}", - author, address - ), - }); - } - } - } - - pk.verify(&digest, sig) - .map_err(|e| IotaError::InvalidSignature { - error: format!("Fail to verify user sig {}", e), - }) - } -} - -#[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, - PasskeyAuthenticator, -} - -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, - SignatureScheme::PasskeyAuthenticator => 0x06, - } - } - - /// 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), - 0x06 => Ok(SignatureScheme::PasskeyAuthenticator), - _ => Err(IotaError::KeyConversion("Invalid key scheme".to_string())), - } - } -} - -impl FromStr for Signature { - type Err = eyre::Report; - fn from_str(s: &str) -> Result { - Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.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 deleted file mode 100644 index 4b3dc51bf..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/digests.rs +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt; - -use schemars::JsonSchema; -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, JsonSchema)] -pub struct Digest( - #[schemars(with = "Base58")] - #[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 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 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(()) - } -} - - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct CheckpointContentsDigest(Digest); - -impl CheckpointContentsDigest { - 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 CheckpointContentsDigest { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsRef<[u8; 32]> for CheckpointContentsDigest { - fn as_ref(&self) -> &[u8; 32] { - self.0.as_ref() - } -} - -impl From for [u8; 32] { - fn from(digest: CheckpointContentsDigest) -> Self { - digest.into_inner() - } -} - -impl From<[u8; 32]> for CheckpointContentsDigest { - fn from(digest: [u8; 32]) -> Self { - Self::new(digest) - } -} - -impl fmt::Display for CheckpointContentsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -impl fmt::Debug for CheckpointContentsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("CheckpointContentsDigest") - .field(&self.0) - .finish() - } -} - -impl std::str::FromStr for CheckpointContentsDigest { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let mut result = [0; 32]; - let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?; - if buffer.len() != 32 { - return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes")); - } - result.copy_from_slice(&buffer); - Ok(CheckpointContentsDigest::new(result)) - } -} - -impl fmt::LowerHex for CheckpointContentsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::LowerHex::fmt(&self.0, f) - } -} - -impl fmt::UpperHex for CheckpointContentsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] -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) - } - - pub fn next_lexicographical(&self) -> Option { - self.0.next_lexicographical().map(Self) - } -} - -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]; - let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?; - if buffer.len() != 32 { - return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes")); - } - result.copy_from_slice(&buffer); - Ok(TransactionDigest::new(result)) - } -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct TransactionEffectsDigest(Digest); - -impl TransactionEffectsDigest { - 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 TransactionEffectsDigest { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsRef<[u8; 32]> for TransactionEffectsDigest { - fn as_ref(&self) -> &[u8; 32] { - self.0.as_ref() - } -} - -impl From for [u8; 32] { - fn from(digest: TransactionEffectsDigest) -> Self { - digest.into_inner() - } -} - -impl From<[u8; 32]> for TransactionEffectsDigest { - fn from(digest: [u8; 32]) -> Self { - Self::new(digest) - } -} - -impl fmt::Display for TransactionEffectsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -impl fmt::Debug for TransactionEffectsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("TransactionEffectsDigest") - .field(&self.0) - .finish() - } -} - -impl fmt::LowerHex for TransactionEffectsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::LowerHex::fmt(&self.0, f) - } -} - -impl fmt::UpperHex for TransactionEffectsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -#[serde_as] -#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)] -pub struct TransactionEventsDigest(Digest); - -impl TransactionEventsDigest { - pub const ZERO: Self = Self(Digest::ZERO); - - pub const fn new(digest: [u8; 32]) -> Self { - Self(Digest::new(digest)) - } - - pub fn random() -> Self { - Self(Digest::random()) - } - - pub fn next_lexicographical(&self) -> Option { - self.0.next_lexicographical().map(Self) - } - - pub fn into_inner(self) -> [u8; 32] { - self.0.into_inner() - } -} - -impl fmt::Debug for TransactionEventsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("TransactionEventsDigest") - .field(&self.0) - .finish() - } -} - -impl AsRef<[u8]> for TransactionEventsDigest { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsRef<[u8; 32]> for TransactionEventsDigest { - fn as_ref(&self) -> &[u8; 32] { - self.0.as_ref() - } -} - -impl std::str::FromStr for TransactionEventsDigest { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let mut result = [0; 32]; - let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?; - if buffer.len() != 32 { - return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes")); - } - result.copy_from_slice(&buffer); - Ok(Self::new(result)) - } -} - -// Each object has a unique digest -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] -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) - } -} 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 deleted file mode 100644 index fa3c16d6a..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/dynamic_field.rs +++ /dev/null @@ -1,156 +0,0 @@ -// 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::Base64}; - -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 deleted file mode 100644 index afcb18401..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/error.rs +++ /dev/null @@ -1,943 +0,0 @@ -// 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::*, - digests::{ObjectDigest, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest, - CheckpointContentsDigest}, - execution_status::{CommandArgumentError, ExecutionFailureStatus}, - 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")] - ImmutableParameterExpected { 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")] - NotSharedObject, - #[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" - )] - TransferObjectWithoutPublicTransfer { 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}")] - MoveFunctionInput(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(CheckpointContentsDigest), - - #[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, - - // Soft Bundle related errors - #[error( - "Number of transactions exceeds the maximum allowed ({:?}) in a Soft Bundle", - limit - )] - TooManyTransactionsInSoftBundle { limit: u64 }, - #[error("Transaction {:?} in Soft Bundle contains no shared objects", digest)] - NoSharedObject { digest: TransactionDigest }, - #[error("Transaction {:?} in Soft Bundle has already been executed", digest)] - AlreadyExecuted { digest: TransactionDigest }, - #[error("At least one certificate in Soft Bundle has already been processed")] - CertificateAlreadyProcessed, - #[error( - "Gas price for transaction {:?} in Soft Bundle mismatch: want {:?}, have {:?}", - digest, - expected, - actual - )] - GasPriceMismatch { - digest: TransactionDigest, - expected: u64, - actual: u64, - }, - - #[error("Coin type is globally paused for use: {coin_type}")] - CoinTypeGlobalPause { coin_type: String }, -} - -#[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)] - Display { 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, - }, - - #[error("Soft bundle must only contain transactions of UserTransaction kind")] - InvalidTxKindInSoftBundle, - - // 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, // Committee is not available for wasm32 - }, - #[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, - - // 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("Invalid DKG message size")] - InvalidDkgMessageSize, - - #[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: TransactionEventsDigest }, - #[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 }, - - // GenericBridge is not available - - #[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("Operations for epoch {0} have ended")] - EpochEnded(EpochId), - #[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("Method not allowed")] - InvalidRpcMethod, - - // TODO: We should fold this into UserInputError::Unsupported. - #[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("Failed to read or deserialize bridge related data structures on-chain: {0}")] - IotaBridgeRead(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 }, - - #[error("Too many requests")] - TooManyRequests, - - #[error("The request did not contain a certificate")] - NoCertificateProvided, -} - -#[repr(u64)] -#[expect(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)] -#[expect(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; - -// iota_protocol_config::Error is not available - -impl From for IotaError { - fn from(error: ExecutionError) -> Self { - IotaError::Execution(error.to_string()) - } -} - -// Status, TypedStoreError, crate::storage::error::Error are not available - -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 From for IotaError { - fn from(error: String) -> Self { - IotaError::GenericAuthority { error } - } -} - -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 { - IotaError::Rpc { .. } => true, - - // Reconfig error - IotaError::ValidatorHaltedAtEpochEnd => true, - IotaError::MissingCommitteeAtEpoch(..) => true, - IotaError::WrongEpoch { .. } => true, - IotaError::EpochEnded { .. } => 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, - - // NB: This is not an internal overload, but instead an imposed rate - // limit / blocking of a client. It must be non-retryable otherwise - // we will make the threat worse through automatic retries. - IotaError::TooManyRequests => 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 { .. }) - } - - pub fn retry_after_secs(&self) -> u64 { - match self { - IotaError::ValidatorOverloadedRetryAfter { retry_after_secs } => *retry_after_secs, - _ => 0, - } - } -} - -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, - )) -} diff --git a/identity_iota_interaction/src/sdk_types/iota_types/event.rs b/identity_iota_interaction/src/sdk_types/iota_types/event.rs deleted file mode 100644 index ddf02a23c..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/event.rs +++ /dev/null @@ -1,55 +0,0 @@ -// 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 an 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 deleted file mode 100644 index 9471ec477..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/execution_status.rs +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt::{self, Display, Formatter}; - -use super::super::move_core_types::language_storage::ModuleId; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -use super::base_types::{ObjectID, IotaAddress, TypeParameterIndex, CodeOffset}; - -#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] // Needed for QuorumDriverTrait implementation in IotaClientTsSdk -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)] -pub struct CongestedObjects(pub Vec); - -impl fmt::Display for CongestedObjects { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - for obj in &self.0 { - write!(f, "{}, ", obj)?; - } - Ok(()) - } -} - -#[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 an 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." - )] - IotaMoveVerificationTimeout, - - #[error("The shared object operation is not allowed.")] - SharedObjectOperationNotAllowed, - - #[error("Certificate cannot be executed due to a dependency on a deleted shared object")] - InputObjectDeleted, - - #[error("Certificate is cancelled due to congestion on shared objects: {congested_objects}")] - ExecutionCancelledDueToSharedObjectCongestion { congested_objects: CongestedObjects }, - - #[error("Address {address:?} is denied for coin {coin_type}")] - AddressDeniedForCoin { - address: IotaAddress, - coin_type: String, - }, - - #[error("Coin type is globally paused for use: {coin_type}")] - CoinTypeGlobalPause { coin_type: String }, - - #[error("Certificate is cancelled because randomness could not be generated this epoch")] - ExecutionCancelledDueToRandomnessUnavailable, - // 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; diff --git a/identity_iota_interaction/src/sdk_types/iota_types/gas.rs b/identity_iota_interaction/src/sdk_types/iota_types/gas.rs deleted file mode 100644 index 3165ad200..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/gas.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; -use super::iota_serde::{BigInt, Readable}; - -/// Summary of the charges in a transaction. -/// Storage is charged independently of computation. -/// There are 3 parts to the storage charges: -/// `storage_cost`: it is the charge of storage at the time the transaction -/// is executed. The cost of storage is the number of -/// bytes of the objects being mutated multiplied by a -/// variable storage cost per byte `storage_rebate`: this is the amount -/// a user gets back when manipulating an object. The -/// `storage_rebate` is the `storage_cost` for an object minus fees. -/// `non_refundable_storage_fee`: not all the value of the object storage -/// cost is given back to user and there -/// is a small fraction that is kept by -/// the system. This value tracks that charge. -/// -/// When looking at a gas cost summary the amount charged to the user is -/// `computation_cost + storage_cost - storage_rebate` -/// and that is the amount that is deducted from the gas coins. -/// `non_refundable_storage_fee` is collected from the objects being -/// mutated/deleted and it is tracked by the system in storage funds. -/// -/// Objects deleted, including the older versions of objects mutated, have -/// the storage field on the objects added up to a pool of "potential -/// rebate". This rebate then is reduced by the "nonrefundable rate" -/// such that: `potential_rebate(storage cost of deleted/mutated -/// objects) = storage_rebate + non_refundable_storage_fee` - -#[serde_as] -#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "camelCase")] -pub struct GasCostSummary { - /// Cost of computation/execution - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub computation_cost: u64, - /// The burned component of the computation/execution costs - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub computation_cost_burned: u64, - /// Storage cost, it's the sum of all storage cost for all objects - /// created or mutated. - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub storage_cost: u64, - /// The amount of storage cost refunded to the user for all objects - /// deleted or mutated in the transaction. - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub storage_rebate: u64, - /// The fee for the rebate. The portion of the storage rebate kept by - /// the system. - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub non_refundable_storage_fee: u64, -} 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 deleted file mode 100644 index b8cafc972..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/gas_coin.rs +++ /dev/null @@ -1,147 +0,0 @@ -// 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() - } - - // MoveObject is not available for wasm32 - // - // pub fn to_object(&self, version: SequenceNumber) -> MoveObject { - // MoveObject::new_gas_coin(version, *self.id(), self.value()) - // } - - 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 `TreasuryCap` object ID. - pub fn id(&self) -> &ObjectID { - self.inner.id.object_id() - } - - /// 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 deleted file mode 100644 index e9ed0fe3e..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/governance.rs +++ /dev/null @@ -1,98 +0,0 @@ -// 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 deleted file mode 100644 index b8727295a..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/id.rs +++ /dev/null @@ -1,108 +0,0 @@ -// 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(Box::new(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_())) - } -} 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 deleted file mode 100644 index f931a3dd0..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs +++ /dev/null @@ -1,409 +0,0 @@ -// 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; -#[allow(unused)] // Kept in sync with original source, so keep as is. -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 schemars::JsonSchema; - -use Result; - -use super::super::move_core_types::{ - account_address::AccountAddress, - language_storage::{StructTag, TypeTag} -}; - -#[allow(unused)] // Kept in sync with original source, so keep as is. -use super::{IOTA_FRAMEWORK_ADDRESS, MOVE_STDLIB_ADDRESS, IOTA_SYSTEM_ADDRESS, - STARDUST_ADDRESS, IOTA_SYSTEM_STATE_ADDRESS, IOTA_CLOCK_ADDRESS }; -use super::parse_iota_struct_tag; -use super::parse_iota_type_tag; - -/// The minimum and maximum protocol versions supported by this build. -const MIN_PROTOCOL_VERSION: u64 = 1; // Originally defined in crates/iota-protocol-config/src/lib.rs -pub const MAX_PROTOCOL_VERSION: u64 = 1; // Originally defined in crates/iota-protocol-config/src/lib.rs - -// ----------------------------------------------------------------------------------------- -// Originally contained in crates/iota-protocol-config/src/lib.rs -// ----------------------------------------------------------------------------------------- - -// Record history of protocol version allocations here: -// -// Version 1: Original version. -// Version 2: Don't redistribute slashed staking rewards, fix computation of -// SystemEpochInfoEventV1. -#[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); - - #[allow(unused)] // Kept in sync with original source, so keep as is. - #[cfg(not(msim))] - const MAX_ALLOWED: Self = Self::MAX; - - // We create 3 additional "fake" versions in simulator builds so that we can - // test upgrades. - #[cfg(msim)] - pub const MAX_ALLOWED: Self = Self(MAX_PROTOCOL_VERSION + 3); - - 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) - } -} - -// ----------------------------------------------------------------------------------------- -// End of originally contained in crates/iota-protocol-config/src/lib.rs section -// ----------------------------------------------------------------------------------------- - -#[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; 7] = [ - AccountAddress::ZERO, - AccountAddress::ONE, - IOTA_FRAMEWORK_ADDRESS, - IOTA_SYSTEM_ADDRESS, - STARDUST_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, JsonSchema)] -pub struct BigInt( - #[schemars(with = "String")] - #[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, JsonSchema)] -pub struct SequenceNumber(#[schemars(with = "BigInt")] 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)) - } -} - -#[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)) - } -} 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 deleted file mode 100644 index a9501dade..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[allow(unused)] // Kept in sync with original source, so keep as is. -use serde::{Deserialize, Serialize}; -use super::super::move_core_types::{ - account_address::AccountAddress, - language_storage::{TypeTag, StructTag}, -}; -use super::base_types::{ObjectID, SequenceNumber, IotaAddress}; -use super::object::OBJECT_START_VERSION; - -macro_rules! built_in_ids { - ($($addr:ident / $id:ident = $init:expr);* $(;)?) => { - $( - pub const $addr: AccountAddress = builtin_address($init); - pub const $id: ObjectID = ObjectID::from_address($addr); - )* - } -} - -macro_rules! built_in_pkgs { - ($($addr:ident / $id:ident = $init:expr);* $(;)?) => { - built_in_ids! { $($addr / $id = $init;)* } - pub const SYSTEM_PACKAGE_ADDRESSES: &[AccountAddress] = &[$($addr),*]; - pub fn is_system_package(addr: impl Into) -> bool { - matches!(addr.into(), $($addr)|*) - } - } -} - -built_in_pkgs! { - MOVE_STDLIB_ADDRESS / MOVE_STDLIB_PACKAGE_ID = 0x1; - IOTA_FRAMEWORK_ADDRESS / IOTA_FRAMEWORK_PACKAGE_ID = 0x2; - IOTA_SYSTEM_ADDRESS / IOTA_SYSTEM_PACKAGE_ID = 0x3; - BRIDGE_ADDRESS / BRIDGE_PACKAGE_ID = 0xb; - STARDUST_ADDRESS / STARDUST_PACKAGE_ID = 0x107a; -} - -built_in_ids! { - IOTA_SYSTEM_STATE_ADDRESS / IOTA_SYSTEM_STATE_OBJECT_ID = 0x5; - IOTA_CLOCK_ADDRESS / IOTA_CLOCK_OBJECT_ID = 0x6; - IOTA_AUTHENTICATOR_STATE_ADDRESS / IOTA_AUTHENTICATOR_STATE_OBJECT_ID = 0x7; - IOTA_RANDOMNESS_STATE_ADDRESS / IOTA_RANDOMNESS_STATE_OBJECT_ID = 0x8; - IOTA_BRIDGE_ADDRESS / IOTA_BRIDGE_OBJECT_ID = 0x9; - IOTA_DENY_LIST_ADDRESS / IOTA_DENY_LIST_OBJECT_ID = 0x403; -} - -pub const IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; -pub const IOTA_CLOCK_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; -pub const IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; - -const fn builtin_address(suffix: u16) -> AccountAddress { - let mut addr = [0u8; AccountAddress::LENGTH]; - let [hi, lo] = suffix.to_be_bytes(); - addr[AccountAddress::LENGTH - 2] = hi; - addr[AccountAddress::LENGTH - 1] = lo; - AccountAddress::new(addr) -} - -/// 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_core_types::parsing::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_core_types::parsing::types::ParsedType; - ParsedType::parse(s)?.into_type_tag(&resolve_address) -} - -/// Resolve well-known named addresses into numeric addresses. -pub fn resolve_address(addr: &str) -> Option { - match addr { - "std" => Some(MOVE_STDLIB_ADDRESS), - "iota" => Some(IOTA_FRAMEWORK_ADDRESS), - "iota_system" => Some(IOTA_SYSTEM_ADDRESS), - "stardust" => Some(STARDUST_ADDRESS), - "bridge" => Some(BRIDGE_ADDRESS), - _ => None, - } -} - -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())) - } -} diff --git a/identity_iota_interaction/src/sdk_types/iota_types/mod.rs b/identity_iota_interaction/src/sdk_types/iota_types/mod.rs deleted file mode 100644 index 8f75e8f72..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -// 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 event; -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 gas; -pub mod storage; - -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 deleted file mode 100644 index e7f63eb30..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/move_package.rs +++ /dev/null @@ -1,84 +0,0 @@ -// 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, - // `struct_name` alias to support backwards compatibility with the old name - #[serde(alias = "struct_name")] - pub datatype_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 { - pub(crate) 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. - pub(crate) version: SequenceNumber, - // TODO use session cache - #[serde_as(as = "BTreeMap<_, Bytes>")] - pub(crate) module_map: BTreeMap>, - - /// Maps struct/module to a package version where it was first defined, - /// stored as a vector for simple serialization and deserialization. - pub(crate) type_origin_table: Vec, - - // For each dependency, maps original package ID to the info about the (upgraded) dependency - // version that this package is using - pub(crate) 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 deleted file mode 100644 index 1c9c7930c..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/object.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt::{Display, Formatter}; - -use schemars::JsonSchema; -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, JsonSchema)] -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 { - initial_shared_version, - } => { - write!(f, "Shared( {} )", initial_shared_version.value()) - } - } - } -} 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 deleted file mode 100644 index 3eb37fcbb..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/quorum_driver_types.rs +++ /dev/null @@ -1,12 +0,0 @@ -// 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 deleted file mode 100644 index f6606f3f8..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/stardust/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub mod output; diff --git a/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs b/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs deleted file mode 100644 index 28d547dcf..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub mod nft; - -pub use nft::*; \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs b/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs deleted file mode 100644 index b53b83d54..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::ident_str; - -use crate::sdk_types::move_types::language_storage::StructTag; -use crate::sdk_types::move_types::identifier::IdentStr; - -use super::super::super::STARDUST_ADDRESS; - -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_ADDRESS.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/storage.rs b/identity_iota_interaction/src/sdk_types/iota_types/storage.rs deleted file mode 100644 index 2d6ca38ea..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/storage.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] -pub enum WriteKind { - /// The object was in storage already but has been modified - Mutate, - /// The object was created in this transaction - Create, - /// The object was previously wrapped in another object, but has been - /// restored to storage - Unwrap, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] -pub enum DeleteKind { - /// An object is provided in the call input, and gets deleted. - Normal, - /// An object is not provided in the call input, but gets unwrapped - /// from another object, and then gets deleted. - UnwrapThenDelete, - /// An object is provided in the call input, and gets wrapped into another - /// object. - Wrap, -} \ 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 deleted file mode 100644 index 9e825bb77..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/timelock/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// 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 deleted file mode 100644 index 65772e9e9..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::Deserialize; -use serde::Serialize; - -use crate::ident_str; - -#[allow(unused)] // Kept in sync with original source, so keep as is. -use crate::sdk_types::move_types::{ - language_storage::{TypeTag, StructTag}, - identifier::{IdentStr}, -}; - -#[allow(unused)] // Kept in sync with original source, so keep as is. -use super::super::{ - IOTA_FRAMEWORK_ADDRESS, - IOTA_SYSTEM_ADDRESS, - base_types::{ObjectID, EpochId}, - balance::Balance, - governance::StakedIota, - id::UID, - error::IotaError, -}; - -#[allow(unused)] // Kept in sync with original source, so keep as is. -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).map_err(|err| IotaError::ObjectDeserialization { - error: format!("Unable to deserialize TimeLock object: {:?}", err), - }) - } - - /// 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, - } -} \ 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 deleted file mode 100644 index 2c052b9f6..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelocked_staked_iota.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::{Deserialize, Serialize}; -use crate::ident_str; -use crate::sdk_types::move_types::identifier::IdentStr; -use crate::sdk_types::move_types::language_storage::StructTag; -use super::super::{ - IOTA_SYSTEM_ADDRESS, - base_types::{ObjectID, EpochId}, - governance::StakedIota, - id::UID, -}; - -pub const TIMELOCKED_STAKED_IOTA_MODULE_NAME: &IdentStr = ident_str!("timelocked_staking"); -pub const TIMELOCKED_STAKED_IOTA_STRUCT_NAME: &IdentStr = ident_str!("TimelockedStakedIota"); - -/// 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/transaction.rs b/identity_iota_interaction/src/sdk_types/iota_types/transaction.rs deleted file mode 100644 index 379e4f1d3..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/transaction.rs +++ /dev/null @@ -1,463 +0,0 @@ -// 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 std::vec::Vec; - -use enum_dispatch::enum_dispatch; -use nonempty::NonEmpty; -use serde::{Deserialize, Serialize}; -use strum::IntoStaticStr; - - -use super::super::move_core_types::identifier::Identifier; -use super::super::move_core_types::language_storage::TypeTag; - -use super::base_types::{EpochId, IotaAddress, ObjectID, ObjectRef, SequenceNumber}; -use super::error::UserInputError; -use super::{ - IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION, IOTA_CLOCK_OBJECT_ID, IOTA_CLOCK_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 = 50_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, - }; -} - -/// A series of commands where the results of one command can be used in future -/// commands -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub struct ProgrammableTransaction { - /// Input objects or primitive values - pub inputs: Vec, - /// The commands to be executed sequentially. A failure in any command will - /// result in the failure of the entire transaction. - pub commands: 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), -} - -/// 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, -} - -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})"), - } - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, IntoStaticStr)] -pub enum TransactionKind { - /// A transaction that allows the interleaving of native commands and Move - /// calls - ProgrammableTransaction(ProgrammableTransaction), -} - -#[derive(Debug, PartialEq, Eq)] -pub struct SharedInputObject { - pub id: ObjectID, - pub initial_shared_version: SequenceNumber, - pub mutable: bool, -} - -impl SharedInputObject { - pub const IOTA_SYSTEM_OBJ: Self = Self { - id: IOTA_SYSTEM_STATE_OBJECT_ID, - initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION, - mutable: true, - }; - - pub fn id(&self) -> ObjectID { - self.id - } - - pub fn into_id_and_version(self) -> (ObjectID, SequenceNumber) { - (self.id, self.initial_shared_version) - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub struct GasData { - pub payment: Vec, - pub owner: IotaAddress, - pub price: u64, - pub budget: u64, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)] -pub enum TransactionExpiration { - /// The transaction has no expiration - None, - /// Validators wont sign a transaction unless the expiration Epoch - /// is greater than or equal to the current epoch - Epoch(EpochId), -} - -#[enum_dispatch(TransactionDataAPI)] -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub enum TransactionData { - V1(TransactionDataV1), - // When new variants are introduced, it is important that we check version support - // in the validity_check function based on the protocol config. -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub struct TransactionDataV1 { - pub kind: TransactionKind, - pub sender: IotaAddress, - pub gas_data: GasData, - pub expiration: TransactionExpiration, -} - -impl TransactionData { - pub fn new( - kind: TransactionKind, - sender: IotaAddress, - gas_payment: ObjectRef, - gas_budget: u64, - gas_price: u64, - ) -> Self { - TransactionData::V1(TransactionDataV1 { - kind, - sender, - gas_data: GasData { - price: gas_price, - owner: sender, - payment: vec![gas_payment], - budget: gas_budget, - }, - expiration: TransactionExpiration::None, - }) - } - - pub fn new_with_gas_coins( - kind: TransactionKind, - sender: IotaAddress, - gas_payment: Vec, - gas_budget: u64, - gas_price: u64, - ) -> Self { - Self::new_with_gas_coins_allow_sponsor( - kind, - sender, - gas_payment, - gas_budget, - gas_price, - sender, - ) - } - - pub fn new_with_gas_coins_allow_sponsor( - kind: TransactionKind, - sender: IotaAddress, - gas_payment: Vec, - gas_budget: u64, - gas_price: u64, - gas_sponsor: IotaAddress, - ) -> Self { - TransactionData::V1(TransactionDataV1 { - kind, - sender, - gas_data: GasData { - price: gas_price, - owner: gas_sponsor, - payment: gas_payment, - budget: gas_budget, - }, - expiration: TransactionExpiration::None, - }) - } - - pub fn new_with_gas_data( - kind: TransactionKind, - sender: IotaAddress, - gas_data: GasData, - ) -> Self { - TransactionData::V1(TransactionDataV1 { - kind, - sender, - gas_data, - expiration: TransactionExpiration::None, - }) - } -} - -#[enum_dispatch] -pub trait TransactionDataAPI { - fn sender(&self) -> IotaAddress; - - // Note: this implies that SingleTransactionKind itself must be versioned, so - // that it can be shared across versions. This will be easy to do since it - // is already an enum. - fn kind(&self) -> &TransactionKind; - - // Used by programmable_transaction_builder - fn kind_mut(&mut self) -> &mut TransactionKind; - - // kind is moved out of often enough that this is worth it to special case. - fn into_kind(self) -> TransactionKind; - - /// Transaction signer and Gas owner - fn signers(&self) -> NonEmpty; - - fn gas_data(&self) -> &GasData; - - fn gas_data_mut(&mut self) -> &mut GasData; - - fn gas_owner(&self) -> IotaAddress; - - fn gas(&self) -> &[ObjectRef]; - - fn gas_price(&self) -> u64; - - fn gas_budget(&self) -> u64; - - fn expiration(&self) -> &TransactionExpiration; - -} - -impl TransactionDataAPI for TransactionDataV1 { - fn sender(&self) -> IotaAddress { - self.sender - } - - fn kind(&self) -> &TransactionKind { - &self.kind - } - - fn kind_mut(&mut self) -> &mut TransactionKind { - &mut self.kind - } - - fn into_kind(self) -> TransactionKind { - self.kind - } - - /// Transaction signer and Gas owner - fn signers(&self) -> NonEmpty { - let mut signers = NonEmpty::new(self.sender); - if self.gas_owner() != self.sender { - signers.push(self.gas_owner()); - } - signers - } - - fn gas_data(&self) -> &GasData { - &self.gas_data - } - - fn gas_owner(&self) -> IotaAddress { - self.gas_data.owner - } - - fn gas(&self) -> &[ObjectRef] { - &self.gas_data.payment - } - - fn gas_price(&self) -> u64 { - self.gas_data.price - } - - fn gas_budget(&self) -> u64 { - self.gas_data.budget - } - - fn expiration(&self) -> &TransactionExpiration { - &self.expiration - } - - fn gas_data_mut(&mut self) -> &mut GasData { - &mut self.gas_data - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)] -pub enum InputObjectKind { - // A Move package, must be immutable. - MovePackage(ObjectID), - // A Move object, either immutable, or owned mutable. - ImmOrOwnedMoveObject(ObjectRef), - // A Move object that's shared and mutable. - SharedMoveObject { - id: ObjectID, - initial_shared_version: SequenceNumber, - mutable: bool, - }, -} - -impl InputObjectKind { - pub fn object_id(&self) -> ObjectID { - match self { - Self::MovePackage(id) => *id, - Self::ImmOrOwnedMoveObject((id, _, _)) => *id, - Self::SharedMoveObject { id, .. } => *id, - } - } - - pub fn version(&self) -> Option { - match self { - Self::MovePackage(..) => None, - Self::ImmOrOwnedMoveObject((_, version, _)) => Some(*version), - Self::SharedMoveObject { .. } => None, - } - } - - pub fn object_not_found_error(&self) -> UserInputError { - match *self { - Self::MovePackage(package_id) => { - UserInputError::DependentPackageNotFound { package_id } - } - Self::ImmOrOwnedMoveObject((object_id, version, _)) => UserInputError::ObjectNotFound { - object_id, - version: Some(version), - }, - Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound { - object_id: id, - version: None, - }, - } - } - - pub fn is_shared_object(&self) -> bool { - matches!(self, Self::SharedMoveObject { .. }) - } - - pub fn is_mutable(&self) -> bool { - match self { - Self::MovePackage(..) => false, - Self::ImmOrOwnedMoveObject((_, _, _)) => true, - Self::SharedMoveObject { mutable, .. } => *mutable, - } - } -} \ 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 deleted file mode 100644 index 6d75bfa2d..000000000 --- a/identity_iota_interaction/src/sdk_types/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// 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 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_core_types/account_address.rs b/identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs deleted file mode 100644 index d52b19195..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs +++ /dev/null @@ -1,337 +0,0 @@ -// 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(); - - pub const fn from_suffix(suffix: u16) -> AccountAddress { - let mut addr = [0u8; AccountAddress::LENGTH]; - let [hi, lo] = suffix.to_be_bytes(); - addr[AccountAddress::LENGTH - 2] = hi; - addr[AccountAddress::LENGTH - 1] = lo; - AccountAddress::new(addr) - } - - 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) - } - - // AbstractMemorySize is not available for wasm32 - // - // /// TODO (ade): use macro to enfornce determinism - // pub fn abstract_size_for_gas_metering(&self) -> AbstractMemorySize { - // AbstractMemorySize::new(Self::LENGTH as u64) - // } -} - -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 deleted file mode 100644 index 09a4ceca5..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/annotated_value.rs +++ /dev/null @@ -1,774 +0,0 @@ -// 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::{ - collections::BTreeMap, - fmt::{self, Debug}, - io::Cursor, -}; - -use anyhow::Result as AResult; -use serde::{ - Deserialize, Serialize, - de::Error as DeError, - ser::{SerializeMap, SerializeSeq, SerializeStruct}, -}; - -use super::{ - VARIANT_COUNT_MAX, - account_address::AccountAddress, - annotated_visitor::{Error as VError, ValueDriver, Visitor, visit_struct, visit_value}, - identifier::Identifier, - language_storage::{StructTag, TypeTag}, - runtime_value::{self as R, MOVE_STRUCT_FIELDS, MOVE_STRUCT_TYPE}, - 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 enum/struct gets serialized into a -/// Serde struct with this as the first field -pub const MOVE_DATA_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_DATA_FIELDS: &str = "fields"; - -/// In the `WithTypes` configuration, a Move enum gets serialized into a Serde -/// struct with this as the second field In the `WithFields` configuration, this -/// is the first field of the serialized enum -pub const MOVE_VARIANT_NAME: &str = "variant_name"; - -/// Field name for the tag of the variant -pub const MOVE_VARIANT_TAG_NAME: &str = "variant_tag"; - -/// In the `WithTypes` configuration, a Move enum gets serialized into a Serde -/// struct with this name -pub const MOVE_ENUM_NAME: &str = "enum"; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct MoveStruct { - pub type_: StructTag, - pub fields: Vec<(Identifier, MoveValue)>, -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct MoveVariant { - pub type_: StructTag, - pub variant_name: Identifier, - pub tag: u16, - 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), - Variant(MoveVariant), -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -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, PartialEq, Eq)] -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, PartialEq, Eq)] -pub struct MoveEnumLayout { - pub type_: StructTag, - pub variants: BTreeMap<(Identifier, u16), Vec>, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum MoveDatatypeLayout { - Struct(Box), - Enum(Box), -} - -impl MoveDatatypeLayout { - pub fn into_layout(self) -> MoveTypeLayout { - match self { - Self::Struct(s) => MoveTypeLayout::Struct(s), - Self::Enum(e) => MoveTypeLayout::Enum(e), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -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(Box), - #[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, - #[serde(rename(serialize = "enum", deserialize = "enum"))] - Enum(Box), -} - -impl MoveStructLayout { - /// Returns `true` if and only if the layout is for `type_`. - pub fn is_type(&self, type_: &StructTag) -> bool { - self.type_ == *type_ - } -} - -impl MoveEnumLayout { - /// Returns `true` if and only if the layout is for `type_`. - pub fn is_type(&self, type_: &StructTag) -> bool { - self.type_ == *type_ - } -} - -impl MoveTypeLayout { - /// Returns `true` if and only if the layout is for `type_`. - pub fn is_type(&self, type_: &TypeTag) -> bool { - use MoveTypeLayout as L; - use TypeTag as T; - - match self { - L::Bool => matches!(type_, T::Bool), - L::U8 => matches!(type_, T::U8), - L::U16 => matches!(type_, T::U16), - L::U32 => matches!(type_, T::U32), - L::U64 => matches!(type_, T::U64), - L::U128 => matches!(type_, T::U128), - L::U256 => matches!(type_, T::U256), - L::Address => matches!(type_, T::Address), - L::Signer => matches!(type_, T::Signer), - L::Vector(l) => matches!(type_, T::Vector(t) if l.is_type(t)), - L::Struct(l) => matches!(type_, T::Struct(t) if l.is_type(t)), - L::Enum(l) => matches!(type_, T::Struct(t) if l.is_type(t)), - } - } -} - -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<'b, 'l, V: Visitor<'b, 'l>>( - blob: &'b [u8], - ty: &'l MoveTypeLayout, - visitor: &mut V, - ) -> AResult - where - V::Error: std::error::Error + Send + Sync + 'static, - { - let mut bytes = Cursor::new(blob); - let res = visit_value(&mut bytes, ty, visitor)?; - if bytes.position() as usize == blob.len() { - Ok(res) - } else { - let remaining = blob.len() - bytes.position() as usize; - Err(VError::TrailingBytes(remaining).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::Variant(v) => R::MoveValue::Variant(v.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<'b, 'l, V: Visitor<'b, 'l>>( - blob: &'b [u8], - ty: &'l MoveStructLayout, - visitor: &mut V, - ) -> AResult - where - V::Error: std::error::Error + Send + Sync + 'static, - { - let mut bytes = Cursor::new(blob); - let driver = ValueDriver::new(&mut bytes, None); - let res = visit_struct(driver, ty, visitor)?; - if bytes.position() as usize == blob.len() { - Ok(res) - } else { - let remaining = blob.len() - bytes.position() as usize; - Err(VError::TrailingBytes(remaining).into()) - } - } - - pub fn into_fields(self) -> Vec { - self.fields.into_iter().map(|(_, v)| v).collect() - } - - pub fn undecorate(self) -> R::MoveStruct { - R::MoveStruct( - self.into_fields() - .into_iter() - .map(MoveValue::undecorate) - .collect(), - ) - } -} - -impl MoveVariant { - pub fn new( - type_: StructTag, - variant_name: Identifier, - tag: u16, - fields: Vec<(Identifier, MoveValue)>, - ) -> Self { - Self { - type_, - variant_name, - tag, - fields, - } - } - - pub fn simple_deserialize(blob: &[u8], ty: &MoveEnumLayout) -> AResult { - Ok(bcs::from_bytes_seed(ty, blob)?) - } - - pub fn into_fields(self) -> Vec { - self.fields.into_iter().map(|(_, v)| v).collect() - } - - pub fn undecorate(self) -> R::MoveVariant { - R::MoveVariant { - tag: self.tag, - fields: 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::Enum(ty) => Ok(MoveValue::Variant(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<'d> serde::de::DeserializeSeed<'d> for &MoveEnumLayout { - type Value = MoveVariant; - fn deserialize>( - self, - deserializer: D, - ) -> Result { - let (variant_name, tag, fields) = - deserializer.deserialize_tuple(2, DecoratedEnumFieldVisitor(&self.variants))?; - Ok(MoveVariant { - type_: self.type_.clone(), - variant_name, - tag, - fields, - }) - } -} - -struct DecoratedEnumFieldVisitor<'a>(&'a BTreeMap<(Identifier, u16), Vec>); - -impl<'d, 'a> serde::de::Visitor<'d> for DecoratedEnumFieldVisitor<'a> { - type Value = (Identifier, u16, Vec<(Identifier, MoveValue)>); - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("Enum") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'d>, - { - let tag = match seq.next_element_seed(&MoveTypeLayout::U8)? { - Some(MoveValue::U8(tag)) if tag as u64 <= VARIANT_COUNT_MAX => tag as u16, - Some(MoveValue::U8(tag)) => return Err(A::Error::invalid_length(tag as usize, &self)), - Some(val) => { - return Err(A::Error::invalid_type( - serde::de::Unexpected::Other(&format!("{val:?}")), - &self, - )); - } - None => return Err(A::Error::invalid_length(0, &self)), - }; - - let Some(((variant_name, _), variant_layout)) = - self.0.iter().find(|((_, v_tag), _)| *v_tag == tag) - else { - return Err(A::Error::invalid_length(tag as usize, &self)); - }; - - let Some(fields) = seq.next_element_seed(&DecoratedVariantFieldLayout(variant_layout))? - else { - return Err(A::Error::invalid_length(1, &self)); - }; - - Ok((variant_name.clone(), tag, fields)) - } -} - -struct DecoratedVariantFieldLayout<'a>(&'a Vec); - -impl<'d, 'a> serde::de::DeserializeSeed<'d> for &DecoratedVariantFieldLayout<'a> { - type Value = Vec<(Identifier, MoveValue)>; - - fn deserialize>( - self, - deserializer: D, - ) -> Result { - deserializer.deserialize_tuple(self.0.len(), DecoratedStructFieldVisitor(self.0)) - } -} - -impl serde::Serialize for MoveValue { - fn serialize(&self, serializer: S) -> Result { - match self { - MoveValue::Struct(s) => s.serialize(serializer), - MoveValue::Variant(v) => v.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 serde::Serialize for MoveVariant { - fn serialize(&self, serializer: S) -> Result { - // Serialize an enum as: - // enum { "type": 0xC::module::enum_type, "variant_name": name, "variant_tag": - // tag, "fields": { ... } } - let mut t = serializer.serialize_struct(MOVE_ENUM_NAME, 4)?; - t.serialize_field(MOVE_DATA_TYPE, &self.type_.to_string())?; - t.serialize_field(MOVE_VARIANT_NAME, &self.variant_name.to_string())?; - t.serialize_field(MOVE_VARIANT_TAG_NAME, &MoveValue::U16(self.tag))?; - t.serialize_field(MOVE_DATA_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}"), - Enum(e) if f.alternate() => write!(f, "{e:#}"), - Enum(e) => write!(f, "enum {}", e), - } - } -} - -/// 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 fmt::Display for MoveEnumLayout { - fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { - use DebugAsDisplay as DD; - write!(f, "enum {} ", self.type_)?; - let mut vmap = f.debug_set(); - for ((variant_name, _), fields) in self.variants.iter() { - vmap.entry(&DD(&MoveVariantDisplay(variant_name.as_str(), fields))); - } - vmap.finish() - } -} - -struct MoveVariantDisplay<'a>(&'a str, &'a [MoveFieldLayout]); - -impl<'a> fmt::Display for MoveVariantDisplay<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { - use DebugAsDisplay as DD; - let mut map = f.debug_struct(self.0); - for field in self.1 { - map.field(field.name.as_str(), &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.as_ref().into())), - MoveTypeLayout::Enum(e) => TypeTag::Struct(Box::new(e.as_ref().into())), - } - } -} - -impl From<&MoveStructLayout> for StructTag { - fn from(val: &MoveStructLayout) -> StructTag { - val.type_.clone() - } -} - -impl From<&MoveEnumLayout> for StructTag { - fn from(val: &MoveEnumLayout) -> 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), - MoveValue::Variant(v) => fmt::Display::fmt(v, 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() - } -} - -impl fmt::Display for MoveVariant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use DebugAsDisplay as DD; - let MoveVariant { - type_, - variant_name, - tag: _, - fields, - } = self; - write!(f, "{}::{}", type_, variant_name)?; - let mut map = f.debug_map(); - for (field, value) in fields { - map.entry(&DD(field), &DD(value)); - } - map.finish() - } -} 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 deleted file mode 100644 index 354e38bf2..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/annotated_visitor.rs +++ /dev/null @@ -1,814 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::io::{Cursor, Read}; - -use super::{ - VARIANT_COUNT_MAX, - account_address::AccountAddress, - annotated_value::{MoveEnumLayout, MoveFieldLayout, MoveStructLayout, MoveTypeLayout}, - identifier::IdentStr, - u256::U256, -}; - -/// Visitors can be used for building values out of a serialized Move struct or -/// value. -pub trait Visitor<'b, 'l> { - 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, - driver: &ValueDriver<'_, 'b, 'l>, - value: u8, - ) -> Result; - - fn visit_u16( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u16, - ) -> Result; - - fn visit_u32( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u32, - ) -> Result; - - fn visit_u64( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u64, - ) -> Result; - - fn visit_u128( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u128, - ) -> Result; - - fn visit_u256( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: U256, - ) -> Result; - - fn visit_bool( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: bool, - ) -> Result; - - fn visit_address( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: AccountAddress, - ) -> Result; - - fn visit_signer( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: AccountAddress, - ) -> Result; - - fn visit_vector( - &mut self, - driver: &mut VecDriver<'_, 'b, 'l>, - ) -> Result; - - fn visit_struct( - &mut self, - driver: &mut StructDriver<'_, 'b, 'l>, - ) -> Result; - - fn visit_variant( - &mut self, - driver: &mut VariantDriver<'_, 'b, 'l>, - ) -> 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<'b, 'l> { - type Error: From; - - fn traverse_u8( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u8, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_u16( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u16, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_u32( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u32, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_u64( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u64, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_u128( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u128, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_u256( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: U256, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_bool( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: bool, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_address( - &mut self, - _: &ValueDriver<'_, 'b, 'l>, - _: AccountAddress, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_signer( - &mut self, - _: &ValueDriver<'_, 'b, 'l>, - _: AccountAddress, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_vector(&mut self, driver: &mut VecDriver<'_, 'b, 'l>) -> Result<(), Self::Error> { - while driver.next_element(self)?.is_some() {} - Ok(()) - } - - fn traverse_struct( - &mut self, - driver: &mut StructDriver<'_, 'b, 'l>, - ) -> Result<(), Self::Error> { - while driver.next_field(self)?.is_some() {} - Ok(()) - } - - fn traverse_variant( - &mut self, - driver: &mut VariantDriver<'_, 'b, 'l>, - ) -> Result<(), Self::Error> { - while driver.next_field(self)?.is_some() {} - Ok(()) - } -} - -/// Default implementation converting any traversal into a visitor. -impl<'b, 'l, T: Traversal<'b, 'l> + ?Sized> Visitor<'b, 'l> for T { - type Value = (); - type Error = T::Error; - - fn visit_u8( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u8, - ) -> Result { - self.traverse_u8(driver, value) - } - - fn visit_u16( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u16, - ) -> Result { - self.traverse_u16(driver, value) - } - - fn visit_u32( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u32, - ) -> Result { - self.traverse_u32(driver, value) - } - - fn visit_u64( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u64, - ) -> Result { - self.traverse_u64(driver, value) - } - - fn visit_u128( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u128, - ) -> Result { - self.traverse_u128(driver, value) - } - - fn visit_u256( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: U256, - ) -> Result { - self.traverse_u256(driver, value) - } - - fn visit_bool( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: bool, - ) -> Result { - self.traverse_bool(driver, value) - } - - fn visit_address( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: AccountAddress, - ) -> Result { - self.traverse_address(driver, value) - } - - fn visit_signer( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: AccountAddress, - ) -> Result { - self.traverse_signer(driver, value) - } - - fn visit_vector( - &mut self, - driver: &mut VecDriver<'_, 'b, 'l>, - ) -> Result { - self.traverse_vector(driver) - } - - fn visit_struct( - &mut self, - driver: &mut StructDriver<'_, 'b, 'l>, - ) -> Result { - self.traverse_struct(driver) - } - - fn visit_variant( - &mut self, - driver: &mut VariantDriver<'_, 'b, 'l>, - ) -> Result { - self.traverse_variant(driver) - } -} - -/// Exposes information about the byte stream that the value being visited came -/// from, namely the bytes themselves, and the offset at which the value starts. -/// Also exposes the layout of the value being visited. -pub struct ValueDriver<'c, 'b, 'l> { - bytes: &'c mut Cursor<&'b [u8]>, - layout: Option<&'l MoveTypeLayout>, - start: usize, -} - -/// 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<'c, 'b, 'l> { - inner: ValueDriver<'c, 'b, 'l>, - 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<'c, 'b, 'l> { - inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveStructLayout, - off: u64, -} - -/// Exposes information about a variant being visited (its layout, details about -/// the next field to be visited, the variant's tag, and name) to a visitor -/// implementation, and allows that visitor to progress the traversal (by -/// visiting or skipping fields). -pub struct VariantDriver<'c, 'b, 'l> { - inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveEnumLayout, - tag: u16, - variant_name: &'l IdentStr, - variant_layout: &'l [MoveFieldLayout], - off: u64, -} - -#[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), - - #[error("invalid variant tag: {0}")] - UnexpectedVariantTag(usize), - - #[error("no layout available for value")] - NoValueLayout, -} - -/// 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<'b, 'l> Traversal<'b, 'l> for NullTraversal { - type Error = Error; -} - -impl<'c, 'b, 'l> ValueDriver<'c, 'b, 'l> { - pub(crate) fn new(bytes: &'c mut Cursor<&'b [u8]>, layout: Option<&'l MoveTypeLayout>) -> Self { - let start = bytes.position() as usize; - Self { - bytes, - layout, - start, - } - } - - /// The offset at which the value being visited starts in the byte stream. - pub fn start(&self) -> usize { - self.start - } - - /// The current position in the byte stream. - pub fn position(&self) -> usize { - self.bytes.position() as usize - } - - /// All the bytes in the byte stream (including the ones that have been - /// read). - pub fn bytes(&self) -> &'b [u8] { - self.bytes.get_ref() - } - /// The bytes that haven't been consumed by the visitor yet. - pub fn remaining_bytes(&self) -> &'b [u8] { - &self.bytes.get_ref()[self.position()..] - } - - /// Type layout for the value being visited. May produce an error if a - /// layout was not supplied when the driver was created (which should - /// only happen if the driver was created for visiting a struct - /// specifically). - pub fn layout(&self) -> Result<&'l MoveTypeLayout, Error> { - self.layout.ok_or(Error::NoValueLayout) - } - - fn read_exact(&mut self) -> Result<[u8; N], Error> { - let mut buf = [0u8; N]; - self.bytes - .read_exact(&mut buf) - .map_err(|_| Error::UnexpectedEof)?; - Ok(buf) - } - - fn read_leb128(&mut self) -> Result { - leb128::read::unsigned(self.bytes).map_err(|_| Error::UnexpectedEof) - } -} - -#[allow(clippy::len_without_is_empty)] -impl<'c, 'b, 'l> VecDriver<'c, 'b, 'l> { - fn new(inner: ValueDriver<'c, 'b, 'l>, layout: &'l MoveTypeLayout, len: u64) -> Self { - Self { - inner, - layout, - len, - off: 0, - } - } - - /// The offset at which the value being visited starts in the byte stream. - pub fn start(&self) -> usize { - self.inner.start() - } - - /// The current position in the byte stream. - pub fn position(&self) -> usize { - self.inner.position() - } - - /// All the bytes in the byte stream (including the ones that have been - /// read). - pub fn bytes(&self) -> &'b [u8] { - self.inner.bytes() - } - - /// The bytes that haven't been consumed by the visitor yet. - pub fn remaining_bytes(&self) -> &'b [u8] { - self.inner.remaining_bytes() - } - - /// Type layout for the vector's inner type. - pub fn element_layout(&self) -> &'l MoveTypeLayout { - self.layout - } - - /// The number of elements in this vector that have been visited so far. - pub fn off(&self) -> u64 { - self.off - } - - /// 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 + ?Sized>( - &mut self, - visitor: &mut V, - ) -> Result, V::Error> { - Ok(if self.off >= self.len { - None - } else { - let res = visit_value(self.inner.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<'c, 'b, 'l> StructDriver<'c, 'b, 'l> { - fn new(inner: ValueDriver<'c, 'b, 'l>, layout: &'l MoveStructLayout) -> Self { - Self { - inner, - layout, - off: 0, - } - } - - /// The offset at which the value being visited starts in the byte stream. - pub fn start(&self) -> usize { - self.inner.start() - } - - /// The current position in the byte stream. - pub fn position(&self) -> usize { - self.inner.position() - } - - /// All the bytes in the byte stream (including the ones that have been - /// read). - pub fn bytes(&self) -> &'b [u8] { - self.inner.bytes() - } - - /// The bytes that haven't been consumed by the visitor yet. - pub fn remaining_bytes(&self) -> &'b [u8] { - self.inner.remaining_bytes() - } - - /// The layout of the struct being visited. - pub fn struct_layout(&self) -> &'l MoveStructLayout { - self.layout - } - - /// The number of fields in this struct that have been visited so far. - pub fn off(&self) -> u64 { - self.off - } - - /// 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 as usize) - } - - /// 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 + ?Sized>( - &mut self, - visitor: &mut V, - ) -> Result, V::Error> { - let Some(field) = self.peek_field() else { - return Ok(None); - }; - - let res = visit_value(self.inner.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)) - } -} - -impl<'c, 'b, 'l> VariantDriver<'c, 'b, 'l> { - fn new( - inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveEnumLayout, - variant_layout: &'l [MoveFieldLayout], - variant_name: &'l IdentStr, - tag: u16, - ) -> Self { - Self { - inner, - layout, - tag, - variant_name, - variant_layout, - off: 0, - } - } - - /// The offset at which the value being visited starts in the byte stream. - pub fn start(&self) -> usize { - self.inner.start() - } - - /// The current position in the byte stream. - pub fn position(&self) -> usize { - self.inner.position() - } - - /// All the bytes in the byte stream (including the ones that have been - /// read). - pub fn bytes(&self) -> &'b [u8] { - self.inner.bytes() - } - - /// The bytes that haven't been consumed by the visitor yet. - pub fn remaining_bytes(&self) -> &'b [u8] { - self.inner.remaining_bytes() - } - - /// The layout of the enum being visited. - pub fn enum_layout(&self) -> &'l MoveEnumLayout { - self.layout - } - - /// The layout of the variant being visited. - pub fn variant_layout(&self) -> &'l [MoveFieldLayout] { - self.variant_layout - } - - /// The tag of the variant being visited. - pub fn tag(&self) -> u16 { - self.tag - } - - /// The name of the enum variant being visited. - pub fn variant_name(&self) -> &'l IdentStr { - self.variant_name - } - - /// The number of elements in this vector that have been visited so far. - pub fn off(&self) -> u64 { - self.off - } - - /// 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.variant_layout.get(self.off as usize) - } - - /// Visit the next field in the variant. 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 variant. - /// - /// Returns `Ok(None)` if there are no more fields in the variant, `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 + ?Sized>( - &mut self, - visitor: &mut V, - ) -> Result, V::Error> { - let Some(field) = self.peek_field() else { - return Ok(None); - }; - - let res = visit_value(self.inner.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 -/// `annoted_value::MoveValue::visit_deserialize` for details. -pub(crate) fn visit_value<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( - bytes: &'c mut Cursor<&'b [u8]>, - layout: &'l MoveTypeLayout, - visitor: &mut V, -) -> Result { - use MoveTypeLayout as L; - - let mut driver = ValueDriver::new(bytes, Some(layout)); - match layout { - L::Bool => match driver.read_exact()? { - [0] => visitor.visit_bool(&driver, false), - [1] => visitor.visit_bool(&driver, true), - [b] => Err(Error::UnexpectedByte(b).into()), - }, - - L::U8 => { - let v = u8::from_le_bytes(driver.read_exact()?); - visitor.visit_u8(&driver, v) - } - - L::U16 => { - let v = u16::from_le_bytes(driver.read_exact()?); - visitor.visit_u16(&driver, v) - } - - L::U32 => { - let v = u32::from_le_bytes(driver.read_exact()?); - visitor.visit_u32(&driver, v) - } - - L::U64 => { - let v = u64::from_le_bytes(driver.read_exact()?); - visitor.visit_u64(&driver, v) - } - - L::U128 => { - let v = u128::from_le_bytes(driver.read_exact()?); - visitor.visit_u128(&driver, v) - } - - L::U256 => { - let v = U256::from_le_bytes(&driver.read_exact()?); - visitor.visit_u256(&driver, v) - } - - L::Address => { - let v = AccountAddress::new(driver.read_exact()?); - visitor.visit_address(&driver, v) - } - - L::Signer => { - let v = AccountAddress::new(driver.read_exact()?); - visitor.visit_signer(&driver, v) - } - - L::Vector(l) => visit_vector(driver, l.as_ref(), visitor), - L::Struct(l) => visit_struct(driver, l, visitor), - L::Enum(e) => visit_variant(driver, e, visitor), - } -} - -/// Like `visit_value` but specialized to visiting a vector (where the `bytes` -/// is known to be a serialized move vector), and the layout is the vector's -/// element's layout. -fn visit_vector<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( - mut inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveTypeLayout, - visitor: &mut V, -) -> Result { - let len = inner.read_leb128()?; - let mut driver = VecDriver::new(inner, layout, len); - let res = visitor.visit_vector(&mut driver)?; - while driver.skip_element()? {} - Ok(res) - } - -/// 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<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( - inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveStructLayout, - visitor: &mut V, -) -> Result { - let mut driver = StructDriver::new(inner, layout); - let res = visitor.visit_struct(&mut driver)?; - while driver.skip_field()?.is_some() {} - Ok(res) -} - -/// Like `visit_struct` but specialized to visiting a variant (where the `bytes` -/// is known to be a serialized move variant), and the layout is an enum layout. -fn visit_variant<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( - mut inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveEnumLayout, - visitor: &mut V, -) -> Result { - // Since variants are bounded at 127, we can read the tag as a single byte. - // When we add true ULEB encoding for enum variants switch to this: - // let tag = inner.read_leb128()?; - let [tag] = inner.read_exact()?; - if tag >= VARIANT_COUNT_MAX as u8 { - return Err(Error::UnexpectedVariantTag(tag as usize).into()); - } - let variant_layout = layout - .variants - .iter() - .find(|((_, vtag), _)| *vtag == tag as u16) - .ok_or(Error::UnexpectedVariantTag(tag as usize))?; - - let mut driver = VariantDriver::new( - inner, - layout, - variant_layout.1, - &variant_layout.0.0, - tag as u16, - ); - let res = visitor.visit_variant(&mut driver)?; - while driver.skip_field()?.is_some() {} - Ok(res) -} 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 deleted file mode 100644 index 82c6f3d0d..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/identifier.rs +++ /dev/null @@ -1,243 +0,0 @@ -// 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); - } - } - - /// Creates a new `Identifier` from a string without checking if it is a - /// valid identifier. This should not be used under normal - /// circumstances, but is used in cases where we need to - /// preserve backwards compatibility. - /// - /// # Safety - /// - /// Only use this function when preserving backwards compatibility. - pub unsafe fn new_unchecked(s: impl Into>) -> Self { - Self(s.into()) - } - - /// 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() - } - - // AbstractMemorySize is not available for wasm32 - // - // /// Returns the abstract size of the struct - // /// TODO (ade): use macro to enfornce determinism - // pub fn abstract_size_for_gas_metering(&self) -> AbstractMemorySize { - // AbstractMemorySize::new((self.len()) as u64) - // } -} - -impl Borrow for Identifier { - fn borrow(&self) -> &IdentStr { - self - } -} - -impl Borrow for Identifier { - fn borrow(&self) -> &str { - &self.0 - } -} - -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 deleted file mode 100644 index 8d9b677a2..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/language_storage.rs +++ /dev/null @@ -1,350 +0,0 @@ -// 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, - identifier::{IdentStr, Identifier}, - parsing::types::{ParsedModuleId, ParsedStructType, ParsedType}, -}; - -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: - /// - /// - "bool", "u8", "u16", "u32", "u64", "u128", "u256", "address", - /// "signer", "vector" for ground types. - /// - /// - Structs are represented as fully qualified type names, with or without - /// the prefix "0x" depending on the `with_prefix` flag, e.g. - /// `0x000...0001::string::String` or - /// `0x000...000a::m::T<0x000...000a::n::U>`. - /// - /// - Addresses are hex-encoded lowercase values of length 32 (zero-padded). - /// - /// Note: this function is guaranteed to be stable -- suitable for use - /// inside Move native functions or the VM. By contrast, this type's - /// `Display` implementation is subject to change and should be used - /// inside code that needs to return a stable output (e.g. that might be - /// committed to effects on-chain). - 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 type with optional - /// 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 { - ParsedType::parse(s)?.into_type_tag(&|_| None) - } -} - -#[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. - /// - /// - Structs are represented as fully qualified type names, with or without - /// the prefix "0x" depending on the `with_prefix` flag, e.g. - /// `0x000...0001::string::String` or - /// `0x000...000a::m::T<0x000...000a::n::U>`. - /// - /// - Addresses are hex-encoded lowercase values of length 32 (zero-padded). - /// - /// Note: this function is guaranteed to be stable -- suitable for use - /// inside Move native functions or the VM. By contrast, this type's - /// `Display` implementation is subject to change and should be used - /// inside code that needs to return a stable output (e.g. that might be - /// committed to effects on-chain). - 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 - /// optional 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 { - ParsedStructType::parse(s)?.into_struct_tag(&|_| None) - } -} - -/// 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 FromStr for ModuleId { - type Err = anyhow::Error; - fn from_str(s: &str) -> Result { - ParsedModuleId::parse(s)?.into_module_id(&|_| None) - } -} - -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 From for TypeTag { - fn from(t: StructTag) -> TypeTag { - TypeTag::Struct(Box::new(t)) - } -} - 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 deleted file mode 100644 index c6278bedd..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -// 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 runtime_value; -pub mod u256; -pub mod parsing; - -use std::fmt; - -pub const VARIANT_COUNT_MAX: u64 = 127; - -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(()) -} diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs deleted file mode 100644 index 0ac720345..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs +++ /dev/null @@ -1,202 +0,0 @@ -// 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::{self, Some, None}; -use std::string::String; - -use anyhow::anyhow; - -use super::super::account_address::AccountAddress; -use super::super::u256::U256; -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: 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 = U256::from_be_bytes(&self.bytes); - 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 PartialEq for NumericalAddress { - fn eq(&self, other: &AccountAddress) -> bool { - let Self { - bytes: self_bytes, - format: _, - } = self; - self_bytes == other - } -} - -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_core_types/parsing/mod.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/mod.rs deleted file mode 100644 index bdb2683bc..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -pub mod address; -pub mod parser; -pub mod types; -pub mod values; diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs deleted file mode 100644 index becea624d..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs +++ /dev/null @@ -1,470 +0,0 @@ -// 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::{ - account_address::AccountAddress, - u256::{U256FromStrError, U256}, -}; - -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; -// See: https://stackoverflow.com/questions/43787672/the-max-number-of-digits-in-an-int-based-on-number-of-bits -const U256_MAX_DECIMAL_DIGITS: usize = 241 * AccountAddress::LENGTH / 100 + 1; - -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()) - } -} - -pub(crate) 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()) { - if allow_trailing_delim { - break; - } else { - bail!("Invalid type list: trailing delimiter '{}'", delim) - } - } - } - 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)?; - if type_args.is_empty() { - bail!("expected at least one type argument") - } - 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<(AccountAddress, NumberFormat)> { - let (txt, base) = determine_num_text_and_base(s); - let txt = txt.replace('_', ""); - let max_len = match base { - NumberFormat::Hex => AccountAddress::LENGTH * 2, - NumberFormat::Decimal => U256_MAX_DECIMAL_DIGITS, - }; - if txt.len() > max_len { - return None; - } - let parsed = U256::from_str_radix(&txt, match base { - NumberFormat::Hex => 16, - NumberFormat::Decimal => 10, - }) - .ok()?; - Some((AccountAddress::new(parsed.to_be_bytes()), base)) -} diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs deleted file mode 100644 index 2dc1142ba..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs +++ /dev/null @@ -1,195 +0,0 @@ -// 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::{ - account_address::AccountAddress, - identifier::{self, Identifier}, - 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')) => { - chars.next().unwrap(); - match chars.next() { - Some(c) if c.is_ascii_hexdigit() => { - // 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(|c| c.is_ascii_digit() || *c == '_') - .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 == '_' - && chars - .peek() - .is_some_and(|c| identifier::is_valid_identifier_char(*c))) => - { - // c + remaining - let len = 1 + chars - .take_while(|c| identifier::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)?)), - }) - } -} diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs deleted file mode 100644 index 1ad1322e2..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs +++ /dev/null @@ -1,320 +0,0 @@ -// 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::{ - account_address::AccountAddress, - identifier, - runtime_value::{MoveStruct, MoveValue}, - u256::U256, -}; - -use super::{ - address::ParsedAddress, - parser::{Parser, Token}, -}; - -#[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), // Imported at the top of this file - U8(u8), - U16(u16), - U32(u32), - U64(u64), - U128(u128), - U256(U256), // Imported at the top of this file - 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| identifier::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), - } - } -} 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 deleted file mode 100644 index ad6cfc115..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/runtime_value.rs +++ /dev/null @@ -1,584 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use super::{ - account_address::AccountAddress, annotated_value as A, fmt_list, u256, VARIANT_COUNT_MAX, -}; -use anyhow::{anyhow, Result as AResult}; -// use move_proc_macros::test_variant_order; -use serde::{ - de::Error as DeError, - ser::{SerializeSeq, SerializeTuple}, - Deserialize, Serialize, -}; -use std::fmt::{self, Debug}; - -/// 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 struct MoveVariant { - pub tag: u16, - pub fields: 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), - Variant(MoveVariant), -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MoveStructLayout(pub Box>); - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MoveEnumLayout(pub Box>>); - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum MoveDatatypeLayout { - Struct(Box), - Enum(Box), -} - -impl MoveDatatypeLayout { - pub fn into_layout(self) -> MoveTypeLayout { - match self { - MoveDatatypeLayout::Struct(layout) => MoveTypeLayout::Struct(layout), - MoveDatatypeLayout::Enum(layout) => MoveTypeLayout::Enum(layout), - } - } -} - -#[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(Box), - #[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, - #[serde(rename(serialize = "enum", deserialize = "enum"))] - Enum(Box), -} - -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::Variant(s), A::MoveTypeLayout::Enum(l)) => { - A::MoveValue::Variant(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.iter()) - .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 MoveVariant { - pub fn new(tag: u16, fields: Vec) -> Self { - Self { tag, fields } - } - - pub fn simple_deserialize(blob: &[u8], ty: &MoveEnumLayout) -> AResult { - Ok(bcs::from_bytes_seed(ty, blob)?) - } - - pub fn decorate(self, layout: &A::MoveEnumLayout) -> A::MoveVariant { - let MoveVariant { tag, fields } = self; - let A::MoveEnumLayout { type_, variants } = layout; - let ((v_name, _), v_layout) = variants - .iter() - .find(|((_, v_tag), _)| *v_tag == tag) - .unwrap(); - A::MoveVariant { - type_: type_.clone(), - tag, - fields: fields - .into_iter() - .zip(v_layout.iter()) - .map(|(v, l)| (l.name.clone(), v.decorate(&l.layout))) - .collect(), - variant_name: v_name.clone(), - } - } - - pub fn fields(&self) -> &[MoveValue] { - &self.fields - } - - pub fn into_fields(self) -> Vec { - self.fields - } -} - -impl MoveStructLayout { - pub fn new(types: Vec) -> Self { - Self(Box::new(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::Enum(ty) => Ok(MoveValue::Variant(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<'d> serde::de::DeserializeSeed<'d> for &MoveEnumLayout { - type Value = MoveVariant; - fn deserialize>( - self, - deserializer: D, - ) -> Result { - deserializer.deserialize_tuple(2, EnumFieldVisitor(&self.0)) - } -} - -struct EnumFieldVisitor<'a>(&'a Vec>); - -impl<'d, 'a> serde::de::Visitor<'d> for EnumFieldVisitor<'a> { - type Value = MoveVariant; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("Enum") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'d>, - { - let tag = match seq.next_element_seed(&MoveTypeLayout::U8)? { - Some(MoveValue::U8(tag)) if tag as u64 <= VARIANT_COUNT_MAX => tag as u16, - Some(MoveValue::U8(tag)) => return Err(A::Error::invalid_length(tag as usize, &self)), - Some(val) => { - return Err(A::Error::invalid_type( - serde::de::Unexpected::Other(&format!("{val:?}")), - &self, - )); - } - None => return Err(A::Error::invalid_length(0, &self)), - }; - - let Some(variant_layout) = self.0.get(tag as usize) else { - return Err(A::Error::invalid_length(tag as usize, &self)); - }; - - let Some(fields) = seq.next_element_seed(&MoveVariantFieldLayout(variant_layout))? else { - return Err(A::Error::invalid_length(1, &self)); - }; - - Ok(MoveVariant { tag, fields }) - } -} - -struct MoveVariantFieldLayout<'a>(&'a [MoveTypeLayout]); - -impl<'d, 'a> serde::de::DeserializeSeed<'d> for &MoveVariantFieldLayout<'a> { - type Value = Vec; - - fn deserialize>( - self, - deserializer: D, - ) -> Result { - 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::Variant(v) => v.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 serde::Serialize for MoveVariant { - // Serialize a variant as: (tag, [fields...]) - // Since we restrict tags to be less than or equal to 127, the tag will always - // be a single byte in uleb encoding and we don't actually need to uleb - // encode it, but we can at a later date if we want/need to. - fn serialize(&self, serializer: S) -> Result { - let tag = if self.tag as u64 > VARIANT_COUNT_MAX { - return Err(serde::ser::Error::custom(format!( - "Variant tag {} is greater than the maximum allowed value of {}", - self.tag, VARIANT_COUNT_MAX - ))); - } else { - self.tag as u8 - }; - - let mut t = serializer.serialize_tuple(2)?; - - t.serialize_element(&tag)?; - t.serialize_element(&MoveFields(&self.fields))?; - - t.end() - } -} - -struct MoveFields<'a>(&'a [MoveValue]); - -impl<'a> serde::Serialize for MoveFields<'a> { - 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}"), - Enum(e) if f.alternate() => write!(f, "{e:#}"), - Enum(e) => write!(f, "{e}"), - } - } -} - -/// 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 MoveEnumLayout { - fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { - write!(f, "enum ")?; - for (tag, variant) in self.0.iter().enumerate() { - write!(f, "variant_tag: {} {{ ", tag)?; - for (i, l) in variant.iter().enumerate() { - write!(f, "{}: {}, ", i, l)? - } - write!(f, " }} ")?; - } - Ok(()) - } -} - -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), - MoveValue::Variant(v) => fmt::Display::fmt(v, f), - } - } -} - -impl fmt::Display for MoveStruct { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_list(f, "struct[", &self.0, "]") - } -} - -impl fmt::Display for MoveVariant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_list( - f, - &format!("variant(tag = {})[", self.tag), - &self.fields, - "]", - ) - } -} 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 deleted file mode 100644 index 82556bd04..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/u256.rs +++ /dev/null @@ -1,391 +0,0 @@ -// 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, - }, -}; - -// This U256 impl was chosen for now but we are open to changing it as needed -use primitive_types::U256 as PrimitiveU256; - -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use uint::FromStrRadixErr; - -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 from 32 big endian bytes - pub fn from_be_bytes(slice: &[u8; U256_NUM_BYTES]) -> Self { - Self(PrimitiveU256::from_big_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 - } - - /// U256 to 32 big endian bytes - pub fn to_be_bytes(self) -> [u8; U256_NUM_BYTES] { - let mut bytes = [0u8; U256_NUM_BYTES]; - self.0.to_big_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 deleted file mode 100644 index 03ff73711..000000000 --- a/identity_iota_interaction/src/sdk_types/shared_crypto/intent.rs +++ /dev/null @@ -1,204 +0,0 @@ -// 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., 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, - Consensus = 1, -} - -// 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 key. */ - BridgeEventUnused = 6, // for bridge purposes but it's currently not included in messages. - ConsensusBlock = 7, // 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 Intent { - pub fn to_bytes(&self) -> [u8; INTENT_PREFIX_LENGTH] { - [self.scope as u8, self.version as u8, self.app_id as u8] - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != INTENT_PREFIX_LENGTH { - return Err(eyre!("Invalid Intent")); - } - Ok(Self { - scope: bytes[0].try_into()?, - version: bytes[1].try_into()?, - app_id: bytes[2].try_into()?, - }) - } -} - -impl FromStr for Intent { - type Err = eyre::Report; - fn from_str(s: &str) -> Result { - let bytes: Vec = decode_bytes_hex(s).map_err(|_| eyre!("Invalid Intent"))?; - Self::from_bytes(bytes.as_slice()) - } -} - -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 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, -} diff --git a/identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs b/identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs deleted file mode 100644 index db49c84ed..000000000 --- a/identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// 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 deleted file mode 100644 index e6bdcc9f2..000000000 --- a/identity_iota_interaction/src/transaction_builder_trait.rs +++ /dev/null @@ -1,15 +0,0 @@ -// 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; -} From c051c49d295a13ba457d9d361589d1540cf36c8f Mon Sep 17 00:00:00 2001 From: chrisgitiota Date: Thu, 24 Apr 2025 17:19:38 +0200 Subject: [PATCH 02/10] Revert "Remove all resources that will be provided by product-core" This reverts commit d4e366cd5f35de358f83bb58c8211fa8f26701b9. --- Cargo.toml | 2 + bindings/wasm/README.md | 4 + bindings/wasm/iota_interaction_ts/Cargo.toml | 57 + bindings/wasm/iota_interaction_ts/LICENSE | 177 +++ bindings/wasm/iota_interaction_ts/README.md | 3 + .../wasm/iota_interaction_ts/lib/index.ts | 6 + .../lib/iota_client_helpers.ts | 181 +++ .../lib/move_calls/asset/create.ts | 27 + .../lib/move_calls/asset/delete.ts | 21 + .../lib/move_calls/asset/index.ts | 7 + .../lib/move_calls/asset/transfer.ts | 80 ++ .../lib/move_calls/asset/update.ts | 23 + .../lib/move_calls/identity/borrow_asset.ts | 140 +++ .../lib/move_calls/identity/config.ts | 73 ++ .../identity/controller_execution.ts | 112 ++ .../lib/move_calls/identity/create.ts | 57 + .../lib/move_calls/identity/index.ts | 11 + .../lib/move_calls/identity/proposal.ts | 30 + .../lib/move_calls/identity/send_asset.ts | 69 ++ .../lib/move_calls/identity/update.ts | 55 + .../lib/move_calls/identity/upgrade.ts | 43 + .../lib/move_calls/index.ts | 6 + .../lib/move_calls/migration.ts | 34 + .../lib/move_calls/utils.ts | 50 + .../iota_interaction_ts/lib/tsconfig.json | 17 + .../iota_interaction_ts/lib/tsconfig.web.json | 19 + .../iota_interaction_ts/package-lock.json | 1068 +++++++++++++++++ .../wasm/iota_interaction_ts/package.json | 48 + .../src/asset_move_calls.rs | 193 +++ .../src/bindings/keytool/mod.rs | 8 + .../src/bindings/keytool/signer.rs | 110 ++ .../src/bindings/keytool/storage.rs | 147 +++ .../iota_interaction_ts/src/bindings/mod.rs | 11 + .../iota_interaction_ts/src/bindings/types.rs | 12 + .../src/bindings/wasm_iota_client.rs | 437 +++++++ .../src/bindings/wasm_types.rs | 581 +++++++++ .../iota_interaction_ts/src/common/macros.rs | 62 + .../iota_interaction_ts/src/common/mod.rs | 11 + .../iota_interaction_ts/src/common/types.rs | 122 ++ .../iota_interaction_ts/src/common/utils.rs | 26 + .../wasm/iota_interaction_ts/src/error.rs | 223 ++++ .../src/identity_move_calls.rs | 673 +++++++++++ .../src/iota_client_ts_sdk.rs | 427 +++++++ bindings/wasm/iota_interaction_ts/src/lib.rs | 58 + .../src/migration_move_calls.rs | 54 + .../src/transaction_builder.rs | 63 + .../wasm/iota_interaction_ts/tsconfig.json | 15 + .../iota_interaction_ts/tsconfig.node.json | 8 + .../iota_interaction_rust/asset_move_calls.rs | 203 ++++ .../identity_move_calls.rs | 851 +++++++++++++ .../iota_client_rust_sdk.rs | 535 +++++++++ .../migration_move_calls.rs | 56 + .../src/iota_interaction_rust/mod.rs | 41 + .../transaction_builder.rs | 53 + .../src/iota_interaction_rust/utils.rs | 122 ++ identity_iota_core/src/lib.rs | 3 + identity_iota_interaction/Cargo.toml | 72 ++ identity_iota_interaction/README.md | 51 + .../src/effects_mut_api.rs | 94 ++ .../src/iota_client_trait.rs | 257 ++++ .../src/iota_verifiable_credential.rs | 39 + .../src/keytool/internal.rs | 126 ++ identity_iota_interaction/src/keytool/mod.rs | 9 + .../src/keytool/signer.rs | 131 ++ .../src/keytool/storage.rs | 145 +++ identity_iota_interaction/src/lib.rs | 132 ++ .../src/move_call_traits.rs | 251 ++++ identity_iota_interaction/src/move_type.rs | 75 ++ .../src/sdk_types/error.rs | 37 + .../src/sdk_types/generated_types.rs | 270 +++++ .../iota_json_rpc_types/iota_coin.rs | 36 + .../iota_json_rpc_types/iota_event.rs | 195 +++ .../iota_json_rpc_types/iota_move.rs | 346 ++++++ .../iota_json_rpc_types/iota_object.rs | 808 +++++++++++++ .../iota_json_rpc_types/iota_transaction.rs | 407 +++++++ .../src/sdk_types/iota_json_rpc_types/mod.rs | 27 + .../src/sdk_types/iota_types/balance.rs | 95 ++ .../src/sdk_types/iota_types/base_types.rs | 814 +++++++++++++ .../src/sdk_types/iota_types/coin.rs | 186 +++ .../sdk_types/iota_types/collection_types.rs | 103 ++ .../src/sdk_types/iota_types/crypto.rs | 685 +++++++++++ .../src/sdk_types/iota_types/digests.rs | 470 ++++++++ .../src/sdk_types/iota_types/dynamic_field.rs | 156 +++ .../src/sdk_types/iota_types/error.rs | 943 +++++++++++++++ .../src/sdk_types/iota_types/event.rs | 55 + .../sdk_types/iota_types/execution_status.rs | 368 ++++++ .../src/sdk_types/iota_types/gas.rs | 63 + .../src/sdk_types/iota_types/gas_coin.rs | 147 +++ .../src/sdk_types/iota_types/governance.rs | 98 ++ .../src/sdk_types/iota_types/id.rs | 108 ++ .../src/sdk_types/iota_types/iota_serde.rs | 409 +++++++ .../sdk_types/iota_types/iota_types_lib.rs | 125 ++ .../src/sdk_types/iota_types/mod.rs | 29 + .../src/sdk_types/iota_types/move_package.rs | 84 ++ .../src/sdk_types/iota_types/object.rs | 110 ++ .../iota_types/quorum_driver_types.rs | 12 + .../src/sdk_types/iota_types/stardust/mod.rs | 4 + .../iota_types/stardust/output/mod.rs | 6 + .../iota_types/stardust/output/nft.rs | 33 + .../src/sdk_types/iota_types/storage.rs | 28 + .../src/sdk_types/iota_types/timelock/mod.rs | 5 + .../sdk_types/iota_types/timelock/timelock.rs | 124 ++ .../timelock/timelocked_staked_iota.rs | 85 ++ .../src/sdk_types/iota_types/transaction.rs | 463 +++++++ .../src/sdk_types/mod.rs | 17 + .../move_core_types/account_address.rs | 337 ++++++ .../move_core_types/annotated_value.rs | 774 ++++++++++++ .../move_core_types/annotated_visitor.rs | 814 +++++++++++++ .../sdk_types/move_core_types/identifier.rs | 243 ++++ .../move_core_types/language_storage.rs | 350 ++++++ .../src/sdk_types/move_core_types/mod.rs | 35 + .../move_core_types/parsing/address.rs | 202 ++++ .../sdk_types/move_core_types/parsing/mod.rs | 11 + .../move_core_types/parsing/parser.rs | 470 ++++++++ .../move_core_types/parsing/types.rs | 195 +++ .../move_core_types/parsing/values.rs | 320 +++++ .../move_core_types/runtime_value.rs | 584 +++++++++ .../src/sdk_types/move_core_types/u256.rs | 391 ++++++ .../src/sdk_types/shared_crypto/intent.rs | 204 ++++ .../src/sdk_types/shared_crypto/mod.rs | 4 + .../src/transaction_builder_trait.rs | 15 + 121 files changed, 21807 insertions(+) create mode 100644 bindings/wasm/iota_interaction_ts/Cargo.toml create mode 100644 bindings/wasm/iota_interaction_ts/LICENSE create mode 100644 bindings/wasm/iota_interaction_ts/README.md create mode 100644 bindings/wasm/iota_interaction_ts/lib/index.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/asset/index.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/index.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/index.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts create mode 100644 bindings/wasm/iota_interaction_ts/lib/tsconfig.json create mode 100644 bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json create mode 100644 bindings/wasm/iota_interaction_ts/package-lock.json create mode 100644 bindings/wasm/iota_interaction_ts/package.json create mode 100644 bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/keytool/mod.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/keytool/signer.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/keytool/storage.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/mod.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/types.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/common/macros.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/common/mod.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/common/types.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/common/utils.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/error.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/lib.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs create mode 100644 bindings/wasm/iota_interaction_ts/src/transaction_builder.rs create mode 100644 bindings/wasm/iota_interaction_ts/tsconfig.json create mode 100644 bindings/wasm/iota_interaction_ts/tsconfig.node.json create mode 100644 identity_iota_core/src/iota_interaction_rust/asset_move_calls.rs create mode 100644 identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs create mode 100644 identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs create mode 100644 identity_iota_core/src/iota_interaction_rust/migration_move_calls.rs create mode 100644 identity_iota_core/src/iota_interaction_rust/mod.rs create mode 100644 identity_iota_core/src/iota_interaction_rust/transaction_builder.rs create mode 100644 identity_iota_core/src/iota_interaction_rust/utils.rs create mode 100644 identity_iota_interaction/Cargo.toml create mode 100644 identity_iota_interaction/README.md create mode 100644 identity_iota_interaction/src/effects_mut_api.rs create mode 100644 identity_iota_interaction/src/iota_client_trait.rs create mode 100644 identity_iota_interaction/src/iota_verifiable_credential.rs create mode 100644 identity_iota_interaction/src/keytool/internal.rs create mode 100644 identity_iota_interaction/src/keytool/mod.rs create mode 100644 identity_iota_interaction/src/keytool/signer.rs create mode 100644 identity_iota_interaction/src/keytool/storage.rs create mode 100644 identity_iota_interaction/src/lib.rs create mode 100644 identity_iota_interaction/src/move_call_traits.rs create mode 100644 identity_iota_interaction/src/move_type.rs create mode 100644 identity_iota_interaction/src/sdk_types/error.rs create mode 100644 identity_iota_interaction/src/sdk_types/generated_types.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_coin.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_event.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_move.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_object.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_transaction.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/mod.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/balance.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/base_types.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/coin.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/collection_types.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/crypto.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/digests.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/dynamic_field.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/error.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/event.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/execution_status.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/gas.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/gas_coin.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/governance.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/id.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/mod.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/move_package.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/object.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/quorum_driver_types.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/stardust/mod.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/storage.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/timelock/mod.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/timelock/timelocked_staked_iota.rs create mode 100644 identity_iota_interaction/src/sdk_types/iota_types/transaction.rs create mode 100644 identity_iota_interaction/src/sdk_types/mod.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/annotated_value.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/annotated_visitor.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/identifier.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/language_storage.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/mod.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/mod.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/runtime_value.rs create mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/u256.rs create mode 100644 identity_iota_interaction/src/sdk_types/shared_crypto/intent.rs create mode 100644 identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs create mode 100644 identity_iota_interaction/src/transaction_builder_trait.rs diff --git a/Cargo.toml b/Cargo.toml index fa5001a50..e9870fa07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,8 @@ members = [ "identity_ecdsa_verifier", "identity_eddsa_verifier", "examples", + "identity_iota_interaction", + "bindings/wasm/iota_interaction_ts", ] exclude = ["bindings/wasm/identity_wasm", "bindings/grpc"] diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 970f2dede..4b2507674 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -10,6 +10,10 @@ Here is an overview of the existing artifacts: * `identity_wasm`
Exports the IdentityClient to TypeScript using wasm-bindgen generated wasm bindings +* `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. + ## Building an Artifact For build instructions please have a look into the artifact README file. diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_interaction_ts/Cargo.toml new file mode 100644 index 000000000..5e6ad0892 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "iota_interaction_ts" +version = "1.6.0-alpha" +authors = ["IOTA Stiftung"] +edition = "2021" +homepage = "https://www.iota.org" +keywords = ["iota", "tangle", "identity", "wasm"] +license = "Apache-2.0" +publish = false +readme = "README.md" +resolver = "2" +description = "identity_iota_interaction Adapters using Web Assembly bindings." + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +anyhow = { version = "1.0.94", features = ["std"] } +async-trait = { version = "0.1", default-features = false } +bcs = "0.1.6" +bls12_381_plus = "0.8.17" +cfg-if = "1.0.0" +console_error_panic_hook = { version = "0.1" } +eyre = "0.6.12" +fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", package = "fastcrypto" } +futures = { version = "0.3" } +identity_core = { version = "=1.6.0-alpha", path = "../../../identity_core" } +identity_iota_interaction = { version = "=1.6.0-alpha", path = "../../../identity_iota_interaction", default-features = false } +js-sys = { version = "0.3.61" } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } +serde = { version = "1.0", features = ["derive"] } +serde-wasm-bindgen = "0.6.5" +serde_json.workspace = true +serde_repr = { version = "0.1", default-features = false } +thiserror.workspace = true +# Want to use the nice API of tokio::sync::RwLock for now even though we can't use threads. +tokio = { version = "1.43", default-features = false, features = ["sync"] } +tsify = "0.4.5" +wasm-bindgen = { version = "0.2.100", features = ["serde-serialize"] } +wasm-bindgen-futures = { version = "0.4", default-features = false } +zkryptium = "0.2.2" + +[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" + +[features] +default = [] +keytool = ["identity_iota_interaction/keytool"] diff --git a/bindings/wasm/iota_interaction_ts/LICENSE b/bindings/wasm/iota_interaction_ts/LICENSE new file mode 100644 index 000000000..4947287f7 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/bindings/wasm/iota_interaction_ts/README.md b/bindings/wasm/iota_interaction_ts/README.md new file mode 100644 index 000000000..6f7839c27 --- /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/index.ts b/bindings/wasm/iota_interaction_ts/lib/index.ts new file mode 100644 index 000000000..1a201cc25 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/index.ts @@ -0,0 +1,6 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from "~iota_interaction_ts"; +export * as iota_client_helpers from "./iota_client_helpers"; +export * as move_calls from "./move_calls"; 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 000000000..1eafe9e97 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts @@ -0,0 +1,181 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { CoinStruct, IotaClient, IotaTransactionBlockResponse, TransactionEffects } from "@iota/iota-sdk/client"; +import { GasData, TransactionDataBuilder } from "@iota/iota-sdk/transactions"; + +export type Signer = { sign(data: Uint8Array): Promise }; + +const MINIMUM_BALANCE_FOR_COIN = BigInt(1_000_000_000); + +export class WasmIotaTransactionBlockResponseWrapper { + response: IotaTransactionBlockResponse; + + constructor(response: IotaTransactionBlockResponse) { + this.response = response; + } + + to_string(): string { + return JSON.stringify(this.response); + } + + get_effects(): TransactionEffects | null | undefined { + return this.response.effects; + } + + get_response(): IotaTransactionBlockResponse { + return this.response; + } + + get_digest(): string { + return this.response.digest; + } +} + +function byHighestBalance({ balance: a }: T, { balance: b }: T) { + if (a > b) { + return -1; + } + if (a < b) { + return 1; + } + return 0; +} + +async function getCoinForTransaction(iotaClient: IotaClient, senderAddress: string): Promise { + let cursor: string | null | undefined = undefined; + do { + const response = await iotaClient.getCoins({ owner: senderAddress, cursor }); + if (response.data.length === 0) { + throw new Error( + `no coin found with minimum required balance of ${MINIMUM_BALANCE_FOR_COIN} for address ${senderAddress}"`, + ); + } + + let sortedValidCoins = response.data + .map((coin) => ({ coin, balance: BigInt(coin.balance) })) + .filter(({ balance }) => balance >= MINIMUM_BALANCE_FOR_COIN) + .sort(byHighestBalance); + + if (sortedValidCoins.length >= 1) { + return sortedValidCoins[0].coin; + } + + cursor = response.nextCursor; + } while (cursor); + + throw new Error( + `no coin found with minimum required balance of ${MINIMUM_BALANCE_FOR_COIN} for address ${senderAddress}"`, + ); +} + +/** + * Inserts these values into the transaction and replaces placeholder values. + * + * - sender (overwritten as we assume a placeholder to be used in prepared transaction) + * - gas budget (value determined automatically if not provided) + * - gas price (value determined automatically) + * - gas coin / payment object (fetched automatically) + * - gas owner (equals sender) + * + * @param iotaClient client instance + * @param senderAddress transaction sender (and the one paying for it) + * @param txBcs transaction data serialized to bcs, most probably having placeholder values + * @param gasBudget optional fixed gas budget, determined automatically with a dry run if not provided + * @returns updated transaction data + */ +export 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: GasData = { + budget: gasBudget ? gasBudget.toString() : "50000000", // 50_000_000 + owner: senderAddress, + payment: [{ + objectId: gasCoin.coinObjectId, + version: gasCoin.version, + digest: gasCoin.digest, + }], + price: gasPrice.toString(), + }; + const overrides = { + gasData, + sender: senderAddress, + }; + // TODO: check why `.build` with `overrides` doesn't override these values + txData.sender = overrides.sender; + txData.gasData = overrides.gasData; + let builtTx = txData.build({ overrides }); + + 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; + + overrides.gasData.budget = budget.toString(); + txData.gasData.budget = budget.toString(); + + builtTx = txData.build({ overrides }); + } + + return builtTx; +} + +// estimate gas, get coin, execute tx here +export async function executeTransaction( + iotaClient: IotaClient, + senderAddress: string, + txBcs: Uint8Array, + signer: Signer, + gasBudget?: bigint, +): Promise { + const txWithGasData = await addGasDataToTransaction(iotaClient, senderAddress, txBcs, gasBudget); + const signature = await signer.sign(txWithGasData); + + const response = await iotaClient.executeTransactionBlock({ + transactionBlock: txWithGasData, + signature, + options: { // equivalent of `IotaTransactionBlockResponseOptions::full_content()` + showEffects: true, + showInput: true, + showRawInput: true, + showEvents: true, + showObjectChanges: true, + showBalanceChanges: true, + showRawEffects: false, + }, + }); + + if (response?.effects?.status.status === "failure") { + throw new Error(`transaction returned an unexpected response; ${response?.effects?.status.error}`); + } + + return new WasmIotaTransactionBlockResponseWrapper(response); +} + +/** + * Helper function to pause execution. + * + * @param durationMs time to sleep in ms + */ +export function sleep(durationMs: number) { + return new Promise(resolve => setTimeout(resolve, durationMs)); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts new file mode 100644 index 000000000..9aee87fa2 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts @@ -0,0 +1,27 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { Transaction } from "@iota/iota-sdk/transactions"; + +export function create( + inner_bytes: Uint8Array, + inner_type: string, + mutable: boolean, + transferable: boolean, + deletable: boolean, + packageId: string, +): Promise { + const tx = new Transaction(); + const inner_arg = tx.pure(inner_bytes); + const mutableArg = tx.pure.bool(mutable); + const transferableArg = tx.pure.bool(transferable); + const deletableArg = tx.pure.bool(deletable); + + tx.moveCall({ + target: `${packageId}::asset::new_with_config`, + typeArguments: [inner_type], + arguments: [inner_arg, mutableArg, transferableArg, deletableArg], + }); + + return tx.build(); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts new file mode 100644 index 000000000..04b4a10f9 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts @@ -0,0 +1,21 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; + +export function remove( + asset: ObjectRef, + asset_type: string, + packageId: string, +): Promise { + const tx = new Transaction(); + const asset_arg = tx.objectRef(asset); + + tx.moveCall({ + target: `${packageId}::asset::delete`, + typeArguments: [asset_type], + arguments: [asset_arg], + }); + + return tx.build(); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/index.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/index.ts new file mode 100644 index 000000000..feaf3adfc --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/index.ts @@ -0,0 +1,7 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from "./create"; +export * from "./delete"; +export * from "./transfer"; +export * from "./update"; diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts new file mode 100644 index 000000000..5ab2b7ad7 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts @@ -0,0 +1,80 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; + +export function transfer( + asset: ObjectRef, + assetType: string, + recipient: string, + packageId: string, +): Promise { + const tx = new Transaction(); + const assetArg = tx.objectRef(asset); + const recipientArg = tx.pure.address(recipient); + + tx.moveCall({ + target: `${packageId}::asset::transfer`, + typeArguments: [assetType], + arguments: [assetArg, recipientArg], + }); + + return tx.build(); +} + +function makeTx( + proposal: SharedObjectRef, + cap: ObjectRef, + asset: ObjectRef, + assetType: string, + packageId: string, + functionName: string, +): Promise { + const tx = new Transaction(); + const proposalArg = tx.sharedObjectRef(proposal); + const capArg = tx.objectRef(cap); + const assetArg = tx.objectRef(asset); + + tx.moveCall({ + target: `${packageId}::asset::${functionName}`, + typeArguments: [assetType], + arguments: [proposalArg, capArg, assetArg], + }); + + return tx.build(); +} + +export function acceptProposal( + proposal: SharedObjectRef, + recipientCap: ObjectRef, + asset: ObjectRef, + assetType: string, + packageId: string, +): Promise { + return makeTx( + proposal, + recipientCap, + asset, + assetType, + packageId, + "accept", + ); +} + +export function concludeOrCancel( + proposal: SharedObjectRef, + senderCap: ObjectRef, + asset: ObjectRef, + assetType: string, + packageId: string, +): Promise { + return makeTx( + proposal, + senderCap, + asset, + assetType, + packageId, + "conclude_or_cancel", + ); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts new file mode 100644 index 000000000..c80f52f9b --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts @@ -0,0 +1,23 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; + +export function update( + asset: ObjectRef, + content: Uint8Array, + contentType: string, + packageId: string, +): Promise { + const tx = new Transaction(); + const contentArg = tx.pure(content); + const assetArg = tx.objectRef(asset); + + tx.moveCall({ + target: `${packageId}::asset::update`, + typeArguments: [contentType], + arguments: [assetArg, contentArg], + }); + + return tx.build(); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts new file mode 100644 index 000000000..774eb6eee --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts @@ -0,0 +1,140 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { IotaObjectData } from "@iota/iota-sdk/dist/cjs/client"; +import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota-sdk/transactions"; +import { getControllerDelegation, putBackDelegationToken } from "../utils"; + +export function proposeBorrow( + identity: SharedObjectRef, + capability: ObjectRef, + objects: string[], + packageId: string, + expiration?: number, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const identityArg = tx.sharedObjectRef(identity); + const exp = tx.pure.option("u64", expiration); + const objectsArg = tx.pure.vector("id", objects); + + tx.moveCall({ + target: `${packageId}::identity::propose_borrow`, + arguments: [identityArg, delegationToken, exp, objectsArg], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + return tx.build(); +} + +export function executeBorrow( + identity: SharedObjectRef, + capability: ObjectRef, + proposalId: string, + objects: IotaObjectData[], + intentFn: (arg0: Transaction, arg1: Map) => void, + packageId: string, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const proposal = tx.pure.id(proposalId); + const identityArg = tx.sharedObjectRef(identity); + + let action = tx.moveCall({ + target: `${packageId}::identity::execute_proposal`, + typeArguments: [`${packageId}::borrow_proposal::Borrow`], + arguments: [identityArg, delegationToken, proposal], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + const objectArgMap = new Map(); + for (const obj of objects) { + const recvObj = tx.receivingRef(obj); + const objArg = tx.moveCall({ + target: `${packageId}::identity::execute_borrow`, + typeArguments: [obj.type!], + arguments: [identityArg, action, recvObj], + }); + + objectArgMap.set(obj.objectId, [objArg, obj]); + } + + intentFn(tx, objectArgMap); + + for (const [obj, objData] of objectArgMap.values()) { + tx.moveCall({ + target: `${packageId}::borrow_proposal::put_back`, + typeArguments: [objData.type!], + arguments: [action, obj], + }); + } + + tx.moveCall({ + target: `${packageId}::transfer_proposal::conclude_borrow`, + arguments: [action], + }); + + return tx.build(); +} + +export function createAndExecuteBorrow( + identity: SharedObjectRef, + capability: ObjectRef, + objects: IotaObjectData[], + intentFn: (arg0: Transaction, arg1: Map) => void, + packageId: string, + expiration?: number, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const identityArg = tx.sharedObjectRef(identity); + const exp = tx.pure.option("u64", expiration); + const objectsArg = tx.pure.vector("id", objects.map(obj => obj.objectId)); + + const proposal = tx.moveCall({ + target: `${packageId}::identity::propose_borrow`, + arguments: [identityArg, delegationToken, exp, objectsArg], + }); + + let action = tx.moveCall({ + target: `${packageId}::identity::execute_proposal`, + typeArguments: [`${packageId}::borrow_proposal::Borrow`], + arguments: [identityArg, delegationToken, proposal], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + const objectArgMap = new Map(); + for (const obj of objects) { + const recvObj = tx.receivingRef(obj); + const objArg = tx.moveCall({ + target: `${packageId}::identity::execute_borrow`, + typeArguments: [obj.type!], + arguments: [identityArg, action, recvObj], + }); + + objectArgMap.set(obj.objectId, [objArg, obj]); + } + + intentFn(tx, objectArgMap); + + for (const [obj, objData] of objectArgMap.values()) { + tx.moveCall({ + target: `${packageId}::borrow_proposal::put_back`, + typeArguments: [objData.type!], + arguments: [action, obj], + }); + } + + tx.moveCall({ + target: `${packageId}::transfer_proposal::conclude_borrow`, + arguments: [action], + }); + return tx.build(); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts new file mode 100644 index 000000000..666b0022e --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts @@ -0,0 +1,73 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; +import { getControllerDelegation, putBackDelegationToken } from "../utils"; + +export function proposeConfigChange( + identity: SharedObjectRef, + controllerCap: ObjectRef, + controllersToAdd: [string, number][], + controllersToRemove: string[], + controllersToUpdate: [string, number][], + packageId: string, + expiration?: number, + threshold?: number, +): Promise { + const tx = new Transaction(); + const addressesToAdd = tx.pure.vector("address", controllersToAdd.map(c => c[0])); + const vpsToAdd = tx.pure.vector("u64", controllersToAdd.map(c => c[1])); + const controllersToAddArg = tx.moveCall({ + target: `${packageId}::utils::vec_map_from_keys_values`, + typeArguments: ["address", "u64"], + arguments: [addressesToAdd, vpsToAdd], + }); + + const idsToUpdate = tx.pure.vector("id", controllersToUpdate.map(c => c[0])); + const vpsToUpdate = tx.pure.vector("u64", controllersToUpdate.map(c => c[1])); + const controllersToUpdateArg = tx.moveCall({ + target: `${packageId}::utils::vec_map_from_keys_values`, + typeArguments: ["id", "u64"], + arguments: [idsToUpdate, vpsToUpdate], + }); + + const identityArg = tx.sharedObjectRef(identity); + const cap = tx.objectRef(controllerCap); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const thresholdArg = tx.pure.option("u64", threshold); + const exp = tx.pure.option("u64", expiration); + const controllersToRemoveArg = tx.pure.vector("id", controllersToRemove); + + tx.moveCall({ + target: `${packageId}::identity::propose_config_change`, + arguments: [identityArg, delegationToken, exp, thresholdArg, controllersToAddArg, controllersToRemoveArg, + controllersToUpdateArg], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + return tx.build(); +} + +export function executeConfigChange( + identity: SharedObjectRef, + capability: ObjectRef, + proposalId: string, + packageId: string, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const proposal = tx.pure.id(proposalId); + const identityArg = tx.sharedObjectRef(identity); + + tx.moveCall({ + target: `${packageId}::identity::execute_config_change`, + arguments: [identityArg, delegationToken, proposal], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + return tx.build(); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts new file mode 100644 index 000000000..332daa33a --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts @@ -0,0 +1,112 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota-sdk/transactions"; +import { getControllerDelegation, putBackDelegationToken } from "../utils"; + +export function proposeControllerExecution( + identity: SharedObjectRef, + capability: ObjectRef, + controllerCapId: string, + packageId: string, + expiration?: number, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const identityArg = tx.sharedObjectRef(identity); + const exp = tx.pure.option("u64", expiration); + const controllerCapIdArg = tx.pure.id(controllerCapId); + + tx.moveCall({ + target: `${packageId}::identity::propose_controller_execution`, + arguments: [identityArg, delegationToken, controllerCapIdArg, exp], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + return tx.build(); +} + +export function executeControllerExecution( + identity: SharedObjectRef, + capability: ObjectRef, + proposalId: string, + controllerCapRef: ObjectRef, + intentFn: (arg0: Transaction, arg1: TransactionArgument) => void, + packageId: string, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const proposal = tx.pure.id(proposalId); + const identityArg = tx.sharedObjectRef(identity); + + let action = tx.moveCall({ + target: `${packageId}::identity::execute_proposal`, + typeArguments: [`${packageId}::borrow_proposal::Borrow`], + arguments: [identityArg, delegationToken, proposal], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + const receiving = tx.receivingRef(controllerCapRef); + const BorrowedControllerCap = tx.moveCall({ + target: `${packageId}::identity::borrow_controller_cap`, + arguments: [identityArg, action, receiving], + }); + + intentFn(tx, BorrowedControllerCap); + + tx.moveCall({ + target: `${packageId}::controller_proposal::put_back`, + arguments: [action, BorrowedControllerCap], + }); + + return tx.build(); +} + +export function createAndExecuteControllerExecution( + identity: SharedObjectRef, + capability: ObjectRef, + controllerCapRef: ObjectRef, + intentFn: (arg0: Transaction, arg1: TransactionArgument) => void, + packageId: string, + expiration?: number, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const identityArg = tx.sharedObjectRef(identity); + const exp = tx.pure.option("u64", expiration); + const controller_cap_id = tx.pure.id(controllerCapRef.objectId); + + const proposal = tx.moveCall({ + target: `${packageId}::identity::propose_controller_execution`, + arguments: [identityArg, delegationToken, controller_cap_id, exp], + }); + + let action = tx.moveCall({ + target: `${packageId}::identity::execute_proposal`, + typeArguments: [`${packageId}::borrow_proposal::Borrow`], + arguments: [identityArg, delegationToken, proposal], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + const receiving = tx.receivingRef(controllerCapRef); + const borrowedControllerCap = tx.moveCall({ + target: `${packageId}::identity::borrow_controller_cap`, + arguments: [identityArg, action, receiving], + }); + + intentFn(tx, borrowedControllerCap); + + tx.moveCall({ + target: `${packageId}::controller_proposal::put_back`, + arguments: [action, borrowedControllerCap], + }); + + return tx.build(); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts new file mode 100644 index 000000000..498dd7013 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts @@ -0,0 +1,57 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { bcs } from "@iota/iota-sdk/bcs"; +import { Transaction } from "@iota/iota-sdk/transactions"; +import { getClockRef } from "../utils"; + +export async function create(didDoc: Uint8Array | undefined, packageId: string): Promise { + const tx = new Transaction(); + const didDocArg = tx.pure(bcs.option(bcs.vector(bcs.U8)).serialize(didDoc)); + const clock = getClockRef(tx); + + tx.moveCall({ + target: `${packageId}::identity::new`, + arguments: [didDocArg, clock], + }); + + return tx.build({ onlyTransactionKind: true }); +} + +export async function newWithControllers( + didDoc: Uint8Array | undefined, + controllers: [string, number][], + threshold: number, + packageId: string, +): Promise { + const tx = new Transaction(); + const ids = tx.pure.vector("address", controllers.map(controller => controller[0])); + const vps = tx.pure.vector("u64", controllers.map(controller => controller[1])); + const controllersArg = tx.moveCall({ + target: `${packageId}::utils::vec_map_from_keys_values`, + typeArguments: ["address", "u64"], + arguments: [ids, vps], + }); + const controllersThatCanDelegate = tx.moveCall({ + target: "0x2::vec_map::empty", + typeArguments: ["address", "u64"], + arguments: [], + }); + const didDocArg = tx.pure(bcs.option(bcs.vector(bcs.U8)).serialize(didDoc)); + const clock = getClockRef(tx); + const thresholdArg = tx.pure.u64(threshold); + + tx.moveCall({ + target: `${packageId}::identity::new_with_controllers`, + arguments: [didDocArg, controllersArg, controllersThatCanDelegate, thresholdArg, clock], + }); + + const tx_kind_bcs = await tx.build({ onlyTransactionKind: true }); + try { + const tx_kind = bcs.TransactionKind.parse(tx_kind_bcs); + } catch (e) { + console.error(`failed to deserialize tx kind: ${e}`); + } finally { + return tx_kind_bcs; + } +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/index.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/index.ts new file mode 100644 index 000000000..8d50ae018 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/index.ts @@ -0,0 +1,11 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from "./borrow_asset"; +export * from "./config"; +export * from "./controller_execution"; +export * from "./create"; +export * from "./proposal"; +export * from "./send_asset"; +export * from "./update"; +export * from "./upgrade"; diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts new file mode 100644 index 000000000..6a683ced3 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts @@ -0,0 +1,30 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; +import { getControllerDelegation, putBackDelegationToken } from "../utils"; + +export function approve( + identity: SharedObjectRef, + capability: ObjectRef, + proposalId: string, + proposalType: string, + packageId: string, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const identityArg = tx.sharedObjectRef(identity); + const proposal = tx.pure.id(proposalId); + + tx.moveCall({ + target: `${packageId}::identity::approve_proposal`, + typeArguments: [proposalType], + arguments: [identityArg, delegationToken, proposal], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + return tx.build(); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts new file mode 100644 index 000000000..2688a0d4f --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts @@ -0,0 +1,69 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; +import { getControllerDelegation, putBackDelegationToken } from "../utils"; + +export function proposeSend( + identity: SharedObjectRef, + capability: ObjectRef, + transferMap: [string, string][], + packageId: string, + expiration?: number, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const identityArg = tx.sharedObjectRef(identity); + const exp = tx.pure.option("u64", expiration); + const objects = tx.pure.vector("id", transferMap.map(t => t[0])); + const recipients = tx.pure.vector("address", transferMap.map(t => t[1])); + + tx.moveCall({ + target: `${packageId}::identity::propose_send`, + arguments: [identityArg, delegationToken, exp, objects, recipients], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + return tx.build(); +} + +export function executeSend( + identity: SharedObjectRef, + capability: ObjectRef, + proposalId: string, + objects: [ObjectRef, string][], + packageId: string, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const proposal = tx.pure.id(proposalId); + const identityArg = tx.sharedObjectRef(identity); + + let action = tx.moveCall({ + target: `${packageId}::identity::execute_proposal`, + typeArguments: [`${packageId}::transfer_proposal::Send`], + arguments: [identityArg, delegationToken, proposal], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + for (const [obj, objType] of objects) { + const recv_obj = tx.receivingRef(obj); + tx.moveCall({ + target: `${packageId}::identity::execute_send`, + typeArguments: [objType], + arguments: [identityArg, action, recv_obj], + }); + } + + tx.moveCall({ + target: `${packageId}::transfer_proposal::complete_send`, + arguments: [action], + }); + + return tx.build(); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts new file mode 100644 index 000000000..b503fe512 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts @@ -0,0 +1,55 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { bcs } from "@iota/iota-sdk/bcs"; +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; +import { getClockRef, getControllerDelegation, putBackDelegationToken } from "../utils"; + +export function proposeUpdate( + identity: SharedObjectRef, + capability: ObjectRef, + didDoc: Uint8Array | undefined, + packageId: string, + expiration?: number, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const identityArg = tx.sharedObjectRef(identity); + const exp = tx.pure.option("u64", expiration); + const doc = tx.pure(bcs.option(bcs.vector(bcs.U8)).serialize(didDoc)); + const clock = getClockRef(tx); + + tx.moveCall({ + target: `${packageId}::identity::propose_update`, + arguments: [identityArg, delegationToken, doc, exp, clock], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + return tx.build({ onlyTransactionKind: true }); +} + +export function executeUpdate( + identity: SharedObjectRef, + capability: ObjectRef, + proposalId: string, + packageId: string, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const [delegationToken, borrow] = getControllerDelegation(tx, cap, packageId); + const proposal = tx.pure.id(proposalId); + const identityArg = tx.sharedObjectRef(identity); + const clock = getClockRef(tx); + + tx.moveCall({ + target: `${packageId}::identity::execute_update`, + arguments: [identityArg, delegationToken, proposal, clock], + }); + + putBackDelegationToken(tx, cap, delegationToken, borrow, packageId); + + return tx.build({ onlyTransactionKind: true }); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts new file mode 100644 index 000000000..cc8622bfe --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts @@ -0,0 +1,43 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; + +export function proposeUpgrade( + identity: SharedObjectRef, + capability: ObjectRef, + packageId: string, + expiration?: number, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const identityArg = tx.sharedObjectRef(identity); + const exp = tx.pure.option("u64", expiration); + + tx.moveCall({ + target: `${packageId}::identity::propose_upgrade`, + arguments: [identityArg, cap, exp], + }); + + return tx.build(); +} + +export function executeUpgrade( + identity: SharedObjectRef, + capability: ObjectRef, + proposalId: string, + packageId: string, +): Promise { + const tx = new Transaction(); + const cap = tx.objectRef(capability); + const proposal = tx.pure.id(proposalId); + const identityArg = tx.sharedObjectRef(identity); + + tx.moveCall({ + target: `${packageId}::identity::execute_upgrade`, + arguments: [identityArg, cap, proposal], + }); + + return tx.build(); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/index.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/index.ts new file mode 100644 index 000000000..7c8b6e125 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/index.ts @@ -0,0 +1,6 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * as asset from "./asset"; +export * as identity from "./identity"; +export * from "./migration"; diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts new file mode 100644 index 000000000..e29cd620f --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts @@ -0,0 +1,34 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { SharedObjectRef } from "@iota/iota-sdk/dist/cjs/bcs/types"; +import { ObjectRef, Transaction } from "@iota/iota-sdk/transactions"; +import { getClockRef } from "./utils"; + +export function migrateDidOutput( + didOutput: ObjectRef, + migrationRegistry: SharedObjectRef, + packageId: string, + creationTimestamp?: number, +): Promise { + const tx = new Transaction(); + const did_output = tx.objectRef(didOutput); + const migration_registry = tx.sharedObjectRef(migrationRegistry); + const clock = getClockRef(tx); + let timestamp; + if (creationTimestamp) { + timestamp = tx.pure.u64(creationTimestamp); + } else { + timestamp = tx.moveCall({ + target: "0x2::clock::timestamp_ms", + arguments: [clock], + }); + } + + tx.moveCall({ + target: `${packageId}::migration::migrate_alias_output`, + arguments: [did_output, migration_registry, timestamp, clock], + }); + + return tx.build(); +} diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts b/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts new file mode 100644 index 000000000..80c49b462 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts @@ -0,0 +1,50 @@ +import { ObjectRef, Transaction, TransactionArgument } from "@iota/iota-sdk/transactions"; +import { IOTA_CLOCK_OBJECT_ID } from "@iota/iota-sdk/utils"; + +const PLACEHOLDER_SENDER = "0x00000000000000090807060504030201"; +const PLACEHOLDER_GAS_BUDGET = 9; +const PLACEHOLDER_GAS_PRICE = 8; +const PLACEHOLDER_GAS_PAYMENT: ObjectRef[] = []; + +export function getClockRef(tx: Transaction): TransactionArgument { + return tx.sharedObjectRef({ objectId: IOTA_CLOCK_OBJECT_ID, initialSharedVersion: 1, mutable: false }); +} + +export function getControllerDelegation( + tx: Transaction, + controllerCap: TransactionArgument, + packageId: string, +): [TransactionArgument, TransactionArgument] { + const [token, borrow] = tx.moveCall({ + target: `${packageId}::controller::borrow`, + arguments: [controllerCap], + }); + return [token, borrow]; +} + +export function putBackDelegationToken( + tx: Transaction, + controllerCap: TransactionArgument, + delegationToken: TransactionArgument, + borrow: TransactionArgument, + packageId: string, +) { + tx.moveCall({ + target: `${packageId}::controller::put_back`, + arguments: [controllerCap, delegationToken, borrow], + }); +} + +/** + * Inserts placeholders related to sender and payment into transaction. + * + * This is required if wanting to call `tx.build`, as this will check if these values have been set. + * + * @param tx transaction to update + */ +export function insertPlaceholders(tx: Transaction) { + tx.setGasPrice(PLACEHOLDER_GAS_PRICE); + tx.setGasBudget(PLACEHOLDER_GAS_BUDGET); + tx.setGasPayment([...PLACEHOLDER_GAS_PAYMENT]); // make sure, we're not sharing the array between tx + tx.setSender(PLACEHOLDER_SENDER); +} 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 000000000..9bee396af --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../tsconfig.node.json", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "../lib": [ + "." + ], + "~iota_interaction_ts": [ + "../node/iota_interaction_ts", + "./iota_interaction_ts.js" + ] + }, + "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 000000000..b1937c9cb --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json @@ -0,0 +1,19 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "target": "ES2020", + "baseUrl": "./", + "paths": { + "../lib": [ + "." + ], + "~iota_interaction_ts": [ + "../web/iota_interaction_ts", + "./iota_interaction_ts.js" + ] + }, + "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 000000000..ff0c6f9bd --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/package-lock.json @@ -0,0 +1,1068 @@ +{ + "name": "@iota/iota-interaction-ts", + "version": "0.3.3", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@iota/iota-interaction-ts", + "version": "0.3.3", + "license": "Apache-2.0", + "devDependencies": { + "@types/node": "^22.0.0", + "dprint": "^0.33.0", + "rimraf": "^6.0.1", + "tsconfig-paths": "^4.1.0", + "typescript": "^5.7.3", + "wasm-opt": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@iota/iota-sdk": "^0.6.0" + } + }, + "node_modules/@0no-co/graphql.web": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.13.tgz", + "integrity": "sha512-jqYxOevheVTU1S36ZdzAkJIdvRp2m3OYIG5SEoKDw5NI8eVwkoI0D/Q3DYNGmXCxkA6CQuoa7zvMiDPTLqUNuw==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" + }, + "peerDependenciesMeta": { + "graphql": { + "optional": true + } + } + }, + "node_modules/@0no-co/graphqlsp": { + "version": "1.12.16", + "resolved": "https://registry.npmjs.org/@0no-co/graphqlsp/-/graphqlsp-1.12.16.tgz", + "integrity": "sha512-B5pyYVH93Etv7xjT6IfB7QtMBdaaC07yjbhN6v8H7KgFStMkPvi+oWYBTibMFRMY89qwc9H8YixXg8SXDVgYWw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@gql.tada/internal": "^1.0.0", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" + }, + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", + "typescript": "^5.0.0" + } + }, + "node_modules/@gql.tada/cli-utils": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@gql.tada/cli-utils/-/cli-utils-1.6.3.tgz", + "integrity": "sha512-jFFSY8OxYeBxdKi58UzeMXG1tdm4FVjXa8WHIi66Gzu9JWtCE6mqom3a8xkmSw+mVaybFW5EN2WXf1WztJVNyQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/internal": "1.0.8", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" + }, + "peerDependencies": { + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/svelte-support": "1.0.1", + "@gql.tada/vue-support": "1.0.1", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "@gql.tada/svelte-support": { + "optional": true + }, + "@gql.tada/vue-support": { + "optional": true + } + } + }, + "node_modules/@gql.tada/internal": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@gql.tada/internal/-/internal-1.0.8.tgz", + "integrity": "sha512-XYdxJhtHC5WtZfdDqtKjcQ4d7R1s0d1rnlSs3OcBEUbYiPoJJfZU7tWsVXuv047Z6msvmr4ompJ7eLSK5Km57g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@0no-co/graphql.web": "^1.0.5" + }, + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", + "typescript": "^5.0.0" + } + }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@iota/bcs": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@iota/bcs/-/bcs-0.2.1.tgz", + "integrity": "sha512-T+iv5gZhUZP7BiDY7+Ir4MA2rYmyGNZA2b+nxjv219Fp8klFt+l38OWA+1RgJXrCmzuZ+M4hbMAeHhHziURX6Q==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "bs58": "^6.0.0" + } + }, + "node_modules/@iota/iota-sdk": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@iota/iota-sdk/-/iota-sdk-0.6.0.tgz", + "integrity": "sha512-NEYiE7bdWw2DA3vLV7dO2EnoLDljN9NPhYrjfDGefTbIS9XpqX0JZTHMi//Q/K0aO4NwSHR5gu7n/ywoOTzTKg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@graphql-typed-document-node/core": "^3.2.0", + "@iota/bcs": "0.2.1", + "@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" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@iota/iota-sdk/node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense", + "peer": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@noble/curves": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.7.1" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/base": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz", + "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", + "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "~1.8.1", + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", + "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "~1.7.1", + "@scure/base": "~1.2.4" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@suchipi/femver": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@suchipi/femver/-/femver-1.0.0.tgz", + "integrity": "sha512-bprE8+K5V+DPX7q2e2K57ImqNBdfGHDIWaGI5xHxZoxbKOuQZn4wzPiUxOAHnsUr3w3xHrWXwN7gnG/iIuEMIg==", + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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 + }, + "node_modules/base-x": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", + "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==", + "license": "MIT", + "peer": true + }, + "node_modules/bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==", + "license": "MIT", + "peer": true + }, + "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, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "license": "MIT", + "peer": true, + "dependencies": { + "base-x": "^5.0.0" + } + }, + "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/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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, + "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 + }, + "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, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "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/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "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, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/glob": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/gql.tada": { + "version": "1.8.10", + "resolved": "https://registry.npmjs.org/gql.tada/-/gql.tada-1.8.10.tgz", + "integrity": "sha512-FrvSxgz838FYVPgZHGOSgbpOjhR+yq44rCzww3oOPJYi0OvBJjAgCiP6LEokZIYND2fUTXzQAyLgcvgw1yNP5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@0no-co/graphql.web": "^1.0.5", + "@0no-co/graphqlsp": "^1.12.13", + "@gql.tada/cli-utils": "1.6.3", + "@gql.tada/internal": "1.0.8" + }, + "bin": { + "gql-tada": "bin/cli.js", + "gql.tada": "bin/cli.js" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } + }, + "node_modules/graphql": { + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.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, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", + "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "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, + "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, + "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, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/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 + }, + "node_modules/string-width-cjs/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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/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, + "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/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, + "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/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "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/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/valibot": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.36.0.tgz", + "integrity": "sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ==", + "license": "MIT", + "peer": true + }, + "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, + "dependencies": { + "node-fetch": "^2.6.9", + "tar": "^6.1.13" + }, + "bin": { + "wasm-opt": "bin/wasm-opt.js" + } + }, + "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==", + "dev": true + }, + "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==", + "dev": true, + "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, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "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, + "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/wrap-ansi-cjs/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/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 + }, + "node_modules/wrap-ansi-cjs/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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 + }, + "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" + } + } + } +} diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_interaction_ts/package.json new file mode 100644 index 000000000..3f4eadcb6 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/package.json @@ -0,0 +1,48 @@ +{ + "name": "@iota/iota-interaction-ts", + "author": "IOTA Foundation ", + "description": "WASM bindings importing types from the IOTA Client typescript SDK to be used in Rust", + "homepage": "https://www.iota.org", + "version": "0.3.3", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/iotaledger/identity.rs.git" + }, + "scripts": { + "build:src": "cargo build --lib --release --target wasm32-unknown-unknown --target-dir ../target", + "build:src:node": "cargo build --lib --release --target wasm32-unknown-unknown --features keytool --target-dir ../target", + "prebundle:nodejs": "rimraf node", + "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", + "prebundle:web": "rimraf web", + "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:node && 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": "npm run build:web && npm run build:nodejs", + "fmt": "dprint fmt" + }, + "bugs": { + "url": "https://github.com/iotaledger/identity.rs/issues" + }, + "publishConfig": { + "access": "public" + }, + "files": [ + "web/*", + "node/*" + ], + "devDependencies": { + "@types/node": "^22.0.0", + "dprint": "^0.33.0", + "rimraf": "^6.0.1", + "tsconfig-paths": "^4.1.0", + "typescript": "^5.7.3", + "wasm-opt": "^1.4.0" + }, + "peerDependencies": { + "@iota/iota-sdk": "^0.6.0" + }, + "engines": { + "node": ">=20" + } +} 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 000000000..be0b7a087 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs @@ -0,0 +1,193 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_iota_interaction::types::execution_status::CommandArgumentError; +use js_sys::Uint8Array; +use serde::Serialize; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +use crate::bindings::WasmObjectRef; +use crate::bindings::WasmSharedObjectRef; +use crate::error::TsSdkError; +use crate::error::WasmError; +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; + +#[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls/asset")] +extern "C" { + #[wasm_bindgen(js_name = "create", catch)] + pub(crate) async fn new_asset( + inner_bytes: &[u8], + inner_type: &str, + mutable: bool, + transferable: bool, + deletable: bool, + package: &str, + ) -> Result; + + #[wasm_bindgen(catch, js_name = "remove")] + pub(crate) async fn delete(asset: WasmObjectRef, asset_type: &str, package: &str) -> Result; + + #[wasm_bindgen(catch)] + pub(crate) async fn update( + asset: WasmObjectRef, + content: &[u8], + content_type: &str, + package: &str, + ) -> Result; + + #[wasm_bindgen(catch)] + pub(crate) async fn transfer( + asset: WasmObjectRef, + asset_type: &str, + recipient: &str, + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "acceptProposal", catch)] + pub(crate) async fn accept_proposal( + proposal: WasmSharedObjectRef, + recipient_cap: WasmObjectRef, + asset: WasmObjectRef, + asset_type: &str, + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "concludeOrCancel", catch)] + pub(crate) async fn conclude_or_cancel( + proposal: WasmSharedObjectRef, + sender_cap: WasmObjectRef, + asset: WasmObjectRef, + asset_type: &str, + package: &str, + ) -> Result; +} + +pub struct AssetMoveCallsTsSdk {} + +impl AssetMoveCalls for AssetMoveCallsTsSdk { + type Error = TsSdkError; + + fn new_asset( + inner: &T, + mutable: bool, + transferable: bool, + deletable: bool, + package: ObjectID, + ) -> Result { + let inner_bytes = bcs::to_bytes(inner).map_err(|_| CommandArgumentError::InvalidBCSBytes)?; + let inner_type = T::move_type(package).to_string(); + let package = package.to_string(); + + futures::executor::block_on(new_asset( + &inner_bytes, + &inner_type, + mutable, + transferable, + deletable, + &package, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn delete(asset: ObjectRef, package: ObjectID) -> Result { + let asset = asset.into(); + let asset_type = T::move_type(package).to_string(); + let package = package.to_string(); + + futures::executor::block_on(delete(asset, &asset_type, &package)) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn transfer( + asset: ObjectRef, + recipient: IotaAddress, + package: ObjectID, + ) -> Result { + let asset = asset.into(); + let asset_type = T::move_type(package).to_string(); + let recipient = recipient.to_string(); + let package = package.to_string(); + + futures::executor::block_on(transfer(asset, &asset_type, &recipient, &package)) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + 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: TypeTag, + package: ObjectID, + ) -> Result { + let proposal = (proposal.0, proposal.1, true).into(); + let asset = asset.into(); + let asset_type = asset_type.to_canonical_string(true); + let recipient = recipient_cap.into(); + let package = package.to_string(); + + futures::executor::block_on(accept_proposal(proposal, recipient, asset, &asset_type, &package)) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn conclude_or_cancel( + proposal: (ObjectID, SequenceNumber), + sender_cap: ObjectRef, + asset: ObjectRef, + asset_type: TypeTag, + package: ObjectID, + ) -> Result { + let proposal = (proposal.0, proposal.1, true).into(); + let asset = asset.into(); + let asset_type = asset_type.to_canonical_string(true); + let sender = sender_cap.into(); + let package = package.to_string(); + + futures::executor::block_on(conclude_or_cancel(proposal, sender, asset, &asset_type, &package)) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn update( + asset: ObjectRef, + new_content: &T, + package: ObjectID, + ) -> Result { + let asset = asset.into(); + let content_type = T::move_type(package).to_string(); + let content = bcs::to_bytes(new_content).map_err(|_| CommandArgumentError::InvalidBCSBytes)?; + let package = package.to_string(); + + futures::executor::block_on(update(asset, &content, &content_type, &package)) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } +} diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/mod.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool/mod.rs new file mode 100644 index 000000000..4bc1a321a --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/keytool/mod.rs @@ -0,0 +1,8 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod signer; +mod storage; + +pub use signer::*; +pub use storage::*; diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/signer.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool/signer.rs new file mode 100644 index 000000000..a5d77cc28 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/keytool/signer.rs @@ -0,0 +1,110 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; + +use crate::error::Result; +use crate::error::WasmResult; +use fastcrypto::traits::EncodeDecodeBase64 as _; +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::KeytoolSigner; +use identity_iota_interaction::KeytoolSignerBuilder; +use secret_storage::Signer; +use serde_json::Value; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsError; + +use crate::WasmPublicKey; + +#[wasm_bindgen(module = buffer)] +extern "C" { + #[wasm_bindgen(typescript_type = Buffer)] + type NodeBuffer; + #[wasm_bindgen(method, js_name = toString)] + fn to_string(this: &NodeBuffer) -> String; +} + +#[wasm_bindgen(module = child_process)] +extern "C" { + #[wasm_bindgen(js_name = execSync, catch)] + fn exec_cli_cmd(cmd: &str) -> Result; +} + +/// An implementation of the Signer interface that relies on +/// IOTA Keytool. +#[wasm_bindgen(js_name = KeytoolSigner)] +pub struct WasmKeytoolSigner(pub(crate) KeytoolSigner); + +#[wasm_bindgen(js_class = KeytoolSigner)] +impl WasmKeytoolSigner { + /// Returns a new {@link KeytoolSigner}. The optional parameters respectively + /// allow to set the signing address and the `iota` binary to use. Defaults + /// to use the current active address and the binary found in $PATH. + #[wasm_bindgen(constructor)] + pub fn new(address: Option, iota_bin_location: Option) -> Result { + let address = address + .as_deref() + .map(IotaAddress::from_str) + .transpose() + .wasm_result()?; + + let builder = address + .map(|address| KeytoolSignerBuilder::new().with_address(address)) + .unwrap_or_default(); + let builder = if let Some(iota_bin_location) = iota_bin_location { + builder.iota_bin_location(iota_bin_location) + } else { + builder + }; + + Ok(WasmKeytoolSigner(builder.build().wasm_result()?)) + } + + /// Returns the signing address. + #[wasm_bindgen] + pub fn address(&self) -> String { + self.0.address().to_string() + } + + // These method definition are needed to make sure `KeytoolSigner` + // implements `Signer` interface. + + #[wasm_bindgen(js_name = keyId)] + pub fn key_id(&self) -> String { + self.address() + } + + #[wasm_bindgen(js_name = publicKey)] + pub async fn public_key(&self) -> Result { + self.0.public_key().try_into() + } + + #[wasm_bindgen] + pub async fn sign(&self, tx_data_bcs: &[u8]) -> Result { + let tx_data = bcs::from_bytes(tx_data_bcs).map_err(|e| JsError::new(&e.to_string()))?; + self + .0 + .sign(&tx_data) + .await + .map(|sig| sig.encode_base64()) + .map_err(|e| JsError::new(&e.to_string()).into()) + } + + #[wasm_bindgen(js_name = iotaPublicKeyBytes)] + pub async fn iota_public_key_bytes(&self) -> Vec { + let pk = self.0.public_key(); + let mut bytes = vec![pk.flag()]; + bytes.extend_from_slice(pk.as_ref()); + + bytes + } +} + +// This is used in KeytoolSigner implementation to issue CLI commands. +#[no_mangle] +pub extern "Rust" fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result { + let output = exec_cli_cmd(cmd) + .map_err(|e| anyhow::anyhow!("exec failed: {e:?}"))? + .to_string(); + serde_json::from_str(&output).map_err(|_| anyhow::anyhow!("failed to deserialize JSON object from command output")) +} diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/storage.rs b/bindings/wasm/iota_interaction_ts/src/bindings/keytool/storage.rs new file mode 100644 index 000000000..a78951a46 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/keytool/storage.rs @@ -0,0 +1,147 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::str::FromStr; + +use identity_iota_interaction::types::base_types::IotaAddress; +use identity_iota_interaction::types::crypto::IotaKeyPair; +use identity_iota_interaction::types::crypto::SignatureScheme; +use identity_iota_interaction::KeytoolStorage; +use js_sys::Array; +use js_sys::JsString; +use wasm_bindgen::prelude::*; + +use crate::error::Result; +use crate::error::WasmResult; +use crate::WasmPublicKey; + +use super::signer::WasmKeytoolSigner; + +const __TS_IMPORTS: &str = r#" +import { PublicKey } from "@iota/iota-sdk/cryptography"; +"#; + +fn make_pk_alias_tuple(pk: &WasmPublicKey, alias: &str) -> Array { + let arr = Array::new(); + arr.push(pk.as_ref()); + arr.push(JsString::from(alias).as_ref()); + + arr +} + +/// IOTA Keytool CLI wrapper. +#[derive(Default, Clone)] +#[wasm_bindgen(js_name = KeytoolStorage)] +pub struct WasmKeytoolStorage(pub(crate) KeytoolStorage); + +impl AsRef for WasmKeytoolStorage { + fn as_ref(&self) -> &KeytoolStorage { + &self.0 + } +} + +#[wasm_bindgen(js_class = KeytoolStorage)] +impl WasmKeytoolStorage { + /// Creates a new {@link KeytoolStorage} that wraps the given iota binary. + /// Attempts to use the one in PATH if none is provided. + #[wasm_bindgen(constructor)] + pub fn new(iota_bin: Option) -> Self { + iota_bin + .as_deref() + .map(KeytoolStorage::new_with_custom_bin) + .map(Self) + .unwrap_or_default() + } + + /// Returns a {@link KeytoolSigner} that will use the provided `address` + /// to sign transactions. If no address is provided the current active + /// one will be used. + pub fn signer(&self, address: Option) -> Result { + let address = address.map(|s| IotaAddress::from_str(&s)).transpose().wasm_result()?; + let mut signer_builder = self.0.signer(); + if let Some(address) = address { + signer_builder = signer_builder.with_address(address); + } + + signer_builder.build().map(WasmKeytoolSigner).wasm_result() + } + + /// Creates a new key of type `key_scheme`. + /// Returns the tuple ([`PublicKey`](https://docs.iota.org/ts-sdk/api/cryptography/classes/PublicKey), alias). + #[wasm_bindgen( + js_name = generateKey, + unchecked_return_type = "[PublicKey, string]", + )] + pub fn generate_key( + &self, + #[wasm_bindgen(unchecked_param_type = "'ed25519' | 'secp256r1' | 'secp256k1'")] key_scheme: &str, + ) -> Result { + let key_scheme = match key_scheme { + "ed25519" => SignatureScheme::ED25519, + "secp256r1" => SignatureScheme::Secp256r1, + "secp256k1" => SignatureScheme::Secp256k1, + _ => return Err(JsError::new("invalid key type").into()), + }; + + self + .0 + .generate_key(key_scheme) + .wasm_result() + .map(|(pk, alias)| make_pk_alias_tuple(&WasmPublicKey::try_from(&pk).unwrap(), &alias)) + } + + /// Inserts a Bech32-encoded private key in the keystore. + /// The key must use the prefix `iotaprivkey`. + /// + /// Returns the key's alias. + #[wasm_bindgen(js_name = insertKey)] + pub fn insert_key(&self, bech32_secret_key: &str) -> Result { + let key = IotaKeyPair::decode(bech32_secret_key) + .map_err(|e| anyhow::anyhow!("{e:?}")) + .wasm_result()?; + self.0.insert_key(key).wasm_result() + } + + /// Signs `data` with `address`'s secret key. + #[wasm_bindgen(js_name = signRaw)] + pub fn sign_raw(&self, address: &str, data: &[u8]) -> Result> { + let address = address.parse().wasm_result()?; + self.0.sign_raw(address, data).wasm_result() + } + + /// Updates an alias from `old_alias` to `new_alias` + /// If no value for `new_alias` is provided, a randomly generated one will be used. + #[wasm_bindgen(js_name = updateAlias)] + pub fn update_alias(&self, old_alias: &str, new_alias: Option) -> Result<()> { + let new_alias = new_alias.as_deref(); + self.0.update_alias(old_alias, new_alias).wasm_result() + } + + /// Returns the [`PublicKey`](https://docs.iota.org/ts-sdk/api/cryptography/classes/PublicKey) for the given address together with its alias. + #[wasm_bindgen( + js_name = getKey, + unchecked_return_type = "[PublicKey, string]", + )] + pub fn get_key(&self, address: &str) -> Result { + let address = address.parse().wasm_result()?; + self + .0 + .get_key(address) + .wasm_result()? + .map(|(pk, alias)| make_pk_alias_tuple(&WasmPublicKey::try_from(&pk).unwrap(), &alias)) + .ok_or_else(|| anyhow::anyhow!("the requested address is not in the keystore")) + .wasm_result() + } + + /// Returns the [`PublicKey`](https://docs.iota.org/ts-sdk/api/cryptography/classes/PublicKey) that has the given alias. + #[wasm_bindgen(js_name = getKeyByAlias)] + pub fn get_key_by_alias(&self, alias: &str) -> Result { + self + .0 + .get_key_by_alias(alias) + .wasm_result()? + .map(|pk| WasmPublicKey::try_from(&pk).unwrap()) + .ok_or_else(|| anyhow::anyhow!("the requested alias is not in the keystore")) + .wasm_result() + } +} 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 000000000..095591007 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs @@ -0,0 +1,11 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#[cfg(feature = "keytool")] +pub mod keytool; +mod types; +mod wasm_iota_client; +mod wasm_types; + +pub use types::*; +pub use wasm_iota_client::*; 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 000000000..fe0645f61 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/types.rs @@ -0,0 +1,12 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use wasm_bindgen::prelude::*; + +pub use super::wasm_types::*; + +#[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 000000000..0bf674cfe --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs @@ -0,0 +1,437 @@ +// 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::WaitForTransactionParams; +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::crypto::Signature; +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::TransactionData; +use js_sys::Promise; +use serde::Serialize; +use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::JsFuture; + +use super::wasm_types::PromiseIotaTransactionBlockResponse; +use super::wasm_types::WasmExecuteTransactionBlockParams; +use super::wasm_types::WasmIotaTransactionBlockResponseWrapper; +use super::PromiseDryRunTransactionBlockResponse; +use super::WasmDryRunTransactionBlockParams; +use super::WasmWaitForTransactionParams; + +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 used as arguments for rust functions via the typescript_type annotation. +// In other words: The typescript_type "IotaClient" is imported here to be bound +// to the WasmIotaClient functions below. +// TODO: check why this isn't done by `module` macro attribute for `WasmIotaClient` +#[wasm_bindgen(typescript_custom_section)] +const IOTA_CLIENT_TYPE: &'static str = r#" + import { IotaClient } from "@iota/iota-sdk/client"; +"#; + +#[wasm_bindgen(module = "@iota/iota-sdk/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 = dryRunTransactionBlock)] + pub fn dry_run_transaction_block( + this: &WasmIotaClient, + params: &WasmDryRunTransactionBlockParams, + ) -> PromiseDryRunTransactionBlockResponse; + + #[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; + + #[wasm_bindgen(method, js_name = waitForTransaction)] + pub fn wait_for_transaction( + this: &WasmIotaClient, + input: &WasmWaitForTransactionParams, + ) -> PromiseIotaTransactionBlockResponse; +} + +// Helper struct used to convert TYPESCRIPT types to RUST types +#[derive(Clone)] +pub struct ManagedWasmIotaClient(pub(crate) 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: TransactionData, + signatures: Vec, + options: Option, + request_type: Option, + ) -> IotaRpcResult { + let params = ExecuteTransactionBlockParams::new(tx_data, signatures, options, request_type); + let wasm_params = params + .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + .map_err(|e| { + IotaRpcError::FfiError(format!( + "failed to convert ExecuteTransactionBlockParams to JS value: {e}" + )) + })? + .into(); + + let promise: Promise = Promise::resolve(&WasmIotaClient::execute_transaction_block(&self.0, &wasm_params)); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + console_log!("Error executing JsFuture::from(promise): {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + })?; + Ok(WasmIotaTransactionBlockResponseWrapper::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)) + })?; + + #[allow(deprecated)] // will be refactored + 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)) + })?; + + #[allow(deprecated)] // will be refactored + 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)) + })?; + + #[allow(deprecated)] // will be refactored + 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(WasmIotaTransactionBlockResponseWrapper::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)) + })?; + + serde_wasm_bindgen::from_value(result) + .map_err(|e| IotaRpcError::FfiError(format!("failed to deserialize gas price from JS value: {e}"))) + } + + 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)) + })?; + + #[allow(deprecated)] // will be refactored + 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)) + })?; + + #[allow(deprecated)] // will be refactored + Ok(result.into_serde()?) + } + + /// Wait for a transaction block result to be available over the API. + /// This can be used in conjunction with `execute_transaction_block` to wait for the transaction to + /// be available via the API. + /// This currently polls the `getTransactionBlock` API to check for the transaction. + /// + /// # Arguments + /// + /// * `digest` - The digest of the queried transaction. + /// * `options` - Options for specifying the content to be returned. + /// * `timeout` - The amount of time to wait for a transaction block. Defaults to one minute. + /// * `poll_interval` - The amount of time to wait between checks for the transaction block. Defaults to 2 seconds. + pub async fn wait_for_transaction( + &self, + digest: TransactionDigest, + options: Option, + timeout: Option, + poll_interval: Option, + ) -> IotaRpcResult { + let params_object = WaitForTransactionParams::new(digest.to_string(), options, timeout, poll_interval); + let params: WasmWaitForTransactionParams = serde_json::to_value(¶ms_object) + .map_err(|e| { + console_log!("Error serializing WaitForTransactionParams to Value: {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + }) + .and_then(|v| { + v.serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + .map_err(|e| { + console_log!("Error serializing Value to WasmWaitForTransactionParams: {:?}", e); + IotaRpcError::FfiError(format!("{:?}", e)) + }) + })? + .into(); + + let promise: Promise = Promise::resolve(&WasmIotaClient::wait_for_transaction(&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(WasmIotaTransactionBlockResponseWrapper::new(result.into())) + } +} 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 000000000..7e7abda81 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs @@ -0,0 +1,581 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +use std::str::FromStr; + +use fastcrypto::encoding::Base64; +use fastcrypto::encoding::Encoding; +use fastcrypto::traits::EncodeDecodeBase64 as _; +use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; +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::crypto::PublicKey; +use identity_iota_interaction::types::crypto::Signature; +use identity_iota_interaction::types::digests::TransactionDigest; +use identity_iota_interaction::types::execution_status::CommandArgumentError; +use identity_iota_interaction::types::object::Owner; +use identity_iota_interaction::types::transaction::TransactionData; +use identity_iota_interaction::ProgrammableTransactionBcs; +use js_sys::Promise; +use js_sys::Uint8Array; +use serde::Deserialize; +use serde::Serialize; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsCast; +use wasm_bindgen::JsError; +use wasm_bindgen::JsValue; +use wasm_bindgen_futures::JsFuture; + +use crate::bindings::WasmIotaClient; +use crate::console_log; +use crate::error::TsSdkError; +use crate::error::WasmError; + +// TODO: fix/add signer or remove functions relying on it +type WasmStorageSigner = (); + +#[wasm_bindgen(typescript_custom_section)] +const TS_SDK_TYPES: &str = r#" + import { + Balance, + ExecuteTransactionBlockParams, + GetCoinsParams, + GetDynamicFieldObjectParams, + GetObjectParams, + GetOwnedObjectsParams, + GetTransactionBlockParams, + IotaClient, + IotaObjectData, + IotaObjectResponse, + IotaTransactionBlockResponse, + IotaTransactionBlockResponseOptions, + ObjectRead, + PaginatedCoins, + PaginatedEvents, + PaginatedObjectsResponse, + QueryEventsParams, + TryGetPastObjectParams, + } from "@iota/iota-sdk/client"; + import { bcs } from "@iota/iota-sdk/bcs"; + import { + executeTransaction, + WasmIotaTransactionBlockResponseWrapper, + } from "./iota_client_helpers" +"#; + +#[wasm_bindgen(module = "@iota/iota-sdk/client")] +extern "C" { + #[wasm_bindgen(typescript_type = "Promise")] + pub type PromiseBalance; + + #[wasm_bindgen(typescript_type = "TransactionArgument")] + pub type WasmTransactionArgument; + + #[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 = "Promise")] + pub type PromiseDryRunTransactionBlockResponse; + + #[wasm_bindgen(typescript_type = "DryRunTransactionBlockResponse")] + #[derive(Clone)] + pub type WasmDryRunTransactionBlockResponse; + + #[wasm_bindgen(typescript_type = "DryRunTransactionBlockParams")] + #[derive(Clone)] + pub type WasmDryRunTransactionBlockParams; + + #[derive(Clone)] + #[wasm_bindgen( + typescript_type = "TransactionEffects", + extends = js_sys::Object, + )] + pub type WasmIotaTransactionBlockEffects; + + #[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 = "IotaObjectRef")] + #[derive(Clone)] + pub type WasmObjectRef; + + #[wasm_bindgen(method, getter, js_name = objectId)] + pub fn object_id(this: &WasmObjectRef) -> String; + + #[wasm_bindgen(method, getter, js_name = digest)] + pub fn digest(this: &WasmObjectRef) -> String; + + #[wasm_bindgen(method, getter, js_name = version)] + pub fn version(this: &WasmObjectRef) -> String; + + #[wasm_bindgen(typescript_type = "SharedObjectRef")] + #[derive(Clone)] + pub type WasmSharedObjectRef; + + #[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 PromiseIotaTransactionBlockResponseWrapper; + + #[wasm_bindgen(typescript_type = "Signature")] + pub type WasmIotaSignature; + + #[wasm_bindgen(typescript_type = "Parameters")] + #[derive(Clone, Debug)] + pub type WasmWaitForTransactionParams; +} + +impl From for IotaTransactionBlockEffects { + fn from(value: WasmIotaTransactionBlockEffects) -> Self { + serde_wasm_bindgen::from_value(value.into()).expect("have the same repr") + } +} + +impl From<&'_ IotaTransactionBlockEffects> for WasmIotaTransactionBlockEffects { + fn from(value: &'_ IotaTransactionBlockEffects) -> Self { + value + .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + .expect("same representation") + .unchecked_into() + } +} + +#[derive(Serialize, Deserialize)] +enum IotaSignatureHelper { + Ed25519IotaSignature(String), + Secp256k1IotaSignature(String), + Secp256r1IotaSignature(String), +} + +impl TryFrom for WasmIotaSignature { + type Error = JsValue; + fn try_from(sig: Signature) -> Result { + let base64sig = Base64::encode(&sig); + let json_signature = match sig { + Signature::Ed25519IotaSignature(_) => IotaSignatureHelper::Ed25519IotaSignature(base64sig), + Signature::Secp256r1IotaSignature(_) => IotaSignatureHelper::Secp256r1IotaSignature(base64sig), + Signature::Secp256k1IotaSignature(_) => IotaSignatureHelper::Secp256k1IotaSignature(base64sig), + }; + + json_signature + .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + .map(JsCast::unchecked_into) + .map_err(|e| e.into()) + } +} + +impl TryFrom for Signature { + type Error = JsValue; + fn try_from(sig: WasmIotaSignature) -> Result { + let sig_helper = serde_wasm_bindgen::from_value(sig.into())?; + let base64sig = match sig_helper { + IotaSignatureHelper::Ed25519IotaSignature(s) => s, + IotaSignatureHelper::Secp256k1IotaSignature(s) => s, + IotaSignatureHelper::Secp256r1IotaSignature(s) => s, + }; + + base64sig + .parse() + .map_err(|e: eyre::Report| JsError::new(&e.to_string()).into()) + } +} + +#[wasm_bindgen(module = "@iota/iota-sdk/transactions")] +extern "C" { + #[derive(Clone)] + #[wasm_bindgen(typescript_type = "Transaction")] + pub type WasmTransaction; + + #[wasm_bindgen(js_name = "from", js_class = "Transaction", static_method_of = WasmTransaction, catch)] + pub fn from_bcs_bytes(bytes: Uint8Array) -> Result; + + #[wasm_bindgen(method, structural, catch)] + pub async fn build( + this: &WasmTransaction, + options: Option, + ) -> Result; + + #[wasm_bindgen(typescript_type = BuildTransactionOptions)] + pub type WasmBuildTransactionOptions; + + #[derive(Clone)] + #[wasm_bindgen(typescript_type = "TransactionData")] + pub type WasmTransactionData; + + #[wasm_bindgen(typescript_type = "Transaction")] + pub type WasmTransactionBuilder; + + #[wasm_bindgen(js_name = "from", js_class = "Transaction", static_method_of = WasmTransactionBuilder, catch)] + pub fn from_bcs_bytes(bytes: Uint8Array) -> Result; + + #[wasm_bindgen(method, structural, catch)] + pub async fn build(this: &WasmTransactionBuilder) -> Result; + + #[derive(Clone)] + #[wasm_bindgen(typescript_type = TransactionDataBuilder)] + pub type WasmTransactionDataBuilder; + + #[wasm_bindgen( + js_name = fromBytes, + js_class = TransactionDataBuilder, + static_method_of = WasmTransactionDataBuilder, + catch + )] + pub fn from_bcs_bytes(bytes: Uint8Array) -> Result; + + #[wasm_bindgen( + js_name = fromKindBytes, + js_class = TransactionDataBuilder, + static_method_of = WasmTransactionDataBuilder, + catch + )] + pub fn from_kind_bcs_bytes(bytes: Uint8Array) -> Result; + + #[wasm_bindgen(method, catch)] + pub fn build(this: &WasmTransactionDataBuilder) -> Result; + // TODO: decide if we need the following functions: "yagni" or not? + + // #[wasm_bindgen(js_name = "setSender", method, catch)] + // pub fn set_sender(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; + + // #[wasm_bindgen(js_name = "setGasOwner", method, catch)] + // pub fn set_gas_owner(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; + + // #[wasm_bindgen(js_name = "setGasPrice", method, catch)] + // pub fn set_gas_price(this: &WasmTransactionBuilder, price: u64) -> Result<(), JsValue>; + + // #[wasm_bindgen(js_name = "setGasPayment", method, catch)] + // pub fn set_gas_payment(this: &WasmTransactionBuilder, payments: Vec) -> Result<(), JsValue>; + + // #[wasm_bindgen(js_name = "setGasBudget", method, catch)] + // pub fn set_gas_budget(this: &WasmTransactionBuilder, budget: u64) -> Result<(), JsValue>; + + // #[wasm_bindgen(js_name = "getData", method, catch)] + // pub fn get_data(this: &WasmTransactionBuilder) -> Result; +} + +impl TryFrom for WasmTransactionData { + type Error = serde_wasm_bindgen::Error; + fn try_from(value: TransactionData) -> Result { + let js_value = serde_wasm_bindgen::to_value(&value)?; + Ok(js_value.unchecked_into()) + } +} + +impl TryFrom for TransactionData { + type Error = serde_wasm_bindgen::Error; + fn try_from(value: WasmTransactionData) -> Result { + serde_wasm_bindgen::from_value(value.into()) + } +} + +#[wasm_bindgen(module = "@iota/iota-sdk/cryptography")] +extern "C" { + #[derive(Clone)] + #[wasm_bindgen(typescript_type = PublicKey)] + pub type WasmPublicKey; + + #[wasm_bindgen(js_name = toIotaPublicKey, method)] + pub fn to_iota_public_key(this: &WasmPublicKey) -> String; + + #[wasm_bindgen(js_name = toRawBytes, method)] + pub fn to_raw_bytes(this: &WasmPublicKey) -> Vec; + + #[wasm_bindgen(js_name = toIotaAddress, method)] + pub fn to_iota_address(this: &WasmPublicKey) -> String; + + #[wasm_bindgen(method)] + pub fn flag(this: &WasmPublicKey) -> u8; +} + +#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/ed25519")] +extern "C" { + #[wasm_bindgen(extends = WasmPublicKey)] + pub type Ed25519PublicKey; + + #[wasm_bindgen(constructor, catch)] + pub fn new_ed25519_pk(bytes: &[u8]) -> Result; +} + +#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/secp256r1")] +extern "C" { + #[wasm_bindgen(extends = WasmPublicKey)] + pub type Secp256r1PublicKey; + + #[wasm_bindgen(constructor, catch)] + pub fn new_secp256r1_pk(bytes: &[u8]) -> Result; +} + +#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/secp256k1")] +extern "C" { + #[wasm_bindgen(extends = WasmPublicKey)] + pub type Secp256k1PublicKey; + + #[wasm_bindgen(constructor, catch)] + pub fn new_secp256k1_pk(bytes: &[u8]) -> Result; +} + +impl TryFrom<&'_ PublicKey> for WasmPublicKey { + type Error = JsValue; + fn try_from(pk: &PublicKey) -> Result { + let pk_bytes = pk.as_ref(); + let wasm_pk: WasmPublicKey = match pk { + PublicKey::Ed25519(_) => Ed25519PublicKey::new_ed25519_pk(pk_bytes)?.into(), + PublicKey::Secp256r1(_) => Secp256r1PublicKey::new_secp256r1_pk(pk_bytes)?.into(), + PublicKey::Secp256k1(_) => Secp256k1PublicKey::new_secp256k1_pk(pk_bytes)?.into(), + _ => return Err(JsError::new("unsupported PublicKey type").into()), + }; + + assert_eq!(pk_bytes, &wasm_pk.to_raw_bytes()); + assert_eq!( + IotaAddress::from(pk), + wasm_pk.to_iota_address().parse().expect("valid iota address") + ); + + Ok(wasm_pk) + } +} + +impl TryFrom for PublicKey { + type Error = JsValue; + fn try_from(wasm_pk: WasmPublicKey) -> Result { + let pk = PublicKey::decode_base64(&wasm_pk.to_iota_public_key()) + .map_err(|_| JsError::new("failed to decode base64 JS PublicKey"))?; + + assert_eq!(&wasm_pk.to_raw_bytes(), pk.as_ref()); + assert_eq!( + IotaAddress::from(&pk), + wasm_pk.to_iota_address().parse().expect("valid iota address") + ); + + Ok(pk) + } +} + +impl TryFrom for ObjectRef { + type Error = anyhow::Error; + fn try_from(value: WasmObjectRef) -> Result { + let digest = serde_json::from_value(serde_json::Value::String(value.digest()))?; + let version = { + let version_number = serde_json::Number::from_str(&value.version())?; + serde_json::from_value(serde_json::Value::Number(version_number))? + }; + let object_id = value.object_id().parse()?; + + Ok((object_id, version, digest)) + } +} + +impl From for WasmObjectRef { + fn from(value: ObjectRef) -> Self { + let json_obj = serde_json::json!({ + "objectId": value.0, + "version": value.1, + "digest": value.2, + }); + + json_obj + .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + .expect("a JSON object is a JS value") + // safety: `json_obj` was constructed following TS ObjectRef's interface. + .unchecked_into() + } +} + +impl From<(ObjectID, SequenceNumber, bool)> for WasmSharedObjectRef { + fn from(value: (ObjectID, SequenceNumber, bool)) -> Self { + let json_obj = serde_json::json!({ + "objectId": value.0, + "initialSharedVersion": value.1, + "mutable": value.2, + }); + + json_obj + .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) + .expect("a JSON object is a JS value") + // safety: `json_obj` was constructed following TS SharedObjectRef's interface. + .unchecked_into() + } +} + +impl TryFrom for WasmSharedObjectRef { + type Error = TsSdkError; + fn try_from(value: OwnedObjectRef) -> Result { + let Owner::Shared { initial_shared_version } = value.owner else { + return Err(TsSdkError::CommandArgumentError(CommandArgumentError::TypeMismatch)); + }; + let obj_id = value.object_id(); + + Ok((obj_id, initial_shared_version, true).into()) + } +} + +impl WasmSharedObjectRef { + #[allow(dead_code)] + pub(crate) fn immutable(self) -> Self { + const JS_FALSE: JsValue = JsValue::from_bool(false); + + let _ = js_sys::Reflect::set(&self, &JsValue::from_str("mutable"), &JS_FALSE); + self + } +} + +#[wasm_bindgen(module = "@iota/iota-interaction-ts/iota_client_helpers")] +extern "C" { + // Please note: For unclear reasons the `typescript_type` name and the `pub type` name defined + // in wasm_bindgen extern "C" scopes must be equal. Otherwise, the JS constructor will not be + // found in the generated js code. + #[wasm_bindgen(typescript_type = "WasmIotaTransactionBlockResponseWrapper")] + #[derive(Clone)] + pub type WasmIotaTransactionBlockResponseWrapper; + + #[wasm_bindgen(constructor)] + pub fn new(response: WasmIotaTransactionBlockResponse) -> WasmIotaTransactionBlockResponseWrapper; + + #[wasm_bindgen(method, js_name = get_effects)] + pub fn effects(this: &WasmIotaTransactionBlockResponseWrapper) -> Option; + + #[wasm_bindgen(method)] + pub fn to_string(this: &WasmIotaTransactionBlockResponseWrapper) -> String; + + #[wasm_bindgen(method, js_name = "get_digest")] + fn digest_inner(this: &WasmIotaTransactionBlockResponseWrapper) -> String; + + #[wasm_bindgen(method, js_name = "get_response")] + pub fn response(this: &WasmIotaTransactionBlockResponseWrapper) -> WasmIotaTransactionBlockResponse; + + #[wasm_bindgen(js_name = executeTransaction)] + fn execute_transaction_inner( + iota_client: &WasmIotaClient, // --> TypeScript: IotaClient + sender_address: String, // --> TypeScript: string + tx_bcs: Vec, // --> TypeScript: Uint8Array, + signer: WasmStorageSigner, // --> TypeScript: Signer (iota_client_helpers module) + gas_budget: Option, // --> TypeScript: optional bigint + ) -> PromiseIotaTransactionBlockResponseWrapper; + + #[wasm_bindgen(js_name = "sleep")] + fn sleep_inner(ms: i32) -> Promise; +} + +/// Helper function to pause execution. +pub async fn sleep(duration_ms: i32) -> Result<(), JsValue> { + let promise = sleep_inner(duration_ms); + let js_fut = JsFuture::from(promise); + js_fut.await?; + Ok(()) +} + +impl WasmIotaTransactionBlockResponseWrapper { + pub fn digest(&self) -> Result { + TransactionDigest::from_str(&self.digest_inner()) + .map_err(|err| TsSdkError::WasmError("Failed to parse transaction block digest".to_string(), err.to_string())) + } +} + +pub async fn execute_transaction( + iota_client: &WasmIotaClient, // --> Binding: WasmIotaClient + sender_address: IotaAddress, // --> Binding: String + 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(), + tx_bcs, + signer, + gas_budget, + )); + let result: JsValue = JsFuture::from(promise).await.map_err(|e| { + let message = "Error executing JsFuture::from(promise) for `execute_transaction`"; + let details = format!("{e:?}"); + console_log!("{message}; {details}"); + TsSdkError::WasmError(message.to_string(), details.to_string()) + })?; + + Ok(WasmIotaTransactionBlockResponseWrapper::new(result.into())) +} + +#[derive(Deserialize)] +#[serde(try_from = "Vec")] +pub struct ProgrammableTransaction(#[allow(dead_code)] pub(crate) WasmTransactionBuilder); +impl TryFrom> for ProgrammableTransaction { + type Error = TsSdkError; + fn try_from(value: Vec) -> Result { + let uint8array: Uint8Array = value.as_slice().into(); + WasmTransactionBuilder::from_bcs_bytes(uint8array) + .map(Self) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } +} 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 000000000..259fc5417 --- /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 000000000..f764977e2 --- /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 000000000..d95f6dcfa --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/common/types.rs @@ -0,0 +1,122 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use identity_core::common::Object; +use identity_iota_interaction::types::transaction::TransactionKind; +use identity_iota_interaction::ProgrammableTransactionBcs; +use js_sys::Promise; +use js_sys::Uint8Array; +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; +use wasm_bindgen_futures::JsFuture; + +use crate::error::TsSdkError; +use crate::error::TsSdkResult; +use crate::error::WasmError; +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()), + #[allow(deprecated)] // will be refactored + &JsValue::from_serde(&value).wasm_result()?, + ); + } + Ok(map.unchecked_into::()) + } +} + +impl Default for MapStringAny { + fn default() -> Self { + js_sys::Map::new().unchecked_into() + } +} + +impl PromiseUint8Array { + /// Helper function to convert Uint8 arrays from contract calls to the internal `ProgrammableTransactionBcs` type. + pub async fn to_programmable_transaction_bcs(&self) -> TsSdkResult { + let promise: Promise = Promise::resolve(self); + let tx_kind_bcs = JsFuture::from(promise) + .await + .map(|v| Uint8Array::from(v).to_vec()) + .map_err(WasmError::from)?; + + let tx_kind = bcs::from_bytes(&tx_kind_bcs).map_err(|e| { + TsSdkError::TransactionSerializationError(format!("failed to deserialize BCS TransactionKind: {e}")) + })?; + + #[allow(irrefutable_let_patterns)] + let TransactionKind::ProgrammableTransaction(pt) = tx_kind + else { + return Err(TsSdkError::WasmError( + "TransactionKind variant".to_owned(), + "only programmable transactions can be used within this library".to_owned(), + )); + }; + + bcs::to_bytes(&pt).map_err(|e| { + TsSdkError::TransactionSerializationError(format!("failed to BCS serialize programmable transaction: {e}")) + }) + } +} 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 000000000..3ed8447a1 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/common/utils.rs @@ -0,0 +1,26 @@ +// Copyright 2020-2023 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::de::DeserializeOwned; +use wasm_bindgen::prelude::*; + +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 000000000..e29a09837 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/error.rs @@ -0,0 +1,223 @@ +// 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()), + } + } +} + +impl From for WasmError<'_> { + fn from(error: anyhow::Error) -> Self { + Self { + name: Cow::Borrowed("anyhow::Error"), + message: Cow::Owned(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(#[from] PackageUpgradeError), + #[error("[TsSdkError] CommandArgumentError: {0}")] + CommandArgumentError(#[from] CommandArgumentError), + #[error("[TsSdkError] ExecutionFailureStatus: {0}")] + ExecutionFailureStatus(#[from] ExecutionFailureStatus), + #[error("[TsSdkError] TypeArgumentError: {0}")] + TypeArgumentError(#[from] TypeArgumentError), + #[error("[TsSdkError] WasmError:{{\n name: {0},\n message: {1}\n}}")] + WasmError(String, String), + #[error("[TsSdkError] JsSysError: {0}")] + JsSysError(String), + #[error("[TsSdkError] TransactionSerializationError: {0}")] + TransactionSerializationError(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 000000000..a26c1ff09 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs @@ -0,0 +1,673 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use js_sys::Uint8Array; +use std::cell::Cell; +use std::collections::HashSet; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsCast; +use wasm_bindgen::JsValue; + +use crate::bindings::WasmIotaObjectData; +use crate::bindings::WasmObjectRef; +use crate::bindings::WasmSharedObjectRef; +use crate::bindings::WasmTransactionArgument; +use crate::bindings::WasmTransactionBuilder; +use crate::common::PromiseUint8Array; +use crate::error::TsSdkError; +use crate::error::WasmError; +use crate::transaction_builder::TransactionBuilderTsSdk; +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::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; + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = "[string, number]")] + pub(crate) type WasmControllerCouple; + + #[wasm_bindgen(typescript_type = "[string, string]")] + pub(crate) type WasmTransferCouple; + + #[wasm_bindgen(typescript_type = "[ObjectRef, string]")] + pub(crate) type WasmObjectRefAndType; + + #[wasm_bindgen(typescript_type = "Map")] + pub(crate) type WasmTxArgumentMap; +} + +impl From<(IotaAddress, u64)> for WasmControllerCouple { + fn from((address, vp): (IotaAddress, u64)) -> Self { + let address = JsValue::from_str(&address.to_string()); + let vp = JsValue::bigint_from_str(&vp.to_string()); + + let arr = js_sys::Array::new(); + arr.push(&address); + arr.push(&vp); + + arr.unchecked_into() + } +} + +#[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls/identity")] +extern "C" { + #[wasm_bindgen(js_name = "create", catch)] + fn identity_new(did: Option<&[u8]>, package: &str) -> Result; + + #[wasm_bindgen(js_name = "newWithControllers", catch)] + fn identity_new_with_controllers( + did: Option<&[u8]>, + controllers: Vec, + threshold: u64, + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "approve", catch)] + async fn approve_proposal( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + proposal: &str, + proposal_type: &str, + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "proposeDeactivation", catch)] + fn propose_deactivation( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + package: &str, + expiration: Option, + ) -> Result; + + #[wasm_bindgen(js_name = "executeDeactivation", catch)] + async fn execute_deactivation( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + proposal: &str, + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "proposeUpgrade", catch)] + async fn propose_upgrade( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + package: &str, + expiration: Option, + ) -> Result; + + #[wasm_bindgen(js_name = "executeUpgrade", catch)] + async fn execute_upgrade( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + proposal: &str, + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "proposeSend", catch)] + async fn propose_send( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + assets: Vec, + package: &str, + expiration: Option, + ) -> Result; + + #[wasm_bindgen(js_name = "executeSend", catch)] + async fn execute_send( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + proposal: &str, + assets: Vec, + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "proposeUpdate", catch)] + fn propose_update( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + did_doc: Option<&[u8]>, + package: &str, + expiration: Option, + ) -> Result; + + #[wasm_bindgen(js_name = "executeUpdate", catch)] + fn execute_update( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + proposal: &str, + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "proposeBorrow", catch)] + async fn propose_borrow( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + objects: Vec, + package: &str, + expiration: Option, + ) -> Result; + + #[wasm_bindgen(js_name = "executeBorrow", catch)] + async fn execute_borrow( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + proposal: &str, + objects: Vec, + intent_fn: &dyn Fn(WasmTransactionBuilder, WasmTxArgumentMap), + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "createAndExecuteBorrow", catch)] + async fn create_and_execute_borrow( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + objects: Vec, + intent_fn: &dyn Fn(WasmTransactionBuilder, WasmTxArgumentMap), + package: &str, + expiration: Option, + ) -> Result; + + #[wasm_bindgen(js_name = "proposeConfigChange", catch)] + async fn propose_config_change( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + controllers_to_add: Vec, + controllers_to_remove: Vec, + controllers_to_update: Vec, + package: &str, + expiration: Option, + threshold: Option, + ) -> Result; + + #[wasm_bindgen(js_name = "executeConfigChange", catch)] + async fn execute_config_change( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + proposal: &str, + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "proposeControllerExecution", catch)] + async fn propose_controller_execution( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + controller_cap_id: &str, + package: &str, + expiration: Option, + ) -> Result; + + #[wasm_bindgen(js_name = "executeControllerExecution", catch)] + async fn execute_controller_execution( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + proposal: &str, + controller_cap_ref: WasmObjectRef, + intent_fn: &dyn Fn(WasmTransactionBuilder, WasmTransactionArgument), + package: &str, + ) -> Result; + + #[wasm_bindgen(js_name = "createAndExecuteControllerExecution", catch)] + async fn create_and_execute_controller_execution( + identity: WasmSharedObjectRef, + capability: WasmObjectRef, + controller_cap_ref: WasmObjectRef, + intent_fn: &dyn Fn(WasmTransactionBuilder, WasmTransactionArgument), + package: &str, + expiration: Option, + ) -> Result; +} + +pub struct IdentityMoveCallsTsSdk {} + +#[async_trait(?Send)] +impl IdentityMoveCalls for IdentityMoveCallsTsSdk { + type Error = TsSdkError; + type NativeTxBuilder = WasmTransactionBuilder; + + fn propose_borrow( + identity: OwnedObjectRef, + capability: ObjectRef, + objects: Vec, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let controller_cap = capability.into(); + let package_id = package_id.to_string(); + let objects = objects.into_iter().map(|obj| obj.to_string()).collect(); + + futures::executor::block_on(propose_borrow( + identity, + controller_cap, + objects, + &package_id, + expiration, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn execute_borrow>( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + objects: Vec, + intent_fn: F, + package: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let capability = capability.into(); + let proposal = proposal_id.to_string(); + let package = package.to_string(); + let objects = objects + .into_iter() + .map(|obj| serde_wasm_bindgen::to_value(&obj).map(WasmIotaObjectData::from)) + .collect::, _>>() + .map_err(WasmError::from)?; + + // Use cell to move `intent_fn` inside `closure` without actually moving it. + // This ensures that `closure` is an `impl Fn(..)` instead of `impl FnOnce(..)` like `intent_fn`. + let wrapped_intent_fn = Cell::new(Some(intent_fn)); + let closure = |tx_builder: WasmTransactionBuilder, args: WasmTxArgumentMap| { + let mut builder = TransactionBuilderTsSdk::new(tx_builder); + let args = serde_wasm_bindgen::from_value(args.into()).expect("failed to convert JS argument map"); + wrapped_intent_fn.take().unwrap()(&mut builder, &args); + }; + + futures::executor::block_on(execute_borrow( + identity, capability, &proposal, objects, &closure, &package, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn create_and_execute_borrow>( + identity: OwnedObjectRef, + capability: ObjectRef, + objects: Vec, + intent_fn: F, + expiration: Option, + package_id: ObjectID, + ) -> anyhow::Result { + let identity = identity.try_into()?; + let capability = capability.into(); + let package = package_id.to_string(); + let objects = objects + .into_iter() + .map(|obj| serde_wasm_bindgen::to_value(&obj).map(WasmIotaObjectData::from)) + .collect::, _>>() + .map_err(WasmError::from)?; + + // Use cell to move `intent_fn` inside `closure` without actually moving it. + // This ensures that `closure` is an `impl Fn(..)` instead of `impl FnOnce(..)` like `intent_fn`. + let wrapped_intent_fn = Cell::new(Some(intent_fn)); + let closure = |tx_builder: WasmTransactionBuilder, args: WasmTxArgumentMap| { + let mut builder = TransactionBuilderTsSdk::new(tx_builder); + let args = serde_wasm_bindgen::from_value(args.into()).expect("failed to convert JS argument map"); + wrapped_intent_fn.take().unwrap()(&mut builder, &args); + }; + + futures::executor::block_on(create_and_execute_borrow( + identity, capability, objects, &closure, &package, expiration, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + 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 identity = identity.try_into()?; + let capability = controller_cap.into(); + let package = package.to_string(); + + let controllers_to_add = controllers_to_add + .into_iter() + .map(|controller| serde_wasm_bindgen::to_value(&controller).map(WasmControllerCouple::from)) + .collect::, _>>() + .map_err(WasmError::from)?; + let controllers_to_remove = controllers_to_remove + .into_iter() + .map(|controller| controller.to_string()) + .collect(); + let controllers_to_update = controllers_to_update + .into_iter() + .map(|controller| serde_wasm_bindgen::to_value(&controller).map(WasmControllerCouple::from)) + .collect::, _>>() + .map_err(WasmError::from)?; + + futures::executor::block_on(propose_config_change( + identity, + capability, + controllers_to_add, + controllers_to_remove, + controllers_to_update, + &package, + expiration, + threshold, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn execute_config_change( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + proposal_id: ObjectID, + package: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let capability = controller_cap.into(); + let proposal = proposal_id.to_string(); + let package = package.to_string(); + + futures::executor::block_on(execute_config_change(identity, capability, &proposal, &package)) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn propose_controller_execution( + identity: OwnedObjectRef, + capability: ObjectRef, + controller_cap_id: ObjectID, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let controller_cap = capability.into(); + let package_id = package_id.to_string(); + let borrowed_cap = controller_cap_id.to_string(); + + futures::executor::block_on(propose_controller_execution( + identity, + controller_cap, + &borrowed_cap, + &package_id, + expiration, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn execute_controller_execution>( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + borrowing_controller_cap_ref: ObjectRef, + intent_fn: F, + package: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let capability = capability.into(); + let proposal = proposal_id.to_string(); + let package = package.to_string(); + let borrowing_cap = borrowing_controller_cap_ref.into(); + + // Use cell to move `intent_fn` inside `closure` without actually moving it. + // This ensures that `closure` is an `impl Fn(..)` instead of `impl FnOnce(..)` like `intent_fn`. + let wrapped_intent_fn = Cell::new(Some(intent_fn)); + let closure = |tx_builder: WasmTransactionBuilder, args: WasmTransactionArgument| { + let mut builder = TransactionBuilderTsSdk::new(tx_builder); + let args = serde_wasm_bindgen::from_value(args.into()).expect("failed to convert JS argument map"); + wrapped_intent_fn.take().unwrap()(&mut builder, &args); + }; + + futures::executor::block_on(execute_controller_execution( + identity, + capability, + &proposal, + borrowing_cap, + &closure, + &package, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + 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, + { + let identity = identity.try_into()?; + let capability = capability.into(); + let package = package_id.to_string(); + let borrowing_cap = borrowing_controller_cap_ref.into(); + + // Use cell to move `intent_fn` inside `closure` without actually moving it. + // This ensures that `closure` is an `impl Fn(..)` instead of `impl FnOnce(..)` like `intent_fn`. + let wrapped_intent_fn = Cell::new(Some(intent_fn)); + let closure = |tx_builder: WasmTransactionBuilder, args: WasmTransactionArgument| { + let mut builder = TransactionBuilderTsSdk::new(tx_builder); + let args = serde_wasm_bindgen::from_value(args.into()).expect("failed to convert JS argument map"); + wrapped_intent_fn.take().unwrap()(&mut builder, &args); + }; + + futures::executor::block_on(create_and_execute_controller_execution( + identity, + capability, + borrowing_cap, + &closure, + &package, + expiration, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + async fn new_identity( + did_doc: Option<&[u8]>, + package_id: ObjectID, + ) -> Result { + let package = package_id.to_string(); + + identity_new(did_doc, &package) + .map_err(WasmError::from)? + .to_programmable_transaction_bcs() + .await + } + + async fn new_with_controllers( + did_doc: Option<&[u8]>, + controllers: C, + threshold: u64, + package_id: ObjectID, + ) -> Result + where + C: IntoIterator, + { + let package = package_id.to_string(); + let controllers = controllers.into_iter().map(Into::into).collect(); + + identity_new_with_controllers(did_doc, controllers, threshold, &package) + .map_err(WasmError::from)? + .to_programmable_transaction_bcs() + .await + } + + fn approve_proposal( + identity: OwnedObjectRef, + controller_cap: ObjectRef, + proposal_id: ObjectID, + package: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let controller_cap = controller_cap.into(); + let proposal_id = proposal_id.to_string(); + let package_id = package.to_string(); + + futures::executor::block_on(approve_proposal( + identity, + controller_cap, + &proposal_id, + &T::move_type(package).to_canonical_string(true), + &package_id, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn propose_send( + identity: OwnedObjectRef, + capability: ObjectRef, + transfer_map: Vec<(ObjectID, IotaAddress)>, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let controller_cap = capability.into(); + let package_id = package_id.to_string(); + let transfer_map = transfer_map + .into_iter() + .map(|tx| serde_wasm_bindgen::to_value(&tx).map(JsValue::into)) + .collect::, _>>() + .map_err(|e| WasmError::from(e))?; + + futures::executor::block_on(propose_send( + identity, + controller_cap, + transfer_map, + &package_id, + expiration, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + 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 { + let identity = identity.try_into()?; + let controller_cap = capability.into(); + let proposal = proposal_id.to_string(); + let package_id = package.to_string(); + let objects = objects + .into_iter() + .map(|tx| serde_wasm_bindgen::to_value(&tx).map(JsValue::into)) + .collect::, _>>() + .map_err(|e| WasmError::from(e))?; + + futures::executor::block_on(execute_send(identity, controller_cap, &proposal, objects, &package_id)) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + async fn propose_update( + identity: OwnedObjectRef, + capability: ObjectRef, + did_doc: Option<&[u8]>, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let controller_cap = capability.into(); + let package_id = package_id.to_string(); + + propose_update(identity, controller_cap, did_doc, &package_id, expiration) + .map_err(WasmError::from)? + .to_programmable_transaction_bcs() + .await + } + + async fn execute_update( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let controller_cap = capability.into(); + let proposal = proposal_id.to_string(); + let package_id = package_id.to_string(); + + execute_update(identity, controller_cap, &proposal, &package_id) + .map_err(WasmError::from)? + .to_programmable_transaction_bcs() + .await + } + + fn propose_upgrade( + identity: OwnedObjectRef, + capability: ObjectRef, + expiration: Option, + package_id: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let capability = capability.into(); + let package = package_id.to_string(); + + futures::executor::block_on(propose_upgrade(identity, capability, &package, expiration)) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } + + fn execute_upgrade( + identity: OwnedObjectRef, + capability: ObjectRef, + proposal_id: ObjectID, + package_id: ObjectID, + ) -> Result { + let identity = identity.try_into()?; + let capability = capability.into(); + let proposal = proposal_id.to_string(); + let package = package_id.to_string(); + + futures::executor::block_on(execute_upgrade(identity, capability, &proposal, &package)) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(TsSdkError::from) + } +} 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 000000000..db6f00e00 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs @@ -0,0 +1,427 @@ +// 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::rpc_types::IotaTransactionBlockEffects; +use identity_iota_interaction::types::crypto::Signature; +use identity_iota_interaction::types::digests::TransactionDigest; +use identity_iota_interaction::types::dynamic_field::DynamicFieldName; +use identity_iota_interaction::types::transaction::TransactionData; +use secret_storage::Signer; + +use identity_iota_interaction::error::Error as IotaRpcError; +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::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::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::types::transaction::ProgrammableTransaction as ProgrammableTransactionSdk; +use identity_iota_interaction::types::transaction::TransactionDataAPI as _; +use identity_iota_interaction::CoinReadTrait; +use identity_iota_interaction::EventTrait; +use identity_iota_interaction::IotaClientTrait; +use identity_iota_interaction::IotaKeySignature; +use identity_iota_interaction::IotaTransactionBlockResponseT; +use identity_iota_interaction::QuorumDriverTrait; +use identity_iota_interaction::ReadTrait; + +use crate::bindings::ManagedWasmIotaClient; +use crate::bindings::WasmIotaClient; +use crate::bindings::WasmIotaTransactionBlockResponseWrapper; +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: WasmIotaTransactionBlockResponseWrapper, + effects: Option, +} + +impl IotaTransactionBlockResponseProvider { + pub fn new(response: WasmIotaTransactionBlockResponseWrapper) -> Self { + let effects = response.effects().map(Into::into); + IotaTransactionBlockResponseProvider { response, effects } + } +} + +#[async_trait::async_trait(?Send)] +impl IotaTransactionBlockResponseT for IotaTransactionBlockResponseProvider { + type Error = TsSdkError; + type NativeResponse = WasmIotaTransactionBlockResponseWrapper; + + fn to_string(&self) -> String { + format!("{:?}", self.response.to_string()) + } + + fn effects(&self) -> Option<&IotaTransactionBlockEffects> { + self.effects.as_ref() + } + + 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() + } + + fn digest(&self) -> Result { + self.response.digest() + } +} + +pub struct ReadAdapter { + client: ManagedWasmIotaClient, +} + +#[async_trait::async_trait(?Send)] +impl ReadTrait for ReadAdapter { + type Error = TsSdkError; + type NativeResponse = WasmIotaTransactionBlockResponseWrapper; + + 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 = WasmIotaTransactionBlockResponseWrapper; + + async fn execute_transaction_block( + &self, + tx_data: TransactionData, + signatures: Vec, + options: Option, + request_type: Option, + ) -> IotaRpcResult { + let wasm_response = self + .client + .execute_transaction_block(tx_data, signatures, options, request_type) + .await?; + + let digest = wasm_response + .digest() + .map_err(|e| IotaRpcError::FfiError(e.to_string()))?; + + self + .client + .wait_for_transaction(digest, Some(IotaTransactionBlockResponseOptions::new()), None, None) + .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 = WasmIotaTransactionBlockResponseWrapper; + + 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, + tx_data: TransactionData, + signer: &S, + ) -> Result< + Box>, + Self::Error, + > { + let response = self.sdk_execute_transaction(tx_data, signer).await?; + + // wait until new transaction block is available + self + .iota_client + .wait_for_transaction( + response.digest()?, + Some(IotaTransactionBlockResponseOptions::new()), + None, + None, + ) + .await + .unwrap(); + + Ok(Box::new(response)) + } + + async fn default_gas_budget( + &self, + _sender_address: IotaAddress, + _tx: &ProgrammableTransactionSdk, + ) -> Result { + Ok(50_000_000) + } + + 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), + }) + } + + pub fn into_inner(self) -> WasmIotaClient { + self.iota_client.clone().0 + } + + // Submit tx to IOTA client, also: + // - signs tx + // - calls execute_transaction_block to submit tx (with signatures created here) + async fn sdk_execute_transaction>( + &self, + tx: TransactionData, + signer: &S, + ) -> Result { + let sender_public_key = signer + .public_key() + .await + .map_err(|e| TsSdkError::WasmError(String::from("SecretStorage"), e.to_string()))?; + let sender_address = IotaAddress::from(&sender_public_key); + if sender_address != tx.sender() { + return Err(TsSdkError::WasmError("SDK".to_owned(), format!("transaction data needs to be signed by address {}, but client can only provide signature for address {sender_address}", tx.sender()))); + } + let signature = signer + .sign(&tx) + .await + .map_err(|e| TsSdkError::WasmError("SecretStorage".to_owned(), e.to_string()))?; + + let wasm_response = self + .quorum_driver_api() + .execute_transaction_block( + tx, + vec![signature], + Some(IotaTransactionBlockResponseOptions::full_content()), + Some(ExecuteTransactionRequestType::WaitForLocalExecution), + ) + .await + .unwrap(); + let native = wasm_response.clone_native_response(); + + Ok(IotaTransactionBlockResponseProvider::new(native)) + } +} 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 000000000..218525657 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/lib.rs @@ -0,0 +1,58 @@ +// 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_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + #[allow(unused_imports)] pub use error::TsSdkError as AdapterError; + #[allow(unused_imports)] pub use asset_move_calls::AssetMoveCallsTsSdk as AssetMoveCallsAdapter; + #[allow(unused_imports)] pub use identity_move_calls::IdentityMoveCallsTsSdk as IdentityMoveCallsAdapter; + #[allow(unused_imports)] pub use iota_client_ts_sdk::IotaClientTsSdk as IotaClientAdapter; + #[allow(unused_imports)] pub use iota_client_ts_sdk::IotaTransactionBlockResponseProvider as IotaTransactionBlockResponseAdapter; + #[allow(unused_imports)] pub use bindings::WasmIotaTransactionBlockResponseWrapper as NativeTransactionBlockResponse; + #[allow(unused_imports)] pub use migration_move_calls::MigrationMoveCallsTsSdk as MigrationMoveCallsAdapter; + #[allow(unused_imports)] pub use transaction_builder::TransactionBuilderTsSdk as TransactionBuilderAdapter; + + #[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 bindings::ProgrammableTransaction; + #[allow(unused_imports)] pub use bindings::WasmPublicKey; + #[allow(unused_imports)] pub use bindings::Ed25519PublicKey as WasmEd25519PublicKey; + #[allow(unused_imports)] pub use bindings::Secp256r1PublicKey as WasmSecp256r1PublicKey; + #[allow(unused_imports)] pub use bindings::Secp256k1PublicKey as WasmSecp256k1PublicKey; + #[allow(unused_imports)] pub use bindings::WasmIotaSignature; + #[cfg(feature = "keytool")] + #[allow(unused_imports)] + pub use bindings::keytool::*; + + #[allow(unused_imports)] pub use transaction_builder::NativeTsTransactionBuilderBindingWrapper; + } +} 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 000000000..5000374fb --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs @@ -0,0 +1,54 @@ +// 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::ObjectID; +use identity_iota_interaction::types::base_types::ObjectRef; +use identity_iota_interaction::MigrationMoveCalls; +use identity_iota_interaction::ProgrammableTransactionBcs; +use js_sys::Uint8Array; +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +use crate::bindings::WasmObjectRef; +use crate::bindings::WasmSharedObjectRef; +use crate::error::TsSdkError; +use crate::error::WasmError; + +#[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls")] +extern "C" { + #[wasm_bindgen(js_name = "migrateDidOutput", catch)] + async fn migrate_did_output_impl( + did_output: WasmObjectRef, + migration_registry: WasmSharedObjectRef, + package: &str, + creation_timestamp: Option, + ) -> Result; +} + +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 { + let did_output = did_output.into(); + let package = package.to_string(); + let migration_registry = migration_registry.try_into()?; + + futures::executor::block_on(migrate_did_output_impl( + did_output, + migration_registry, + &package, + creation_timestamp, + )) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(Self::Error::from) + } +} 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 000000000..0d95bb740 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/src/transaction_builder.rs @@ -0,0 +1,63 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::ops::Deref; +use std::ops::DerefMut; + +use crate::bindings::WasmTransactionBuilder; +use crate::error::TsSdkError; +use crate::error::WasmError; +use identity_iota_interaction::ProgrammableTransactionBcs; +use identity_iota_interaction::TransactionBuilderT; + +pub type NativeTsTransactionBuilderBindingWrapper = WasmTransactionBuilder; + +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 { + futures::executor::block_on(self.builder.build()) + .map(|js_arr| js_arr.to_vec()) + .map_err(WasmError::from) + .map_err(Self::Error::from) + } + + fn as_native_tx_builder(&mut self) -> &mut Self::NativeTxBuilder { + &mut self.builder + } + + fn into_native_tx_builder(self) -> Self::NativeTxBuilder { + self.builder + } +} + +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 000000000..e8a42d2a3 --- /dev/null +++ b/bindings/wasm/iota_interaction_ts/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../tsconfig.json", + "entryPoints": [ + "./node/" + ], + "out": "./docs/wasm", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@iota/iota-interaction-ts/*": [ + "./*" + ], + } + } +} 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 000000000..c75065fb2 --- /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/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 000000000..f5122c566 --- /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 000000000..2e5dc788f --- /dev/null +++ b/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs @@ -0,0 +1,851 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use async_trait::async_trait; +use identity_iota_interaction::OptionalSend; +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 {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +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())?) + } + + async fn new_identity( + did_doc: Option<&[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())?) + } + + async fn new_with_controllers( + did_doc: Option<&[u8]>, + controllers: C, + threshold: u64, + package_id: ObjectID, + ) -> Result + where + C: IntoIterator + OptionalSend, + { + 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 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())?) + } + + async fn propose_update( + identity: OwnedObjectRef, + capability: ObjectRef, + did_doc: Option<&[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).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())?) + } + + async 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 000000000..115b07d06 --- /dev/null +++ b/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs @@ -0,0 +1,535 @@ +// 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 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::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::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::types::transaction::TransactionDataAPI as _; +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::OptionalSync; +use identity_iota_interaction::QuorumDriverTrait; +use identity_iota_interaction::ReadTrait; + +/// 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(crate) 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(&self) -> Option<&IotaTransactionBlockEffects> { + self.response.effects.as_ref() + } + + fn to_string(&self) -> String { + format!("{:?}", self.response) + } + + 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() + } + + fn digest(&self) -> Result { + Ok(self.response.digest) + } +} + +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: TransactionData, + signatures: Vec, + options: Option, + request_type: Option, + ) -> IotaRpcResult { + let tx = Transaction::from_data(tx_data, signatures); + 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( + &self, + tx_data: TransactionData, + signer: &S, + ) -> Result + where + S: Signer + OptionalSync, + { + let response = self.sdk_execute_transaction(tx_data, signer).await?; + Ok(Box::new(IotaTransactionBlockResponseProvider::new(response))) + } + + async fn default_gas_budget(&self, sender_address: IotaAddress, tx: &ProgrammableTransaction) -> Result { + 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, + tx: TransactionData, + signer: &S, + ) -> Result { + let public_key = signer + .public_key() + .await + .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; + let sender_address = IotaAddress::from(&public_key); + + if sender_address != tx.sender() { + return Err(Error::TransactionSigningFailed(format!("transaction data needs to be signed by address {}, but client can only provide signature for address {sender_address}", tx.sender()))); + } + + let signature = signer + .sign(&tx) + .await + .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; + + // execute tx + let response = self + .iota_client + .quorum_driver_api() + .execute_transaction_block( + Transaction::from_data(tx, 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_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 coin found with minimum required balance of {} for address {}", + MINIMUM_BALANCE, 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 000000000..eaeb087ce --- /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 000000000..ab6ed93f5 --- /dev/null +++ b/identity_iota_core/src/iota_interaction_rust/mod.rs @@ -0,0 +1,41 @@ +// 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 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; 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 000000000..9854e1893 --- /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 000000000..357d3647a --- /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 f0a272b74..012fe35d9 100644 --- a/identity_iota_core/src/lib.rs +++ b/identity_iota_core/src/lib.rs @@ -34,6 +34,9 @@ mod state_metadata; 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; diff --git a/identity_iota_interaction/Cargo.toml b/identity_iota_interaction/Cargo.toml new file mode 100644 index 000000000..a580760c6 --- /dev/null +++ b/identity_iota_interaction/Cargo.toml @@ -0,0 +1,72 @@ +[package] +name = "identity_iota_interaction" +version = "1.6.0-alpha" +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 } +bcs = "0.1.4" +cfg-if = "1.0.0" +fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", package = "fastcrypto", features = ["copy_key"] } +jsonpath-rust = { version = "0.5.1", optional = true } +secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } +serde.workspace = true +serde_json.workspace = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.12.0-rc" } +move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.12.0-rc" } +tokio = { version = "1", optional = true, default-features = false, features = ["process"] } + +shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "shared-crypto", tag = "v0.12.0-rc" } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +eyre = { version = "0.6" } +fastcrypto-zkp = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", 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_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" } +derive_more = "0.99.18" +enum_dispatch = "0.3.13" +schemars = "0.8.21" +tap = "1" +nonempty = "0.11" + +[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/send-sync-storage"] +send-sync-transaction = ["secret-storage/send-sync-storage"] +keytool = ["dep:tokio", "dep:jsonpath-rust"] + +[lints.clippy] +result_large_err = "allow" + +[lints.rust] +# from local sdk types +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(msim)'] } diff --git a/identity_iota_interaction/README.md b/identity_iota_interaction/README.md new file mode 100644 index 000000000..abee8ad9e --- /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/effects_mut_api.rs b/identity_iota_interaction/src/effects_mut_api.rs new file mode 100644 index 000000000..83b1879be --- /dev/null +++ b/identity_iota_interaction/src/effects_mut_api.rs @@ -0,0 +1,94 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::rpc_types::IotaObjectRef; +use crate::rpc_types::IotaTransactionBlockEffects; +use crate::rpc_types::IotaTransactionBlockEffectsAPI; +use crate::rpc_types::IotaTransactionBlockEffectsV1; +use crate::rpc_types::OwnedObjectRef; + +/// A mutable version of [IotaTransactionBlockEffectsAPI] that allows the +/// in-place mutation of [IotaTransactionBlockEffects] +pub trait IotaTransactionBlockEffectsMutAPI: IotaTransactionBlockEffectsAPI { + fn shared_objects_mut(&mut self) -> &mut Vec; + fn created_mut(&mut self) -> &mut Vec; + fn mutated_mut(&mut self) -> &mut Vec; + fn unwrapped_mut(&mut self) -> &mut Vec; + fn deleted_mut(&mut self) -> &mut Vec; + fn unwrapped_then_deleted_mut(&mut self) -> &mut Vec; + fn wrapped_mut(&mut self) -> &mut Vec; +} + +impl IotaTransactionBlockEffectsMutAPI for IotaTransactionBlockEffectsV1 { + fn shared_objects_mut(&mut self) -> &mut Vec { + &mut self.shared_objects + } + + fn created_mut(&mut self) -> &mut Vec { + &mut self.created + } + + fn mutated_mut(&mut self) -> &mut Vec { + &mut self.mutated + } + + fn unwrapped_mut(&mut self) -> &mut Vec { + &mut self.unwrapped + } + + fn deleted_mut(&mut self) -> &mut Vec { + &mut self.deleted + } + + fn unwrapped_then_deleted_mut(&mut self) -> &mut Vec { + &mut self.unwrapped_then_deleted + } + + fn wrapped_mut(&mut self) -> &mut Vec { + &mut self.wrapped + } +} + +impl IotaTransactionBlockEffectsMutAPI for IotaTransactionBlockEffects { + fn shared_objects_mut(&mut self) -> &mut Vec { + match self { + Self::V1(effects) => &mut effects.shared_objects, + } + } + + fn created_mut(&mut self) -> &mut Vec { + match self { + Self::V1(effects) => &mut effects.created, + } + } + + fn mutated_mut(&mut self) -> &mut Vec { + match self { + Self::V1(effects) => &mut effects.mutated, + } + } + + fn unwrapped_mut(&mut self) -> &mut Vec { + match self { + Self::V1(effects) => &mut effects.unwrapped, + } + } + + fn deleted_mut(&mut self) -> &mut Vec { + match self { + Self::V1(effects) => &mut effects.deleted, + } + } + + fn unwrapped_then_deleted_mut(&mut self) -> &mut Vec { + match self { + Self::V1(effects) => &mut effects.unwrapped_then_deleted, + } + } + + fn wrapped_mut(&mut self) -> &mut Vec { + match self { + Self::V1(effects) => &mut effects.wrapped, + } + } +} 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 000000000..acec57a1c --- /dev/null +++ b/identity_iota_interaction/src/iota_client_trait.rs @@ -0,0 +1,257 @@ +// 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::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::IotaTransactionBlockEffects; +use crate::rpc_types::IotaTransactionBlockResponseOptions; +use crate::rpc_types::ObjectsPage; +use crate::types::base_types::IotaAddress; +use crate::types::base_types::ObjectID; +use crate::types::base_types::SequenceNumber; +use crate::types::crypto::PublicKey; +use crate::types::crypto::Signature; +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::types::transaction::ProgrammableTransaction; +use crate::types::transaction::TransactionData; +use crate::OptionalSend; +use async_trait::async_trait; +use secret_storage::SignatureScheme as SignatureSchemeSecretStorage; +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; + +#[cfg(feature = "send-sync-transaction")] +use crate::OptionalSync; + +pub struct IotaKeySignature { + pub public_key: PublicKey, + pub signature: Signature, +} + +impl SignatureSchemeSecretStorage for IotaKeySignature { + type PublicKey = PublicKey; + type Signature = Signature; + type Input = TransactionData; +} + +//******************************************************************** +// 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; + + /// Returns Debug representation of the IotaTransactionBlockResponse + fn to_string(&self) -> String; + + /// Returns the effects of this transaction + fn effects(&self) -> Option<&IotaTransactionBlockEffects>; + + /// 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; + + // Returns digest for transaction block. + fn digest(&self) -> Result; +} + +#[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: TransactionData, + signatures: Vec, + 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, + tx_data: TransactionData, + signer: &S, + ) -> Result< + Box>, + Self::Error, + >; + #[cfg(feature = "send-sync-transaction")] + async fn execute_transaction + OptionalSync>( + &self, + tx_data: TransactionData, + signer: &S, + ) -> Result< + Box>, + Self::Error, + >; + + async fn default_gas_budget( + &self, + sender_address: IotaAddress, + tx: &ProgrammableTransaction, + ) -> 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 000000000..7f2ab9dcd --- /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, PartialEq, Eq)] +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/keytool/internal.rs b/identity_iota_interaction/src/keytool/internal.rs new file mode 100644 index 000000000..e09c206c3 --- /dev/null +++ b/identity_iota_interaction/src/keytool/internal.rs @@ -0,0 +1,126 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::path::Path; +use std::path::PathBuf; +use std::str::FromStr as _; + +use anyhow::anyhow; +use anyhow::Context as _; +use fastcrypto::traits::EncodeDecodeBase64 as _; +use jsonpath_rust::JsonPathQuery as _; +use serde::Deserialize; +use serde_json::Value; + +use crate::types::base_types::IotaAddress; +use crate::types::crypto::PublicKey; + +#[derive(Debug, Clone)] +pub(super) struct IotaCliWrapper { + iota_bin: PathBuf, +} + +impl Default for IotaCliWrapper { + fn default() -> Self { + Self { + iota_bin: PathBuf::from_str("iota").expect("infallible"), + } + } +} + +impl IotaCliWrapper { + /// Creates a new [IotaCliWrapper] that will use the iota binary found at + /// the provided path. + pub fn new_with_custom_bin(iota_bin: impl AsRef) -> Self { + Self { + iota_bin: iota_bin.as_ref().to_owned(), + } + } + + /// Returns the location of the iota binary used. + pub fn iota_bin(&self) -> &Path { + &self.iota_bin + } + + /// Executes a given "iota" command with the provided string-encoded args. + /// Returns the parsed JSON output. + pub fn run_command(&self, args: &str) -> anyhow::Result { + cfg_if::cfg_if! { + if #[cfg(not(target_arch = "wasm32"))] { + let output = std::process::Command::new(&self.iota_bin) + .args(args.split_ascii_whitespace()) + .arg("--json") + .output() + .map_err(|e| anyhow!("failed to run command: {e}"))?; + + if !output.status.success() { + let err_msg = + String::from_utf8(output.stderr).map_err(|e| anyhow!("command failed with non-utf8 error message: {e}"))?; + return Err(anyhow!("failed to run keytool cmd: {err_msg}")); + } + + let trimmed_output = { + let start_of_json = output.stdout.iter().enumerate().find_map(|(i, b)| matches!(*b, b'[' | b'{' | b'\"').then_some(i)).context("no JSON in command output")?; + &output.stdout[start_of_json..] + }; + + serde_json::from_slice(trimmed_output).context("invalid JSON object in command output") + } else { + extern "Rust" { + fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result; + } + let iota_bin = self.iota_bin.to_str().context("invalid IOTA bin path")?; + let cmd = format!("{iota_bin} {args} --json"); + unsafe { __wasm_exec_iota_cmd(&cmd) } + } + } + } + + /// Returns the current active address. + pub fn get_active_address(&self) -> anyhow::Result { + self + .run_command("client active-address") + .and_then(|value| serde_json::from_value(value).context("failed to parse IotaAddress from output")) + } + + fn get_key_impl(&self, json_path_query: &str) -> anyhow::Result> { + let Some(pk_json_data) = self + .run_command("keytool list")? + .path(json_path_query) + .map_err(|e| anyhow!("failed to query JSON output: {e}"))? + .get_mut(0) + .map(Value::take) + else { + return Ok(None); + }; + + let KeytoolPublicKeyHelper { + public_base64_key_with_flag, + alias, + .. + } = serde_json::from_value(pk_json_data)?; + + let pk = PublicKey::decode_base64(&public_base64_key_with_flag).map_err(|e| anyhow!("{e:?}"))?; + + Ok(Some((pk, alias))) + } + + /// Returns the public key of a given address, if any. + pub fn get_key(&self, address: IotaAddress) -> anyhow::Result> { + let query = format!("$[?(@.iotaAddress==\"{}\")]", address); + self.get_key_impl(&query) + } + + /// Returns the public key with the given alias, if any. + pub fn get_key_by_alias(&self, alias: &str) -> anyhow::Result> { + let query = format!("$[?(@.alias==\"{}\")]", alias); + Ok(self.get_key_impl(&query)?.map(|(pk, _)| pk)) + } +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct KeytoolPublicKeyHelper { + alias: String, + public_base64_key_with_flag: String, +} diff --git a/identity_iota_interaction/src/keytool/mod.rs b/identity_iota_interaction/src/keytool/mod.rs new file mode 100644 index 000000000..2b1b517e3 --- /dev/null +++ b/identity_iota_interaction/src/keytool/mod.rs @@ -0,0 +1,9 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod internal; +mod signer; +mod storage; + +pub use signer::*; +pub use storage::*; diff --git a/identity_iota_interaction/src/keytool/signer.rs b/identity_iota_interaction/src/keytool/signer.rs new file mode 100644 index 000000000..dc307e514 --- /dev/null +++ b/identity_iota_interaction/src/keytool/signer.rs @@ -0,0 +1,131 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::path::Path; +use std::path::PathBuf; + +use crate::types::base_types::IotaAddress; +use crate::types::crypto::PublicKey; +use crate::types::crypto::Signature; +use crate::types::transaction::TransactionData; +use crate::IotaKeySignature; +use anyhow::anyhow; +use anyhow::Context as _; +use async_trait::async_trait; +use fastcrypto::encoding::Base64; +use fastcrypto::encoding::Encoding; +use secret_storage::Error as SecretStorageError; +use secret_storage::Signer; + +use super::internal::IotaCliWrapper; + +/// Builder structure to ease the creation of a [KeytoolSigner]. +#[derive(Debug, Default)] +pub struct KeytoolSignerBuilder { + address: Option, + iota_bin: Option, +} + +impl KeytoolSignerBuilder { + /// Returns a new [KeytoolSignerBuilder] with default configuration: + /// - use current active address; + /// - assumes `iota` binary to be in PATH; + pub fn new() -> Self { + Self::default() + } + + /// Sets the address the signer will use. + /// Defaults to current active address if no address is provided. + pub fn with_address(mut self, address: IotaAddress) -> Self { + self.address = Some(address); + self + } + + /// Sets the path to the `iota` binary to use. + /// Assumes `iota` to be in PATH if no value is provided. + pub fn iota_bin_location(mut self, path: impl AsRef) -> Self { + let path = path.as_ref().to_path_buf(); + self.iota_bin = Some(path); + + self + } + + /// Builds a new [KeytoolSigner] using the provided configuration. + pub fn build(self) -> anyhow::Result { + let KeytoolSignerBuilder { address, iota_bin } = self; + let iota_cli_wrapper = iota_bin.map(IotaCliWrapper::new_with_custom_bin).unwrap_or_default(); + let address = if let Some(address) = address { + address + } else { + iota_cli_wrapper.get_active_address()? + }; + + let public_key = iota_cli_wrapper.get_key(address)?.context("key doens't exist")?.0; + + Ok(KeytoolSigner { + public_key, + iota_cli_wrapper, + address, + }) + } +} + +/// IOTA Keytool [Signer] implementation. +#[derive(Debug)] +pub struct KeytoolSigner { + public_key: PublicKey, + iota_cli_wrapper: IotaCliWrapper, + address: IotaAddress, +} + +impl KeytoolSigner { + /// Returns a [KeytoolSignerBuilder]. + pub fn builder() -> KeytoolSignerBuilder { + KeytoolSignerBuilder::default() + } + + /// Returns the [IotaAddress] used by this [KeytoolSigner]. + pub fn address(&self) -> IotaAddress { + self.address + } + + /// Returns the [PublicKey] used by this [KeytoolSigner]. + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } +} + +#[cfg_attr(feature = "send-sync-transaction", async_trait)] +#[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] +impl Signer for KeytoolSigner { + type KeyId = IotaAddress; + + fn key_id(&self) -> Self::KeyId { + self.address + } + + async fn public_key(&self) -> Result { + Ok(self.public_key.clone()) + } + + async fn sign(&self, data: &TransactionData) -> Result { + let tx_data_bcs = + bcs::to_bytes(data).map_err(|e| SecretStorageError::Other(anyhow!("bcs serialization failed: {e}")))?; + let base64_data = Base64::encode(&tx_data_bcs); + let command = format!("keytool sign --address {} --data {base64_data}", self.address); + + self + .iota_cli_wrapper + .run_command(&command) + .and_then(|json| { + json + .get("iotaSignature") + .context("invalid JSON output: missing iotaSignature")? + .as_str() + .context("not a JSON string")? + .parse() + .map_err(|e| anyhow!("invalid IOTA signature: {e}")) + }) + .map_err(SecretStorageError::Other) + } +} diff --git a/identity_iota_interaction/src/keytool/storage.rs b/identity_iota_interaction/src/keytool/storage.rs new file mode 100644 index 000000000..6dc0f3c01 --- /dev/null +++ b/identity_iota_interaction/src/keytool/storage.rs @@ -0,0 +1,145 @@ +// Copyright 2020-2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::path::Path; + +use anyhow::anyhow; +use anyhow::Context as _; +use fastcrypto::ed25519::Ed25519Signature; +use fastcrypto::secp256k1::Secp256k1Signature; +use fastcrypto::secp256r1::Secp256r1Signature; +use fastcrypto::traits::Signer; +use serde::Deserialize; + +use crate::types::base_types::IotaAddress; +use crate::types::crypto::IotaKeyPair; +use crate::types::crypto::PublicKey; +use crate::types::crypto::SignatureScheme as IotaSignatureScheme; + +use super::internal::IotaCliWrapper; +use super::KeytoolSignerBuilder; + +#[derive(Clone, Default)] +pub struct KeytoolStorage { + iota_cli_wrapper: IotaCliWrapper, +} + +impl KeytoolStorage { + /// Returns a new [KeytoolStorage] that will use the IOTA binary in PATH. + pub fn new() -> Self { + Self::default() + } + + /// Returns a new [KeytoolStorage] that will use the provided IOTA binary. + pub fn new_with_custom_bin(iota_bin: impl AsRef) -> Self { + Self { + iota_cli_wrapper: IotaCliWrapper::new_with_custom_bin(iota_bin), + } + } + + /// Returns a [KeytoolSignerBuilder] to construct a [super::KeytoolSigner] after + /// selecting an address. + pub fn signer(&self) -> KeytoolSignerBuilder { + KeytoolSignerBuilder::new().iota_bin_location(self.iota_cli_wrapper.iota_bin()) + } + + /// Generates a new keypair of type `key_scheme`. + /// Returns the resulting [PublicKey] together with its alias. + pub fn generate_key(&self, key_scheme: IotaSignatureScheme) -> anyhow::Result<(PublicKey, String)> { + if !matches!( + &key_scheme, + IotaSignatureScheme::ED25519 | IotaSignatureScheme::Secp256k1 | IotaSignatureScheme::Secp256r1 + ) { + anyhow::bail!("key scheme {key_scheme} is not supported by the keytool"); + } + + let cmd = format!("client new-address --key-scheme {key_scheme}"); + let KeyGenOutput { alias, address } = { + let json_output = self.iota_cli_wrapper.run_command(&cmd)?; + serde_json::from_value(json_output)? + }; + + let pk = self + .iota_cli_wrapper + .get_key(address)? + .ok_or_else(|| anyhow!("key for address {address} wasn't found"))? + .0; + + Ok((pk, alias)) + } + + /// Inserts a new key in this keytool. + /// Returns the alias assigned to the inserted key. + pub fn insert_key(&self, key: IotaKeyPair) -> anyhow::Result { + let bech32_encoded_key = key.encode().map_err(|e| anyhow!("{e:?}"))?; + let key_scheme = key.public().scheme().to_string(); + let cmd = format!("keytool import {bech32_encoded_key} {key_scheme}"); + + let json_output = self.iota_cli_wrapper.run_command(&cmd)?; + let KeyGenOutput { alias, .. } = serde_json::from_value(json_output)?; + + Ok(alias) + } + + /// Uses the private key corresponding to [IotaAddress] `address` to sign `data`. + /// ## Notes + /// - SHA-512 is used to produce signatures when the key is ed25519. + /// - SHA-256 is used otherwise. + pub fn sign_raw(&self, address: IotaAddress, data: impl AsRef<[u8]>) -> anyhow::Result> { + let cmd = format!("keytool export {address}"); + let keypair = { + let json_output = self.iota_cli_wrapper.run_command(&cmd)?; + let KeyExportOutput { + exported_private_key: bech32_encoded_sk, + } = serde_json::from_value(json_output)?; + + IotaKeyPair::decode(&bech32_encoded_sk).map_err(|e| anyhow!("failed to decode private key: {e:?}"))? + }; + let data = data.as_ref(); + + let sig = match keypair { + IotaKeyPair::Ed25519(sk) => Signer::::sign(&sk, data).sig.to_bytes().to_vec(), + IotaKeyPair::Secp256r1(sk) => Signer::::sign(&sk, data).sig.to_vec(), + IotaKeyPair::Secp256k1(sk) => { + let sig = Signer::::sign(&sk, data); + sig.as_ref().to_vec() + } + }; + + Ok(sig) + } + + /// Updates an alias from `old_alias` to `new_alias` + /// If no value for `new_alias` is provided, a randomly generated one will be used. + pub fn update_alias(&self, old_alias: &str, new_alias: Option<&str>) -> anyhow::Result<()> { + let cmd = format!("keytool update-alias {old_alias} {}", new_alias.unwrap_or_default()); + self + .iota_cli_wrapper + .run_command(&cmd) + .context("failed to update alias")?; + + Ok(()) + } + + /// Returns the [PublicKey] for the given [IotaAddress] together with its alias. + pub fn get_key(&self, address: IotaAddress) -> anyhow::Result> { + self.iota_cli_wrapper.get_key(address) + } + + /// Returns the [PublicKey] that has the given alias, if any. + pub fn get_key_by_alias(&self, alias: &str) -> anyhow::Result> { + self.iota_cli_wrapper.get_key_by_alias(alias) + } +} + +#[derive(Deserialize)] +struct KeyGenOutput { + alias: String, + address: IotaAddress, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct KeyExportOutput { + exported_private_key: String, +} diff --git a/identity_iota_interaction/src/lib.rs b/identity_iota_interaction/src/lib.rs new file mode 100644 index 000000000..64ea7b62d --- /dev/null +++ b/identity_iota_interaction/src/lib.rs @@ -0,0 +1,132 @@ +// 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 effects_mut_api; +mod iota_client_trait; +mod iota_verifiable_credential; +#[cfg(feature = "keytool")] +pub mod keytool; +mod move_call_traits; +mod move_type; +mod transaction_builder_trait; + +pub use effects_mut_api::*; +pub use iota_client_trait::*; +pub use iota_verifiable_credential::*; +#[cfg(feature = "keytool")] +pub use keytool::*; +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 000000000..d1d9e8e41 --- /dev/null +++ b/identity_iota_interaction/src/move_call_traits.rs @@ -0,0 +1,251 @@ +// Copyright 2020-2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; +use std::collections::HashSet; +use std::iter::IntoIterator; + +use async_trait::async_trait; +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::OptionalSend; +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) {} + +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] +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; + + async fn new_identity( + did_doc: Option<&[u8]>, + package_id: ObjectID, + ) -> Result; + + async fn new_with_controllers + OptionalSend>( + did_doc: Option<&[u8]>, + controllers: C, + threshold: u64, + 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; + + async fn propose_update( + identity: OwnedObjectRef, + capability: ObjectRef, + did_doc: Option<&[u8]>, + expiration: Option, + package_id: ObjectID, + ) -> Result; + + async 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 000000000..ac49e6ee4 --- /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 000000000..1448df9a3 --- /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 funds for address [{address}], requested amount: {amount}")] + InsufficientFunds { address: IotaAddress, amount: u128 }, + #[error(transparent)] + Json(#[from] serde_json::Error), + #[error("Error caused by a foreign function interface call: {0}")] + FfiError(String), // Added for IOTA interaction +} \ 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 000000000..03319749d --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/generated_types.rs @@ -0,0 +1,270 @@ +// 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 super::types::crypto::Signature; +use super::types::transaction::TransactionData; + +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; + +// 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_data: TransactionData, + signatures: Vec, + options: Option, + request_type: Option, + ) -> Self { + let tx_data_bcs = bcs::to_bytes(&tx_data).expect("this serialization cannot fail"); + let signatures_b64 = signatures + .into_iter() + .map(|sig| Base64::from_bytes(sig.as_ref())) + .collect(); + ExecuteTransactionBlockParams { + transaction_block: Base64::from_bytes(&tx_data_bcs), + signature: signatures_b64, + 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 + #[serde(skip_serializing_if = "Option::is_none")] + 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, + } + } +} + +/// Params for `wait_for_transaction` / `wait_for_transaction`. +/// +/// Be careful when serializing with `serde_wasm_bindgen::to_value`, as `#[serde(flatten)]` +/// will turn the object into a `Map` instead of a plain object in Js. +/// Prefer serializing with `serde_wasm_bindgen::Serializer::json_compatible` or perform custom serialization. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct WaitForTransactionParams { + /// Block digest and options for content that should be returned. + #[serde(flatten)] + get_transaction_block_params: GetTransactionBlockParams, + /// The amount of time to wait for a transaction block. Defaults to one minute. + #[serde(skip_serializing_if = "Option::is_none")] + timeout: Option, + /// The amount of time to wait between checks for the transaction block. Defaults to 2 seconds. + #[serde(skip_serializing_if = "Option::is_none")] + poll_interval: Option, +} + +impl WaitForTransactionParams { + pub fn new( + digest: String, + options: Option, + timeout: Option, + poll_interval: Option, + ) -> Self { + WaitForTransactionParams { + get_transaction_block_params: GetTransactionBlockParams::new(digest, options), + timeout, + poll_interval, + } + } +} 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 000000000..818dbb4fd --- /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 000000000..339386eb0 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_event.rs @@ -0,0 +1,195 @@ +// 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, Base64}; + +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, + /// Base64 encoded bcs bytes of the move event + #[serde(flatten)] + pub bcs: BcsEvent, + /// 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(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase", tag = "bcsEncoding")] +#[serde(from = "MaybeTaggedBcsEvent")] +pub enum BcsEvent { + Base64 { + #[serde_as(as = "Base64")] + bcs: Vec, + }, + Base58 { + #[serde_as(as = "Base58")] + bcs: Vec, + }, +} + +impl BcsEvent { + pub fn new(bytes: Vec) -> Self { + Self::Base64 { bcs: bytes } + } + + pub fn bytes(&self) -> &[u8] { + match self { + BcsEvent::Base64 { bcs } => bcs.as_ref(), + BcsEvent::Base58 { bcs } => bcs.as_ref(), + } + } + + pub fn into_bytes(self) -> Vec { + match self { + BcsEvent::Base64 { bcs } => bcs, + BcsEvent::Base58 { bcs } => bcs, + } + } +} + +#[allow(unused)] +#[serde_as] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase", untagged)] +enum MaybeTaggedBcsEvent { + Tagged(TaggedBcsEvent), + Base58 { + #[serde_as(as = "Base58")] + bcs: Vec, + }, +} + +#[serde_as] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase", tag = "bcsEncoding")] +enum TaggedBcsEvent { + Base64 { + #[serde_as(as = "Base64")] + bcs: Vec, + }, + Base58 { + #[serde_as(as = "Base58")] + bcs: Vec, + }, +} + +impl From for BcsEvent { + fn from(event: MaybeTaggedBcsEvent) -> BcsEvent { + let bcs = match event { + MaybeTaggedBcsEvent::Tagged(TaggedBcsEvent::Base58 { bcs }) + | MaybeTaggedBcsEvent::Base58 { bcs } => bcs, + MaybeTaggedBcsEvent::Tagged(TaggedBcsEvent::Base64 { bcs }) => bcs, + }; + + // Bytes are already decoded, force into Base64 variant to avoid serializing to + // base58 + Self::Base64 { bcs } + } +} + +#[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 000000000..fa5e6b996 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_move.rs @@ -0,0 +1,346 @@ +// 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, MoveVariant}, + 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>), + Variant(IotaMoveVariant), +} + +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), + IotaMoveValue::Variant(v) => v.to_json_value(), + } + } +} + +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") + )?; + } + IotaMoveValue::Variant(value) => write!(writer, "{value}")?, + } + 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))) + } + MoveValue::Variant(MoveVariant { + type_, + variant_name, + tag: _, + fields, + }) => IotaMoveValue::Variant(IotaMoveVariant { + type_: type_.clone(), + variant: variant_name.to_string(), + fields: fields + .into_iter() + .map(|(id, value)| (id.into_string(), value.into())) + .collect::>(), + }), + } + } +} + +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(rename = "MoveVariant")] +pub struct IotaMoveVariant { + #[serde(rename = "type")] + #[serde_as(as = "IotaStructTag")] + pub type_: StructTag, + pub variant: String, + pub fields: BTreeMap, +} + +impl IotaMoveVariant { + pub fn to_json_value(self) -> Value { + // We only care about values here, assuming type information is known at the + // client side. + let fields = self + .fields + .into_iter() + .map(|(key, value)| (key, value.to_json_value())) + .collect::>(); + json!({ + "variant": self.variant, + "fields": fields, + }) + } +} + +impl Display for IotaMoveVariant { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let mut writer = String::new(); + let IotaMoveVariant { + type_, + variant, + fields, + } = self; + writeln!(writer)?; + writeln!(writer, " {}: {type_}", "type")?; + writeln!(writer, " {}: {variant}", "variant")?; + 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')) + } +} + +#[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(), + } + } +} 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 000000000..41e84546a --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_object.rs @@ -0,0 +1,808 @@ +// 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 schemars::JsonSchema; +use serde::Deserialize; +use serde::Serialize; +use serde_with::{DisplayFromStr, serde_as}; +use serde_json::Value; + +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 fastcrypto::encoding::Base64; + +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 + #[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", + )?; + } + if let Some(content) = self.content.as_ref() { + writeln!(writer, "{}", "----- Data -----")?; + write!(writer, "{content}")?; + } + + 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, JsonSchema)] +#[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 dependencies + // 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; + + // try_from_object() and try_from_package() are not defined here because + // MoveObject and MoveStructLayout introduce too many dependencies + + 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; + + // try_from_object() and try_from_package() are not defined here because + // MoveObject and MoveStructLayout introduce too many dependencies + + 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 fields: IotaMoveStruct, +} + +impl IotaParsedMoveObject { + // try_from_object_read()is not defined here because + // MoveObject introduces too many dependencies + + 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 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), + } + } +} 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 000000000..cc2ffcf40 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_transaction.rs @@ -0,0 +1,407 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + vec::Vec, + fmt::{self, Display, Formatter}, +}; + +use enum_dispatch::enum_dispatch; +use schemars::JsonSchema; +use serde::Deserialize; +use serde::Serialize; +use serde_with::serde_as; + +use crate::{iota_types::{base_types::EpochId, digests::{TransactionDigest, TransactionEventsDigest}, gas::GasCostSummary, storage::{DeleteKind, WriteKind}}, types::{ + base_types::{ObjectID, SequenceNumber}, execution_status::ExecutionStatus, object::Owner, quorum_driver_types::ExecuteTransactionRequestType, + iota_serde::{BigInt, SequenceNumber as AsSequenceNumber}, +}}; + +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_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, JsonSchema)] +#[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 Display for IotaExecutionStatus { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Success => write!(f, "success"), + Self::Failure { error } => write!(f, "failure due to {error}"), + } + } +} + +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, JsonSchema)] +#[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 + } +} + +#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)] +#[enum_dispatch(IotaTransactionBlockEffectsAPI)] +#[serde( + rename = "TransactionBlockEffects", + rename_all = "camelCase", + tag = "messageVersion" +)] +pub enum IotaTransactionBlockEffects { + V1(IotaTransactionBlockEffectsV1), +} + +#[enum_dispatch] +pub trait IotaTransactionBlockEffectsAPI { + fn status(&self) -> &IotaExecutionStatus; + fn into_status(self) -> IotaExecutionStatus; + fn shared_objects(&self) -> &[IotaObjectRef]; + fn created(&self) -> &[OwnedObjectRef]; + fn mutated(&self) -> &[OwnedObjectRef]; + fn unwrapped(&self) -> &[OwnedObjectRef]; + fn deleted(&self) -> &[IotaObjectRef]; + fn unwrapped_then_deleted(&self) -> &[IotaObjectRef]; + fn wrapped(&self) -> &[IotaObjectRef]; + fn gas_object(&self) -> &OwnedObjectRef; + fn events_digest(&self) -> Option<&TransactionEventsDigest>; + fn dependencies(&self) -> &[TransactionDigest]; + fn executed_epoch(&self) -> EpochId; + fn transaction_digest(&self) -> &TransactionDigest; + fn gas_cost_summary(&self) -> &GasCostSummary; + + /// Return an iterator of mutated objects, but excluding the gas object. + fn mutated_excluding_gas(&self) -> Vec; + fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>; + fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)>; + fn all_deleted_objects(&self) -> Vec<(&IotaObjectRef, DeleteKind)>; +} + +#[serde_as] +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde( + rename = "TransactionBlockEffectsModifiedAtVersions", + rename_all = "camelCase" +)] +pub struct IotaTransactionBlockEffectsModifiedAtVersions { + object_id: ObjectID, + #[schemars(with = "AsSequenceNumber")] + #[serde_as(as = "AsSequenceNumber")] + sequence_number: SequenceNumber, +} + +/// The response from processing a transaction or a certified transaction +#[serde_as] +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename = "TransactionBlockEffectsV1", rename_all = "camelCase")] +pub struct IotaTransactionBlockEffectsV1 { + /// The status of the execution + pub status: IotaExecutionStatus, + /// The epoch when this transaction was executed. + #[schemars(with = "BigInt")] + #[serde_as(as = "BigInt")] + pub executed_epoch: EpochId, + pub gas_used: GasCostSummary, + /// The version that every modified (mutated or deleted) object had before + /// it was modified by this transaction. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub modified_at_versions: Vec, + /// The object references of the shared objects used in this transaction. + /// Empty if no shared objects were used. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub shared_objects: Vec, + /// The transaction digest + pub transaction_digest: TransactionDigest, + /// ObjectRef and owner of new objects created. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub created: Vec, + /// ObjectRef and owner of mutated objects, including gas object. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub mutated: Vec, + /// ObjectRef and owner of objects that are unwrapped in this transaction. + /// Unwrapped objects are objects that were wrapped into other objects in + /// the past, and just got extracted out. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub unwrapped: Vec, + /// Object Refs of objects now deleted (the old refs). + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub deleted: Vec, + /// Object refs of objects previously wrapped in other objects but now + /// deleted. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub unwrapped_then_deleted: Vec, + /// Object refs of objects now wrapped in other objects. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub wrapped: Vec, + /// The updated gas object reference. Have a dedicated field for convenient + /// access. It's also included in mutated. + pub gas_object: OwnedObjectRef, + /// The digest of the events emitted during execution, + /// can be None if the transaction does not emit any event. + #[serde(skip_serializing_if = "Option::is_none")] + pub events_digest: Option, + /// The set of transaction digests this transaction depends on. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub dependencies: Vec, +} + +impl IotaTransactionBlockEffectsAPI for IotaTransactionBlockEffectsV1 { + fn status(&self) -> &IotaExecutionStatus { + &self.status + } + fn into_status(self) -> IotaExecutionStatus { + self.status + } + fn shared_objects(&self) -> &[IotaObjectRef] { + &self.shared_objects + } + fn created(&self) -> &[OwnedObjectRef] { + &self.created + } + fn mutated(&self) -> &[OwnedObjectRef] { + &self.mutated + } + fn unwrapped(&self) -> &[OwnedObjectRef] { + &self.unwrapped + } + fn deleted(&self) -> &[IotaObjectRef] { + &self.deleted + } + fn unwrapped_then_deleted(&self) -> &[IotaObjectRef] { + &self.unwrapped_then_deleted + } + fn wrapped(&self) -> &[IotaObjectRef] { + &self.wrapped + } + fn gas_object(&self) -> &OwnedObjectRef { + &self.gas_object + } + fn events_digest(&self) -> Option<&TransactionEventsDigest> { + self.events_digest.as_ref() + } + fn dependencies(&self) -> &[TransactionDigest] { + &self.dependencies + } + + fn executed_epoch(&self) -> EpochId { + self.executed_epoch + } + + fn transaction_digest(&self) -> &TransactionDigest { + &self.transaction_digest + } + + fn gas_cost_summary(&self) -> &GasCostSummary { + &self.gas_used + } + + fn mutated_excluding_gas(&self) -> Vec { + self.mutated + .iter() + .filter(|o| *o != &self.gas_object) + .cloned() + .collect() + } + + fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> { + self.modified_at_versions + .iter() + .map(|v| (v.object_id, v.sequence_number)) + .collect::>() + } + + fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)> { + self.mutated + .iter() + .map(|owner_ref| (owner_ref, WriteKind::Mutate)) + .chain( + self.created + .iter() + .map(|owner_ref| (owner_ref, WriteKind::Create)), + ) + .chain( + self.unwrapped + .iter() + .map(|owner_ref| (owner_ref, WriteKind::Unwrap)), + ) + .collect() + } + + fn all_deleted_objects(&self) -> Vec<(&IotaObjectRef, DeleteKind)> { + self.deleted + .iter() + .map(|r| (r, DeleteKind::Normal)) + .chain( + self.unwrapped_then_deleted + .iter() + .map(|r| (r, DeleteKind::UnwrapThenDelete)), + ) + .chain(self.wrapped.iter().map(|r| (r, DeleteKind::Wrap))) + .collect() + } +} \ No newline at end of file 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 000000000..47aa47d99 --- /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 000000000..c4867a4a1 --- /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 000000000..cfb71ba85 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/base_types.rs @@ -0,0 +1,814 @@ +// 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 schemars::JsonSchema; +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, to_iota_struct_tag_string}; +use super::timelock::timelock::{self, TimeLock}; +use super::timelock::timelocked_staked_iota::TimelockedStakedIota; +use super::stardust::output::Nft; +use super::gas_coin::GasCoin; +use super::object::{Owner}; +use super::parse_iota_struct_tag; + +pub use super::digests::{ObjectDigest, TransactionDigest}; + +// ----------------------------------------------------------------- +// Originally defined in crates/iota-types/src/committee.rs +// ----------------------------------------------------------------- +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; +// ----------------------------------------------------------------- +// Originally defined in crates/iota-types/src/execution_status.rs +// ----------------------------------------------------------------- +pub type CommandIndex = usize; +// ----------------------------------------------------------------- +// Originally defined in external-crates/move/crates/move-binary-format/src/file_format.rs +// ----------------------------------------------------------------- +/// Index into the code stream for a jump. The offset is relative to the +/// beginning of the instruction stream. +pub type CodeOffset = u16; +/// 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; +// ----------------------------------------------------------------- + +#[derive( + Eq, + PartialEq, + Ord, + PartialOrd, + Copy, + Clone, + Hash, + Default, + Debug, + Serialize, + Deserialize, + JsonSchema, +)] +pub struct SequenceNumber(u64); + +impl fmt::Display for SequenceNumber { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:#x}", self.0) + } +} + +pub type AuthorityName = AuthorityPublicKeyBytes; + +#[serde_as] +#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] +pub struct ObjectID( + #[schemars(with = "Hex")] + #[serde_as(as = "Readable")] + AccountAddress, +); + +pub type ObjectRef = (ObjectID, SequenceNumber, ObjectDigest); + +/// 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), + /// An 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_v1(&self) -> bool { + self.address() == IOTA_FRAMEWORK_ADDRESS + && self.module().as_str() == "coin" + && self.name().as_str() == "DenyCapV1" + } + + 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 an 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, JsonSchema)] +pub struct IotaAddress( + #[schemars(with = "Hex")] + #[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)) + } +} + +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, +); + +// 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) + } +} + +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::TryFromSlice) + .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 + } +} + +#[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")] + TryFromSlice, +} + +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) + } +} + +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), + } + } +} \ 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 000000000..e1cd2a27c --- /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 COIN_JOIN_FUNC_NAME: &IdentStr = ident_str!("join"); + +pub const PAY_MODULE_NAME: &IdentStr = ident_str!("pay"); +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(Box::new(UID::layout())), + ), + MoveFieldLayout::new( + ident_str!("balance").to_owned(), + MoveTypeLayout::Struct(Box::new(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 000000000..28d851db4 --- /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 000000000..0ad555fe6 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs @@ -0,0 +1,685 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +use std::hash::Hash; +use std::str::FromStr; + +use enum_dispatch::enum_dispatch; +use strum::EnumString; +use schemars::JsonSchema; +use derive_more::{AsRef, AsMut, From}; +use eyre::{eyre, Report}; + +use fastcrypto::{ + bls12381::min_sig::{ + BLS12381AggregateSignature, BLS12381AggregateSignatureAsBytes, BLS12381KeyPair, + BLS12381PrivateKey, BLS12381PublicKey, BLS12381Signature, + }, ed25519::{ + Ed25519KeyPair, Ed25519PrivateKey, Ed25519PublicKey, Ed25519PublicKeyAsBytes, + Ed25519Signature + }, encoding::{Base64, Bech32, Encoding}, error::{FastCryptoError, FastCryptoResult}, hash::{Blake2b256, HashFunction}, secp256k1::{Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, Secp256k1Signature}, secp256r1::{Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1PublicKeyAsBytes, Secp256r1Signature}, traits::{Authenticator, EncodeDecodeBase64, KeyPair as KeypairTraits, Signer, ToFromBytes, VerifyingKey} +}; +use fastcrypto_zkp::zk_login_utils::Bn254FrElement; + +use serde::{Deserialize, Deserializer, Serializer}; +use serde::Serialize; +use serde_with::{serde_as, Bytes}; + +use crate::shared_crypto::intent::IntentMessage; + +use super::{ + base_types::IotaAddress, error::{IotaError, IotaResult}, iota_serde::Readable +}; + +const IOTA_PRIV_KEY_PREFIX: &str = "iotaprivkey"; + +// 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; + +// Account Keys +// +// * The following section defines the keypairs that are used by +// * accounts to interact with Iota. +// * Currently we support eddsa and ecdsa on Iota. + +#[expect(clippy::large_enum_variant)] +#[derive(Debug, From, PartialEq, Eq)] +pub enum IotaKeyPair { + Ed25519(Ed25519KeyPair), + Secp256k1(Secp256k1KeyPair), + Secp256r1(Secp256r1KeyPair), +} + +impl IotaKeyPair { + pub fn public(&self) -> PublicKey { + match self { + IotaKeyPair::Ed25519(kp) => PublicKey::Ed25519(kp.public().into()), + IotaKeyPair::Secp256k1(kp) => PublicKey::Secp256k1(kp.public().into()), + IotaKeyPair::Secp256r1(kp) => PublicKey::Secp256r1(kp.public().into()), + } + } + + pub fn copy(&self) -> Self { + match self { + IotaKeyPair::Ed25519(kp) => kp.copy().into(), + IotaKeyPair::Secp256k1(kp) => kp.copy().into(), + IotaKeyPair::Secp256r1(kp) => kp.copy().into(), + } + } +} + +impl EncodeDecodeBase64 for IotaKeyPair { + fn encode_base64(&self) -> String { + Base64::encode(self.to_bytes()) + } + + fn decode_base64(value: &str) -> FastCryptoResult { + let bytes = Base64::decode(value)?; + Self::from_bytes(&bytes).map_err(|_| FastCryptoError::InvalidInput) + } +} +impl IotaKeyPair { + pub fn to_bytes(&self) -> Vec { + let mut bytes: Vec = Vec::new(); + bytes.push(self.public().flag()); + + match self { + IotaKeyPair::Ed25519(kp) => { + bytes.extend_from_slice(kp.as_bytes()); + } + IotaKeyPair::Secp256k1(kp) => { + bytes.extend_from_slice(kp.as_bytes()); + } + IotaKeyPair::Secp256r1(kp) => { + bytes.extend_from_slice(kp.as_bytes()); + } + } + bytes + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + match SignatureScheme::from_flag_byte(bytes.first().ok_or_else(|| eyre!("Invalid length"))?) + { + Ok(x) => match x { + SignatureScheme::ED25519 => Ok(IotaKeyPair::Ed25519(Ed25519KeyPair::from_bytes( + bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, + )?)), + SignatureScheme::Secp256k1 => { + Ok(IotaKeyPair::Secp256k1(Secp256k1KeyPair::from_bytes( + bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, + )?)) + } + SignatureScheme::Secp256r1 => { + Ok(IotaKeyPair::Secp256r1(Secp256r1KeyPair::from_bytes( + bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, + )?)) + } + _ => Err(eyre!("Invalid flag byte")), + }, + _ => Err(eyre!("Invalid bytes")), + } + } + + pub fn to_bytes_no_flag(&self) -> Vec { + match self { + IotaKeyPair::Ed25519(kp) => kp.as_bytes().to_vec(), + IotaKeyPair::Secp256k1(kp) => kp.as_bytes().to_vec(), + IotaKeyPair::Secp256r1(kp) => kp.as_bytes().to_vec(), + } + } + + /// Encode a IotaKeyPair as `flag || privkey` in Bech32 starting with + /// "iotaprivkey" to a string. Note that the pubkey is not encoded. + pub fn encode(&self) -> Result { + Bech32::encode(self.to_bytes(), IOTA_PRIV_KEY_PREFIX).map_err(|e| eyre!(e)) + } + + /// Decode a IotaKeyPair from `flag || privkey` in Bech32 starting with + /// "iotaprivkey" to IotaKeyPair. The public key is computed directly from + /// the private key bytes. + pub fn decode(value: &str) -> Result { + let bytes = Bech32::decode(value, IOTA_PRIV_KEY_PREFIX)?; + Self::from_bytes(&bytes) + } +} + +impl Serialize for IotaKeyPair { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s = self.encode_base64(); + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for IotaKeyPair { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + let s = String::deserialize(deserializer)?; + IotaKeyPair::decode_base64(&s).map_err(|e| Error::custom(e.to_string())) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum PublicKey { + Ed25519(Ed25519PublicKeyAsBytes), + Secp256k1(Secp256k1PublicKeyAsBytes), + Secp256r1(Secp256r1PublicKeyAsBytes), + ZkLogin(ZkLoginPublicIdentifier), + Passkey(Secp256r1PublicKeyAsBytes), +} + +/// 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, + PublicKey::Passkey(pk) => &pk.0, + } + } +} + +impl EncodeDecodeBase64 for PublicKey { + fn encode_base64(&self) -> String { + let mut bytes: Vec = Vec::new(); + bytes.extend_from_slice(&[self.flag()]); + bytes.extend_from_slice(self.as_ref()); + Base64::encode(&bytes[..]) + } + + fn decode_base64(value: &str) -> FastCryptoResult { + let bytes = Base64::decode(value)?; + match bytes.first() { + Some(x) => { + if x == &SignatureScheme::ED25519.flag() { + let pk: Ed25519PublicKey = + Ed25519PublicKey::from_bytes(bytes.get(1..).ok_or( + FastCryptoError::InputLengthWrong(Ed25519PublicKey::LENGTH + 1), + )?)?; + Ok(PublicKey::Ed25519((&pk).into())) + } else if x == &SignatureScheme::Secp256k1.flag() { + let pk = Secp256k1PublicKey::from_bytes(bytes.get(1..).ok_or( + FastCryptoError::InputLengthWrong(Secp256k1PublicKey::LENGTH + 1), + )?)?; + Ok(PublicKey::Secp256k1((&pk).into())) + } else if x == &SignatureScheme::Secp256r1.flag() { + let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or( + FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1), + )?)?; + Ok(PublicKey::Secp256r1((&pk).into())) + } else if x == &SignatureScheme::PasskeyAuthenticator.flag() { + let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or( + FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1), + )?)?; + Ok(PublicKey::Passkey((&pk).into())) + } else { + Err(FastCryptoError::InvalidInput) + } + } + _ => Err(FastCryptoError::InvalidInput), + } + } +} + +impl PublicKey { + pub fn flag(&self) -> u8 { + self.scheme().flag() + } + + pub fn try_from_bytes( + curve: SignatureScheme, + key_bytes: &[u8], + ) -> Result { + match curve { + SignatureScheme::ED25519 => Ok(PublicKey::Ed25519( + (&Ed25519PublicKey::from_bytes(key_bytes)?).into(), + )), + SignatureScheme::Secp256k1 => Ok(PublicKey::Secp256k1( + (&Secp256k1PublicKey::from_bytes(key_bytes)?).into(), + )), + SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1( + (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), + )), + SignatureScheme::PasskeyAuthenticator => Ok(PublicKey::Passkey( + (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), + )), + _ => Err(eyre!("Unsupported curve")), + } + } + + pub fn scheme(&self) -> SignatureScheme { + match self { + PublicKey::Ed25519(_) => SignatureScheme::ED25519, // Equals Ed25519IotaSignature::SCHEME + PublicKey::Secp256k1(_) => SignatureScheme::Secp256k1, // Equals Secp256k1IotaSignature::SCHEME + PublicKey::Secp256r1(_) => SignatureScheme::Secp256r1, // Equals Secp256r1IotaSignature::SCHEME + PublicKey::ZkLogin(_) => SignatureScheme::ZkLoginAuthenticator, + PublicKey::Passkey(_) => SignatureScheme::PasskeyAuthenticator, + } + } +} + +/// 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 // schemars::JsonSchema and AsRef are omitted here, having Debug instead +)] +pub struct AuthorityPublicKeyBytes( + #[serde_as(as = "Readable")] + pub [u8; AuthorityPublicKey::LENGTH], +); + +// Enums for signature scheme signatures +#[enum_dispatch] +#[derive(Clone, JsonSchema, Debug, PartialEq, Eq, Hash)] +pub enum Signature { + Ed25519IotaSignature, + Secp256k1IotaSignature, + Secp256r1IotaSignature, +} + +impl Serialize for Signature { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let bytes = self.as_ref(); + + if serializer.is_human_readable() { + let s = Base64::encode(bytes); + serializer.serialize_str(&s) + } else { + serializer.serialize_bytes(bytes) + } + } +} + +impl<'de> Deserialize<'de> for Signature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + + let bytes = if deserializer.is_human_readable() { + let s = String::deserialize(deserializer)?; + Base64::decode(&s).map_err(|e| Error::custom(e.to_string()))? + } else { + let data: Vec = Vec::deserialize(deserializer)?; + data + }; + + Self::from_bytes(&bytes).map_err(|e| Error::custom(e.to_string())) + } +} + +impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { + match self { + Signature::Ed25519IotaSignature(sig) => sig.as_ref(), + Signature::Secp256k1IotaSignature(sig) => sig.as_ref(), + Signature::Secp256r1IotaSignature(sig) => sig.as_ref(), + } + } +} +impl AsMut<[u8]> for Signature { + fn as_mut(&mut self) -> &mut [u8] { + match self { + Signature::Ed25519IotaSignature(sig) => sig.as_mut(), + Signature::Secp256k1IotaSignature(sig) => sig.as_mut(), + Signature::Secp256r1IotaSignature(sig) => sig.as_mut(), + } + } +} + +impl ToFromBytes for Signature { + fn from_bytes(bytes: &[u8]) -> Result { + match bytes.first() { + Some(x) => { + if x == &Ed25519IotaSignature::SCHEME.flag() { + Ok(::from_bytes(bytes)?.into()) + } else if x == &Secp256k1IotaSignature::SCHEME.flag() { + Ok(::from_bytes(bytes)?.into()) + } else if x == &Secp256r1IotaSignature::SCHEME.flag() { + Ok(::from_bytes(bytes)?.into()) + } else { + Err(FastCryptoError::InvalidInput) + } + } + _ => Err(FastCryptoError::InvalidInput), + } + } +} + +// Ed25519 Iota Signature port +// + +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] +#[as_ref(forward)] +#[as_mut(forward)] +pub struct Ed25519IotaSignature( + #[schemars(with = "Base64")] + #[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 IotaSignatureInner for Ed25519IotaSignature { + type Sig = Ed25519Signature; + type PubKey = Ed25519PublicKey; + type KeyPair = Ed25519KeyPair; + const LENGTH: usize = Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1; +} + +impl IotaPublicKey for Ed25519PublicKey { + const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::ED25519; +} + +impl ToFromBytes for Ed25519IotaSignature { + fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != Self::LENGTH { + return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + } + let mut sig_bytes = [0; Self::LENGTH]; + sig_bytes.copy_from_slice(bytes); + Ok(Self(sig_bytes)) + } +} + +// Secp256k1 Iota Signature port +// +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] +#[as_ref(forward)] +#[as_mut(forward)] +pub struct Secp256k1IotaSignature( + #[schemars(with = "Base64")] + #[serde_as(as = "Readable")] + [u8; Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1], +); + +impl IotaSignatureInner for Secp256k1IotaSignature { + type Sig = Secp256k1Signature; + type PubKey = Secp256k1PublicKey; + type KeyPair = Secp256k1KeyPair; + const LENGTH: usize = Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1; +} + +impl IotaPublicKey for Secp256k1PublicKey { + const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256k1; +} + +impl ToFromBytes for Secp256k1IotaSignature { + fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != Self::LENGTH { + return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + } + let mut sig_bytes = [0; Self::LENGTH]; + sig_bytes.copy_from_slice(bytes); + Ok(Self(sig_bytes)) + } +} + +// Secp256r1 Iota Signature port +// +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] +#[as_ref(forward)] +#[as_mut(forward)] +pub struct Secp256r1IotaSignature( + #[schemars(with = "Base64")] + #[serde_as(as = "Readable")] + [u8; Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1], +); + +impl IotaSignatureInner for Secp256r1IotaSignature { + type Sig = Secp256r1Signature; + type PubKey = Secp256r1PublicKey; + type KeyPair = Secp256r1KeyPair; + const LENGTH: usize = Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1; +} + +impl IotaPublicKey for Secp256r1PublicKey { + const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256r1; +} + +impl ToFromBytes for Secp256r1IotaSignature { + fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != Self::LENGTH { + return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); + } + let mut sig_bytes = [0; Self::LENGTH]; + sig_bytes.copy_from_slice(bytes); + Ok(Self(sig_bytes)) + } +} + +// This struct exists due to the limitations of the `enum_dispatch` library. +// +pub trait IotaSignatureInner: Sized + ToFromBytes + PartialEq + Eq + Hash { + type Sig: Authenticator; + type PubKey: VerifyingKey + IotaPublicKey; + type KeyPair: KeypairTraits; + + const LENGTH: usize = Self::Sig::LENGTH + Self::PubKey::LENGTH + 1; + const SCHEME: SignatureScheme = Self::PubKey::SIGNATURE_SCHEME; + + /// Returns the deserialized signature and deserialized pubkey. + fn get_verification_inputs(&self) -> IotaResult<(Self::Sig, Self::PubKey)> { + let pk = Self::PubKey::from_bytes(self.public_key_bytes()) + .map_err(|_| IotaError::KeyConversion("Invalid public key".to_string()))?; + + // deserialize the signature + let signature = Self::Sig::from_bytes(self.signature_bytes()).map_err(|_| { + IotaError::InvalidSignature { + error: "Fail to get pubkey and sig".to_string(), + } + })?; + + Ok((signature, pk)) + } + + fn new(kp: &Self::KeyPair, message: &[u8]) -> Self { + let sig = Signer::sign(kp, message); + + let mut signature_bytes: Vec = Vec::new(); + signature_bytes + .extend_from_slice(&[::SIGNATURE_SCHEME.flag()]); + signature_bytes.extend_from_slice(sig.as_ref()); + signature_bytes.extend_from_slice(kp.public().as_ref()); + Self::from_bytes(&signature_bytes[..]) + .expect("Serialized signature did not have expected size") + } +} + +pub trait IotaPublicKey: VerifyingKey { + const SIGNATURE_SCHEME: SignatureScheme; +} + +#[enum_dispatch(Signature)] +pub trait IotaSignature: Sized + ToFromBytes { + fn signature_bytes(&self) -> &[u8]; + fn public_key_bytes(&self) -> &[u8]; + fn scheme(&self) -> SignatureScheme; + + fn verify_secure( + &self, + value: &IntentMessage, + author: IotaAddress, + scheme: SignatureScheme, + ) -> IotaResult<()> + where + T: Serialize; +} + +impl IotaSignature for S { + fn signature_bytes(&self) -> &[u8] { + // Access array slice is safe because the array bytes is initialized as + // flag || signature || pubkey with its defined length. + &self.as_ref()[1..1 + S::Sig::LENGTH] + } + + fn public_key_bytes(&self) -> &[u8] { + // Access array slice is safe because the array bytes is initialized as + // flag || signature || pubkey with its defined length. + &self.as_ref()[S::Sig::LENGTH + 1..] + } + + fn scheme(&self) -> SignatureScheme { + S::PubKey::SIGNATURE_SCHEME + } + + fn verify_secure( + &self, + value: &IntentMessage, + author: IotaAddress, + scheme: SignatureScheme, + ) -> Result<(), IotaError> + where + T: Serialize, + { + let mut hasher = DefaultHash::default(); + hasher.update(bcs::to_bytes(&value).expect("Message serialization should not fail")); + let digest = hasher.finalize().digest; + + let (sig, pk) = &self.get_verification_inputs()?; + match scheme { + SignatureScheme::ZkLoginAuthenticator => {} // Pass this check because zk login does + // not derive address from pubkey. + _ => { + let address = IotaAddress::from(pk); + if author != address { + return Err(IotaError::IncorrectSigner { + error: format!( + "Incorrect signer, expected {:?}, got {:?}", + author, address + ), + }); + } + } + } + + pk.verify(&digest, sig) + .map_err(|e| IotaError::InvalidSignature { + error: format!("Fail to verify user sig {}", e), + }) + } +} + +#[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, + PasskeyAuthenticator, +} + +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, + SignatureScheme::PasskeyAuthenticator => 0x06, + } + } + + /// 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), + 0x06 => Ok(SignatureScheme::PasskeyAuthenticator), + _ => Err(IotaError::KeyConversion("Invalid key scheme".to_string())), + } + } +} + +impl FromStr for Signature { + type Err = eyre::Report; + fn from_str(s: &str) -> Result { + Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.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 000000000..4b3dc51bf --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/digests.rs @@ -0,0 +1,470 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt; + +use schemars::JsonSchema; +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, JsonSchema)] +pub struct Digest( + #[schemars(with = "Base58")] + #[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 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 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(()) + } +} + + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct CheckpointContentsDigest(Digest); + +impl CheckpointContentsDigest { + 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 CheckpointContentsDigest { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsRef<[u8; 32]> for CheckpointContentsDigest { + fn as_ref(&self) -> &[u8; 32] { + self.0.as_ref() + } +} + +impl From for [u8; 32] { + fn from(digest: CheckpointContentsDigest) -> Self { + digest.into_inner() + } +} + +impl From<[u8; 32]> for CheckpointContentsDigest { + fn from(digest: [u8; 32]) -> Self { + Self::new(digest) + } +} + +impl fmt::Display for CheckpointContentsDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl fmt::Debug for CheckpointContentsDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("CheckpointContentsDigest") + .field(&self.0) + .finish() + } +} + +impl std::str::FromStr for CheckpointContentsDigest { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let mut result = [0; 32]; + let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?; + if buffer.len() != 32 { + return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes")); + } + result.copy_from_slice(&buffer); + Ok(CheckpointContentsDigest::new(result)) + } +} + +impl fmt::LowerHex for CheckpointContentsDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl fmt::UpperHex for CheckpointContentsDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] +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) + } + + pub fn next_lexicographical(&self) -> Option { + self.0.next_lexicographical().map(Self) + } +} + +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]; + let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?; + if buffer.len() != 32 { + return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes")); + } + result.copy_from_slice(&buffer); + Ok(TransactionDigest::new(result)) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct TransactionEffectsDigest(Digest); + +impl TransactionEffectsDigest { + 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 TransactionEffectsDigest { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsRef<[u8; 32]> for TransactionEffectsDigest { + fn as_ref(&self) -> &[u8; 32] { + self.0.as_ref() + } +} + +impl From for [u8; 32] { + fn from(digest: TransactionEffectsDigest) -> Self { + digest.into_inner() + } +} + +impl From<[u8; 32]> for TransactionEffectsDigest { + fn from(digest: [u8; 32]) -> Self { + Self::new(digest) + } +} + +impl fmt::Display for TransactionEffectsDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl fmt::Debug for TransactionEffectsDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("TransactionEffectsDigest") + .field(&self.0) + .finish() + } +} + +impl fmt::LowerHex for TransactionEffectsDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl fmt::UpperHex for TransactionEffectsDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +#[serde_as] +#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)] +pub struct TransactionEventsDigest(Digest); + +impl TransactionEventsDigest { + pub const ZERO: Self = Self(Digest::ZERO); + + pub const fn new(digest: [u8; 32]) -> Self { + Self(Digest::new(digest)) + } + + pub fn random() -> Self { + Self(Digest::random()) + } + + pub fn next_lexicographical(&self) -> Option { + self.0.next_lexicographical().map(Self) + } + + pub fn into_inner(self) -> [u8; 32] { + self.0.into_inner() + } +} + +impl fmt::Debug for TransactionEventsDigest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("TransactionEventsDigest") + .field(&self.0) + .finish() + } +} + +impl AsRef<[u8]> for TransactionEventsDigest { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsRef<[u8; 32]> for TransactionEventsDigest { + fn as_ref(&self) -> &[u8; 32] { + self.0.as_ref() + } +} + +impl std::str::FromStr for TransactionEventsDigest { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let mut result = [0; 32]; + let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?; + if buffer.len() != 32 { + return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes")); + } + result.copy_from_slice(&buffer); + Ok(Self::new(result)) + } +} + +// Each object has a unique digest +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] +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) + } +} 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 000000000..fa3c16d6a --- /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::Base64}; + +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 000000000..afcb18401 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/error.rs @@ -0,0 +1,943 @@ +// 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::*, + digests::{ObjectDigest, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest, + CheckpointContentsDigest}, + execution_status::{CommandArgumentError, ExecutionFailureStatus}, + 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")] + ImmutableParameterExpected { 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")] + NotSharedObject, + #[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" + )] + TransferObjectWithoutPublicTransfer { 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}")] + MoveFunctionInput(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(CheckpointContentsDigest), + + #[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, + + // Soft Bundle related errors + #[error( + "Number of transactions exceeds the maximum allowed ({:?}) in a Soft Bundle", + limit + )] + TooManyTransactionsInSoftBundle { limit: u64 }, + #[error("Transaction {:?} in Soft Bundle contains no shared objects", digest)] + NoSharedObject { digest: TransactionDigest }, + #[error("Transaction {:?} in Soft Bundle has already been executed", digest)] + AlreadyExecuted { digest: TransactionDigest }, + #[error("At least one certificate in Soft Bundle has already been processed")] + CertificateAlreadyProcessed, + #[error( + "Gas price for transaction {:?} in Soft Bundle mismatch: want {:?}, have {:?}", + digest, + expected, + actual + )] + GasPriceMismatch { + digest: TransactionDigest, + expected: u64, + actual: u64, + }, + + #[error("Coin type is globally paused for use: {coin_type}")] + CoinTypeGlobalPause { coin_type: String }, +} + +#[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)] + Display { 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, + }, + + #[error("Soft bundle must only contain transactions of UserTransaction kind")] + InvalidTxKindInSoftBundle, + + // 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, // Committee is not available for wasm32 + }, + #[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, + + // 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("Invalid DKG message size")] + InvalidDkgMessageSize, + + #[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: TransactionEventsDigest }, + #[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 }, + + // GenericBridge is not available + + #[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("Operations for epoch {0} have ended")] + EpochEnded(EpochId), + #[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("Method not allowed")] + InvalidRpcMethod, + + // TODO: We should fold this into UserInputError::Unsupported. + #[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("Failed to read or deserialize bridge related data structures on-chain: {0}")] + IotaBridgeRead(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 }, + + #[error("Too many requests")] + TooManyRequests, + + #[error("The request did not contain a certificate")] + NoCertificateProvided, +} + +#[repr(u64)] +#[expect(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)] +#[expect(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; + +// iota_protocol_config::Error is not available + +impl From for IotaError { + fn from(error: ExecutionError) -> Self { + IotaError::Execution(error.to_string()) + } +} + +// Status, TypedStoreError, crate::storage::error::Error are not available + +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 From for IotaError { + fn from(error: String) -> Self { + IotaError::GenericAuthority { error } + } +} + +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 { + IotaError::Rpc { .. } => true, + + // Reconfig error + IotaError::ValidatorHaltedAtEpochEnd => true, + IotaError::MissingCommitteeAtEpoch(..) => true, + IotaError::WrongEpoch { .. } => true, + IotaError::EpochEnded { .. } => 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, + + // NB: This is not an internal overload, but instead an imposed rate + // limit / blocking of a client. It must be non-retryable otherwise + // we will make the threat worse through automatic retries. + IotaError::TooManyRequests => 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 { .. }) + } + + pub fn retry_after_secs(&self) -> u64 { + match self { + IotaError::ValidatorOverloadedRetryAfter { retry_after_secs } => *retry_after_secs, + _ => 0, + } + } +} + +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, + )) +} 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 000000000..ddf02a23c --- /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 an 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 000000000..9471ec477 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/execution_status.rs @@ -0,0 +1,368 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::{self, Display, Formatter}; + +use super::super::move_core_types::language_storage::ModuleId; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use super::base_types::{ObjectID, IotaAddress, TypeParameterIndex, CodeOffset}; + +#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] // Needed for QuorumDriverTrait implementation in IotaClientTsSdk +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)] +pub struct CongestedObjects(pub Vec); + +impl fmt::Display for CongestedObjects { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + for obj in &self.0 { + write!(f, "{}, ", obj)?; + } + Ok(()) + } +} + +#[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 an 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." + )] + IotaMoveVerificationTimeout, + + #[error("The shared object operation is not allowed.")] + SharedObjectOperationNotAllowed, + + #[error("Certificate cannot be executed due to a dependency on a deleted shared object")] + InputObjectDeleted, + + #[error("Certificate is cancelled due to congestion on shared objects: {congested_objects}")] + ExecutionCancelledDueToSharedObjectCongestion { congested_objects: CongestedObjects }, + + #[error("Address {address:?} is denied for coin {coin_type}")] + AddressDeniedForCoin { + address: IotaAddress, + coin_type: String, + }, + + #[error("Coin type is globally paused for use: {coin_type}")] + CoinTypeGlobalPause { coin_type: String }, + + #[error("Certificate is cancelled because randomness could not be generated this epoch")] + ExecutionCancelledDueToRandomnessUnavailable, + // 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; diff --git a/identity_iota_interaction/src/sdk_types/iota_types/gas.rs b/identity_iota_interaction/src/sdk_types/iota_types/gas.rs new file mode 100644 index 000000000..3165ad200 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/gas.rs @@ -0,0 +1,63 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use super::iota_serde::{BigInt, Readable}; + +/// Summary of the charges in a transaction. +/// Storage is charged independently of computation. +/// There are 3 parts to the storage charges: +/// `storage_cost`: it is the charge of storage at the time the transaction +/// is executed. The cost of storage is the number of +/// bytes of the objects being mutated multiplied by a +/// variable storage cost per byte `storage_rebate`: this is the amount +/// a user gets back when manipulating an object. The +/// `storage_rebate` is the `storage_cost` for an object minus fees. +/// `non_refundable_storage_fee`: not all the value of the object storage +/// cost is given back to user and there +/// is a small fraction that is kept by +/// the system. This value tracks that charge. +/// +/// When looking at a gas cost summary the amount charged to the user is +/// `computation_cost + storage_cost - storage_rebate` +/// and that is the amount that is deducted from the gas coins. +/// `non_refundable_storage_fee` is collected from the objects being +/// mutated/deleted and it is tracked by the system in storage funds. +/// +/// Objects deleted, including the older versions of objects mutated, have +/// the storage field on the objects added up to a pool of "potential +/// rebate". This rebate then is reduced by the "nonrefundable rate" +/// such that: `potential_rebate(storage cost of deleted/mutated +/// objects) = storage_rebate + non_refundable_storage_fee` + +#[serde_as] +#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct GasCostSummary { + /// Cost of computation/execution + #[schemars(with = "BigInt")] + #[serde_as(as = "Readable, _>")] + pub computation_cost: u64, + /// The burned component of the computation/execution costs + #[schemars(with = "BigInt")] + #[serde_as(as = "Readable, _>")] + pub computation_cost_burned: u64, + /// Storage cost, it's the sum of all storage cost for all objects + /// created or mutated. + #[schemars(with = "BigInt")] + #[serde_as(as = "Readable, _>")] + pub storage_cost: u64, + /// The amount of storage cost refunded to the user for all objects + /// deleted or mutated in the transaction. + #[schemars(with = "BigInt")] + #[serde_as(as = "Readable, _>")] + pub storage_rebate: u64, + /// The fee for the rebate. The portion of the storage rebate kept by + /// the system. + #[schemars(with = "BigInt")] + #[serde_as(as = "Readable, _>")] + pub non_refundable_storage_fee: u64, +} 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 000000000..b8cafc972 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/gas_coin.rs @@ -0,0 +1,147 @@ +// 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() + } + + // MoveObject is not available for wasm32 + // + // pub fn to_object(&self, version: SequenceNumber) -> MoveObject { + // MoveObject::new_gas_coin(version, *self.id(), self.value()) + // } + + 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 `TreasuryCap` object ID. + pub fn id(&self) -> &ObjectID { + self.inner.id.object_id() + } + + /// 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 000000000..e9ed0fe3e --- /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 000000000..b8727295a --- /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(Box::new(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_())) + } +} 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 000000000..f931a3dd0 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs @@ -0,0 +1,409 @@ +// 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; +#[allow(unused)] // Kept in sync with original source, so keep as is. +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 schemars::JsonSchema; + +use Result; + +use super::super::move_core_types::{ + account_address::AccountAddress, + language_storage::{StructTag, TypeTag} +}; + +#[allow(unused)] // Kept in sync with original source, so keep as is. +use super::{IOTA_FRAMEWORK_ADDRESS, MOVE_STDLIB_ADDRESS, IOTA_SYSTEM_ADDRESS, + STARDUST_ADDRESS, IOTA_SYSTEM_STATE_ADDRESS, IOTA_CLOCK_ADDRESS }; +use super::parse_iota_struct_tag; +use super::parse_iota_type_tag; + +/// The minimum and maximum protocol versions supported by this build. +const MIN_PROTOCOL_VERSION: u64 = 1; // Originally defined in crates/iota-protocol-config/src/lib.rs +pub const MAX_PROTOCOL_VERSION: u64 = 1; // Originally defined in crates/iota-protocol-config/src/lib.rs + +// ----------------------------------------------------------------------------------------- +// Originally contained in crates/iota-protocol-config/src/lib.rs +// ----------------------------------------------------------------------------------------- + +// Record history of protocol version allocations here: +// +// Version 1: Original version. +// Version 2: Don't redistribute slashed staking rewards, fix computation of +// SystemEpochInfoEventV1. +#[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); + + #[allow(unused)] // Kept in sync with original source, so keep as is. + #[cfg(not(msim))] + const MAX_ALLOWED: Self = Self::MAX; + + // We create 3 additional "fake" versions in simulator builds so that we can + // test upgrades. + #[cfg(msim)] + pub const MAX_ALLOWED: Self = Self(MAX_PROTOCOL_VERSION + 3); + + 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) + } +} + +// ----------------------------------------------------------------------------------------- +// End of originally contained in crates/iota-protocol-config/src/lib.rs section +// ----------------------------------------------------------------------------------------- + +#[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; 7] = [ + AccountAddress::ZERO, + AccountAddress::ONE, + IOTA_FRAMEWORK_ADDRESS, + IOTA_SYSTEM_ADDRESS, + STARDUST_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, JsonSchema)] +pub struct BigInt( + #[schemars(with = "String")] + #[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, JsonSchema)] +pub struct SequenceNumber(#[schemars(with = "BigInt")] 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)) + } +} + +#[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)) + } +} 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 000000000..a9501dade --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs @@ -0,0 +1,125 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#[allow(unused)] // Kept in sync with original source, so keep as is. +use serde::{Deserialize, Serialize}; +use super::super::move_core_types::{ + account_address::AccountAddress, + language_storage::{TypeTag, StructTag}, +}; +use super::base_types::{ObjectID, SequenceNumber, IotaAddress}; +use super::object::OBJECT_START_VERSION; + +macro_rules! built_in_ids { + ($($addr:ident / $id:ident = $init:expr);* $(;)?) => { + $( + pub const $addr: AccountAddress = builtin_address($init); + pub const $id: ObjectID = ObjectID::from_address($addr); + )* + } +} + +macro_rules! built_in_pkgs { + ($($addr:ident / $id:ident = $init:expr);* $(;)?) => { + built_in_ids! { $($addr / $id = $init;)* } + pub const SYSTEM_PACKAGE_ADDRESSES: &[AccountAddress] = &[$($addr),*]; + pub fn is_system_package(addr: impl Into) -> bool { + matches!(addr.into(), $($addr)|*) + } + } +} + +built_in_pkgs! { + MOVE_STDLIB_ADDRESS / MOVE_STDLIB_PACKAGE_ID = 0x1; + IOTA_FRAMEWORK_ADDRESS / IOTA_FRAMEWORK_PACKAGE_ID = 0x2; + IOTA_SYSTEM_ADDRESS / IOTA_SYSTEM_PACKAGE_ID = 0x3; + BRIDGE_ADDRESS / BRIDGE_PACKAGE_ID = 0xb; + STARDUST_ADDRESS / STARDUST_PACKAGE_ID = 0x107a; +} + +built_in_ids! { + IOTA_SYSTEM_STATE_ADDRESS / IOTA_SYSTEM_STATE_OBJECT_ID = 0x5; + IOTA_CLOCK_ADDRESS / IOTA_CLOCK_OBJECT_ID = 0x6; + IOTA_AUTHENTICATOR_STATE_ADDRESS / IOTA_AUTHENTICATOR_STATE_OBJECT_ID = 0x7; + IOTA_RANDOMNESS_STATE_ADDRESS / IOTA_RANDOMNESS_STATE_OBJECT_ID = 0x8; + IOTA_BRIDGE_ADDRESS / IOTA_BRIDGE_OBJECT_ID = 0x9; + IOTA_DENY_LIST_ADDRESS / IOTA_DENY_LIST_OBJECT_ID = 0x403; +} + +pub const IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; +pub const IOTA_CLOCK_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; +pub const IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; + +const fn builtin_address(suffix: u16) -> AccountAddress { + let mut addr = [0u8; AccountAddress::LENGTH]; + let [hi, lo] = suffix.to_be_bytes(); + addr[AccountAddress::LENGTH - 2] = hi; + addr[AccountAddress::LENGTH - 1] = lo; + AccountAddress::new(addr) +} + +/// 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_core_types::parsing::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_core_types::parsing::types::ParsedType; + ParsedType::parse(s)?.into_type_tag(&resolve_address) +} + +/// Resolve well-known named addresses into numeric addresses. +pub fn resolve_address(addr: &str) -> Option { + match addr { + "std" => Some(MOVE_STDLIB_ADDRESS), + "iota" => Some(IOTA_FRAMEWORK_ADDRESS), + "iota_system" => Some(IOTA_SYSTEM_ADDRESS), + "stardust" => Some(STARDUST_ADDRESS), + "bridge" => Some(BRIDGE_ADDRESS), + _ => None, + } +} + +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())) + } +} 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 000000000..8f75e8f72 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/mod.rs @@ -0,0 +1,29 @@ +// 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 event; +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 gas; +pub mod storage; + +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 000000000..e7f63eb30 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/move_package.rs @@ -0,0 +1,84 @@ +// 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, + // `struct_name` alias to support backwards compatibility with the old name + #[serde(alias = "struct_name")] + pub datatype_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 { + pub(crate) 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. + pub(crate) version: SequenceNumber, + // TODO use session cache + #[serde_as(as = "BTreeMap<_, Bytes>")] + pub(crate) module_map: BTreeMap>, + + /// Maps struct/module to a package version where it was first defined, + /// stored as a vector for simple serialization and deserialization. + pub(crate) type_origin_table: Vec, + + // For each dependency, maps original package ID to the info about the (upgraded) dependency + // version that this package is using + pub(crate) 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 000000000..1c9c7930c --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/object.rs @@ -0,0 +1,110 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::{Display, Formatter}; + +use schemars::JsonSchema; +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, JsonSchema)] +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 { + initial_shared_version, + } => { + write!(f, "Shared( {} )", initial_shared_version.value()) + } + } + } +} 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 000000000..3eb37fcbb --- /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 000000000..f6606f3f8 --- /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 output; diff --git a/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs b/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs new file mode 100644 index 000000000..28d547dcf --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs @@ -0,0 +1,6 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +pub mod nft; + +pub use nft::*; \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs b/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs new file mode 100644 index 000000000..b53b83d54 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs @@ -0,0 +1,33 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::ident_str; + +use crate::sdk_types::move_types::language_storage::StructTag; +use crate::sdk_types::move_types::identifier::IdentStr; + +use super::super::super::STARDUST_ADDRESS; + +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_ADDRESS.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/storage.rs b/identity_iota_interaction/src/sdk_types/iota_types/storage.rs new file mode 100644 index 000000000..2d6ca38ea --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/storage.rs @@ -0,0 +1,28 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub enum WriteKind { + /// The object was in storage already but has been modified + Mutate, + /// The object was created in this transaction + Create, + /// The object was previously wrapped in another object, but has been + /// restored to storage + Unwrap, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] +pub enum DeleteKind { + /// An object is provided in the call input, and gets deleted. + Normal, + /// An object is not provided in the call input, but gets unwrapped + /// from another object, and then gets deleted. + UnwrapThenDelete, + /// An object is provided in the call input, and gets wrapped into another + /// object. + Wrap, +} \ 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 000000000..9e825bb77 --- /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 000000000..65772e9e9 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs @@ -0,0 +1,124 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::Deserialize; +use serde::Serialize; + +use crate::ident_str; + +#[allow(unused)] // Kept in sync with original source, so keep as is. +use crate::sdk_types::move_types::{ + language_storage::{TypeTag, StructTag}, + identifier::{IdentStr}, +}; + +#[allow(unused)] // Kept in sync with original source, so keep as is. +use super::super::{ + IOTA_FRAMEWORK_ADDRESS, + IOTA_SYSTEM_ADDRESS, + base_types::{ObjectID, EpochId}, + balance::Balance, + governance::StakedIota, + id::UID, + error::IotaError, +}; + +#[allow(unused)] // Kept in sync with original source, so keep as is. +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).map_err(|err| IotaError::ObjectDeserialization { + error: format!("Unable to deserialize TimeLock object: {:?}", err), + }) + } + + /// 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, + } +} \ 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 000000000..2c052b9f6 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelocked_staked_iota.rs @@ -0,0 +1,85 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; +use crate::ident_str; +use crate::sdk_types::move_types::identifier::IdentStr; +use crate::sdk_types::move_types::language_storage::StructTag; +use super::super::{ + IOTA_SYSTEM_ADDRESS, + base_types::{ObjectID, EpochId}, + governance::StakedIota, + id::UID, +}; + +pub const TIMELOCKED_STAKED_IOTA_MODULE_NAME: &IdentStr = ident_str!("timelocked_staking"); +pub const TIMELOCKED_STAKED_IOTA_STRUCT_NAME: &IdentStr = ident_str!("TimelockedStakedIota"); + +/// 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/transaction.rs b/identity_iota_interaction/src/sdk_types/iota_types/transaction.rs new file mode 100644 index 000000000..379e4f1d3 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/iota_types/transaction.rs @@ -0,0 +1,463 @@ +// 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 std::vec::Vec; + +use enum_dispatch::enum_dispatch; +use nonempty::NonEmpty; +use serde::{Deserialize, Serialize}; +use strum::IntoStaticStr; + + +use super::super::move_core_types::identifier::Identifier; +use super::super::move_core_types::language_storage::TypeTag; + +use super::base_types::{EpochId, IotaAddress, ObjectID, ObjectRef, SequenceNumber}; +use super::error::UserInputError; +use super::{ + IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION, IOTA_CLOCK_OBJECT_ID, IOTA_CLOCK_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 = 50_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, + }; +} + +/// A series of commands where the results of one command can be used in future +/// commands +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct ProgrammableTransaction { + /// Input objects or primitive values + pub inputs: Vec, + /// The commands to be executed sequentially. A failure in any command will + /// result in the failure of the entire transaction. + pub commands: 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), +} + +/// 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, +} + +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})"), + } + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, IntoStaticStr)] +pub enum TransactionKind { + /// A transaction that allows the interleaving of native commands and Move + /// calls + ProgrammableTransaction(ProgrammableTransaction), +} + +#[derive(Debug, PartialEq, Eq)] +pub struct SharedInputObject { + pub id: ObjectID, + pub initial_shared_version: SequenceNumber, + pub mutable: bool, +} + +impl SharedInputObject { + pub const IOTA_SYSTEM_OBJ: Self = Self { + id: IOTA_SYSTEM_STATE_OBJECT_ID, + initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION, + mutable: true, + }; + + pub fn id(&self) -> ObjectID { + self.id + } + + pub fn into_id_and_version(self) -> (ObjectID, SequenceNumber) { + (self.id, self.initial_shared_version) + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct GasData { + pub payment: Vec, + pub owner: IotaAddress, + pub price: u64, + pub budget: u64, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)] +pub enum TransactionExpiration { + /// The transaction has no expiration + None, + /// Validators wont sign a transaction unless the expiration Epoch + /// is greater than or equal to the current epoch + Epoch(EpochId), +} + +#[enum_dispatch(TransactionDataAPI)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub enum TransactionData { + V1(TransactionDataV1), + // When new variants are introduced, it is important that we check version support + // in the validity_check function based on the protocol config. +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] +pub struct TransactionDataV1 { + pub kind: TransactionKind, + pub sender: IotaAddress, + pub gas_data: GasData, + pub expiration: TransactionExpiration, +} + +impl TransactionData { + pub fn new( + kind: TransactionKind, + sender: IotaAddress, + gas_payment: ObjectRef, + gas_budget: u64, + gas_price: u64, + ) -> Self { + TransactionData::V1(TransactionDataV1 { + kind, + sender, + gas_data: GasData { + price: gas_price, + owner: sender, + payment: vec![gas_payment], + budget: gas_budget, + }, + expiration: TransactionExpiration::None, + }) + } + + pub fn new_with_gas_coins( + kind: TransactionKind, + sender: IotaAddress, + gas_payment: Vec, + gas_budget: u64, + gas_price: u64, + ) -> Self { + Self::new_with_gas_coins_allow_sponsor( + kind, + sender, + gas_payment, + gas_budget, + gas_price, + sender, + ) + } + + pub fn new_with_gas_coins_allow_sponsor( + kind: TransactionKind, + sender: IotaAddress, + gas_payment: Vec, + gas_budget: u64, + gas_price: u64, + gas_sponsor: IotaAddress, + ) -> Self { + TransactionData::V1(TransactionDataV1 { + kind, + sender, + gas_data: GasData { + price: gas_price, + owner: gas_sponsor, + payment: gas_payment, + budget: gas_budget, + }, + expiration: TransactionExpiration::None, + }) + } + + pub fn new_with_gas_data( + kind: TransactionKind, + sender: IotaAddress, + gas_data: GasData, + ) -> Self { + TransactionData::V1(TransactionDataV1 { + kind, + sender, + gas_data, + expiration: TransactionExpiration::None, + }) + } +} + +#[enum_dispatch] +pub trait TransactionDataAPI { + fn sender(&self) -> IotaAddress; + + // Note: this implies that SingleTransactionKind itself must be versioned, so + // that it can be shared across versions. This will be easy to do since it + // is already an enum. + fn kind(&self) -> &TransactionKind; + + // Used by programmable_transaction_builder + fn kind_mut(&mut self) -> &mut TransactionKind; + + // kind is moved out of often enough that this is worth it to special case. + fn into_kind(self) -> TransactionKind; + + /// Transaction signer and Gas owner + fn signers(&self) -> NonEmpty; + + fn gas_data(&self) -> &GasData; + + fn gas_data_mut(&mut self) -> &mut GasData; + + fn gas_owner(&self) -> IotaAddress; + + fn gas(&self) -> &[ObjectRef]; + + fn gas_price(&self) -> u64; + + fn gas_budget(&self) -> u64; + + fn expiration(&self) -> &TransactionExpiration; + +} + +impl TransactionDataAPI for TransactionDataV1 { + fn sender(&self) -> IotaAddress { + self.sender + } + + fn kind(&self) -> &TransactionKind { + &self.kind + } + + fn kind_mut(&mut self) -> &mut TransactionKind { + &mut self.kind + } + + fn into_kind(self) -> TransactionKind { + self.kind + } + + /// Transaction signer and Gas owner + fn signers(&self) -> NonEmpty { + let mut signers = NonEmpty::new(self.sender); + if self.gas_owner() != self.sender { + signers.push(self.gas_owner()); + } + signers + } + + fn gas_data(&self) -> &GasData { + &self.gas_data + } + + fn gas_owner(&self) -> IotaAddress { + self.gas_data.owner + } + + fn gas(&self) -> &[ObjectRef] { + &self.gas_data.payment + } + + fn gas_price(&self) -> u64 { + self.gas_data.price + } + + fn gas_budget(&self) -> u64 { + self.gas_data.budget + } + + fn expiration(&self) -> &TransactionExpiration { + &self.expiration + } + + fn gas_data_mut(&mut self) -> &mut GasData { + &mut self.gas_data + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)] +pub enum InputObjectKind { + // A Move package, must be immutable. + MovePackage(ObjectID), + // A Move object, either immutable, or owned mutable. + ImmOrOwnedMoveObject(ObjectRef), + // A Move object that's shared and mutable. + SharedMoveObject { + id: ObjectID, + initial_shared_version: SequenceNumber, + mutable: bool, + }, +} + +impl InputObjectKind { + pub fn object_id(&self) -> ObjectID { + match self { + Self::MovePackage(id) => *id, + Self::ImmOrOwnedMoveObject((id, _, _)) => *id, + Self::SharedMoveObject { id, .. } => *id, + } + } + + pub fn version(&self) -> Option { + match self { + Self::MovePackage(..) => None, + Self::ImmOrOwnedMoveObject((_, version, _)) => Some(*version), + Self::SharedMoveObject { .. } => None, + } + } + + pub fn object_not_found_error(&self) -> UserInputError { + match *self { + Self::MovePackage(package_id) => { + UserInputError::DependentPackageNotFound { package_id } + } + Self::ImmOrOwnedMoveObject((object_id, version, _)) => UserInputError::ObjectNotFound { + object_id, + version: Some(version), + }, + Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound { + object_id: id, + version: None, + }, + } + } + + pub fn is_shared_object(&self) -> bool { + matches!(self, Self::SharedMoveObject { .. }) + } + + pub fn is_mutable(&self) -> bool { + match self { + Self::MovePackage(..) => false, + Self::ImmOrOwnedMoveObject((_, _, _)) => true, + Self::SharedMoveObject { mutable, .. } => *mutable, + } + } +} \ 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 000000000..6d75bfa2d --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/mod.rs @@ -0,0 +1,17 @@ +// 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 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_core_types/account_address.rs b/identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs new file mode 100644 index 000000000..d52b19195 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs @@ -0,0 +1,337 @@ +// 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(); + + pub const fn from_suffix(suffix: u16) -> AccountAddress { + let mut addr = [0u8; AccountAddress::LENGTH]; + let [hi, lo] = suffix.to_be_bytes(); + addr[AccountAddress::LENGTH - 2] = hi; + addr[AccountAddress::LENGTH - 1] = lo; + AccountAddress::new(addr) + } + + 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) + } + + // AbstractMemorySize is not available for wasm32 + // + // /// TODO (ade): use macro to enfornce determinism + // pub fn abstract_size_for_gas_metering(&self) -> AbstractMemorySize { + // AbstractMemorySize::new(Self::LENGTH as u64) + // } +} + +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 000000000..09a4ceca5 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/annotated_value.rs @@ -0,0 +1,774 @@ +// 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::{ + collections::BTreeMap, + fmt::{self, Debug}, + io::Cursor, +}; + +use anyhow::Result as AResult; +use serde::{ + Deserialize, Serialize, + de::Error as DeError, + ser::{SerializeMap, SerializeSeq, SerializeStruct}, +}; + +use super::{ + VARIANT_COUNT_MAX, + account_address::AccountAddress, + annotated_visitor::{Error as VError, ValueDriver, Visitor, visit_struct, visit_value}, + identifier::Identifier, + language_storage::{StructTag, TypeTag}, + runtime_value::{self as R, MOVE_STRUCT_FIELDS, MOVE_STRUCT_TYPE}, + 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 enum/struct gets serialized into a +/// Serde struct with this as the first field +pub const MOVE_DATA_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_DATA_FIELDS: &str = "fields"; + +/// In the `WithTypes` configuration, a Move enum gets serialized into a Serde +/// struct with this as the second field In the `WithFields` configuration, this +/// is the first field of the serialized enum +pub const MOVE_VARIANT_NAME: &str = "variant_name"; + +/// Field name for the tag of the variant +pub const MOVE_VARIANT_TAG_NAME: &str = "variant_tag"; + +/// In the `WithTypes` configuration, a Move enum gets serialized into a Serde +/// struct with this name +pub const MOVE_ENUM_NAME: &str = "enum"; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct MoveStruct { + pub type_: StructTag, + pub fields: Vec<(Identifier, MoveValue)>, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct MoveVariant { + pub type_: StructTag, + pub variant_name: Identifier, + pub tag: u16, + 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), + Variant(MoveVariant), +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +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, PartialEq, Eq)] +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, PartialEq, Eq)] +pub struct MoveEnumLayout { + pub type_: StructTag, + pub variants: BTreeMap<(Identifier, u16), Vec>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum MoveDatatypeLayout { + Struct(Box), + Enum(Box), +} + +impl MoveDatatypeLayout { + pub fn into_layout(self) -> MoveTypeLayout { + match self { + Self::Struct(s) => MoveTypeLayout::Struct(s), + Self::Enum(e) => MoveTypeLayout::Enum(e), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +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(Box), + #[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, + #[serde(rename(serialize = "enum", deserialize = "enum"))] + Enum(Box), +} + +impl MoveStructLayout { + /// Returns `true` if and only if the layout is for `type_`. + pub fn is_type(&self, type_: &StructTag) -> bool { + self.type_ == *type_ + } +} + +impl MoveEnumLayout { + /// Returns `true` if and only if the layout is for `type_`. + pub fn is_type(&self, type_: &StructTag) -> bool { + self.type_ == *type_ + } +} + +impl MoveTypeLayout { + /// Returns `true` if and only if the layout is for `type_`. + pub fn is_type(&self, type_: &TypeTag) -> bool { + use MoveTypeLayout as L; + use TypeTag as T; + + match self { + L::Bool => matches!(type_, T::Bool), + L::U8 => matches!(type_, T::U8), + L::U16 => matches!(type_, T::U16), + L::U32 => matches!(type_, T::U32), + L::U64 => matches!(type_, T::U64), + L::U128 => matches!(type_, T::U128), + L::U256 => matches!(type_, T::U256), + L::Address => matches!(type_, T::Address), + L::Signer => matches!(type_, T::Signer), + L::Vector(l) => matches!(type_, T::Vector(t) if l.is_type(t)), + L::Struct(l) => matches!(type_, T::Struct(t) if l.is_type(t)), + L::Enum(l) => matches!(type_, T::Struct(t) if l.is_type(t)), + } + } +} + +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<'b, 'l, V: Visitor<'b, 'l>>( + blob: &'b [u8], + ty: &'l MoveTypeLayout, + visitor: &mut V, + ) -> AResult + where + V::Error: std::error::Error + Send + Sync + 'static, + { + let mut bytes = Cursor::new(blob); + let res = visit_value(&mut bytes, ty, visitor)?; + if bytes.position() as usize == blob.len() { + Ok(res) + } else { + let remaining = blob.len() - bytes.position() as usize; + Err(VError::TrailingBytes(remaining).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::Variant(v) => R::MoveValue::Variant(v.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<'b, 'l, V: Visitor<'b, 'l>>( + blob: &'b [u8], + ty: &'l MoveStructLayout, + visitor: &mut V, + ) -> AResult + where + V::Error: std::error::Error + Send + Sync + 'static, + { + let mut bytes = Cursor::new(blob); + let driver = ValueDriver::new(&mut bytes, None); + let res = visit_struct(driver, ty, visitor)?; + if bytes.position() as usize == blob.len() { + Ok(res) + } else { + let remaining = blob.len() - bytes.position() as usize; + Err(VError::TrailingBytes(remaining).into()) + } + } + + pub fn into_fields(self) -> Vec { + self.fields.into_iter().map(|(_, v)| v).collect() + } + + pub fn undecorate(self) -> R::MoveStruct { + R::MoveStruct( + self.into_fields() + .into_iter() + .map(MoveValue::undecorate) + .collect(), + ) + } +} + +impl MoveVariant { + pub fn new( + type_: StructTag, + variant_name: Identifier, + tag: u16, + fields: Vec<(Identifier, MoveValue)>, + ) -> Self { + Self { + type_, + variant_name, + tag, + fields, + } + } + + pub fn simple_deserialize(blob: &[u8], ty: &MoveEnumLayout) -> AResult { + Ok(bcs::from_bytes_seed(ty, blob)?) + } + + pub fn into_fields(self) -> Vec { + self.fields.into_iter().map(|(_, v)| v).collect() + } + + pub fn undecorate(self) -> R::MoveVariant { + R::MoveVariant { + tag: self.tag, + fields: 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::Enum(ty) => Ok(MoveValue::Variant(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<'d> serde::de::DeserializeSeed<'d> for &MoveEnumLayout { + type Value = MoveVariant; + fn deserialize>( + self, + deserializer: D, + ) -> Result { + let (variant_name, tag, fields) = + deserializer.deserialize_tuple(2, DecoratedEnumFieldVisitor(&self.variants))?; + Ok(MoveVariant { + type_: self.type_.clone(), + variant_name, + tag, + fields, + }) + } +} + +struct DecoratedEnumFieldVisitor<'a>(&'a BTreeMap<(Identifier, u16), Vec>); + +impl<'d, 'a> serde::de::Visitor<'d> for DecoratedEnumFieldVisitor<'a> { + type Value = (Identifier, u16, Vec<(Identifier, MoveValue)>); + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("Enum") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'d>, + { + let tag = match seq.next_element_seed(&MoveTypeLayout::U8)? { + Some(MoveValue::U8(tag)) if tag as u64 <= VARIANT_COUNT_MAX => tag as u16, + Some(MoveValue::U8(tag)) => return Err(A::Error::invalid_length(tag as usize, &self)), + Some(val) => { + return Err(A::Error::invalid_type( + serde::de::Unexpected::Other(&format!("{val:?}")), + &self, + )); + } + None => return Err(A::Error::invalid_length(0, &self)), + }; + + let Some(((variant_name, _), variant_layout)) = + self.0.iter().find(|((_, v_tag), _)| *v_tag == tag) + else { + return Err(A::Error::invalid_length(tag as usize, &self)); + }; + + let Some(fields) = seq.next_element_seed(&DecoratedVariantFieldLayout(variant_layout))? + else { + return Err(A::Error::invalid_length(1, &self)); + }; + + Ok((variant_name.clone(), tag, fields)) + } +} + +struct DecoratedVariantFieldLayout<'a>(&'a Vec); + +impl<'d, 'a> serde::de::DeserializeSeed<'d> for &DecoratedVariantFieldLayout<'a> { + type Value = Vec<(Identifier, MoveValue)>; + + fn deserialize>( + self, + deserializer: D, + ) -> Result { + deserializer.deserialize_tuple(self.0.len(), DecoratedStructFieldVisitor(self.0)) + } +} + +impl serde::Serialize for MoveValue { + fn serialize(&self, serializer: S) -> Result { + match self { + MoveValue::Struct(s) => s.serialize(serializer), + MoveValue::Variant(v) => v.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 serde::Serialize for MoveVariant { + fn serialize(&self, serializer: S) -> Result { + // Serialize an enum as: + // enum { "type": 0xC::module::enum_type, "variant_name": name, "variant_tag": + // tag, "fields": { ... } } + let mut t = serializer.serialize_struct(MOVE_ENUM_NAME, 4)?; + t.serialize_field(MOVE_DATA_TYPE, &self.type_.to_string())?; + t.serialize_field(MOVE_VARIANT_NAME, &self.variant_name.to_string())?; + t.serialize_field(MOVE_VARIANT_TAG_NAME, &MoveValue::U16(self.tag))?; + t.serialize_field(MOVE_DATA_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}"), + Enum(e) if f.alternate() => write!(f, "{e:#}"), + Enum(e) => write!(f, "enum {}", e), + } + } +} + +/// 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 fmt::Display for MoveEnumLayout { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + use DebugAsDisplay as DD; + write!(f, "enum {} ", self.type_)?; + let mut vmap = f.debug_set(); + for ((variant_name, _), fields) in self.variants.iter() { + vmap.entry(&DD(&MoveVariantDisplay(variant_name.as_str(), fields))); + } + vmap.finish() + } +} + +struct MoveVariantDisplay<'a>(&'a str, &'a [MoveFieldLayout]); + +impl<'a> fmt::Display for MoveVariantDisplay<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + use DebugAsDisplay as DD; + let mut map = f.debug_struct(self.0); + for field in self.1 { + map.field(field.name.as_str(), &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.as_ref().into())), + MoveTypeLayout::Enum(e) => TypeTag::Struct(Box::new(e.as_ref().into())), + } + } +} + +impl From<&MoveStructLayout> for StructTag { + fn from(val: &MoveStructLayout) -> StructTag { + val.type_.clone() + } +} + +impl From<&MoveEnumLayout> for StructTag { + fn from(val: &MoveEnumLayout) -> 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), + MoveValue::Variant(v) => fmt::Display::fmt(v, 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() + } +} + +impl fmt::Display for MoveVariant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use DebugAsDisplay as DD; + let MoveVariant { + type_, + variant_name, + tag: _, + fields, + } = self; + write!(f, "{}::{}", type_, variant_name)?; + let mut map = f.debug_map(); + for (field, value) in fields { + map.entry(&DD(field), &DD(value)); + } + map.finish() + } +} 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 000000000..354e38bf2 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/annotated_visitor.rs @@ -0,0 +1,814 @@ +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::io::{Cursor, Read}; + +use super::{ + VARIANT_COUNT_MAX, + account_address::AccountAddress, + annotated_value::{MoveEnumLayout, MoveFieldLayout, MoveStructLayout, MoveTypeLayout}, + identifier::IdentStr, + u256::U256, +}; + +/// Visitors can be used for building values out of a serialized Move struct or +/// value. +pub trait Visitor<'b, 'l> { + 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, + driver: &ValueDriver<'_, 'b, 'l>, + value: u8, + ) -> Result; + + fn visit_u16( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: u16, + ) -> Result; + + fn visit_u32( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: u32, + ) -> Result; + + fn visit_u64( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: u64, + ) -> Result; + + fn visit_u128( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: u128, + ) -> Result; + + fn visit_u256( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: U256, + ) -> Result; + + fn visit_bool( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: bool, + ) -> Result; + + fn visit_address( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: AccountAddress, + ) -> Result; + + fn visit_signer( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: AccountAddress, + ) -> Result; + + fn visit_vector( + &mut self, + driver: &mut VecDriver<'_, 'b, 'l>, + ) -> Result; + + fn visit_struct( + &mut self, + driver: &mut StructDriver<'_, 'b, 'l>, + ) -> Result; + + fn visit_variant( + &mut self, + driver: &mut VariantDriver<'_, 'b, 'l>, + ) -> 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<'b, 'l> { + type Error: From; + + fn traverse_u8( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: u8, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_u16( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: u16, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_u32( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: u32, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_u64( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: u64, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_u128( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: u128, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_u256( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: U256, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_bool( + &mut self, + _driver: &ValueDriver<'_, 'b, 'l>, + _value: bool, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_address( + &mut self, + _: &ValueDriver<'_, 'b, 'l>, + _: AccountAddress, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_signer( + &mut self, + _: &ValueDriver<'_, 'b, 'l>, + _: AccountAddress, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn traverse_vector(&mut self, driver: &mut VecDriver<'_, 'b, 'l>) -> Result<(), Self::Error> { + while driver.next_element(self)?.is_some() {} + Ok(()) + } + + fn traverse_struct( + &mut self, + driver: &mut StructDriver<'_, 'b, 'l>, + ) -> Result<(), Self::Error> { + while driver.next_field(self)?.is_some() {} + Ok(()) + } + + fn traverse_variant( + &mut self, + driver: &mut VariantDriver<'_, 'b, 'l>, + ) -> Result<(), Self::Error> { + while driver.next_field(self)?.is_some() {} + Ok(()) + } +} + +/// Default implementation converting any traversal into a visitor. +impl<'b, 'l, T: Traversal<'b, 'l> + ?Sized> Visitor<'b, 'l> for T { + type Value = (); + type Error = T::Error; + + fn visit_u8( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: u8, + ) -> Result { + self.traverse_u8(driver, value) + } + + fn visit_u16( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: u16, + ) -> Result { + self.traverse_u16(driver, value) + } + + fn visit_u32( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: u32, + ) -> Result { + self.traverse_u32(driver, value) + } + + fn visit_u64( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: u64, + ) -> Result { + self.traverse_u64(driver, value) + } + + fn visit_u128( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: u128, + ) -> Result { + self.traverse_u128(driver, value) + } + + fn visit_u256( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: U256, + ) -> Result { + self.traverse_u256(driver, value) + } + + fn visit_bool( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: bool, + ) -> Result { + self.traverse_bool(driver, value) + } + + fn visit_address( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: AccountAddress, + ) -> Result { + self.traverse_address(driver, value) + } + + fn visit_signer( + &mut self, + driver: &ValueDriver<'_, 'b, 'l>, + value: AccountAddress, + ) -> Result { + self.traverse_signer(driver, value) + } + + fn visit_vector( + &mut self, + driver: &mut VecDriver<'_, 'b, 'l>, + ) -> Result { + self.traverse_vector(driver) + } + + fn visit_struct( + &mut self, + driver: &mut StructDriver<'_, 'b, 'l>, + ) -> Result { + self.traverse_struct(driver) + } + + fn visit_variant( + &mut self, + driver: &mut VariantDriver<'_, 'b, 'l>, + ) -> Result { + self.traverse_variant(driver) + } +} + +/// Exposes information about the byte stream that the value being visited came +/// from, namely the bytes themselves, and the offset at which the value starts. +/// Also exposes the layout of the value being visited. +pub struct ValueDriver<'c, 'b, 'l> { + bytes: &'c mut Cursor<&'b [u8]>, + layout: Option<&'l MoveTypeLayout>, + start: usize, +} + +/// 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<'c, 'b, 'l> { + inner: ValueDriver<'c, 'b, 'l>, + 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<'c, 'b, 'l> { + inner: ValueDriver<'c, 'b, 'l>, + layout: &'l MoveStructLayout, + off: u64, +} + +/// Exposes information about a variant being visited (its layout, details about +/// the next field to be visited, the variant's tag, and name) to a visitor +/// implementation, and allows that visitor to progress the traversal (by +/// visiting or skipping fields). +pub struct VariantDriver<'c, 'b, 'l> { + inner: ValueDriver<'c, 'b, 'l>, + layout: &'l MoveEnumLayout, + tag: u16, + variant_name: &'l IdentStr, + variant_layout: &'l [MoveFieldLayout], + off: u64, +} + +#[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), + + #[error("invalid variant tag: {0}")] + UnexpectedVariantTag(usize), + + #[error("no layout available for value")] + NoValueLayout, +} + +/// 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<'b, 'l> Traversal<'b, 'l> for NullTraversal { + type Error = Error; +} + +impl<'c, 'b, 'l> ValueDriver<'c, 'b, 'l> { + pub(crate) fn new(bytes: &'c mut Cursor<&'b [u8]>, layout: Option<&'l MoveTypeLayout>) -> Self { + let start = bytes.position() as usize; + Self { + bytes, + layout, + start, + } + } + + /// The offset at which the value being visited starts in the byte stream. + pub fn start(&self) -> usize { + self.start + } + + /// The current position in the byte stream. + pub fn position(&self) -> usize { + self.bytes.position() as usize + } + + /// All the bytes in the byte stream (including the ones that have been + /// read). + pub fn bytes(&self) -> &'b [u8] { + self.bytes.get_ref() + } + /// The bytes that haven't been consumed by the visitor yet. + pub fn remaining_bytes(&self) -> &'b [u8] { + &self.bytes.get_ref()[self.position()..] + } + + /// Type layout for the value being visited. May produce an error if a + /// layout was not supplied when the driver was created (which should + /// only happen if the driver was created for visiting a struct + /// specifically). + pub fn layout(&self) -> Result<&'l MoveTypeLayout, Error> { + self.layout.ok_or(Error::NoValueLayout) + } + + fn read_exact(&mut self) -> Result<[u8; N], Error> { + let mut buf = [0u8; N]; + self.bytes + .read_exact(&mut buf) + .map_err(|_| Error::UnexpectedEof)?; + Ok(buf) + } + + fn read_leb128(&mut self) -> Result { + leb128::read::unsigned(self.bytes).map_err(|_| Error::UnexpectedEof) + } +} + +#[allow(clippy::len_without_is_empty)] +impl<'c, 'b, 'l> VecDriver<'c, 'b, 'l> { + fn new(inner: ValueDriver<'c, 'b, 'l>, layout: &'l MoveTypeLayout, len: u64) -> Self { + Self { + inner, + layout, + len, + off: 0, + } + } + + /// The offset at which the value being visited starts in the byte stream. + pub fn start(&self) -> usize { + self.inner.start() + } + + /// The current position in the byte stream. + pub fn position(&self) -> usize { + self.inner.position() + } + + /// All the bytes in the byte stream (including the ones that have been + /// read). + pub fn bytes(&self) -> &'b [u8] { + self.inner.bytes() + } + + /// The bytes that haven't been consumed by the visitor yet. + pub fn remaining_bytes(&self) -> &'b [u8] { + self.inner.remaining_bytes() + } + + /// Type layout for the vector's inner type. + pub fn element_layout(&self) -> &'l MoveTypeLayout { + self.layout + } + + /// The number of elements in this vector that have been visited so far. + pub fn off(&self) -> u64 { + self.off + } + + /// 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 + ?Sized>( + &mut self, + visitor: &mut V, + ) -> Result, V::Error> { + Ok(if self.off >= self.len { + None + } else { + let res = visit_value(self.inner.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<'c, 'b, 'l> StructDriver<'c, 'b, 'l> { + fn new(inner: ValueDriver<'c, 'b, 'l>, layout: &'l MoveStructLayout) -> Self { + Self { + inner, + layout, + off: 0, + } + } + + /// The offset at which the value being visited starts in the byte stream. + pub fn start(&self) -> usize { + self.inner.start() + } + + /// The current position in the byte stream. + pub fn position(&self) -> usize { + self.inner.position() + } + + /// All the bytes in the byte stream (including the ones that have been + /// read). + pub fn bytes(&self) -> &'b [u8] { + self.inner.bytes() + } + + /// The bytes that haven't been consumed by the visitor yet. + pub fn remaining_bytes(&self) -> &'b [u8] { + self.inner.remaining_bytes() + } + + /// The layout of the struct being visited. + pub fn struct_layout(&self) -> &'l MoveStructLayout { + self.layout + } + + /// The number of fields in this struct that have been visited so far. + pub fn off(&self) -> u64 { + self.off + } + + /// 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 as usize) + } + + /// 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 + ?Sized>( + &mut self, + visitor: &mut V, + ) -> Result, V::Error> { + let Some(field) = self.peek_field() else { + return Ok(None); + }; + + let res = visit_value(self.inner.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)) + } +} + +impl<'c, 'b, 'l> VariantDriver<'c, 'b, 'l> { + fn new( + inner: ValueDriver<'c, 'b, 'l>, + layout: &'l MoveEnumLayout, + variant_layout: &'l [MoveFieldLayout], + variant_name: &'l IdentStr, + tag: u16, + ) -> Self { + Self { + inner, + layout, + tag, + variant_name, + variant_layout, + off: 0, + } + } + + /// The offset at which the value being visited starts in the byte stream. + pub fn start(&self) -> usize { + self.inner.start() + } + + /// The current position in the byte stream. + pub fn position(&self) -> usize { + self.inner.position() + } + + /// All the bytes in the byte stream (including the ones that have been + /// read). + pub fn bytes(&self) -> &'b [u8] { + self.inner.bytes() + } + + /// The bytes that haven't been consumed by the visitor yet. + pub fn remaining_bytes(&self) -> &'b [u8] { + self.inner.remaining_bytes() + } + + /// The layout of the enum being visited. + pub fn enum_layout(&self) -> &'l MoveEnumLayout { + self.layout + } + + /// The layout of the variant being visited. + pub fn variant_layout(&self) -> &'l [MoveFieldLayout] { + self.variant_layout + } + + /// The tag of the variant being visited. + pub fn tag(&self) -> u16 { + self.tag + } + + /// The name of the enum variant being visited. + pub fn variant_name(&self) -> &'l IdentStr { + self.variant_name + } + + /// The number of elements in this vector that have been visited so far. + pub fn off(&self) -> u64 { + self.off + } + + /// 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.variant_layout.get(self.off as usize) + } + + /// Visit the next field in the variant. 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 variant. + /// + /// Returns `Ok(None)` if there are no more fields in the variant, `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 + ?Sized>( + &mut self, + visitor: &mut V, + ) -> Result, V::Error> { + let Some(field) = self.peek_field() else { + return Ok(None); + }; + + let res = visit_value(self.inner.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 +/// `annoted_value::MoveValue::visit_deserialize` for details. +pub(crate) fn visit_value<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( + bytes: &'c mut Cursor<&'b [u8]>, + layout: &'l MoveTypeLayout, + visitor: &mut V, +) -> Result { + use MoveTypeLayout as L; + + let mut driver = ValueDriver::new(bytes, Some(layout)); + match layout { + L::Bool => match driver.read_exact()? { + [0] => visitor.visit_bool(&driver, false), + [1] => visitor.visit_bool(&driver, true), + [b] => Err(Error::UnexpectedByte(b).into()), + }, + + L::U8 => { + let v = u8::from_le_bytes(driver.read_exact()?); + visitor.visit_u8(&driver, v) + } + + L::U16 => { + let v = u16::from_le_bytes(driver.read_exact()?); + visitor.visit_u16(&driver, v) + } + + L::U32 => { + let v = u32::from_le_bytes(driver.read_exact()?); + visitor.visit_u32(&driver, v) + } + + L::U64 => { + let v = u64::from_le_bytes(driver.read_exact()?); + visitor.visit_u64(&driver, v) + } + + L::U128 => { + let v = u128::from_le_bytes(driver.read_exact()?); + visitor.visit_u128(&driver, v) + } + + L::U256 => { + let v = U256::from_le_bytes(&driver.read_exact()?); + visitor.visit_u256(&driver, v) + } + + L::Address => { + let v = AccountAddress::new(driver.read_exact()?); + visitor.visit_address(&driver, v) + } + + L::Signer => { + let v = AccountAddress::new(driver.read_exact()?); + visitor.visit_signer(&driver, v) + } + + L::Vector(l) => visit_vector(driver, l.as_ref(), visitor), + L::Struct(l) => visit_struct(driver, l, visitor), + L::Enum(e) => visit_variant(driver, e, visitor), + } +} + +/// Like `visit_value` but specialized to visiting a vector (where the `bytes` +/// is known to be a serialized move vector), and the layout is the vector's +/// element's layout. +fn visit_vector<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( + mut inner: ValueDriver<'c, 'b, 'l>, + layout: &'l MoveTypeLayout, + visitor: &mut V, +) -> Result { + let len = inner.read_leb128()?; + let mut driver = VecDriver::new(inner, layout, len); + let res = visitor.visit_vector(&mut driver)?; + while driver.skip_element()? {} + Ok(res) + } + +/// 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<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( + inner: ValueDriver<'c, 'b, 'l>, + layout: &'l MoveStructLayout, + visitor: &mut V, +) -> Result { + let mut driver = StructDriver::new(inner, layout); + let res = visitor.visit_struct(&mut driver)?; + while driver.skip_field()?.is_some() {} + Ok(res) +} + +/// Like `visit_struct` but specialized to visiting a variant (where the `bytes` +/// is known to be a serialized move variant), and the layout is an enum layout. +fn visit_variant<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( + mut inner: ValueDriver<'c, 'b, 'l>, + layout: &'l MoveEnumLayout, + visitor: &mut V, +) -> Result { + // Since variants are bounded at 127, we can read the tag as a single byte. + // When we add true ULEB encoding for enum variants switch to this: + // let tag = inner.read_leb128()?; + let [tag] = inner.read_exact()?; + if tag >= VARIANT_COUNT_MAX as u8 { + return Err(Error::UnexpectedVariantTag(tag as usize).into()); + } + let variant_layout = layout + .variants + .iter() + .find(|((_, vtag), _)| *vtag == tag as u16) + .ok_or(Error::UnexpectedVariantTag(tag as usize))?; + + let mut driver = VariantDriver::new( + inner, + layout, + variant_layout.1, + &variant_layout.0.0, + tag as u16, + ); + let res = visitor.visit_variant(&mut driver)?; + while driver.skip_field()?.is_some() {} + Ok(res) +} 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 000000000..82c6f3d0d --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/identifier.rs @@ -0,0 +1,243 @@ +// 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); + } + } + + /// Creates a new `Identifier` from a string without checking if it is a + /// valid identifier. This should not be used under normal + /// circumstances, but is used in cases where we need to + /// preserve backwards compatibility. + /// + /// # Safety + /// + /// Only use this function when preserving backwards compatibility. + pub unsafe fn new_unchecked(s: impl Into>) -> Self { + Self(s.into()) + } + + /// 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() + } + + // AbstractMemorySize is not available for wasm32 + // + // /// Returns the abstract size of the struct + // /// TODO (ade): use macro to enfornce determinism + // pub fn abstract_size_for_gas_metering(&self) -> AbstractMemorySize { + // AbstractMemorySize::new((self.len()) as u64) + // } +} + +impl Borrow for Identifier { + fn borrow(&self) -> &IdentStr { + self + } +} + +impl Borrow for Identifier { + fn borrow(&self) -> &str { + &self.0 + } +} + +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 000000000..8d9b677a2 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/language_storage.rs @@ -0,0 +1,350 @@ +// 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, + identifier::{IdentStr, Identifier}, + parsing::types::{ParsedModuleId, ParsedStructType, ParsedType}, +}; + +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: + /// + /// - "bool", "u8", "u16", "u32", "u64", "u128", "u256", "address", + /// "signer", "vector" for ground types. + /// + /// - Structs are represented as fully qualified type names, with or without + /// the prefix "0x" depending on the `with_prefix` flag, e.g. + /// `0x000...0001::string::String` or + /// `0x000...000a::m::T<0x000...000a::n::U>`. + /// + /// - Addresses are hex-encoded lowercase values of length 32 (zero-padded). + /// + /// Note: this function is guaranteed to be stable -- suitable for use + /// inside Move native functions or the VM. By contrast, this type's + /// `Display` implementation is subject to change and should be used + /// inside code that needs to return a stable output (e.g. that might be + /// committed to effects on-chain). + 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 type with optional + /// 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 { + ParsedType::parse(s)?.into_type_tag(&|_| None) + } +} + +#[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. + /// + /// - Structs are represented as fully qualified type names, with or without + /// the prefix "0x" depending on the `with_prefix` flag, e.g. + /// `0x000...0001::string::String` or + /// `0x000...000a::m::T<0x000...000a::n::U>`. + /// + /// - Addresses are hex-encoded lowercase values of length 32 (zero-padded). + /// + /// Note: this function is guaranteed to be stable -- suitable for use + /// inside Move native functions or the VM. By contrast, this type's + /// `Display` implementation is subject to change and should be used + /// inside code that needs to return a stable output (e.g. that might be + /// committed to effects on-chain). + 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 + /// optional 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 { + ParsedStructType::parse(s)?.into_struct_tag(&|_| None) + } +} + +/// 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 FromStr for ModuleId { + type Err = anyhow::Error; + fn from_str(s: &str) -> Result { + ParsedModuleId::parse(s)?.into_module_id(&|_| None) + } +} + +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 From for TypeTag { + fn from(t: StructTag) -> TypeTag { + TypeTag::Struct(Box::new(t)) + } +} + 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 000000000..c6278bedd --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/mod.rs @@ -0,0 +1,35 @@ +// 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 runtime_value; +pub mod u256; +pub mod parsing; + +use std::fmt; + +pub const VARIANT_COUNT_MAX: u64 = 127; + +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(()) +} diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs new file mode 100644 index 000000000..0ac720345 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs @@ -0,0 +1,202 @@ +// 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::{self, Some, None}; +use std::string::String; + +use anyhow::anyhow; + +use super::super::account_address::AccountAddress; +use super::super::u256::U256; +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: 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 = U256::from_be_bytes(&self.bytes); + 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 PartialEq for NumericalAddress { + fn eq(&self, other: &AccountAddress) -> bool { + let Self { + bytes: self_bytes, + format: _, + } = self; + self_bytes == other + } +} + +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_core_types/parsing/mod.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/mod.rs new file mode 100644 index 000000000..bdb2683bc --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/mod.rs @@ -0,0 +1,11 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] + +pub mod address; +pub mod parser; +pub mod types; +pub mod values; diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs new file mode 100644 index 000000000..becea624d --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs @@ -0,0 +1,470 @@ +// 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::{ + account_address::AccountAddress, + u256::{U256FromStrError, U256}, +}; + +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; +// See: https://stackoverflow.com/questions/43787672/the-max-number-of-digits-in-an-int-based-on-number-of-bits +const U256_MAX_DECIMAL_DIGITS: usize = 241 * AccountAddress::LENGTH / 100 + 1; + +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()) + } +} + +pub(crate) 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()) { + if allow_trailing_delim { + break; + } else { + bail!("Invalid type list: trailing delimiter '{}'", delim) + } + } + } + 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)?; + if type_args.is_empty() { + bail!("expected at least one type argument") + } + 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<(AccountAddress, NumberFormat)> { + let (txt, base) = determine_num_text_and_base(s); + let txt = txt.replace('_', ""); + let max_len = match base { + NumberFormat::Hex => AccountAddress::LENGTH * 2, + NumberFormat::Decimal => U256_MAX_DECIMAL_DIGITS, + }; + if txt.len() > max_len { + return None; + } + let parsed = U256::from_str_radix(&txt, match base { + NumberFormat::Hex => 16, + NumberFormat::Decimal => 10, + }) + .ok()?; + Some((AccountAddress::new(parsed.to_be_bytes()), base)) +} diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs new file mode 100644 index 000000000..2dc1142ba --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs @@ -0,0 +1,195 @@ +// 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::{ + account_address::AccountAddress, + identifier::{self, Identifier}, + 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')) => { + chars.next().unwrap(); + match chars.next() { + Some(c) if c.is_ascii_hexdigit() => { + // 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(|c| c.is_ascii_digit() || *c == '_') + .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 == '_' + && chars + .peek() + .is_some_and(|c| identifier::is_valid_identifier_char(*c))) => + { + // c + remaining + let len = 1 + chars + .take_while(|c| identifier::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)?)), + }) + } +} diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs new file mode 100644 index 000000000..1ad1322e2 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs @@ -0,0 +1,320 @@ +// 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::{ + account_address::AccountAddress, + identifier, + runtime_value::{MoveStruct, MoveValue}, + u256::U256, +}; + +use super::{ + address::ParsedAddress, + parser::{Parser, Token}, +}; + +#[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), // Imported at the top of this file + U8(u8), + U16(u16), + U32(u32), + U64(u64), + U128(u128), + U256(U256), // Imported at the top of this file + 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| identifier::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), + } + } +} 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 000000000..ad6cfc115 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/runtime_value.rs @@ -0,0 +1,584 @@ +// Copyright (c) The Diem Core Contributors +// Copyright (c) The Move Contributors +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use super::{ + account_address::AccountAddress, annotated_value as A, fmt_list, u256, VARIANT_COUNT_MAX, +}; +use anyhow::{anyhow, Result as AResult}; +// use move_proc_macros::test_variant_order; +use serde::{ + de::Error as DeError, + ser::{SerializeSeq, SerializeTuple}, + Deserialize, Serialize, +}; +use std::fmt::{self, Debug}; + +/// 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 struct MoveVariant { + pub tag: u16, + pub fields: 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), + Variant(MoveVariant), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MoveStructLayout(pub Box>); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MoveEnumLayout(pub Box>>); + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum MoveDatatypeLayout { + Struct(Box), + Enum(Box), +} + +impl MoveDatatypeLayout { + pub fn into_layout(self) -> MoveTypeLayout { + match self { + MoveDatatypeLayout::Struct(layout) => MoveTypeLayout::Struct(layout), + MoveDatatypeLayout::Enum(layout) => MoveTypeLayout::Enum(layout), + } + } +} + +#[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(Box), + #[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, + #[serde(rename(serialize = "enum", deserialize = "enum"))] + Enum(Box), +} + +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::Variant(s), A::MoveTypeLayout::Enum(l)) => { + A::MoveValue::Variant(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.iter()) + .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 MoveVariant { + pub fn new(tag: u16, fields: Vec) -> Self { + Self { tag, fields } + } + + pub fn simple_deserialize(blob: &[u8], ty: &MoveEnumLayout) -> AResult { + Ok(bcs::from_bytes_seed(ty, blob)?) + } + + pub fn decorate(self, layout: &A::MoveEnumLayout) -> A::MoveVariant { + let MoveVariant { tag, fields } = self; + let A::MoveEnumLayout { type_, variants } = layout; + let ((v_name, _), v_layout) = variants + .iter() + .find(|((_, v_tag), _)| *v_tag == tag) + .unwrap(); + A::MoveVariant { + type_: type_.clone(), + tag, + fields: fields + .into_iter() + .zip(v_layout.iter()) + .map(|(v, l)| (l.name.clone(), v.decorate(&l.layout))) + .collect(), + variant_name: v_name.clone(), + } + } + + pub fn fields(&self) -> &[MoveValue] { + &self.fields + } + + pub fn into_fields(self) -> Vec { + self.fields + } +} + +impl MoveStructLayout { + pub fn new(types: Vec) -> Self { + Self(Box::new(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::Enum(ty) => Ok(MoveValue::Variant(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<'d> serde::de::DeserializeSeed<'d> for &MoveEnumLayout { + type Value = MoveVariant; + fn deserialize>( + self, + deserializer: D, + ) -> Result { + deserializer.deserialize_tuple(2, EnumFieldVisitor(&self.0)) + } +} + +struct EnumFieldVisitor<'a>(&'a Vec>); + +impl<'d, 'a> serde::de::Visitor<'d> for EnumFieldVisitor<'a> { + type Value = MoveVariant; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("Enum") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'d>, + { + let tag = match seq.next_element_seed(&MoveTypeLayout::U8)? { + Some(MoveValue::U8(tag)) if tag as u64 <= VARIANT_COUNT_MAX => tag as u16, + Some(MoveValue::U8(tag)) => return Err(A::Error::invalid_length(tag as usize, &self)), + Some(val) => { + return Err(A::Error::invalid_type( + serde::de::Unexpected::Other(&format!("{val:?}")), + &self, + )); + } + None => return Err(A::Error::invalid_length(0, &self)), + }; + + let Some(variant_layout) = self.0.get(tag as usize) else { + return Err(A::Error::invalid_length(tag as usize, &self)); + }; + + let Some(fields) = seq.next_element_seed(&MoveVariantFieldLayout(variant_layout))? else { + return Err(A::Error::invalid_length(1, &self)); + }; + + Ok(MoveVariant { tag, fields }) + } +} + +struct MoveVariantFieldLayout<'a>(&'a [MoveTypeLayout]); + +impl<'d, 'a> serde::de::DeserializeSeed<'d> for &MoveVariantFieldLayout<'a> { + type Value = Vec; + + fn deserialize>( + self, + deserializer: D, + ) -> Result { + 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::Variant(v) => v.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 serde::Serialize for MoveVariant { + // Serialize a variant as: (tag, [fields...]) + // Since we restrict tags to be less than or equal to 127, the tag will always + // be a single byte in uleb encoding and we don't actually need to uleb + // encode it, but we can at a later date if we want/need to. + fn serialize(&self, serializer: S) -> Result { + let tag = if self.tag as u64 > VARIANT_COUNT_MAX { + return Err(serde::ser::Error::custom(format!( + "Variant tag {} is greater than the maximum allowed value of {}", + self.tag, VARIANT_COUNT_MAX + ))); + } else { + self.tag as u8 + }; + + let mut t = serializer.serialize_tuple(2)?; + + t.serialize_element(&tag)?; + t.serialize_element(&MoveFields(&self.fields))?; + + t.end() + } +} + +struct MoveFields<'a>(&'a [MoveValue]); + +impl<'a> serde::Serialize for MoveFields<'a> { + 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}"), + Enum(e) if f.alternate() => write!(f, "{e:#}"), + Enum(e) => write!(f, "{e}"), + } + } +} + +/// 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 MoveEnumLayout { + fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { + write!(f, "enum ")?; + for (tag, variant) in self.0.iter().enumerate() { + write!(f, "variant_tag: {} {{ ", tag)?; + for (i, l) in variant.iter().enumerate() { + write!(f, "{}: {}, ", i, l)? + } + write!(f, " }} ")?; + } + Ok(()) + } +} + +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), + MoveValue::Variant(v) => fmt::Display::fmt(v, f), + } + } +} + +impl fmt::Display for MoveStruct { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt_list(f, "struct[", &self.0, "]") + } +} + +impl fmt::Display for MoveVariant { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt_list( + f, + &format!("variant(tag = {})[", self.tag), + &self.fields, + "]", + ) + } +} 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 000000000..82556bd04 --- /dev/null +++ b/identity_iota_interaction/src/sdk_types/move_core_types/u256.rs @@ -0,0 +1,391 @@ +// 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, + }, +}; + +// This U256 impl was chosen for now but we are open to changing it as needed +use primitive_types::U256 as PrimitiveU256; + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use uint::FromStrRadixErr; + +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 from 32 big endian bytes + pub fn from_be_bytes(slice: &[u8; U256_NUM_BYTES]) -> Self { + Self(PrimitiveU256::from_big_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 + } + + /// U256 to 32 big endian bytes + pub fn to_be_bytes(self) -> [u8; U256_NUM_BYTES] { + let mut bytes = [0u8; U256_NUM_BYTES]; + self.0.to_big_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 000000000..03ff73711 --- /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., 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, + Consensus = 1, +} + +// 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 key. */ + BridgeEventUnused = 6, // for bridge purposes but it's currently not included in messages. + ConsensusBlock = 7, // 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 Intent { + pub fn to_bytes(&self) -> [u8; INTENT_PREFIX_LENGTH] { + [self.scope as u8, self.version as u8, self.app_id as u8] + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() != INTENT_PREFIX_LENGTH { + return Err(eyre!("Invalid Intent")); + } + Ok(Self { + scope: bytes[0].try_into()?, + version: bytes[1].try_into()?, + app_id: bytes[2].try_into()?, + }) + } +} + +impl FromStr for Intent { + type Err = eyre::Report; + fn from_str(s: &str) -> Result { + let bytes: Vec = decode_bytes_hex(s).map_err(|_| eyre!("Invalid Intent"))?; + Self::from_bytes(bytes.as_slice()) + } +} + +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 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, +} 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 000000000..db49c84ed --- /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 000000000..e6bdcc9f2 --- /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; +} From a1f6c38a7871ad40af588f3653c36f12bb0e2a78 Mon Sep 17 00:00:00 2001 From: chrisgitiota Date: Thu, 24 Apr 2025 19:21:06 +0200 Subject: [PATCH 03/10] Removed all resources that shall be provided by product-core --- Cargo.toml | 8 +- .../Cargo.toml | 6 +- .../LICENSE | 0 .../README.md | 0 .../lib/index.ts | 0 .../lib/iota_client_helpers.ts | 0 .../lib/move_calls/asset/create.ts | 0 .../lib/move_calls/asset/delete.ts | 0 .../lib/move_calls/asset/index.ts | 0 .../lib/move_calls/asset/transfer.ts | 0 .../lib/move_calls/asset/update.ts | 0 .../lib/move_calls/identity/borrow_asset.ts | 0 .../lib/move_calls/identity/config.ts | 0 .../identity/controller_execution.ts | 0 .../lib/move_calls/identity/create.ts | 0 .../lib/move_calls/identity/index.ts | 0 .../lib/move_calls/identity/proposal.ts | 0 .../lib/move_calls/identity/send_asset.ts | 0 .../lib/move_calls/identity/update.ts | 0 .../lib/move_calls/identity/upgrade.ts | 0 .../lib/move_calls/index.ts | 0 .../lib/move_calls/migration.ts | 0 .../lib/move_calls/utils.ts | 0 .../lib/tsconfig.json | 0 .../lib/tsconfig.web.json | 0 .../package-lock.json | 0 .../package.json | 0 .../src/asset_move_calls.rs | 0 .../src/bindings/keytool/mod.rs | 0 .../src/bindings/keytool/signer.rs | 0 .../src/bindings/keytool/storage.rs | 0 .../src/bindings/mod.rs | 0 .../src/bindings/types.rs | 0 .../src/bindings/wasm_iota_client.rs | 0 .../src/bindings/wasm_types.rs | 0 .../src/common/macros.rs | 0 .../src/common/mod.rs | 0 .../src/common/types.rs | 0 .../src/common/utils.rs | 0 .../src/error.rs | 0 .../src/identity_move_calls.rs | 0 .../src/iota_client_ts_sdk.rs | 0 .../src/lib.rs | 0 .../src/migration_move_calls.rs | 0 .../src/transaction_builder.rs | 0 .../tsconfig.json | 0 .../tsconfig.node.json | 0 identity_core/src/common/object.rs | 10 - identity_iota/Cargo.toml | 4 +- identity_iota_core/Cargo.toml | 10 +- .../iota_client_rust_sdk.rs | 535 ---------- .../src/iota_interaction_rust/mod.rs | 41 - .../transaction_builder.rs | 53 - .../src/iota_interaction_rust/utils.rs | 122 --- .../asset_move_calls.rs | 0 .../identity_move_calls.rs | 0 .../migration_move_calls.rs | 0 .../src/iota_move_calls_rust/mod.rs | 10 + identity_iota_core/src/lib.rs | 4 +- identity_iota_interaction/README.md | 51 - .../src/effects_mut_api.rs | 94 -- .../src/iota_client_trait.rs | 257 ----- .../src/iota_verifiable_credential.rs | 39 - .../src/keytool/internal.rs | 126 --- identity_iota_interaction/src/keytool/mod.rs | 9 - .../src/keytool/signer.rs | 131 --- .../src/keytool/storage.rs | 145 --- identity_iota_interaction/src/move_type.rs | 75 -- .../src/sdk_types/error.rs | 37 - .../src/sdk_types/generated_types.rs | 270 ----- .../iota_json_rpc_types/iota_coin.rs | 36 - .../iota_json_rpc_types/iota_event.rs | 195 ---- .../iota_json_rpc_types/iota_move.rs | 346 ------- .../iota_json_rpc_types/iota_object.rs | 808 --------------- .../iota_json_rpc_types/iota_transaction.rs | 407 -------- .../src/sdk_types/iota_json_rpc_types/mod.rs | 27 - .../src/sdk_types/iota_types/balance.rs | 95 -- .../src/sdk_types/iota_types/base_types.rs | 814 --------------- .../src/sdk_types/iota_types/coin.rs | 186 ---- .../sdk_types/iota_types/collection_types.rs | 103 -- .../src/sdk_types/iota_types/crypto.rs | 685 ------------- .../src/sdk_types/iota_types/digests.rs | 470 --------- .../src/sdk_types/iota_types/dynamic_field.rs | 156 --- .../src/sdk_types/iota_types/error.rs | 943 ------------------ .../src/sdk_types/iota_types/event.rs | 55 - .../sdk_types/iota_types/execution_status.rs | 368 ------- .../src/sdk_types/iota_types/gas.rs | 63 -- .../src/sdk_types/iota_types/gas_coin.rs | 147 --- .../src/sdk_types/iota_types/governance.rs | 98 -- .../src/sdk_types/iota_types/id.rs | 108 -- .../src/sdk_types/iota_types/iota_serde.rs | 409 -------- .../sdk_types/iota_types/iota_types_lib.rs | 125 --- .../src/sdk_types/iota_types/mod.rs | 29 - .../src/sdk_types/iota_types/move_package.rs | 84 -- .../src/sdk_types/iota_types/object.rs | 110 -- .../iota_types/quorum_driver_types.rs | 12 - .../src/sdk_types/iota_types/stardust/mod.rs | 4 - .../iota_types/stardust/output/mod.rs | 6 - .../iota_types/stardust/output/nft.rs | 33 - .../src/sdk_types/iota_types/storage.rs | 28 - .../src/sdk_types/iota_types/timelock/mod.rs | 5 - .../sdk_types/iota_types/timelock/timelock.rs | 124 --- .../timelock/timelocked_staked_iota.rs | 85 -- .../src/sdk_types/iota_types/transaction.rs | 463 --------- .../src/sdk_types/mod.rs | 17 - .../move_core_types/account_address.rs | 337 ------- .../move_core_types/annotated_value.rs | 774 -------------- .../move_core_types/annotated_visitor.rs | 814 --------------- .../sdk_types/move_core_types/identifier.rs | 243 ----- .../move_core_types/language_storage.rs | 350 ------- .../src/sdk_types/move_core_types/mod.rs | 35 - .../move_core_types/parsing/address.rs | 202 ---- .../sdk_types/move_core_types/parsing/mod.rs | 11 - .../move_core_types/parsing/parser.rs | 470 --------- .../move_core_types/parsing/types.rs | 195 ---- .../move_core_types/parsing/values.rs | 320 ------ .../move_core_types/runtime_value.rs | 584 ----------- .../src/sdk_types/move_core_types/u256.rs | 391 -------- .../src/sdk_types/shared_crypto/intent.rs | 204 ---- .../src/sdk_types/shared_crypto/mod.rs | 4 - .../src/transaction_builder_trait.rs | 15 - .../Cargo.toml | 2 +- identity_iota_move_calls/README.md | 8 + .../src/lib.rs | 0 .../src/move_call_traits.rs | 0 identity_storage/Cargo.toml | 10 +- 126 files changed, 42 insertions(+), 14608 deletions(-) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/Cargo.toml (88%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/LICENSE (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/README.md (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/index.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/iota_client_helpers.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/asset/create.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/asset/delete.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/asset/index.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/asset/transfer.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/asset/update.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/identity/borrow_asset.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/identity/config.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/identity/controller_execution.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/identity/create.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/identity/index.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/identity/proposal.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/identity/send_asset.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/identity/update.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/identity/upgrade.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/index.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/migration.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/move_calls/utils.ts (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/tsconfig.json (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/lib/tsconfig.web.json (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/package-lock.json (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/package.json (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/asset_move_calls.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/bindings/keytool/mod.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/bindings/keytool/signer.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/bindings/keytool/storage.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/bindings/mod.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/bindings/types.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/bindings/wasm_iota_client.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/bindings/wasm_types.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/common/macros.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/common/mod.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/common/types.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/common/utils.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/error.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/identity_move_calls.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/iota_client_ts_sdk.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/lib.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/migration_move_calls.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/src/transaction_builder.rs (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/tsconfig.json (100%) rename bindings/wasm/{iota_interaction_ts => iota_move_calls_ts}/tsconfig.node.json (100%) delete mode 100644 identity_core/src/common/object.rs delete mode 100644 identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs delete mode 100644 identity_iota_core/src/iota_interaction_rust/mod.rs delete mode 100644 identity_iota_core/src/iota_interaction_rust/transaction_builder.rs delete mode 100644 identity_iota_core/src/iota_interaction_rust/utils.rs rename identity_iota_core/src/{iota_interaction_rust => iota_move_calls_rust}/asset_move_calls.rs (100%) rename identity_iota_core/src/{iota_interaction_rust => iota_move_calls_rust}/identity_move_calls.rs (100%) rename identity_iota_core/src/{iota_interaction_rust => iota_move_calls_rust}/migration_move_calls.rs (100%) create mode 100644 identity_iota_core/src/iota_move_calls_rust/mod.rs delete mode 100644 identity_iota_interaction/README.md delete mode 100644 identity_iota_interaction/src/effects_mut_api.rs delete mode 100644 identity_iota_interaction/src/iota_client_trait.rs delete mode 100644 identity_iota_interaction/src/iota_verifiable_credential.rs delete mode 100644 identity_iota_interaction/src/keytool/internal.rs delete mode 100644 identity_iota_interaction/src/keytool/mod.rs delete mode 100644 identity_iota_interaction/src/keytool/signer.rs delete mode 100644 identity_iota_interaction/src/keytool/storage.rs delete mode 100644 identity_iota_interaction/src/move_type.rs delete mode 100644 identity_iota_interaction/src/sdk_types/error.rs delete mode 100644 identity_iota_interaction/src/sdk_types/generated_types.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_coin.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_event.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_move.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_object.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_transaction.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_json_rpc_types/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/balance.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/base_types.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/coin.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/collection_types.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/crypto.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/digests.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/dynamic_field.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/error.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/event.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/execution_status.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/gas.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/gas_coin.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/governance.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/id.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/move_package.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/object.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/quorum_driver_types.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/stardust/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/storage.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/timelock/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/timelock/timelocked_staked_iota.rs delete mode 100644 identity_iota_interaction/src/sdk_types/iota_types/transaction.rs delete mode 100644 identity_iota_interaction/src/sdk_types/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/annotated_value.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/annotated_visitor.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/identifier.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/language_storage.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/mod.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/runtime_value.rs delete mode 100644 identity_iota_interaction/src/sdk_types/move_core_types/u256.rs delete mode 100644 identity_iota_interaction/src/sdk_types/shared_crypto/intent.rs delete mode 100644 identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs delete mode 100644 identity_iota_interaction/src/transaction_builder_trait.rs rename {identity_iota_interaction => identity_iota_move_calls}/Cargo.toml (98%) create mode 100644 identity_iota_move_calls/README.md rename {identity_iota_interaction => identity_iota_move_calls}/src/lib.rs (100%) rename {identity_iota_interaction => identity_iota_move_calls}/src/move_call_traits.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index e9870fa07..c96c8e8ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,14 +23,18 @@ members = [ "identity_ecdsa_verifier", "identity_eddsa_verifier", "examples", - "identity_iota_interaction", - "bindings/wasm/iota_interaction_ts", + "identity_iota_move_calls", + "bindings/wasm/iota_move_calls_ts", ] exclude = ["bindings/wasm/identity_wasm", "bindings/grpc"] [workspace.dependencies] bls12_381_plus = { version = "0.8.17" } +#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction" } +iota_interaction = { path = "../product-core/iota_interaction" } +#iota_interaction_rust = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_rust" } +iota_interaction_rust = { path = "../product-core/iota_interaction_rust" } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } serde_json = { version = "1.0", default-features = false } strum = { version = "0.25", default-features = false, features = ["std", "derive"] } diff --git a/bindings/wasm/iota_interaction_ts/Cargo.toml b/bindings/wasm/iota_move_calls_ts/Cargo.toml similarity index 88% rename from bindings/wasm/iota_interaction_ts/Cargo.toml rename to bindings/wasm/iota_move_calls_ts/Cargo.toml index 5e6ad0892..c9c60c243 100644 --- a/bindings/wasm/iota_interaction_ts/Cargo.toml +++ b/bindings/wasm/iota_move_calls_ts/Cargo.toml @@ -9,7 +9,7 @@ license = "Apache-2.0" publish = false readme = "README.md" resolver = "2" -description = "identity_iota_interaction Adapters using Web Assembly bindings." +description = "identity_iota_move_calls adapters using Web Assembly bindings." [lib] crate-type = ["cdylib", "rlib"] @@ -25,7 +25,7 @@ eyre = "0.6.12" fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", package = "fastcrypto" } futures = { version = "0.3" } identity_core = { version = "=1.6.0-alpha", path = "../../../identity_core" } -identity_iota_interaction = { version = "=1.6.0-alpha", path = "../../../identity_iota_interaction", default-features = false } +identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../../../identity_iota_move_calls", default-features = false } js-sys = { version = "0.3.61" } secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } serde = { version = "1.0", features = ["derive"] } @@ -54,4 +54,4 @@ empty_docs = "allow" [features] default = [] -keytool = ["identity_iota_interaction/keytool"] +keytool = ["iota_interaction/keytool"] diff --git a/bindings/wasm/iota_interaction_ts/LICENSE b/bindings/wasm/iota_move_calls_ts/LICENSE similarity index 100% rename from bindings/wasm/iota_interaction_ts/LICENSE rename to bindings/wasm/iota_move_calls_ts/LICENSE diff --git a/bindings/wasm/iota_interaction_ts/README.md b/bindings/wasm/iota_move_calls_ts/README.md similarity index 100% rename from bindings/wasm/iota_interaction_ts/README.md rename to bindings/wasm/iota_move_calls_ts/README.md diff --git a/bindings/wasm/iota_interaction_ts/lib/index.ts b/bindings/wasm/iota_move_calls_ts/lib/index.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/index.ts rename to bindings/wasm/iota_move_calls_ts/lib/index.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_move_calls_ts/lib/iota_client_helpers.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/iota_client_helpers.ts rename to bindings/wasm/iota_move_calls_ts/lib/iota_client_helpers.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/asset/create.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/asset/create.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/asset/create.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/asset/delete.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/asset/delete.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/asset/delete.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/index.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/asset/index.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/asset/index.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/asset/index.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/asset/transfer.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/asset/transfer.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/asset/transfer.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/asset/update.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/asset/update.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/asset/update.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/borrow_asset.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/identity/borrow_asset.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/borrow_asset.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/config.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/identity/config.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/config.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/controller_execution.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/identity/controller_execution.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/controller_execution.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/create.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/identity/create.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/create.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/index.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/index.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/identity/index.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/index.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/proposal.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/identity/proposal.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/proposal.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/send_asset.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/identity/send_asset.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/send_asset.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/update.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/identity/update.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/update.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/upgrade.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/identity/upgrade.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/identity/upgrade.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/index.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/index.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/index.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/index.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/migration.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/migration.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/migration.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts b/bindings/wasm/iota_move_calls_ts/lib/move_calls/utils.ts similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/move_calls/utils.ts rename to bindings/wasm/iota_move_calls_ts/lib/move_calls/utils.ts diff --git a/bindings/wasm/iota_interaction_ts/lib/tsconfig.json b/bindings/wasm/iota_move_calls_ts/lib/tsconfig.json similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/tsconfig.json rename to bindings/wasm/iota_move_calls_ts/lib/tsconfig.json diff --git a/bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json b/bindings/wasm/iota_move_calls_ts/lib/tsconfig.web.json similarity index 100% rename from bindings/wasm/iota_interaction_ts/lib/tsconfig.web.json rename to bindings/wasm/iota_move_calls_ts/lib/tsconfig.web.json diff --git a/bindings/wasm/iota_interaction_ts/package-lock.json b/bindings/wasm/iota_move_calls_ts/package-lock.json similarity index 100% rename from bindings/wasm/iota_interaction_ts/package-lock.json rename to bindings/wasm/iota_move_calls_ts/package-lock.json diff --git a/bindings/wasm/iota_interaction_ts/package.json b/bindings/wasm/iota_move_calls_ts/package.json similarity index 100% rename from bindings/wasm/iota_interaction_ts/package.json rename to bindings/wasm/iota_move_calls_ts/package.json diff --git a/bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/asset_move_calls.rs rename to bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/mod.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/mod.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/bindings/keytool/mod.rs rename to bindings/wasm/iota_move_calls_ts/src/bindings/keytool/mod.rs diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/signer.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/signer.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/bindings/keytool/signer.rs rename to bindings/wasm/iota_move_calls_ts/src/bindings/keytool/signer.rs diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/keytool/storage.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/storage.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/bindings/keytool/storage.rs rename to bindings/wasm/iota_move_calls_ts/src/bindings/keytool/storage.rs diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/mod.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/mod.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/bindings/mod.rs rename to bindings/wasm/iota_move_calls_ts/src/bindings/mod.rs diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/types.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/types.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/bindings/types.rs rename to bindings/wasm/iota_move_calls_ts/src/bindings/types.rs diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_iota_client.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/bindings/wasm_iota_client.rs rename to bindings/wasm/iota_move_calls_ts/src/bindings/wasm_iota_client.rs diff --git a/bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_types.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/bindings/wasm_types.rs rename to bindings/wasm/iota_move_calls_ts/src/bindings/wasm_types.rs diff --git a/bindings/wasm/iota_interaction_ts/src/common/macros.rs b/bindings/wasm/iota_move_calls_ts/src/common/macros.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/common/macros.rs rename to bindings/wasm/iota_move_calls_ts/src/common/macros.rs diff --git a/bindings/wasm/iota_interaction_ts/src/common/mod.rs b/bindings/wasm/iota_move_calls_ts/src/common/mod.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/common/mod.rs rename to bindings/wasm/iota_move_calls_ts/src/common/mod.rs diff --git a/bindings/wasm/iota_interaction_ts/src/common/types.rs b/bindings/wasm/iota_move_calls_ts/src/common/types.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/common/types.rs rename to bindings/wasm/iota_move_calls_ts/src/common/types.rs diff --git a/bindings/wasm/iota_interaction_ts/src/common/utils.rs b/bindings/wasm/iota_move_calls_ts/src/common/utils.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/common/utils.rs rename to bindings/wasm/iota_move_calls_ts/src/common/utils.rs diff --git a/bindings/wasm/iota_interaction_ts/src/error.rs b/bindings/wasm/iota_move_calls_ts/src/error.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/error.rs rename to bindings/wasm/iota_move_calls_ts/src/error.rs diff --git a/bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/identity_move_calls.rs rename to bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs diff --git a/bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs b/bindings/wasm/iota_move_calls_ts/src/iota_client_ts_sdk.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/iota_client_ts_sdk.rs rename to bindings/wasm/iota_move_calls_ts/src/iota_client_ts_sdk.rs diff --git a/bindings/wasm/iota_interaction_ts/src/lib.rs b/bindings/wasm/iota_move_calls_ts/src/lib.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/lib.rs rename to bindings/wasm/iota_move_calls_ts/src/lib.rs diff --git a/bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/migration_move_calls.rs rename to bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs diff --git a/bindings/wasm/iota_interaction_ts/src/transaction_builder.rs b/bindings/wasm/iota_move_calls_ts/src/transaction_builder.rs similarity index 100% rename from bindings/wasm/iota_interaction_ts/src/transaction_builder.rs rename to bindings/wasm/iota_move_calls_ts/src/transaction_builder.rs diff --git a/bindings/wasm/iota_interaction_ts/tsconfig.json b/bindings/wasm/iota_move_calls_ts/tsconfig.json similarity index 100% rename from bindings/wasm/iota_interaction_ts/tsconfig.json rename to bindings/wasm/iota_move_calls_ts/tsconfig.json diff --git a/bindings/wasm/iota_interaction_ts/tsconfig.node.json b/bindings/wasm/iota_move_calls_ts/tsconfig.node.json similarity index 100% rename from bindings/wasm/iota_interaction_ts/tsconfig.node.json rename to bindings/wasm/iota_move_calls_ts/tsconfig.node.json diff --git a/identity_core/src/common/object.rs b/identity_core/src/common/object.rs deleted file mode 100644 index 580840260..000000000 --- a/identity_core/src/common/object.rs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2020-2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::collections::BTreeMap; - -#[doc(inline)] -pub use serde_json::Value; - -/// An alias for an ordered map of key-[value][`Value`] pairs. -pub type Object = BTreeMap; diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 75c191088..864323d37 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -21,10 +21,10 @@ identity_storage = { version = "=1.6.0-alpha", path = "../identity_storage", def identity_verification = { version = "=1.6.0-alpha", path = "../identity_verification", default-features = false } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -identity_iota_interaction = { version = "=1.6.0-alpha", path = "../identity_iota_interaction" } +identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls" } [target.'cfg(target_arch = "wasm32")'.dependencies] -identity_iota_interaction = { version = "=1.6.0-alpha", path = "../identity_iota_interaction", default-features = false } +identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", default-features = false } [dev-dependencies] # required for doc test diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 42b5353c6..d12465989 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -43,7 +43,7 @@ secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag serde-aux = { version = "4.5.0", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -identity_iota_interaction = { version = "=1.6.0-alpha", path = "../identity_iota_interaction", optional = true } +identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", optional = true } iota-config = { git = "https://github.com/iotaledger/iota.git", package = "iota-config", tag = "v0.12.0-rc", optional = true } iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.12.0-rc", optional = true } move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.12.0-rc", optional = true } @@ -51,7 +51,7 @@ shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "sha tokio = { version = "1.43", default-features = false, features = ["macros", "sync", "rt", "process"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -identity_iota_interaction = { version = "=1.6.0-alpha", path = "../identity_iota_interaction", default-features = false, optional = true } +identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", default-features = false, optional = true } tokio = { version = "1.43", default-features = false, features = ["sync"] } # Dependency iota_interaction_ts is always used on wasm32 platform. It is not controlled by the "iota-client" feature # because it's unclear how to implement this. wasm32 build will most probably always use the "iota-client" feature @@ -82,7 +82,7 @@ iota-client = [ "dep:bcs", "dep:fastcrypto", "dep:identity_eddsa_verifier", - "dep:identity_iota_interaction", + "dep:identity_iota_move_calls", "dep:identity_jose", "dep:iota-config", "dep:iota-crypto", @@ -98,13 +98,13 @@ iota-client = [ revocation-bitmap = ["identity_credential/revocation-bitmap"] # Enables `Send` + `Sync` bounds for the storage and client interaction traits. -send-sync = ["send-sync-storage", "send-sync-client-ext", "identity_iota_interaction/send-sync-transaction"] +send-sync = ["send-sync-storage", "send-sync-client-ext", "iota_interaction/send-sync-transaction"] # 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 = [] keytool = [ - "identity_iota_interaction/keytool", + "iota_interaction/keytool", "identity_storage/keytool", "iota_interaction_ts/keytool", "iota-client", 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 deleted file mode 100644 index 115b07d06..000000000 --- a/identity_iota_core/src/iota_interaction_rust/iota_client_rust_sdk.rs +++ /dev/null @@ -1,535 +0,0 @@ -// 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 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::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::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::types::transaction::TransactionDataAPI as _; -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::OptionalSync; -use identity_iota_interaction::QuorumDriverTrait; -use identity_iota_interaction::ReadTrait; - -/// 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(crate) 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(&self) -> Option<&IotaTransactionBlockEffects> { - self.response.effects.as_ref() - } - - fn to_string(&self) -> String { - format!("{:?}", self.response) - } - - 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() - } - - fn digest(&self) -> Result { - Ok(self.response.digest) - } -} - -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: TransactionData, - signatures: Vec, - options: Option, - request_type: Option, - ) -> IotaRpcResult { - let tx = Transaction::from_data(tx_data, signatures); - 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( - &self, - tx_data: TransactionData, - signer: &S, - ) -> Result - where - S: Signer + OptionalSync, - { - let response = self.sdk_execute_transaction(tx_data, signer).await?; - Ok(Box::new(IotaTransactionBlockResponseProvider::new(response))) - } - - async fn default_gas_budget(&self, sender_address: IotaAddress, tx: &ProgrammableTransaction) -> Result { - 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, - tx: TransactionData, - signer: &S, - ) -> Result { - let public_key = signer - .public_key() - .await - .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; - let sender_address = IotaAddress::from(&public_key); - - if sender_address != tx.sender() { - return Err(Error::TransactionSigningFailed(format!("transaction data needs to be signed by address {}, but client can only provide signature for address {sender_address}", tx.sender()))); - } - - let signature = signer - .sign(&tx) - .await - .map_err(|e| Error::TransactionSigningFailed(e.to_string()))?; - - // execute tx - let response = self - .iota_client - .quorum_driver_api() - .execute_transaction_block( - Transaction::from_data(tx, 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_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 coin found with minimum required balance of {} for address {}", - MINIMUM_BALANCE, 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/mod.rs b/identity_iota_core/src/iota_interaction_rust/mod.rs deleted file mode 100644 index ab6ed93f5..000000000 --- a/identity_iota_core/src/iota_interaction_rust/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -// 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 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; diff --git a/identity_iota_core/src/iota_interaction_rust/transaction_builder.rs b/identity_iota_core/src/iota_interaction_rust/transaction_builder.rs deleted file mode 100644 index 9854e1893..000000000 --- a/identity_iota_core/src/iota_interaction_rust/transaction_builder.rs +++ /dev/null @@ -1,53 +0,0 @@ -// 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 deleted file mode 100644 index 357d3647a..000000000 --- a/identity_iota_core/src/iota_interaction_rust/utils.rs +++ /dev/null @@ -1,122 +0,0 @@ -// 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/iota_interaction_rust/asset_move_calls.rs b/identity_iota_core/src/iota_move_calls_rust/asset_move_calls.rs similarity index 100% rename from identity_iota_core/src/iota_interaction_rust/asset_move_calls.rs rename to identity_iota_core/src/iota_move_calls_rust/asset_move_calls.rs diff --git a/identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs b/identity_iota_core/src/iota_move_calls_rust/identity_move_calls.rs similarity index 100% rename from identity_iota_core/src/iota_interaction_rust/identity_move_calls.rs rename to identity_iota_core/src/iota_move_calls_rust/identity_move_calls.rs diff --git a/identity_iota_core/src/iota_interaction_rust/migration_move_calls.rs b/identity_iota_core/src/iota_move_calls_rust/migration_move_calls.rs similarity index 100% rename from identity_iota_core/src/iota_interaction_rust/migration_move_calls.rs rename to identity_iota_core/src/iota_move_calls_rust/migration_move_calls.rs diff --git a/identity_iota_core/src/iota_move_calls_rust/mod.rs b/identity_iota_core/src/iota_move_calls_rust/mod.rs new file mode 100644 index 000000000..76fa6c7b9 --- /dev/null +++ b/identity_iota_core/src/iota_move_calls_rust/mod.rs @@ -0,0 +1,10 @@ +// 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 migration_move_calls; + +pub(crate) use asset_move_calls::AssetMoveCallsRustSdk as AssetMoveCallsAdapter; +pub(crate) use identity_move_calls::IdentityMoveCallsRustSdk as IdentityMoveCallsAdapter; +pub(crate) use migration_move_calls::MigrationMoveCallsRustSdk as MigrationMoveCallsAdapter; \ No newline at end of file diff --git a/identity_iota_core/src/lib.rs b/identity_iota_core/src/lib.rs index 012fe35d9..9b0694922 100644 --- a/identity_iota_core/src/lib.rs +++ b/identity_iota_core/src/lib.rs @@ -35,8 +35,8 @@ 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; +/// IOTA Rust SDK based implementation of the identity_iota_move_calls interface for non wasm targets. +mod iota_move_calls_rust; #[cfg(feature = "iota-client")] /// Contains the rebased Identity and the interaction with the IOTA Client. pub mod rebased; diff --git a/identity_iota_interaction/README.md b/identity_iota_interaction/README.md deleted file mode 100644 index abee8ad9e..000000000 --- a/identity_iota_interaction/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# 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/effects_mut_api.rs b/identity_iota_interaction/src/effects_mut_api.rs deleted file mode 100644 index 83b1879be..000000000 --- a/identity_iota_interaction/src/effects_mut_api.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::rpc_types::IotaObjectRef; -use crate::rpc_types::IotaTransactionBlockEffects; -use crate::rpc_types::IotaTransactionBlockEffectsAPI; -use crate::rpc_types::IotaTransactionBlockEffectsV1; -use crate::rpc_types::OwnedObjectRef; - -/// A mutable version of [IotaTransactionBlockEffectsAPI] that allows the -/// in-place mutation of [IotaTransactionBlockEffects] -pub trait IotaTransactionBlockEffectsMutAPI: IotaTransactionBlockEffectsAPI { - fn shared_objects_mut(&mut self) -> &mut Vec; - fn created_mut(&mut self) -> &mut Vec; - fn mutated_mut(&mut self) -> &mut Vec; - fn unwrapped_mut(&mut self) -> &mut Vec; - fn deleted_mut(&mut self) -> &mut Vec; - fn unwrapped_then_deleted_mut(&mut self) -> &mut Vec; - fn wrapped_mut(&mut self) -> &mut Vec; -} - -impl IotaTransactionBlockEffectsMutAPI for IotaTransactionBlockEffectsV1 { - fn shared_objects_mut(&mut self) -> &mut Vec { - &mut self.shared_objects - } - - fn created_mut(&mut self) -> &mut Vec { - &mut self.created - } - - fn mutated_mut(&mut self) -> &mut Vec { - &mut self.mutated - } - - fn unwrapped_mut(&mut self) -> &mut Vec { - &mut self.unwrapped - } - - fn deleted_mut(&mut self) -> &mut Vec { - &mut self.deleted - } - - fn unwrapped_then_deleted_mut(&mut self) -> &mut Vec { - &mut self.unwrapped_then_deleted - } - - fn wrapped_mut(&mut self) -> &mut Vec { - &mut self.wrapped - } -} - -impl IotaTransactionBlockEffectsMutAPI for IotaTransactionBlockEffects { - fn shared_objects_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.shared_objects, - } - } - - fn created_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.created, - } - } - - fn mutated_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.mutated, - } - } - - fn unwrapped_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.unwrapped, - } - } - - fn deleted_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.deleted, - } - } - - fn unwrapped_then_deleted_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.unwrapped_then_deleted, - } - } - - fn wrapped_mut(&mut self) -> &mut Vec { - match self { - Self::V1(effects) => &mut effects.wrapped, - } - } -} diff --git a/identity_iota_interaction/src/iota_client_trait.rs b/identity_iota_interaction/src/iota_client_trait.rs deleted file mode 100644 index acec57a1c..000000000 --- a/identity_iota_interaction/src/iota_client_trait.rs +++ /dev/null @@ -1,257 +0,0 @@ -// 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::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::IotaTransactionBlockEffects; -use crate::rpc_types::IotaTransactionBlockResponseOptions; -use crate::rpc_types::ObjectsPage; -use crate::types::base_types::IotaAddress; -use crate::types::base_types::ObjectID; -use crate::types::base_types::SequenceNumber; -use crate::types::crypto::PublicKey; -use crate::types::crypto::Signature; -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::types::transaction::ProgrammableTransaction; -use crate::types::transaction::TransactionData; -use crate::OptionalSend; -use async_trait::async_trait; -use secret_storage::SignatureScheme as SignatureSchemeSecretStorage; -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; - -#[cfg(feature = "send-sync-transaction")] -use crate::OptionalSync; - -pub struct IotaKeySignature { - pub public_key: PublicKey, - pub signature: Signature, -} - -impl SignatureSchemeSecretStorage for IotaKeySignature { - type PublicKey = PublicKey; - type Signature = Signature; - type Input = TransactionData; -} - -//******************************************************************** -// 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; - - /// Returns Debug representation of the IotaTransactionBlockResponse - fn to_string(&self) -> String; - - /// Returns the effects of this transaction - fn effects(&self) -> Option<&IotaTransactionBlockEffects>; - - /// 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; - - // Returns digest for transaction block. - fn digest(&self) -> Result; -} - -#[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: TransactionData, - signatures: Vec, - 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, - tx_data: TransactionData, - signer: &S, - ) -> Result< - Box>, - Self::Error, - >; - #[cfg(feature = "send-sync-transaction")] - async fn execute_transaction + OptionalSync>( - &self, - tx_data: TransactionData, - signer: &S, - ) -> Result< - Box>, - Self::Error, - >; - - async fn default_gas_budget( - &self, - sender_address: IotaAddress, - tx: &ProgrammableTransaction, - ) -> 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 deleted file mode 100644 index 7f2ab9dcd..000000000 --- a/identity_iota_interaction/src/iota_verifiable_credential.rs +++ /dev/null @@ -1,39 +0,0 @@ -// 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, PartialEq, Eq)] -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/keytool/internal.rs b/identity_iota_interaction/src/keytool/internal.rs deleted file mode 100644 index e09c206c3..000000000 --- a/identity_iota_interaction/src/keytool/internal.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::path::Path; -use std::path::PathBuf; -use std::str::FromStr as _; - -use anyhow::anyhow; -use anyhow::Context as _; -use fastcrypto::traits::EncodeDecodeBase64 as _; -use jsonpath_rust::JsonPathQuery as _; -use serde::Deserialize; -use serde_json::Value; - -use crate::types::base_types::IotaAddress; -use crate::types::crypto::PublicKey; - -#[derive(Debug, Clone)] -pub(super) struct IotaCliWrapper { - iota_bin: PathBuf, -} - -impl Default for IotaCliWrapper { - fn default() -> Self { - Self { - iota_bin: PathBuf::from_str("iota").expect("infallible"), - } - } -} - -impl IotaCliWrapper { - /// Creates a new [IotaCliWrapper] that will use the iota binary found at - /// the provided path. - pub fn new_with_custom_bin(iota_bin: impl AsRef) -> Self { - Self { - iota_bin: iota_bin.as_ref().to_owned(), - } - } - - /// Returns the location of the iota binary used. - pub fn iota_bin(&self) -> &Path { - &self.iota_bin - } - - /// Executes a given "iota" command with the provided string-encoded args. - /// Returns the parsed JSON output. - pub fn run_command(&self, args: &str) -> anyhow::Result { - cfg_if::cfg_if! { - if #[cfg(not(target_arch = "wasm32"))] { - let output = std::process::Command::new(&self.iota_bin) - .args(args.split_ascii_whitespace()) - .arg("--json") - .output() - .map_err(|e| anyhow!("failed to run command: {e}"))?; - - if !output.status.success() { - let err_msg = - String::from_utf8(output.stderr).map_err(|e| anyhow!("command failed with non-utf8 error message: {e}"))?; - return Err(anyhow!("failed to run keytool cmd: {err_msg}")); - } - - let trimmed_output = { - let start_of_json = output.stdout.iter().enumerate().find_map(|(i, b)| matches!(*b, b'[' | b'{' | b'\"').then_some(i)).context("no JSON in command output")?; - &output.stdout[start_of_json..] - }; - - serde_json::from_slice(trimmed_output).context("invalid JSON object in command output") - } else { - extern "Rust" { - fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result; - } - let iota_bin = self.iota_bin.to_str().context("invalid IOTA bin path")?; - let cmd = format!("{iota_bin} {args} --json"); - unsafe { __wasm_exec_iota_cmd(&cmd) } - } - } - } - - /// Returns the current active address. - pub fn get_active_address(&self) -> anyhow::Result { - self - .run_command("client active-address") - .and_then(|value| serde_json::from_value(value).context("failed to parse IotaAddress from output")) - } - - fn get_key_impl(&self, json_path_query: &str) -> anyhow::Result> { - let Some(pk_json_data) = self - .run_command("keytool list")? - .path(json_path_query) - .map_err(|e| anyhow!("failed to query JSON output: {e}"))? - .get_mut(0) - .map(Value::take) - else { - return Ok(None); - }; - - let KeytoolPublicKeyHelper { - public_base64_key_with_flag, - alias, - .. - } = serde_json::from_value(pk_json_data)?; - - let pk = PublicKey::decode_base64(&public_base64_key_with_flag).map_err(|e| anyhow!("{e:?}"))?; - - Ok(Some((pk, alias))) - } - - /// Returns the public key of a given address, if any. - pub fn get_key(&self, address: IotaAddress) -> anyhow::Result> { - let query = format!("$[?(@.iotaAddress==\"{}\")]", address); - self.get_key_impl(&query) - } - - /// Returns the public key with the given alias, if any. - pub fn get_key_by_alias(&self, alias: &str) -> anyhow::Result> { - let query = format!("$[?(@.alias==\"{}\")]", alias); - Ok(self.get_key_impl(&query)?.map(|(pk, _)| pk)) - } -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct KeytoolPublicKeyHelper { - alias: String, - public_base64_key_with_flag: String, -} diff --git a/identity_iota_interaction/src/keytool/mod.rs b/identity_iota_interaction/src/keytool/mod.rs deleted file mode 100644 index 2b1b517e3..000000000 --- a/identity_iota_interaction/src/keytool/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod internal; -mod signer; -mod storage; - -pub use signer::*; -pub use storage::*; diff --git a/identity_iota_interaction/src/keytool/signer.rs b/identity_iota_interaction/src/keytool/signer.rs deleted file mode 100644 index dc307e514..000000000 --- a/identity_iota_interaction/src/keytool/signer.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::path::Path; -use std::path::PathBuf; - -use crate::types::base_types::IotaAddress; -use crate::types::crypto::PublicKey; -use crate::types::crypto::Signature; -use crate::types::transaction::TransactionData; -use crate::IotaKeySignature; -use anyhow::anyhow; -use anyhow::Context as _; -use async_trait::async_trait; -use fastcrypto::encoding::Base64; -use fastcrypto::encoding::Encoding; -use secret_storage::Error as SecretStorageError; -use secret_storage::Signer; - -use super::internal::IotaCliWrapper; - -/// Builder structure to ease the creation of a [KeytoolSigner]. -#[derive(Debug, Default)] -pub struct KeytoolSignerBuilder { - address: Option, - iota_bin: Option, -} - -impl KeytoolSignerBuilder { - /// Returns a new [KeytoolSignerBuilder] with default configuration: - /// - use current active address; - /// - assumes `iota` binary to be in PATH; - pub fn new() -> Self { - Self::default() - } - - /// Sets the address the signer will use. - /// Defaults to current active address if no address is provided. - pub fn with_address(mut self, address: IotaAddress) -> Self { - self.address = Some(address); - self - } - - /// Sets the path to the `iota` binary to use. - /// Assumes `iota` to be in PATH if no value is provided. - pub fn iota_bin_location(mut self, path: impl AsRef) -> Self { - let path = path.as_ref().to_path_buf(); - self.iota_bin = Some(path); - - self - } - - /// Builds a new [KeytoolSigner] using the provided configuration. - pub fn build(self) -> anyhow::Result { - let KeytoolSignerBuilder { address, iota_bin } = self; - let iota_cli_wrapper = iota_bin.map(IotaCliWrapper::new_with_custom_bin).unwrap_or_default(); - let address = if let Some(address) = address { - address - } else { - iota_cli_wrapper.get_active_address()? - }; - - let public_key = iota_cli_wrapper.get_key(address)?.context("key doens't exist")?.0; - - Ok(KeytoolSigner { - public_key, - iota_cli_wrapper, - address, - }) - } -} - -/// IOTA Keytool [Signer] implementation. -#[derive(Debug)] -pub struct KeytoolSigner { - public_key: PublicKey, - iota_cli_wrapper: IotaCliWrapper, - address: IotaAddress, -} - -impl KeytoolSigner { - /// Returns a [KeytoolSignerBuilder]. - pub fn builder() -> KeytoolSignerBuilder { - KeytoolSignerBuilder::default() - } - - /// Returns the [IotaAddress] used by this [KeytoolSigner]. - pub fn address(&self) -> IotaAddress { - self.address - } - - /// Returns the [PublicKey] used by this [KeytoolSigner]. - pub fn public_key(&self) -> &PublicKey { - &self.public_key - } -} - -#[cfg_attr(feature = "send-sync-transaction", async_trait)] -#[cfg_attr(not(feature = "send-sync-transaction"), async_trait(?Send))] -impl Signer for KeytoolSigner { - type KeyId = IotaAddress; - - fn key_id(&self) -> Self::KeyId { - self.address - } - - async fn public_key(&self) -> Result { - Ok(self.public_key.clone()) - } - - async fn sign(&self, data: &TransactionData) -> Result { - let tx_data_bcs = - bcs::to_bytes(data).map_err(|e| SecretStorageError::Other(anyhow!("bcs serialization failed: {e}")))?; - let base64_data = Base64::encode(&tx_data_bcs); - let command = format!("keytool sign --address {} --data {base64_data}", self.address); - - self - .iota_cli_wrapper - .run_command(&command) - .and_then(|json| { - json - .get("iotaSignature") - .context("invalid JSON output: missing iotaSignature")? - .as_str() - .context("not a JSON string")? - .parse() - .map_err(|e| anyhow!("invalid IOTA signature: {e}")) - }) - .map_err(SecretStorageError::Other) - } -} diff --git a/identity_iota_interaction/src/keytool/storage.rs b/identity_iota_interaction/src/keytool/storage.rs deleted file mode 100644 index 6dc0f3c01..000000000 --- a/identity_iota_interaction/src/keytool/storage.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::path::Path; - -use anyhow::anyhow; -use anyhow::Context as _; -use fastcrypto::ed25519::Ed25519Signature; -use fastcrypto::secp256k1::Secp256k1Signature; -use fastcrypto::secp256r1::Secp256r1Signature; -use fastcrypto::traits::Signer; -use serde::Deserialize; - -use crate::types::base_types::IotaAddress; -use crate::types::crypto::IotaKeyPair; -use crate::types::crypto::PublicKey; -use crate::types::crypto::SignatureScheme as IotaSignatureScheme; - -use super::internal::IotaCliWrapper; -use super::KeytoolSignerBuilder; - -#[derive(Clone, Default)] -pub struct KeytoolStorage { - iota_cli_wrapper: IotaCliWrapper, -} - -impl KeytoolStorage { - /// Returns a new [KeytoolStorage] that will use the IOTA binary in PATH. - pub fn new() -> Self { - Self::default() - } - - /// Returns a new [KeytoolStorage] that will use the provided IOTA binary. - pub fn new_with_custom_bin(iota_bin: impl AsRef) -> Self { - Self { - iota_cli_wrapper: IotaCliWrapper::new_with_custom_bin(iota_bin), - } - } - - /// Returns a [KeytoolSignerBuilder] to construct a [super::KeytoolSigner] after - /// selecting an address. - pub fn signer(&self) -> KeytoolSignerBuilder { - KeytoolSignerBuilder::new().iota_bin_location(self.iota_cli_wrapper.iota_bin()) - } - - /// Generates a new keypair of type `key_scheme`. - /// Returns the resulting [PublicKey] together with its alias. - pub fn generate_key(&self, key_scheme: IotaSignatureScheme) -> anyhow::Result<(PublicKey, String)> { - if !matches!( - &key_scheme, - IotaSignatureScheme::ED25519 | IotaSignatureScheme::Secp256k1 | IotaSignatureScheme::Secp256r1 - ) { - anyhow::bail!("key scheme {key_scheme} is not supported by the keytool"); - } - - let cmd = format!("client new-address --key-scheme {key_scheme}"); - let KeyGenOutput { alias, address } = { - let json_output = self.iota_cli_wrapper.run_command(&cmd)?; - serde_json::from_value(json_output)? - }; - - let pk = self - .iota_cli_wrapper - .get_key(address)? - .ok_or_else(|| anyhow!("key for address {address} wasn't found"))? - .0; - - Ok((pk, alias)) - } - - /// Inserts a new key in this keytool. - /// Returns the alias assigned to the inserted key. - pub fn insert_key(&self, key: IotaKeyPair) -> anyhow::Result { - let bech32_encoded_key = key.encode().map_err(|e| anyhow!("{e:?}"))?; - let key_scheme = key.public().scheme().to_string(); - let cmd = format!("keytool import {bech32_encoded_key} {key_scheme}"); - - let json_output = self.iota_cli_wrapper.run_command(&cmd)?; - let KeyGenOutput { alias, .. } = serde_json::from_value(json_output)?; - - Ok(alias) - } - - /// Uses the private key corresponding to [IotaAddress] `address` to sign `data`. - /// ## Notes - /// - SHA-512 is used to produce signatures when the key is ed25519. - /// - SHA-256 is used otherwise. - pub fn sign_raw(&self, address: IotaAddress, data: impl AsRef<[u8]>) -> anyhow::Result> { - let cmd = format!("keytool export {address}"); - let keypair = { - let json_output = self.iota_cli_wrapper.run_command(&cmd)?; - let KeyExportOutput { - exported_private_key: bech32_encoded_sk, - } = serde_json::from_value(json_output)?; - - IotaKeyPair::decode(&bech32_encoded_sk).map_err(|e| anyhow!("failed to decode private key: {e:?}"))? - }; - let data = data.as_ref(); - - let sig = match keypair { - IotaKeyPair::Ed25519(sk) => Signer::::sign(&sk, data).sig.to_bytes().to_vec(), - IotaKeyPair::Secp256r1(sk) => Signer::::sign(&sk, data).sig.to_vec(), - IotaKeyPair::Secp256k1(sk) => { - let sig = Signer::::sign(&sk, data); - sig.as_ref().to_vec() - } - }; - - Ok(sig) - } - - /// Updates an alias from `old_alias` to `new_alias` - /// If no value for `new_alias` is provided, a randomly generated one will be used. - pub fn update_alias(&self, old_alias: &str, new_alias: Option<&str>) -> anyhow::Result<()> { - let cmd = format!("keytool update-alias {old_alias} {}", new_alias.unwrap_or_default()); - self - .iota_cli_wrapper - .run_command(&cmd) - .context("failed to update alias")?; - - Ok(()) - } - - /// Returns the [PublicKey] for the given [IotaAddress] together with its alias. - pub fn get_key(&self, address: IotaAddress) -> anyhow::Result> { - self.iota_cli_wrapper.get_key(address) - } - - /// Returns the [PublicKey] that has the given alias, if any. - pub fn get_key_by_alias(&self, alias: &str) -> anyhow::Result> { - self.iota_cli_wrapper.get_key_by_alias(alias) - } -} - -#[derive(Deserialize)] -struct KeyGenOutput { - alias: String, - address: IotaAddress, -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct KeyExportOutput { - exported_private_key: String, -} diff --git a/identity_iota_interaction/src/move_type.rs b/identity_iota_interaction/src/move_type.rs deleted file mode 100644 index ac49e6ee4..000000000 --- a/identity_iota_interaction/src/move_type.rs +++ /dev/null @@ -1,75 +0,0 @@ -// 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 deleted file mode 100644 index 1448df9a3..000000000 --- a/identity_iota_interaction/src/sdk_types/error.rs +++ /dev/null @@ -1,37 +0,0 @@ -// 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 funds for address [{address}], requested amount: {amount}")] - InsufficientFunds { address: IotaAddress, amount: u128 }, - #[error(transparent)] - Json(#[from] serde_json::Error), - #[error("Error caused by a foreign function interface call: {0}")] - FfiError(String), // Added for IOTA interaction -} \ 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 deleted file mode 100644 index 03319749d..000000000 --- a/identity_iota_interaction/src/sdk_types/generated_types.rs +++ /dev/null @@ -1,270 +0,0 @@ -// 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 super::types::crypto::Signature; -use super::types::transaction::TransactionData; - -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; - -// 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_data: TransactionData, - signatures: Vec, - options: Option, - request_type: Option, - ) -> Self { - let tx_data_bcs = bcs::to_bytes(&tx_data).expect("this serialization cannot fail"); - let signatures_b64 = signatures - .into_iter() - .map(|sig| Base64::from_bytes(sig.as_ref())) - .collect(); - ExecuteTransactionBlockParams { - transaction_block: Base64::from_bytes(&tx_data_bcs), - signature: signatures_b64, - 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 - #[serde(skip_serializing_if = "Option::is_none")] - 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, - } - } -} - -/// Params for `wait_for_transaction` / `wait_for_transaction`. -/// -/// Be careful when serializing with `serde_wasm_bindgen::to_value`, as `#[serde(flatten)]` -/// will turn the object into a `Map` instead of a plain object in Js. -/// Prefer serializing with `serde_wasm_bindgen::Serializer::json_compatible` or perform custom serialization. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct WaitForTransactionParams { - /// Block digest and options for content that should be returned. - #[serde(flatten)] - get_transaction_block_params: GetTransactionBlockParams, - /// The amount of time to wait for a transaction block. Defaults to one minute. - #[serde(skip_serializing_if = "Option::is_none")] - timeout: Option, - /// The amount of time to wait between checks for the transaction block. Defaults to 2 seconds. - #[serde(skip_serializing_if = "Option::is_none")] - poll_interval: Option, -} - -impl WaitForTransactionParams { - pub fn new( - digest: String, - options: Option, - timeout: Option, - poll_interval: Option, - ) -> Self { - WaitForTransactionParams { - get_transaction_block_params: GetTransactionBlockParams::new(digest, options), - timeout, - poll_interval, - } - } -} 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 deleted file mode 100644 index 818dbb4fd..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_coin.rs +++ /dev/null @@ -1,36 +0,0 @@ -// 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 deleted file mode 100644 index 339386eb0..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_event.rs +++ /dev/null @@ -1,195 +0,0 @@ -// 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, Base64}; - -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, - /// Base64 encoded bcs bytes of the move event - #[serde(flatten)] - pub bcs: BcsEvent, - /// 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(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase", tag = "bcsEncoding")] -#[serde(from = "MaybeTaggedBcsEvent")] -pub enum BcsEvent { - Base64 { - #[serde_as(as = "Base64")] - bcs: Vec, - }, - Base58 { - #[serde_as(as = "Base58")] - bcs: Vec, - }, -} - -impl BcsEvent { - pub fn new(bytes: Vec) -> Self { - Self::Base64 { bcs: bytes } - } - - pub fn bytes(&self) -> &[u8] { - match self { - BcsEvent::Base64 { bcs } => bcs.as_ref(), - BcsEvent::Base58 { bcs } => bcs.as_ref(), - } - } - - pub fn into_bytes(self) -> Vec { - match self { - BcsEvent::Base64 { bcs } => bcs, - BcsEvent::Base58 { bcs } => bcs, - } - } -} - -#[allow(unused)] -#[serde_as] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase", untagged)] -enum MaybeTaggedBcsEvent { - Tagged(TaggedBcsEvent), - Base58 { - #[serde_as(as = "Base58")] - bcs: Vec, - }, -} - -#[serde_as] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase", tag = "bcsEncoding")] -enum TaggedBcsEvent { - Base64 { - #[serde_as(as = "Base64")] - bcs: Vec, - }, - Base58 { - #[serde_as(as = "Base58")] - bcs: Vec, - }, -} - -impl From for BcsEvent { - fn from(event: MaybeTaggedBcsEvent) -> BcsEvent { - let bcs = match event { - MaybeTaggedBcsEvent::Tagged(TaggedBcsEvent::Base58 { bcs }) - | MaybeTaggedBcsEvent::Base58 { bcs } => bcs, - MaybeTaggedBcsEvent::Tagged(TaggedBcsEvent::Base64 { bcs }) => bcs, - }; - - // Bytes are already decoded, force into Base64 variant to avoid serializing to - // base58 - Self::Base64 { bcs } - } -} - -#[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 deleted file mode 100644 index fa5e6b996..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_move.rs +++ /dev/null @@ -1,346 +0,0 @@ -// 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, MoveVariant}, - 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>), - Variant(IotaMoveVariant), -} - -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), - IotaMoveValue::Variant(v) => v.to_json_value(), - } - } -} - -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") - )?; - } - IotaMoveValue::Variant(value) => write!(writer, "{value}")?, - } - 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))) - } - MoveValue::Variant(MoveVariant { - type_, - variant_name, - tag: _, - fields, - }) => IotaMoveValue::Variant(IotaMoveVariant { - type_: type_.clone(), - variant: variant_name.to_string(), - fields: fields - .into_iter() - .map(|(id, value)| (id.into_string(), value.into())) - .collect::>(), - }), - } - } -} - -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(rename = "MoveVariant")] -pub struct IotaMoveVariant { - #[serde(rename = "type")] - #[serde_as(as = "IotaStructTag")] - pub type_: StructTag, - pub variant: String, - pub fields: BTreeMap, -} - -impl IotaMoveVariant { - pub fn to_json_value(self) -> Value { - // We only care about values here, assuming type information is known at the - // client side. - let fields = self - .fields - .into_iter() - .map(|(key, value)| (key, value.to_json_value())) - .collect::>(); - json!({ - "variant": self.variant, - "fields": fields, - }) - } -} - -impl Display for IotaMoveVariant { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut writer = String::new(); - let IotaMoveVariant { - type_, - variant, - fields, - } = self; - writeln!(writer)?; - writeln!(writer, " {}: {type_}", "type")?; - writeln!(writer, " {}: {variant}", "variant")?; - 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')) - } -} - -#[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(), - } - } -} 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 deleted file mode 100644 index 41e84546a..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_object.rs +++ /dev/null @@ -1,808 +0,0 @@ -// 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 schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use serde_with::{DisplayFromStr, serde_as}; -use serde_json::Value; - -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 fastcrypto::encoding::Base64; - -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 - #[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", - )?; - } - if let Some(content) = self.content.as_ref() { - writeln!(writer, "{}", "----- Data -----")?; - write!(writer, "{content}")?; - } - - 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, JsonSchema)] -#[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 dependencies - // 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; - - // try_from_object() and try_from_package() are not defined here because - // MoveObject and MoveStructLayout introduce too many dependencies - - 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; - - // try_from_object() and try_from_package() are not defined here because - // MoveObject and MoveStructLayout introduce too many dependencies - - 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 fields: IotaMoveStruct, -} - -impl IotaParsedMoveObject { - // try_from_object_read()is not defined here because - // MoveObject introduces too many dependencies - - 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 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), - } - } -} 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 deleted file mode 100644 index cc2ffcf40..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/iota_transaction.rs +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::{ - vec::Vec, - fmt::{self, Display, Formatter}, -}; - -use enum_dispatch::enum_dispatch; -use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use serde_with::serde_as; - -use crate::{iota_types::{base_types::EpochId, digests::{TransactionDigest, TransactionEventsDigest}, gas::GasCostSummary, storage::{DeleteKind, WriteKind}}, types::{ - base_types::{ObjectID, SequenceNumber}, execution_status::ExecutionStatus, object::Owner, quorum_driver_types::ExecuteTransactionRequestType, - iota_serde::{BigInt, SequenceNumber as AsSequenceNumber}, -}}; - -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_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, JsonSchema)] -#[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 Display for IotaExecutionStatus { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Self::Success => write!(f, "success"), - Self::Failure { error } => write!(f, "failure due to {error}"), - } - } -} - -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, JsonSchema)] -#[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 - } -} - -#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)] -#[enum_dispatch(IotaTransactionBlockEffectsAPI)] -#[serde( - rename = "TransactionBlockEffects", - rename_all = "camelCase", - tag = "messageVersion" -)] -pub enum IotaTransactionBlockEffects { - V1(IotaTransactionBlockEffectsV1), -} - -#[enum_dispatch] -pub trait IotaTransactionBlockEffectsAPI { - fn status(&self) -> &IotaExecutionStatus; - fn into_status(self) -> IotaExecutionStatus; - fn shared_objects(&self) -> &[IotaObjectRef]; - fn created(&self) -> &[OwnedObjectRef]; - fn mutated(&self) -> &[OwnedObjectRef]; - fn unwrapped(&self) -> &[OwnedObjectRef]; - fn deleted(&self) -> &[IotaObjectRef]; - fn unwrapped_then_deleted(&self) -> &[IotaObjectRef]; - fn wrapped(&self) -> &[IotaObjectRef]; - fn gas_object(&self) -> &OwnedObjectRef; - fn events_digest(&self) -> Option<&TransactionEventsDigest>; - fn dependencies(&self) -> &[TransactionDigest]; - fn executed_epoch(&self) -> EpochId; - fn transaction_digest(&self) -> &TransactionDigest; - fn gas_cost_summary(&self) -> &GasCostSummary; - - /// Return an iterator of mutated objects, but excluding the gas object. - fn mutated_excluding_gas(&self) -> Vec; - fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)>; - fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)>; - fn all_deleted_objects(&self) -> Vec<(&IotaObjectRef, DeleteKind)>; -} - -#[serde_as] -#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)] -#[serde( - rename = "TransactionBlockEffectsModifiedAtVersions", - rename_all = "camelCase" -)] -pub struct IotaTransactionBlockEffectsModifiedAtVersions { - object_id: ObjectID, - #[schemars(with = "AsSequenceNumber")] - #[serde_as(as = "AsSequenceNumber")] - sequence_number: SequenceNumber, -} - -/// The response from processing a transaction or a certified transaction -#[serde_as] -#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, JsonSchema)] -#[serde(rename = "TransactionBlockEffectsV1", rename_all = "camelCase")] -pub struct IotaTransactionBlockEffectsV1 { - /// The status of the execution - pub status: IotaExecutionStatus, - /// The epoch when this transaction was executed. - #[schemars(with = "BigInt")] - #[serde_as(as = "BigInt")] - pub executed_epoch: EpochId, - pub gas_used: GasCostSummary, - /// The version that every modified (mutated or deleted) object had before - /// it was modified by this transaction. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub modified_at_versions: Vec, - /// The object references of the shared objects used in this transaction. - /// Empty if no shared objects were used. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub shared_objects: Vec, - /// The transaction digest - pub transaction_digest: TransactionDigest, - /// ObjectRef and owner of new objects created. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub created: Vec, - /// ObjectRef and owner of mutated objects, including gas object. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub mutated: Vec, - /// ObjectRef and owner of objects that are unwrapped in this transaction. - /// Unwrapped objects are objects that were wrapped into other objects in - /// the past, and just got extracted out. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub unwrapped: Vec, - /// Object Refs of objects now deleted (the old refs). - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub deleted: Vec, - /// Object refs of objects previously wrapped in other objects but now - /// deleted. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub unwrapped_then_deleted: Vec, - /// Object refs of objects now wrapped in other objects. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub wrapped: Vec, - /// The updated gas object reference. Have a dedicated field for convenient - /// access. It's also included in mutated. - pub gas_object: OwnedObjectRef, - /// The digest of the events emitted during execution, - /// can be None if the transaction does not emit any event. - #[serde(skip_serializing_if = "Option::is_none")] - pub events_digest: Option, - /// The set of transaction digests this transaction depends on. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub dependencies: Vec, -} - -impl IotaTransactionBlockEffectsAPI for IotaTransactionBlockEffectsV1 { - fn status(&self) -> &IotaExecutionStatus { - &self.status - } - fn into_status(self) -> IotaExecutionStatus { - self.status - } - fn shared_objects(&self) -> &[IotaObjectRef] { - &self.shared_objects - } - fn created(&self) -> &[OwnedObjectRef] { - &self.created - } - fn mutated(&self) -> &[OwnedObjectRef] { - &self.mutated - } - fn unwrapped(&self) -> &[OwnedObjectRef] { - &self.unwrapped - } - fn deleted(&self) -> &[IotaObjectRef] { - &self.deleted - } - fn unwrapped_then_deleted(&self) -> &[IotaObjectRef] { - &self.unwrapped_then_deleted - } - fn wrapped(&self) -> &[IotaObjectRef] { - &self.wrapped - } - fn gas_object(&self) -> &OwnedObjectRef { - &self.gas_object - } - fn events_digest(&self) -> Option<&TransactionEventsDigest> { - self.events_digest.as_ref() - } - fn dependencies(&self) -> &[TransactionDigest] { - &self.dependencies - } - - fn executed_epoch(&self) -> EpochId { - self.executed_epoch - } - - fn transaction_digest(&self) -> &TransactionDigest { - &self.transaction_digest - } - - fn gas_cost_summary(&self) -> &GasCostSummary { - &self.gas_used - } - - fn mutated_excluding_gas(&self) -> Vec { - self.mutated - .iter() - .filter(|o| *o != &self.gas_object) - .cloned() - .collect() - } - - fn modified_at_versions(&self) -> Vec<(ObjectID, SequenceNumber)> { - self.modified_at_versions - .iter() - .map(|v| (v.object_id, v.sequence_number)) - .collect::>() - } - - fn all_changed_objects(&self) -> Vec<(&OwnedObjectRef, WriteKind)> { - self.mutated - .iter() - .map(|owner_ref| (owner_ref, WriteKind::Mutate)) - .chain( - self.created - .iter() - .map(|owner_ref| (owner_ref, WriteKind::Create)), - ) - .chain( - self.unwrapped - .iter() - .map(|owner_ref| (owner_ref, WriteKind::Unwrap)), - ) - .collect() - } - - fn all_deleted_objects(&self) -> Vec<(&IotaObjectRef, DeleteKind)> { - self.deleted - .iter() - .map(|r| (r, DeleteKind::Normal)) - .chain( - self.unwrapped_then_deleted - .iter() - .map(|r| (r, DeleteKind::UnwrapThenDelete)), - ) - .chain(self.wrapped.iter().map(|r| (r, DeleteKind::Wrap))) - .collect() - } -} \ No newline at end of file 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 deleted file mode 100644 index 47aa47d99..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_json_rpc_types/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -// 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 deleted file mode 100644 index c4867a4a1..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/balance.rs +++ /dev/null @@ -1,95 +0,0 @@ -// 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 deleted file mode 100644 index cfb71ba85..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/base_types.rs +++ /dev/null @@ -1,814 +0,0 @@ -// 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 schemars::JsonSchema; -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, to_iota_struct_tag_string}; -use super::timelock::timelock::{self, TimeLock}; -use super::timelock::timelocked_staked_iota::TimelockedStakedIota; -use super::stardust::output::Nft; -use super::gas_coin::GasCoin; -use super::object::{Owner}; -use super::parse_iota_struct_tag; - -pub use super::digests::{ObjectDigest, TransactionDigest}; - -// ----------------------------------------------------------------- -// Originally defined in crates/iota-types/src/committee.rs -// ----------------------------------------------------------------- -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; -// ----------------------------------------------------------------- -// Originally defined in crates/iota-types/src/execution_status.rs -// ----------------------------------------------------------------- -pub type CommandIndex = usize; -// ----------------------------------------------------------------- -// Originally defined in external-crates/move/crates/move-binary-format/src/file_format.rs -// ----------------------------------------------------------------- -/// Index into the code stream for a jump. The offset is relative to the -/// beginning of the instruction stream. -pub type CodeOffset = u16; -/// 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; -// ----------------------------------------------------------------- - -#[derive( - Eq, - PartialEq, - Ord, - PartialOrd, - Copy, - Clone, - Hash, - Default, - Debug, - Serialize, - Deserialize, - JsonSchema, -)] -pub struct SequenceNumber(u64); - -impl fmt::Display for SequenceNumber { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:#x}", self.0) - } -} - -pub type AuthorityName = AuthorityPublicKeyBytes; - -#[serde_as] -#[derive(Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] -pub struct ObjectID( - #[schemars(with = "Hex")] - #[serde_as(as = "Readable")] - AccountAddress, -); - -pub type ObjectRef = (ObjectID, SequenceNumber, ObjectDigest); - -/// 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), - /// An 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_v1(&self) -> bool { - self.address() == IOTA_FRAMEWORK_ADDRESS - && self.module().as_str() == "coin" - && self.name().as_str() == "DenyCapV1" - } - - 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 an 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, JsonSchema)] -pub struct IotaAddress( - #[schemars(with = "Hex")] - #[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)) - } -} - -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, -); - -// 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) - } -} - -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::TryFromSlice) - .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 - } -} - -#[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")] - TryFromSlice, -} - -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) - } -} - -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), - } - } -} \ 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 deleted file mode 100644 index e1cd2a27c..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/coin.rs +++ /dev/null @@ -1,186 +0,0 @@ -// 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 COIN_JOIN_FUNC_NAME: &IdentStr = ident_str!("join"); - -pub const PAY_MODULE_NAME: &IdentStr = ident_str!("pay"); -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(Box::new(UID::layout())), - ), - MoveFieldLayout::new( - ident_str!("balance").to_owned(), - MoveTypeLayout::Struct(Box::new(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 deleted file mode 100644 index 28d851db4..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/collection_types.rs +++ /dev/null @@ -1,103 +0,0 @@ -// 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 deleted file mode 100644 index 0ad555fe6..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/crypto.rs +++ /dev/null @@ -1,685 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 -use std::hash::Hash; -use std::str::FromStr; - -use enum_dispatch::enum_dispatch; -use strum::EnumString; -use schemars::JsonSchema; -use derive_more::{AsRef, AsMut, From}; -use eyre::{eyre, Report}; - -use fastcrypto::{ - bls12381::min_sig::{ - BLS12381AggregateSignature, BLS12381AggregateSignatureAsBytes, BLS12381KeyPair, - BLS12381PrivateKey, BLS12381PublicKey, BLS12381Signature, - }, ed25519::{ - Ed25519KeyPair, Ed25519PrivateKey, Ed25519PublicKey, Ed25519PublicKeyAsBytes, - Ed25519Signature - }, encoding::{Base64, Bech32, Encoding}, error::{FastCryptoError, FastCryptoResult}, hash::{Blake2b256, HashFunction}, secp256k1::{Secp256k1KeyPair, Secp256k1PublicKey, Secp256k1PublicKeyAsBytes, Secp256k1Signature}, secp256r1::{Secp256r1KeyPair, Secp256r1PublicKey, Secp256r1PublicKeyAsBytes, Secp256r1Signature}, traits::{Authenticator, EncodeDecodeBase64, KeyPair as KeypairTraits, Signer, ToFromBytes, VerifyingKey} -}; -use fastcrypto_zkp::zk_login_utils::Bn254FrElement; - -use serde::{Deserialize, Deserializer, Serializer}; -use serde::Serialize; -use serde_with::{serde_as, Bytes}; - -use crate::shared_crypto::intent::IntentMessage; - -use super::{ - base_types::IotaAddress, error::{IotaError, IotaResult}, iota_serde::Readable -}; - -const IOTA_PRIV_KEY_PREFIX: &str = "iotaprivkey"; - -// 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; - -// Account Keys -// -// * The following section defines the keypairs that are used by -// * accounts to interact with Iota. -// * Currently we support eddsa and ecdsa on Iota. - -#[expect(clippy::large_enum_variant)] -#[derive(Debug, From, PartialEq, Eq)] -pub enum IotaKeyPair { - Ed25519(Ed25519KeyPair), - Secp256k1(Secp256k1KeyPair), - Secp256r1(Secp256r1KeyPair), -} - -impl IotaKeyPair { - pub fn public(&self) -> PublicKey { - match self { - IotaKeyPair::Ed25519(kp) => PublicKey::Ed25519(kp.public().into()), - IotaKeyPair::Secp256k1(kp) => PublicKey::Secp256k1(kp.public().into()), - IotaKeyPair::Secp256r1(kp) => PublicKey::Secp256r1(kp.public().into()), - } - } - - pub fn copy(&self) -> Self { - match self { - IotaKeyPair::Ed25519(kp) => kp.copy().into(), - IotaKeyPair::Secp256k1(kp) => kp.copy().into(), - IotaKeyPair::Secp256r1(kp) => kp.copy().into(), - } - } -} - -impl EncodeDecodeBase64 for IotaKeyPair { - fn encode_base64(&self) -> String { - Base64::encode(self.to_bytes()) - } - - fn decode_base64(value: &str) -> FastCryptoResult { - let bytes = Base64::decode(value)?; - Self::from_bytes(&bytes).map_err(|_| FastCryptoError::InvalidInput) - } -} -impl IotaKeyPair { - pub fn to_bytes(&self) -> Vec { - let mut bytes: Vec = Vec::new(); - bytes.push(self.public().flag()); - - match self { - IotaKeyPair::Ed25519(kp) => { - bytes.extend_from_slice(kp.as_bytes()); - } - IotaKeyPair::Secp256k1(kp) => { - bytes.extend_from_slice(kp.as_bytes()); - } - IotaKeyPair::Secp256r1(kp) => { - bytes.extend_from_slice(kp.as_bytes()); - } - } - bytes - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - match SignatureScheme::from_flag_byte(bytes.first().ok_or_else(|| eyre!("Invalid length"))?) - { - Ok(x) => match x { - SignatureScheme::ED25519 => Ok(IotaKeyPair::Ed25519(Ed25519KeyPair::from_bytes( - bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, - )?)), - SignatureScheme::Secp256k1 => { - Ok(IotaKeyPair::Secp256k1(Secp256k1KeyPair::from_bytes( - bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, - )?)) - } - SignatureScheme::Secp256r1 => { - Ok(IotaKeyPair::Secp256r1(Secp256r1KeyPair::from_bytes( - bytes.get(1..).ok_or_else(|| eyre!("Invalid length"))?, - )?)) - } - _ => Err(eyre!("Invalid flag byte")), - }, - _ => Err(eyre!("Invalid bytes")), - } - } - - pub fn to_bytes_no_flag(&self) -> Vec { - match self { - IotaKeyPair::Ed25519(kp) => kp.as_bytes().to_vec(), - IotaKeyPair::Secp256k1(kp) => kp.as_bytes().to_vec(), - IotaKeyPair::Secp256r1(kp) => kp.as_bytes().to_vec(), - } - } - - /// Encode a IotaKeyPair as `flag || privkey` in Bech32 starting with - /// "iotaprivkey" to a string. Note that the pubkey is not encoded. - pub fn encode(&self) -> Result { - Bech32::encode(self.to_bytes(), IOTA_PRIV_KEY_PREFIX).map_err(|e| eyre!(e)) - } - - /// Decode a IotaKeyPair from `flag || privkey` in Bech32 starting with - /// "iotaprivkey" to IotaKeyPair. The public key is computed directly from - /// the private key bytes. - pub fn decode(value: &str) -> Result { - let bytes = Bech32::decode(value, IOTA_PRIV_KEY_PREFIX)?; - Self::from_bytes(&bytes) - } -} - -impl Serialize for IotaKeyPair { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let s = self.encode_base64(); - serializer.serialize_str(&s) - } -} - -impl<'de> Deserialize<'de> for IotaKeyPair { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - use serde::de::Error; - let s = String::deserialize(deserializer)?; - IotaKeyPair::decode_base64(&s).map_err(|e| Error::custom(e.to_string())) - } -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub enum PublicKey { - Ed25519(Ed25519PublicKeyAsBytes), - Secp256k1(Secp256k1PublicKeyAsBytes), - Secp256r1(Secp256r1PublicKeyAsBytes), - ZkLogin(ZkLoginPublicIdentifier), - Passkey(Secp256r1PublicKeyAsBytes), -} - -/// 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, - PublicKey::Passkey(pk) => &pk.0, - } - } -} - -impl EncodeDecodeBase64 for PublicKey { - fn encode_base64(&self) -> String { - let mut bytes: Vec = Vec::new(); - bytes.extend_from_slice(&[self.flag()]); - bytes.extend_from_slice(self.as_ref()); - Base64::encode(&bytes[..]) - } - - fn decode_base64(value: &str) -> FastCryptoResult { - let bytes = Base64::decode(value)?; - match bytes.first() { - Some(x) => { - if x == &SignatureScheme::ED25519.flag() { - let pk: Ed25519PublicKey = - Ed25519PublicKey::from_bytes(bytes.get(1..).ok_or( - FastCryptoError::InputLengthWrong(Ed25519PublicKey::LENGTH + 1), - )?)?; - Ok(PublicKey::Ed25519((&pk).into())) - } else if x == &SignatureScheme::Secp256k1.flag() { - let pk = Secp256k1PublicKey::from_bytes(bytes.get(1..).ok_or( - FastCryptoError::InputLengthWrong(Secp256k1PublicKey::LENGTH + 1), - )?)?; - Ok(PublicKey::Secp256k1((&pk).into())) - } else if x == &SignatureScheme::Secp256r1.flag() { - let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or( - FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1), - )?)?; - Ok(PublicKey::Secp256r1((&pk).into())) - } else if x == &SignatureScheme::PasskeyAuthenticator.flag() { - let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or( - FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1), - )?)?; - Ok(PublicKey::Passkey((&pk).into())) - } else { - Err(FastCryptoError::InvalidInput) - } - } - _ => Err(FastCryptoError::InvalidInput), - } - } -} - -impl PublicKey { - pub fn flag(&self) -> u8 { - self.scheme().flag() - } - - pub fn try_from_bytes( - curve: SignatureScheme, - key_bytes: &[u8], - ) -> Result { - match curve { - SignatureScheme::ED25519 => Ok(PublicKey::Ed25519( - (&Ed25519PublicKey::from_bytes(key_bytes)?).into(), - )), - SignatureScheme::Secp256k1 => Ok(PublicKey::Secp256k1( - (&Secp256k1PublicKey::from_bytes(key_bytes)?).into(), - )), - SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1( - (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), - )), - SignatureScheme::PasskeyAuthenticator => Ok(PublicKey::Passkey( - (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), - )), - _ => Err(eyre!("Unsupported curve")), - } - } - - pub fn scheme(&self) -> SignatureScheme { - match self { - PublicKey::Ed25519(_) => SignatureScheme::ED25519, // Equals Ed25519IotaSignature::SCHEME - PublicKey::Secp256k1(_) => SignatureScheme::Secp256k1, // Equals Secp256k1IotaSignature::SCHEME - PublicKey::Secp256r1(_) => SignatureScheme::Secp256r1, // Equals Secp256r1IotaSignature::SCHEME - PublicKey::ZkLogin(_) => SignatureScheme::ZkLoginAuthenticator, - PublicKey::Passkey(_) => SignatureScheme::PasskeyAuthenticator, - } - } -} - -/// 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 // schemars::JsonSchema and AsRef are omitted here, having Debug instead -)] -pub struct AuthorityPublicKeyBytes( - #[serde_as(as = "Readable")] - pub [u8; AuthorityPublicKey::LENGTH], -); - -// Enums for signature scheme signatures -#[enum_dispatch] -#[derive(Clone, JsonSchema, Debug, PartialEq, Eq, Hash)] -pub enum Signature { - Ed25519IotaSignature, - Secp256k1IotaSignature, - Secp256r1IotaSignature, -} - -impl Serialize for Signature { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let bytes = self.as_ref(); - - if serializer.is_human_readable() { - let s = Base64::encode(bytes); - serializer.serialize_str(&s) - } else { - serializer.serialize_bytes(bytes) - } - } -} - -impl<'de> Deserialize<'de> for Signature { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - use serde::de::Error; - - let bytes = if deserializer.is_human_readable() { - let s = String::deserialize(deserializer)?; - Base64::decode(&s).map_err(|e| Error::custom(e.to_string()))? - } else { - let data: Vec = Vec::deserialize(deserializer)?; - data - }; - - Self::from_bytes(&bytes).map_err(|e| Error::custom(e.to_string())) - } -} - -impl AsRef<[u8]> for Signature { - fn as_ref(&self) -> &[u8] { - match self { - Signature::Ed25519IotaSignature(sig) => sig.as_ref(), - Signature::Secp256k1IotaSignature(sig) => sig.as_ref(), - Signature::Secp256r1IotaSignature(sig) => sig.as_ref(), - } - } -} -impl AsMut<[u8]> for Signature { - fn as_mut(&mut self) -> &mut [u8] { - match self { - Signature::Ed25519IotaSignature(sig) => sig.as_mut(), - Signature::Secp256k1IotaSignature(sig) => sig.as_mut(), - Signature::Secp256r1IotaSignature(sig) => sig.as_mut(), - } - } -} - -impl ToFromBytes for Signature { - fn from_bytes(bytes: &[u8]) -> Result { - match bytes.first() { - Some(x) => { - if x == &Ed25519IotaSignature::SCHEME.flag() { - Ok(::from_bytes(bytes)?.into()) - } else if x == &Secp256k1IotaSignature::SCHEME.flag() { - Ok(::from_bytes(bytes)?.into()) - } else if x == &Secp256r1IotaSignature::SCHEME.flag() { - Ok(::from_bytes(bytes)?.into()) - } else { - Err(FastCryptoError::InvalidInput) - } - } - _ => Err(FastCryptoError::InvalidInput), - } - } -} - -// Ed25519 Iota Signature port -// - -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] -#[as_ref(forward)] -#[as_mut(forward)] -pub struct Ed25519IotaSignature( - #[schemars(with = "Base64")] - #[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 IotaSignatureInner for Ed25519IotaSignature { - type Sig = Ed25519Signature; - type PubKey = Ed25519PublicKey; - type KeyPair = Ed25519KeyPair; - const LENGTH: usize = Ed25519PublicKey::LENGTH + Ed25519Signature::LENGTH + 1; -} - -impl IotaPublicKey for Ed25519PublicKey { - const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::ED25519; -} - -impl ToFromBytes for Ed25519IotaSignature { - fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != Self::LENGTH { - return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); - } - let mut sig_bytes = [0; Self::LENGTH]; - sig_bytes.copy_from_slice(bytes); - Ok(Self(sig_bytes)) - } -} - -// Secp256k1 Iota Signature port -// -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] -#[as_ref(forward)] -#[as_mut(forward)] -pub struct Secp256k1IotaSignature( - #[schemars(with = "Base64")] - #[serde_as(as = "Readable")] - [u8; Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1], -); - -impl IotaSignatureInner for Secp256k1IotaSignature { - type Sig = Secp256k1Signature; - type PubKey = Secp256k1PublicKey; - type KeyPair = Secp256k1KeyPair; - const LENGTH: usize = Secp256k1PublicKey::LENGTH + Secp256k1Signature::LENGTH + 1; -} - -impl IotaPublicKey for Secp256k1PublicKey { - const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256k1; -} - -impl ToFromBytes for Secp256k1IotaSignature { - fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != Self::LENGTH { - return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); - } - let mut sig_bytes = [0; Self::LENGTH]; - sig_bytes.copy_from_slice(bytes); - Ok(Self(sig_bytes)) - } -} - -// Secp256r1 Iota Signature port -// -#[serde_as] -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, AsRef, AsMut)] -#[as_ref(forward)] -#[as_mut(forward)] -pub struct Secp256r1IotaSignature( - #[schemars(with = "Base64")] - #[serde_as(as = "Readable")] - [u8; Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1], -); - -impl IotaSignatureInner for Secp256r1IotaSignature { - type Sig = Secp256r1Signature; - type PubKey = Secp256r1PublicKey; - type KeyPair = Secp256r1KeyPair; - const LENGTH: usize = Secp256r1PublicKey::LENGTH + Secp256r1Signature::LENGTH + 1; -} - -impl IotaPublicKey for Secp256r1PublicKey { - const SIGNATURE_SCHEME: SignatureScheme = SignatureScheme::Secp256r1; -} - -impl ToFromBytes for Secp256r1IotaSignature { - fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != Self::LENGTH { - return Err(FastCryptoError::InputLengthWrong(Self::LENGTH)); - } - let mut sig_bytes = [0; Self::LENGTH]; - sig_bytes.copy_from_slice(bytes); - Ok(Self(sig_bytes)) - } -} - -// This struct exists due to the limitations of the `enum_dispatch` library. -// -pub trait IotaSignatureInner: Sized + ToFromBytes + PartialEq + Eq + Hash { - type Sig: Authenticator; - type PubKey: VerifyingKey + IotaPublicKey; - type KeyPair: KeypairTraits; - - const LENGTH: usize = Self::Sig::LENGTH + Self::PubKey::LENGTH + 1; - const SCHEME: SignatureScheme = Self::PubKey::SIGNATURE_SCHEME; - - /// Returns the deserialized signature and deserialized pubkey. - fn get_verification_inputs(&self) -> IotaResult<(Self::Sig, Self::PubKey)> { - let pk = Self::PubKey::from_bytes(self.public_key_bytes()) - .map_err(|_| IotaError::KeyConversion("Invalid public key".to_string()))?; - - // deserialize the signature - let signature = Self::Sig::from_bytes(self.signature_bytes()).map_err(|_| { - IotaError::InvalidSignature { - error: "Fail to get pubkey and sig".to_string(), - } - })?; - - Ok((signature, pk)) - } - - fn new(kp: &Self::KeyPair, message: &[u8]) -> Self { - let sig = Signer::sign(kp, message); - - let mut signature_bytes: Vec = Vec::new(); - signature_bytes - .extend_from_slice(&[::SIGNATURE_SCHEME.flag()]); - signature_bytes.extend_from_slice(sig.as_ref()); - signature_bytes.extend_from_slice(kp.public().as_ref()); - Self::from_bytes(&signature_bytes[..]) - .expect("Serialized signature did not have expected size") - } -} - -pub trait IotaPublicKey: VerifyingKey { - const SIGNATURE_SCHEME: SignatureScheme; -} - -#[enum_dispatch(Signature)] -pub trait IotaSignature: Sized + ToFromBytes { - fn signature_bytes(&self) -> &[u8]; - fn public_key_bytes(&self) -> &[u8]; - fn scheme(&self) -> SignatureScheme; - - fn verify_secure( - &self, - value: &IntentMessage, - author: IotaAddress, - scheme: SignatureScheme, - ) -> IotaResult<()> - where - T: Serialize; -} - -impl IotaSignature for S { - fn signature_bytes(&self) -> &[u8] { - // Access array slice is safe because the array bytes is initialized as - // flag || signature || pubkey with its defined length. - &self.as_ref()[1..1 + S::Sig::LENGTH] - } - - fn public_key_bytes(&self) -> &[u8] { - // Access array slice is safe because the array bytes is initialized as - // flag || signature || pubkey with its defined length. - &self.as_ref()[S::Sig::LENGTH + 1..] - } - - fn scheme(&self) -> SignatureScheme { - S::PubKey::SIGNATURE_SCHEME - } - - fn verify_secure( - &self, - value: &IntentMessage, - author: IotaAddress, - scheme: SignatureScheme, - ) -> Result<(), IotaError> - where - T: Serialize, - { - let mut hasher = DefaultHash::default(); - hasher.update(bcs::to_bytes(&value).expect("Message serialization should not fail")); - let digest = hasher.finalize().digest; - - let (sig, pk) = &self.get_verification_inputs()?; - match scheme { - SignatureScheme::ZkLoginAuthenticator => {} // Pass this check because zk login does - // not derive address from pubkey. - _ => { - let address = IotaAddress::from(pk); - if author != address { - return Err(IotaError::IncorrectSigner { - error: format!( - "Incorrect signer, expected {:?}, got {:?}", - author, address - ), - }); - } - } - } - - pk.verify(&digest, sig) - .map_err(|e| IotaError::InvalidSignature { - error: format!("Fail to verify user sig {}", e), - }) - } -} - -#[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, - PasskeyAuthenticator, -} - -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, - SignatureScheme::PasskeyAuthenticator => 0x06, - } - } - - /// 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), - 0x06 => Ok(SignatureScheme::PasskeyAuthenticator), - _ => Err(IotaError::KeyConversion("Invalid key scheme".to_string())), - } - } -} - -impl FromStr for Signature { - type Err = eyre::Report; - fn from_str(s: &str) -> Result { - Self::decode_base64(s).map_err(|e| eyre!("Fail to decode base64 {}", e.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 deleted file mode 100644 index 4b3dc51bf..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/digests.rs +++ /dev/null @@ -1,470 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt; - -use schemars::JsonSchema; -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, JsonSchema)] -pub struct Digest( - #[schemars(with = "Base58")] - #[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 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 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(()) - } -} - - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct CheckpointContentsDigest(Digest); - -impl CheckpointContentsDigest { - 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 CheckpointContentsDigest { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsRef<[u8; 32]> for CheckpointContentsDigest { - fn as_ref(&self) -> &[u8; 32] { - self.0.as_ref() - } -} - -impl From for [u8; 32] { - fn from(digest: CheckpointContentsDigest) -> Self { - digest.into_inner() - } -} - -impl From<[u8; 32]> for CheckpointContentsDigest { - fn from(digest: [u8; 32]) -> Self { - Self::new(digest) - } -} - -impl fmt::Display for CheckpointContentsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -impl fmt::Debug for CheckpointContentsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("CheckpointContentsDigest") - .field(&self.0) - .finish() - } -} - -impl std::str::FromStr for CheckpointContentsDigest { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let mut result = [0; 32]; - let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?; - if buffer.len() != 32 { - return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes")); - } - result.copy_from_slice(&buffer); - Ok(CheckpointContentsDigest::new(result)) - } -} - -impl fmt::LowerHex for CheckpointContentsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::LowerHex::fmt(&self.0, f) - } -} - -impl fmt::UpperHex for CheckpointContentsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] -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) - } - - pub fn next_lexicographical(&self) -> Option { - self.0.next_lexicographical().map(Self) - } -} - -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]; - let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?; - if buffer.len() != 32 { - return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes")); - } - result.copy_from_slice(&buffer); - Ok(TransactionDigest::new(result)) - } -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct TransactionEffectsDigest(Digest); - -impl TransactionEffectsDigest { - 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 TransactionEffectsDigest { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsRef<[u8; 32]> for TransactionEffectsDigest { - fn as_ref(&self) -> &[u8; 32] { - self.0.as_ref() - } -} - -impl From for [u8; 32] { - fn from(digest: TransactionEffectsDigest) -> Self { - digest.into_inner() - } -} - -impl From<[u8; 32]> for TransactionEffectsDigest { - fn from(digest: [u8; 32]) -> Self { - Self::new(digest) - } -} - -impl fmt::Display for TransactionEffectsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.0, f) - } -} - -impl fmt::Debug for TransactionEffectsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("TransactionEffectsDigest") - .field(&self.0) - .finish() - } -} - -impl fmt::LowerHex for TransactionEffectsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::LowerHex::fmt(&self.0, f) - } -} - -impl fmt::UpperHex for TransactionEffectsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -#[serde_as] -#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Hash, Serialize, Deserialize, JsonSchema)] -pub struct TransactionEventsDigest(Digest); - -impl TransactionEventsDigest { - pub const ZERO: Self = Self(Digest::ZERO); - - pub const fn new(digest: [u8; 32]) -> Self { - Self(Digest::new(digest)) - } - - pub fn random() -> Self { - Self(Digest::random()) - } - - pub fn next_lexicographical(&self) -> Option { - self.0.next_lexicographical().map(Self) - } - - pub fn into_inner(self) -> [u8; 32] { - self.0.into_inner() - } -} - -impl fmt::Debug for TransactionEventsDigest { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("TransactionEventsDigest") - .field(&self.0) - .finish() - } -} - -impl AsRef<[u8]> for TransactionEventsDigest { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl AsRef<[u8; 32]> for TransactionEventsDigest { - fn as_ref(&self) -> &[u8; 32] { - self.0.as_ref() - } -} - -impl std::str::FromStr for TransactionEventsDigest { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let mut result = [0; 32]; - let buffer = Base58::decode(s).map_err(|e| anyhow::anyhow!(e))?; - if buffer.len() != 32 { - return Err(anyhow::anyhow!("Invalid digest length. Expected 32 bytes")); - } - result.copy_from_slice(&buffer); - Ok(Self::new(result)) - } -} - -// Each object has a unique digest -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema)] -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) - } -} 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 deleted file mode 100644 index fa3c16d6a..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/dynamic_field.rs +++ /dev/null @@ -1,156 +0,0 @@ -// 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::Base64}; - -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 deleted file mode 100644 index afcb18401..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/error.rs +++ /dev/null @@ -1,943 +0,0 @@ -// 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::*, - digests::{ObjectDigest, TransactionDigest, TransactionEffectsDigest, TransactionEventsDigest, - CheckpointContentsDigest}, - execution_status::{CommandArgumentError, ExecutionFailureStatus}, - 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")] - ImmutableParameterExpected { 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")] - NotSharedObject, - #[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" - )] - TransferObjectWithoutPublicTransfer { 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}")] - MoveFunctionInput(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(CheckpointContentsDigest), - - #[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, - - // Soft Bundle related errors - #[error( - "Number of transactions exceeds the maximum allowed ({:?}) in a Soft Bundle", - limit - )] - TooManyTransactionsInSoftBundle { limit: u64 }, - #[error("Transaction {:?} in Soft Bundle contains no shared objects", digest)] - NoSharedObject { digest: TransactionDigest }, - #[error("Transaction {:?} in Soft Bundle has already been executed", digest)] - AlreadyExecuted { digest: TransactionDigest }, - #[error("At least one certificate in Soft Bundle has already been processed")] - CertificateAlreadyProcessed, - #[error( - "Gas price for transaction {:?} in Soft Bundle mismatch: want {:?}, have {:?}", - digest, - expected, - actual - )] - GasPriceMismatch { - digest: TransactionDigest, - expected: u64, - actual: u64, - }, - - #[error("Coin type is globally paused for use: {coin_type}")] - CoinTypeGlobalPause { coin_type: String }, -} - -#[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)] - Display { 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, - }, - - #[error("Soft bundle must only contain transactions of UserTransaction kind")] - InvalidTxKindInSoftBundle, - - // 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, // Committee is not available for wasm32 - }, - #[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, - - // 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("Invalid DKG message size")] - InvalidDkgMessageSize, - - #[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: TransactionEventsDigest }, - #[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 }, - - // GenericBridge is not available - - #[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("Operations for epoch {0} have ended")] - EpochEnded(EpochId), - #[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("Method not allowed")] - InvalidRpcMethod, - - // TODO: We should fold this into UserInputError::Unsupported. - #[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("Failed to read or deserialize bridge related data structures on-chain: {0}")] - IotaBridgeRead(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 }, - - #[error("Too many requests")] - TooManyRequests, - - #[error("The request did not contain a certificate")] - NoCertificateProvided, -} - -#[repr(u64)] -#[expect(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)] -#[expect(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; - -// iota_protocol_config::Error is not available - -impl From for IotaError { - fn from(error: ExecutionError) -> Self { - IotaError::Execution(error.to_string()) - } -} - -// Status, TypedStoreError, crate::storage::error::Error are not available - -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 From for IotaError { - fn from(error: String) -> Self { - IotaError::GenericAuthority { error } - } -} - -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 { - IotaError::Rpc { .. } => true, - - // Reconfig error - IotaError::ValidatorHaltedAtEpochEnd => true, - IotaError::MissingCommitteeAtEpoch(..) => true, - IotaError::WrongEpoch { .. } => true, - IotaError::EpochEnded { .. } => 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, - - // NB: This is not an internal overload, but instead an imposed rate - // limit / blocking of a client. It must be non-retryable otherwise - // we will make the threat worse through automatic retries. - IotaError::TooManyRequests => 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 { .. }) - } - - pub fn retry_after_secs(&self) -> u64 { - match self { - IotaError::ValidatorOverloadedRetryAfter { retry_after_secs } => *retry_after_secs, - _ => 0, - } - } -} - -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, - )) -} diff --git a/identity_iota_interaction/src/sdk_types/iota_types/event.rs b/identity_iota_interaction/src/sdk_types/iota_types/event.rs deleted file mode 100644 index ddf02a23c..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/event.rs +++ /dev/null @@ -1,55 +0,0 @@ -// 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 an 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 deleted file mode 100644 index 9471ec477..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/execution_status.rs +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt::{self, Display, Formatter}; - -use super::super::move_core_types::language_storage::ModuleId; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -use super::base_types::{ObjectID, IotaAddress, TypeParameterIndex, CodeOffset}; - -#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "lowercase")] // Needed for QuorumDriverTrait implementation in IotaClientTsSdk -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)] -pub struct CongestedObjects(pub Vec); - -impl fmt::Display for CongestedObjects { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - for obj in &self.0 { - write!(f, "{}, ", obj)?; - } - Ok(()) - } -} - -#[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 an 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." - )] - IotaMoveVerificationTimeout, - - #[error("The shared object operation is not allowed.")] - SharedObjectOperationNotAllowed, - - #[error("Certificate cannot be executed due to a dependency on a deleted shared object")] - InputObjectDeleted, - - #[error("Certificate is cancelled due to congestion on shared objects: {congested_objects}")] - ExecutionCancelledDueToSharedObjectCongestion { congested_objects: CongestedObjects }, - - #[error("Address {address:?} is denied for coin {coin_type}")] - AddressDeniedForCoin { - address: IotaAddress, - coin_type: String, - }, - - #[error("Coin type is globally paused for use: {coin_type}")] - CoinTypeGlobalPause { coin_type: String }, - - #[error("Certificate is cancelled because randomness could not be generated this epoch")] - ExecutionCancelledDueToRandomnessUnavailable, - // 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; diff --git a/identity_iota_interaction/src/sdk_types/iota_types/gas.rs b/identity_iota_interaction/src/sdk_types/iota_types/gas.rs deleted file mode 100644 index 3165ad200..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/gas.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; -use super::iota_serde::{BigInt, Readable}; - -/// Summary of the charges in a transaction. -/// Storage is charged independently of computation. -/// There are 3 parts to the storage charges: -/// `storage_cost`: it is the charge of storage at the time the transaction -/// is executed. The cost of storage is the number of -/// bytes of the objects being mutated multiplied by a -/// variable storage cost per byte `storage_rebate`: this is the amount -/// a user gets back when manipulating an object. The -/// `storage_rebate` is the `storage_cost` for an object minus fees. -/// `non_refundable_storage_fee`: not all the value of the object storage -/// cost is given back to user and there -/// is a small fraction that is kept by -/// the system. This value tracks that charge. -/// -/// When looking at a gas cost summary the amount charged to the user is -/// `computation_cost + storage_cost - storage_rebate` -/// and that is the amount that is deducted from the gas coins. -/// `non_refundable_storage_fee` is collected from the objects being -/// mutated/deleted and it is tracked by the system in storage funds. -/// -/// Objects deleted, including the older versions of objects mutated, have -/// the storage field on the objects added up to a pool of "potential -/// rebate". This rebate then is reduced by the "nonrefundable rate" -/// such that: `potential_rebate(storage cost of deleted/mutated -/// objects) = storage_rebate + non_refundable_storage_fee` - -#[serde_as] -#[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] -#[serde(rename_all = "camelCase")] -pub struct GasCostSummary { - /// Cost of computation/execution - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub computation_cost: u64, - /// The burned component of the computation/execution costs - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub computation_cost_burned: u64, - /// Storage cost, it's the sum of all storage cost for all objects - /// created or mutated. - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub storage_cost: u64, - /// The amount of storage cost refunded to the user for all objects - /// deleted or mutated in the transaction. - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub storage_rebate: u64, - /// The fee for the rebate. The portion of the storage rebate kept by - /// the system. - #[schemars(with = "BigInt")] - #[serde_as(as = "Readable, _>")] - pub non_refundable_storage_fee: u64, -} 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 deleted file mode 100644 index b8cafc972..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/gas_coin.rs +++ /dev/null @@ -1,147 +0,0 @@ -// 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() - } - - // MoveObject is not available for wasm32 - // - // pub fn to_object(&self, version: SequenceNumber) -> MoveObject { - // MoveObject::new_gas_coin(version, *self.id(), self.value()) - // } - - 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 `TreasuryCap` object ID. - pub fn id(&self) -> &ObjectID { - self.inner.id.object_id() - } - - /// 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 deleted file mode 100644 index e9ed0fe3e..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/governance.rs +++ /dev/null @@ -1,98 +0,0 @@ -// 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 deleted file mode 100644 index b8727295a..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/id.rs +++ /dev/null @@ -1,108 +0,0 @@ -// 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(Box::new(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_())) - } -} 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 deleted file mode 100644 index f931a3dd0..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/iota_serde.rs +++ /dev/null @@ -1,409 +0,0 @@ -// 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; -#[allow(unused)] // Kept in sync with original source, so keep as is. -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 schemars::JsonSchema; - -use Result; - -use super::super::move_core_types::{ - account_address::AccountAddress, - language_storage::{StructTag, TypeTag} -}; - -#[allow(unused)] // Kept in sync with original source, so keep as is. -use super::{IOTA_FRAMEWORK_ADDRESS, MOVE_STDLIB_ADDRESS, IOTA_SYSTEM_ADDRESS, - STARDUST_ADDRESS, IOTA_SYSTEM_STATE_ADDRESS, IOTA_CLOCK_ADDRESS }; -use super::parse_iota_struct_tag; -use super::parse_iota_type_tag; - -/// The minimum and maximum protocol versions supported by this build. -const MIN_PROTOCOL_VERSION: u64 = 1; // Originally defined in crates/iota-protocol-config/src/lib.rs -pub const MAX_PROTOCOL_VERSION: u64 = 1; // Originally defined in crates/iota-protocol-config/src/lib.rs - -// ----------------------------------------------------------------------------------------- -// Originally contained in crates/iota-protocol-config/src/lib.rs -// ----------------------------------------------------------------------------------------- - -// Record history of protocol version allocations here: -// -// Version 1: Original version. -// Version 2: Don't redistribute slashed staking rewards, fix computation of -// SystemEpochInfoEventV1. -#[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); - - #[allow(unused)] // Kept in sync with original source, so keep as is. - #[cfg(not(msim))] - const MAX_ALLOWED: Self = Self::MAX; - - // We create 3 additional "fake" versions in simulator builds so that we can - // test upgrades. - #[cfg(msim)] - pub const MAX_ALLOWED: Self = Self(MAX_PROTOCOL_VERSION + 3); - - 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) - } -} - -// ----------------------------------------------------------------------------------------- -// End of originally contained in crates/iota-protocol-config/src/lib.rs section -// ----------------------------------------------------------------------------------------- - -#[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; 7] = [ - AccountAddress::ZERO, - AccountAddress::ONE, - IOTA_FRAMEWORK_ADDRESS, - IOTA_SYSTEM_ADDRESS, - STARDUST_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, JsonSchema)] -pub struct BigInt( - #[schemars(with = "String")] - #[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, JsonSchema)] -pub struct SequenceNumber(#[schemars(with = "BigInt")] 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)) - } -} - -#[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)) - } -} 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 deleted file mode 100644 index a9501dade..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/iota_types_lib.rs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[allow(unused)] // Kept in sync with original source, so keep as is. -use serde::{Deserialize, Serialize}; -use super::super::move_core_types::{ - account_address::AccountAddress, - language_storage::{TypeTag, StructTag}, -}; -use super::base_types::{ObjectID, SequenceNumber, IotaAddress}; -use super::object::OBJECT_START_VERSION; - -macro_rules! built_in_ids { - ($($addr:ident / $id:ident = $init:expr);* $(;)?) => { - $( - pub const $addr: AccountAddress = builtin_address($init); - pub const $id: ObjectID = ObjectID::from_address($addr); - )* - } -} - -macro_rules! built_in_pkgs { - ($($addr:ident / $id:ident = $init:expr);* $(;)?) => { - built_in_ids! { $($addr / $id = $init;)* } - pub const SYSTEM_PACKAGE_ADDRESSES: &[AccountAddress] = &[$($addr),*]; - pub fn is_system_package(addr: impl Into) -> bool { - matches!(addr.into(), $($addr)|*) - } - } -} - -built_in_pkgs! { - MOVE_STDLIB_ADDRESS / MOVE_STDLIB_PACKAGE_ID = 0x1; - IOTA_FRAMEWORK_ADDRESS / IOTA_FRAMEWORK_PACKAGE_ID = 0x2; - IOTA_SYSTEM_ADDRESS / IOTA_SYSTEM_PACKAGE_ID = 0x3; - BRIDGE_ADDRESS / BRIDGE_PACKAGE_ID = 0xb; - STARDUST_ADDRESS / STARDUST_PACKAGE_ID = 0x107a; -} - -built_in_ids! { - IOTA_SYSTEM_STATE_ADDRESS / IOTA_SYSTEM_STATE_OBJECT_ID = 0x5; - IOTA_CLOCK_ADDRESS / IOTA_CLOCK_OBJECT_ID = 0x6; - IOTA_AUTHENTICATOR_STATE_ADDRESS / IOTA_AUTHENTICATOR_STATE_OBJECT_ID = 0x7; - IOTA_RANDOMNESS_STATE_ADDRESS / IOTA_RANDOMNESS_STATE_OBJECT_ID = 0x8; - IOTA_BRIDGE_ADDRESS / IOTA_BRIDGE_OBJECT_ID = 0x9; - IOTA_DENY_LIST_ADDRESS / IOTA_DENY_LIST_OBJECT_ID = 0x403; -} - -pub const IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; -pub const IOTA_CLOCK_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; -pub const IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION: SequenceNumber = OBJECT_START_VERSION; - -const fn builtin_address(suffix: u16) -> AccountAddress { - let mut addr = [0u8; AccountAddress::LENGTH]; - let [hi, lo] = suffix.to_be_bytes(); - addr[AccountAddress::LENGTH - 2] = hi; - addr[AccountAddress::LENGTH - 1] = lo; - AccountAddress::new(addr) -} - -/// 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_core_types::parsing::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_core_types::parsing::types::ParsedType; - ParsedType::parse(s)?.into_type_tag(&resolve_address) -} - -/// Resolve well-known named addresses into numeric addresses. -pub fn resolve_address(addr: &str) -> Option { - match addr { - "std" => Some(MOVE_STDLIB_ADDRESS), - "iota" => Some(IOTA_FRAMEWORK_ADDRESS), - "iota_system" => Some(IOTA_SYSTEM_ADDRESS), - "stardust" => Some(STARDUST_ADDRESS), - "bridge" => Some(BRIDGE_ADDRESS), - _ => None, - } -} - -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())) - } -} diff --git a/identity_iota_interaction/src/sdk_types/iota_types/mod.rs b/identity_iota_interaction/src/sdk_types/iota_types/mod.rs deleted file mode 100644 index 8f75e8f72..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/mod.rs +++ /dev/null @@ -1,29 +0,0 @@ -// 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 event; -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 gas; -pub mod storage; - -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 deleted file mode 100644 index e7f63eb30..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/move_package.rs +++ /dev/null @@ -1,84 +0,0 @@ -// 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, - // `struct_name` alias to support backwards compatibility with the old name - #[serde(alias = "struct_name")] - pub datatype_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 { - pub(crate) 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. - pub(crate) version: SequenceNumber, - // TODO use session cache - #[serde_as(as = "BTreeMap<_, Bytes>")] - pub(crate) module_map: BTreeMap>, - - /// Maps struct/module to a package version where it was first defined, - /// stored as a vector for simple serialization and deserialization. - pub(crate) type_origin_table: Vec, - - // For each dependency, maps original package ID to the info about the (upgraded) dependency - // version that this package is using - pub(crate) 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 deleted file mode 100644 index 1c9c7930c..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/object.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt::{Display, Formatter}; - -use schemars::JsonSchema; -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, JsonSchema)] -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 { - initial_shared_version, - } => { - write!(f, "Shared( {} )", initial_shared_version.value()) - } - } - } -} 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 deleted file mode 100644 index 3eb37fcbb..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/quorum_driver_types.rs +++ /dev/null @@ -1,12 +0,0 @@ -// 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 deleted file mode 100644 index f6606f3f8..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/stardust/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub mod output; diff --git a/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs b/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs deleted file mode 100644 index 28d547dcf..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -pub mod nft; - -pub use nft::*; \ No newline at end of file diff --git a/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs b/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs deleted file mode 100644 index b53b83d54..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/stardust/output/nft.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use crate::ident_str; - -use crate::sdk_types::move_types::language_storage::StructTag; -use crate::sdk_types::move_types::identifier::IdentStr; - -use super::super::super::STARDUST_ADDRESS; - -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_ADDRESS.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/storage.rs b/identity_iota_interaction/src/sdk_types/iota_types/storage.rs deleted file mode 100644 index 2d6ca38ea..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/storage.rs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Mysten Labs, Inc. -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::{Deserialize, Serialize}; - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] -pub enum WriteKind { - /// The object was in storage already but has been modified - Mutate, - /// The object was created in this transaction - Create, - /// The object was previously wrapped in another object, but has been - /// restored to storage - Unwrap, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] -pub enum DeleteKind { - /// An object is provided in the call input, and gets deleted. - Normal, - /// An object is not provided in the call input, but gets unwrapped - /// from another object, and then gets deleted. - UnwrapThenDelete, - /// An object is provided in the call input, and gets wrapped into another - /// object. - Wrap, -} \ 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 deleted file mode 100644 index 9e825bb77..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/timelock/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// 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 deleted file mode 100644 index 65772e9e9..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelock.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::Deserialize; -use serde::Serialize; - -use crate::ident_str; - -#[allow(unused)] // Kept in sync with original source, so keep as is. -use crate::sdk_types::move_types::{ - language_storage::{TypeTag, StructTag}, - identifier::{IdentStr}, -}; - -#[allow(unused)] // Kept in sync with original source, so keep as is. -use super::super::{ - IOTA_FRAMEWORK_ADDRESS, - IOTA_SYSTEM_ADDRESS, - base_types::{ObjectID, EpochId}, - balance::Balance, - governance::StakedIota, - id::UID, - error::IotaError, -}; - -#[allow(unused)] // Kept in sync with original source, so keep as is. -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).map_err(|err| IotaError::ObjectDeserialization { - error: format!("Unable to deserialize TimeLock object: {:?}", err), - }) - } - - /// 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, - } -} \ 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 deleted file mode 100644 index 2c052b9f6..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/timelock/timelocked_staked_iota.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::{Deserialize, Serialize}; -use crate::ident_str; -use crate::sdk_types::move_types::identifier::IdentStr; -use crate::sdk_types::move_types::language_storage::StructTag; -use super::super::{ - IOTA_SYSTEM_ADDRESS, - base_types::{ObjectID, EpochId}, - governance::StakedIota, - id::UID, -}; - -pub const TIMELOCKED_STAKED_IOTA_MODULE_NAME: &IdentStr = ident_str!("timelocked_staking"); -pub const TIMELOCKED_STAKED_IOTA_STRUCT_NAME: &IdentStr = ident_str!("TimelockedStakedIota"); - -/// 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/transaction.rs b/identity_iota_interaction/src/sdk_types/iota_types/transaction.rs deleted file mode 100644 index 379e4f1d3..000000000 --- a/identity_iota_interaction/src/sdk_types/iota_types/transaction.rs +++ /dev/null @@ -1,463 +0,0 @@ -// 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 std::vec::Vec; - -use enum_dispatch::enum_dispatch; -use nonempty::NonEmpty; -use serde::{Deserialize, Serialize}; -use strum::IntoStaticStr; - - -use super::super::move_core_types::identifier::Identifier; -use super::super::move_core_types::language_storage::TypeTag; - -use super::base_types::{EpochId, IotaAddress, ObjectID, ObjectRef, SequenceNumber}; -use super::error::UserInputError; -use super::{ - IOTA_AUTHENTICATOR_STATE_OBJECT_ID, IOTA_AUTHENTICATOR_STATE_OBJECT_SHARED_VERSION, IOTA_CLOCK_OBJECT_ID, IOTA_CLOCK_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 = 50_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, - }; -} - -/// A series of commands where the results of one command can be used in future -/// commands -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub struct ProgrammableTransaction { - /// Input objects or primitive values - pub inputs: Vec, - /// The commands to be executed sequentially. A failure in any command will - /// result in the failure of the entire transaction. - pub commands: 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), -} - -/// 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, -} - -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})"), - } - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, IntoStaticStr)] -pub enum TransactionKind { - /// A transaction that allows the interleaving of native commands and Move - /// calls - ProgrammableTransaction(ProgrammableTransaction), -} - -#[derive(Debug, PartialEq, Eq)] -pub struct SharedInputObject { - pub id: ObjectID, - pub initial_shared_version: SequenceNumber, - pub mutable: bool, -} - -impl SharedInputObject { - pub const IOTA_SYSTEM_OBJ: Self = Self { - id: IOTA_SYSTEM_STATE_OBJECT_ID, - initial_shared_version: IOTA_SYSTEM_STATE_OBJECT_SHARED_VERSION, - mutable: true, - }; - - pub fn id(&self) -> ObjectID { - self.id - } - - pub fn into_id_and_version(self) -> (ObjectID, SequenceNumber) { - (self.id, self.initial_shared_version) - } -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub struct GasData { - pub payment: Vec, - pub owner: IotaAddress, - pub price: u64, - pub budget: u64, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)] -pub enum TransactionExpiration { - /// The transaction has no expiration - None, - /// Validators wont sign a transaction unless the expiration Epoch - /// is greater than or equal to the current epoch - Epoch(EpochId), -} - -#[enum_dispatch(TransactionDataAPI)] -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub enum TransactionData { - V1(TransactionDataV1), - // When new variants are introduced, it is important that we check version support - // in the validity_check function based on the protocol config. -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)] -pub struct TransactionDataV1 { - pub kind: TransactionKind, - pub sender: IotaAddress, - pub gas_data: GasData, - pub expiration: TransactionExpiration, -} - -impl TransactionData { - pub fn new( - kind: TransactionKind, - sender: IotaAddress, - gas_payment: ObjectRef, - gas_budget: u64, - gas_price: u64, - ) -> Self { - TransactionData::V1(TransactionDataV1 { - kind, - sender, - gas_data: GasData { - price: gas_price, - owner: sender, - payment: vec![gas_payment], - budget: gas_budget, - }, - expiration: TransactionExpiration::None, - }) - } - - pub fn new_with_gas_coins( - kind: TransactionKind, - sender: IotaAddress, - gas_payment: Vec, - gas_budget: u64, - gas_price: u64, - ) -> Self { - Self::new_with_gas_coins_allow_sponsor( - kind, - sender, - gas_payment, - gas_budget, - gas_price, - sender, - ) - } - - pub fn new_with_gas_coins_allow_sponsor( - kind: TransactionKind, - sender: IotaAddress, - gas_payment: Vec, - gas_budget: u64, - gas_price: u64, - gas_sponsor: IotaAddress, - ) -> Self { - TransactionData::V1(TransactionDataV1 { - kind, - sender, - gas_data: GasData { - price: gas_price, - owner: gas_sponsor, - payment: gas_payment, - budget: gas_budget, - }, - expiration: TransactionExpiration::None, - }) - } - - pub fn new_with_gas_data( - kind: TransactionKind, - sender: IotaAddress, - gas_data: GasData, - ) -> Self { - TransactionData::V1(TransactionDataV1 { - kind, - sender, - gas_data, - expiration: TransactionExpiration::None, - }) - } -} - -#[enum_dispatch] -pub trait TransactionDataAPI { - fn sender(&self) -> IotaAddress; - - // Note: this implies that SingleTransactionKind itself must be versioned, so - // that it can be shared across versions. This will be easy to do since it - // is already an enum. - fn kind(&self) -> &TransactionKind; - - // Used by programmable_transaction_builder - fn kind_mut(&mut self) -> &mut TransactionKind; - - // kind is moved out of often enough that this is worth it to special case. - fn into_kind(self) -> TransactionKind; - - /// Transaction signer and Gas owner - fn signers(&self) -> NonEmpty; - - fn gas_data(&self) -> &GasData; - - fn gas_data_mut(&mut self) -> &mut GasData; - - fn gas_owner(&self) -> IotaAddress; - - fn gas(&self) -> &[ObjectRef]; - - fn gas_price(&self) -> u64; - - fn gas_budget(&self) -> u64; - - fn expiration(&self) -> &TransactionExpiration; - -} - -impl TransactionDataAPI for TransactionDataV1 { - fn sender(&self) -> IotaAddress { - self.sender - } - - fn kind(&self) -> &TransactionKind { - &self.kind - } - - fn kind_mut(&mut self) -> &mut TransactionKind { - &mut self.kind - } - - fn into_kind(self) -> TransactionKind { - self.kind - } - - /// Transaction signer and Gas owner - fn signers(&self) -> NonEmpty { - let mut signers = NonEmpty::new(self.sender); - if self.gas_owner() != self.sender { - signers.push(self.gas_owner()); - } - signers - } - - fn gas_data(&self) -> &GasData { - &self.gas_data - } - - fn gas_owner(&self) -> IotaAddress { - self.gas_data.owner - } - - fn gas(&self) -> &[ObjectRef] { - &self.gas_data.payment - } - - fn gas_price(&self) -> u64 { - self.gas_data.price - } - - fn gas_budget(&self) -> u64 { - self.gas_data.budget - } - - fn expiration(&self) -> &TransactionExpiration { - &self.expiration - } - - fn gas_data_mut(&mut self) -> &mut GasData { - &mut self.gas_data - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, PartialOrd, Ord, Hash)] -pub enum InputObjectKind { - // A Move package, must be immutable. - MovePackage(ObjectID), - // A Move object, either immutable, or owned mutable. - ImmOrOwnedMoveObject(ObjectRef), - // A Move object that's shared and mutable. - SharedMoveObject { - id: ObjectID, - initial_shared_version: SequenceNumber, - mutable: bool, - }, -} - -impl InputObjectKind { - pub fn object_id(&self) -> ObjectID { - match self { - Self::MovePackage(id) => *id, - Self::ImmOrOwnedMoveObject((id, _, _)) => *id, - Self::SharedMoveObject { id, .. } => *id, - } - } - - pub fn version(&self) -> Option { - match self { - Self::MovePackage(..) => None, - Self::ImmOrOwnedMoveObject((_, version, _)) => Some(*version), - Self::SharedMoveObject { .. } => None, - } - } - - pub fn object_not_found_error(&self) -> UserInputError { - match *self { - Self::MovePackage(package_id) => { - UserInputError::DependentPackageNotFound { package_id } - } - Self::ImmOrOwnedMoveObject((object_id, version, _)) => UserInputError::ObjectNotFound { - object_id, - version: Some(version), - }, - Self::SharedMoveObject { id, .. } => UserInputError::ObjectNotFound { - object_id: id, - version: None, - }, - } - } - - pub fn is_shared_object(&self) -> bool { - matches!(self, Self::SharedMoveObject { .. }) - } - - pub fn is_mutable(&self) -> bool { - match self { - Self::MovePackage(..) => false, - Self::ImmOrOwnedMoveObject((_, _, _)) => true, - Self::SharedMoveObject { mutable, .. } => *mutable, - } - } -} \ 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 deleted file mode 100644 index 6d75bfa2d..000000000 --- a/identity_iota_interaction/src/sdk_types/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -// 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 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_core_types/account_address.rs b/identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs deleted file mode 100644 index d52b19195..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/account_address.rs +++ /dev/null @@ -1,337 +0,0 @@ -// 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(); - - pub const fn from_suffix(suffix: u16) -> AccountAddress { - let mut addr = [0u8; AccountAddress::LENGTH]; - let [hi, lo] = suffix.to_be_bytes(); - addr[AccountAddress::LENGTH - 2] = hi; - addr[AccountAddress::LENGTH - 1] = lo; - AccountAddress::new(addr) - } - - 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) - } - - // AbstractMemorySize is not available for wasm32 - // - // /// TODO (ade): use macro to enfornce determinism - // pub fn abstract_size_for_gas_metering(&self) -> AbstractMemorySize { - // AbstractMemorySize::new(Self::LENGTH as u64) - // } -} - -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 deleted file mode 100644 index 09a4ceca5..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/annotated_value.rs +++ /dev/null @@ -1,774 +0,0 @@ -// 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::{ - collections::BTreeMap, - fmt::{self, Debug}, - io::Cursor, -}; - -use anyhow::Result as AResult; -use serde::{ - Deserialize, Serialize, - de::Error as DeError, - ser::{SerializeMap, SerializeSeq, SerializeStruct}, -}; - -use super::{ - VARIANT_COUNT_MAX, - account_address::AccountAddress, - annotated_visitor::{Error as VError, ValueDriver, Visitor, visit_struct, visit_value}, - identifier::Identifier, - language_storage::{StructTag, TypeTag}, - runtime_value::{self as R, MOVE_STRUCT_FIELDS, MOVE_STRUCT_TYPE}, - 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 enum/struct gets serialized into a -/// Serde struct with this as the first field -pub const MOVE_DATA_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_DATA_FIELDS: &str = "fields"; - -/// In the `WithTypes` configuration, a Move enum gets serialized into a Serde -/// struct with this as the second field In the `WithFields` configuration, this -/// is the first field of the serialized enum -pub const MOVE_VARIANT_NAME: &str = "variant_name"; - -/// Field name for the tag of the variant -pub const MOVE_VARIANT_TAG_NAME: &str = "variant_tag"; - -/// In the `WithTypes` configuration, a Move enum gets serialized into a Serde -/// struct with this name -pub const MOVE_ENUM_NAME: &str = "enum"; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct MoveStruct { - pub type_: StructTag, - pub fields: Vec<(Identifier, MoveValue)>, -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct MoveVariant { - pub type_: StructTag, - pub variant_name: Identifier, - pub tag: u16, - 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), - Variant(MoveVariant), -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -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, PartialEq, Eq)] -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, PartialEq, Eq)] -pub struct MoveEnumLayout { - pub type_: StructTag, - pub variants: BTreeMap<(Identifier, u16), Vec>, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum MoveDatatypeLayout { - Struct(Box), - Enum(Box), -} - -impl MoveDatatypeLayout { - pub fn into_layout(self) -> MoveTypeLayout { - match self { - Self::Struct(s) => MoveTypeLayout::Struct(s), - Self::Enum(e) => MoveTypeLayout::Enum(e), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -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(Box), - #[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, - #[serde(rename(serialize = "enum", deserialize = "enum"))] - Enum(Box), -} - -impl MoveStructLayout { - /// Returns `true` if and only if the layout is for `type_`. - pub fn is_type(&self, type_: &StructTag) -> bool { - self.type_ == *type_ - } -} - -impl MoveEnumLayout { - /// Returns `true` if and only if the layout is for `type_`. - pub fn is_type(&self, type_: &StructTag) -> bool { - self.type_ == *type_ - } -} - -impl MoveTypeLayout { - /// Returns `true` if and only if the layout is for `type_`. - pub fn is_type(&self, type_: &TypeTag) -> bool { - use MoveTypeLayout as L; - use TypeTag as T; - - match self { - L::Bool => matches!(type_, T::Bool), - L::U8 => matches!(type_, T::U8), - L::U16 => matches!(type_, T::U16), - L::U32 => matches!(type_, T::U32), - L::U64 => matches!(type_, T::U64), - L::U128 => matches!(type_, T::U128), - L::U256 => matches!(type_, T::U256), - L::Address => matches!(type_, T::Address), - L::Signer => matches!(type_, T::Signer), - L::Vector(l) => matches!(type_, T::Vector(t) if l.is_type(t)), - L::Struct(l) => matches!(type_, T::Struct(t) if l.is_type(t)), - L::Enum(l) => matches!(type_, T::Struct(t) if l.is_type(t)), - } - } -} - -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<'b, 'l, V: Visitor<'b, 'l>>( - blob: &'b [u8], - ty: &'l MoveTypeLayout, - visitor: &mut V, - ) -> AResult - where - V::Error: std::error::Error + Send + Sync + 'static, - { - let mut bytes = Cursor::new(blob); - let res = visit_value(&mut bytes, ty, visitor)?; - if bytes.position() as usize == blob.len() { - Ok(res) - } else { - let remaining = blob.len() - bytes.position() as usize; - Err(VError::TrailingBytes(remaining).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::Variant(v) => R::MoveValue::Variant(v.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<'b, 'l, V: Visitor<'b, 'l>>( - blob: &'b [u8], - ty: &'l MoveStructLayout, - visitor: &mut V, - ) -> AResult - where - V::Error: std::error::Error + Send + Sync + 'static, - { - let mut bytes = Cursor::new(blob); - let driver = ValueDriver::new(&mut bytes, None); - let res = visit_struct(driver, ty, visitor)?; - if bytes.position() as usize == blob.len() { - Ok(res) - } else { - let remaining = blob.len() - bytes.position() as usize; - Err(VError::TrailingBytes(remaining).into()) - } - } - - pub fn into_fields(self) -> Vec { - self.fields.into_iter().map(|(_, v)| v).collect() - } - - pub fn undecorate(self) -> R::MoveStruct { - R::MoveStruct( - self.into_fields() - .into_iter() - .map(MoveValue::undecorate) - .collect(), - ) - } -} - -impl MoveVariant { - pub fn new( - type_: StructTag, - variant_name: Identifier, - tag: u16, - fields: Vec<(Identifier, MoveValue)>, - ) -> Self { - Self { - type_, - variant_name, - tag, - fields, - } - } - - pub fn simple_deserialize(blob: &[u8], ty: &MoveEnumLayout) -> AResult { - Ok(bcs::from_bytes_seed(ty, blob)?) - } - - pub fn into_fields(self) -> Vec { - self.fields.into_iter().map(|(_, v)| v).collect() - } - - pub fn undecorate(self) -> R::MoveVariant { - R::MoveVariant { - tag: self.tag, - fields: 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::Enum(ty) => Ok(MoveValue::Variant(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<'d> serde::de::DeserializeSeed<'d> for &MoveEnumLayout { - type Value = MoveVariant; - fn deserialize>( - self, - deserializer: D, - ) -> Result { - let (variant_name, tag, fields) = - deserializer.deserialize_tuple(2, DecoratedEnumFieldVisitor(&self.variants))?; - Ok(MoveVariant { - type_: self.type_.clone(), - variant_name, - tag, - fields, - }) - } -} - -struct DecoratedEnumFieldVisitor<'a>(&'a BTreeMap<(Identifier, u16), Vec>); - -impl<'d, 'a> serde::de::Visitor<'d> for DecoratedEnumFieldVisitor<'a> { - type Value = (Identifier, u16, Vec<(Identifier, MoveValue)>); - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("Enum") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'d>, - { - let tag = match seq.next_element_seed(&MoveTypeLayout::U8)? { - Some(MoveValue::U8(tag)) if tag as u64 <= VARIANT_COUNT_MAX => tag as u16, - Some(MoveValue::U8(tag)) => return Err(A::Error::invalid_length(tag as usize, &self)), - Some(val) => { - return Err(A::Error::invalid_type( - serde::de::Unexpected::Other(&format!("{val:?}")), - &self, - )); - } - None => return Err(A::Error::invalid_length(0, &self)), - }; - - let Some(((variant_name, _), variant_layout)) = - self.0.iter().find(|((_, v_tag), _)| *v_tag == tag) - else { - return Err(A::Error::invalid_length(tag as usize, &self)); - }; - - let Some(fields) = seq.next_element_seed(&DecoratedVariantFieldLayout(variant_layout))? - else { - return Err(A::Error::invalid_length(1, &self)); - }; - - Ok((variant_name.clone(), tag, fields)) - } -} - -struct DecoratedVariantFieldLayout<'a>(&'a Vec); - -impl<'d, 'a> serde::de::DeserializeSeed<'d> for &DecoratedVariantFieldLayout<'a> { - type Value = Vec<(Identifier, MoveValue)>; - - fn deserialize>( - self, - deserializer: D, - ) -> Result { - deserializer.deserialize_tuple(self.0.len(), DecoratedStructFieldVisitor(self.0)) - } -} - -impl serde::Serialize for MoveValue { - fn serialize(&self, serializer: S) -> Result { - match self { - MoveValue::Struct(s) => s.serialize(serializer), - MoveValue::Variant(v) => v.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 serde::Serialize for MoveVariant { - fn serialize(&self, serializer: S) -> Result { - // Serialize an enum as: - // enum { "type": 0xC::module::enum_type, "variant_name": name, "variant_tag": - // tag, "fields": { ... } } - let mut t = serializer.serialize_struct(MOVE_ENUM_NAME, 4)?; - t.serialize_field(MOVE_DATA_TYPE, &self.type_.to_string())?; - t.serialize_field(MOVE_VARIANT_NAME, &self.variant_name.to_string())?; - t.serialize_field(MOVE_VARIANT_TAG_NAME, &MoveValue::U16(self.tag))?; - t.serialize_field(MOVE_DATA_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}"), - Enum(e) if f.alternate() => write!(f, "{e:#}"), - Enum(e) => write!(f, "enum {}", e), - } - } -} - -/// 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 fmt::Display for MoveEnumLayout { - fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { - use DebugAsDisplay as DD; - write!(f, "enum {} ", self.type_)?; - let mut vmap = f.debug_set(); - for ((variant_name, _), fields) in self.variants.iter() { - vmap.entry(&DD(&MoveVariantDisplay(variant_name.as_str(), fields))); - } - vmap.finish() - } -} - -struct MoveVariantDisplay<'a>(&'a str, &'a [MoveFieldLayout]); - -impl<'a> fmt::Display for MoveVariantDisplay<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { - use DebugAsDisplay as DD; - let mut map = f.debug_struct(self.0); - for field in self.1 { - map.field(field.name.as_str(), &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.as_ref().into())), - MoveTypeLayout::Enum(e) => TypeTag::Struct(Box::new(e.as_ref().into())), - } - } -} - -impl From<&MoveStructLayout> for StructTag { - fn from(val: &MoveStructLayout) -> StructTag { - val.type_.clone() - } -} - -impl From<&MoveEnumLayout> for StructTag { - fn from(val: &MoveEnumLayout) -> 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), - MoveValue::Variant(v) => fmt::Display::fmt(v, 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() - } -} - -impl fmt::Display for MoveVariant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use DebugAsDisplay as DD; - let MoveVariant { - type_, - variant_name, - tag: _, - fields, - } = self; - write!(f, "{}::{}", type_, variant_name)?; - let mut map = f.debug_map(); - for (field, value) in fields { - map.entry(&DD(field), &DD(value)); - } - map.finish() - } -} 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 deleted file mode 100644 index 354e38bf2..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/annotated_visitor.rs +++ /dev/null @@ -1,814 +0,0 @@ -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::io::{Cursor, Read}; - -use super::{ - VARIANT_COUNT_MAX, - account_address::AccountAddress, - annotated_value::{MoveEnumLayout, MoveFieldLayout, MoveStructLayout, MoveTypeLayout}, - identifier::IdentStr, - u256::U256, -}; - -/// Visitors can be used for building values out of a serialized Move struct or -/// value. -pub trait Visitor<'b, 'l> { - 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, - driver: &ValueDriver<'_, 'b, 'l>, - value: u8, - ) -> Result; - - fn visit_u16( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u16, - ) -> Result; - - fn visit_u32( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u32, - ) -> Result; - - fn visit_u64( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u64, - ) -> Result; - - fn visit_u128( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u128, - ) -> Result; - - fn visit_u256( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: U256, - ) -> Result; - - fn visit_bool( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: bool, - ) -> Result; - - fn visit_address( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: AccountAddress, - ) -> Result; - - fn visit_signer( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: AccountAddress, - ) -> Result; - - fn visit_vector( - &mut self, - driver: &mut VecDriver<'_, 'b, 'l>, - ) -> Result; - - fn visit_struct( - &mut self, - driver: &mut StructDriver<'_, 'b, 'l>, - ) -> Result; - - fn visit_variant( - &mut self, - driver: &mut VariantDriver<'_, 'b, 'l>, - ) -> 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<'b, 'l> { - type Error: From; - - fn traverse_u8( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u8, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_u16( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u16, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_u32( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u32, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_u64( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u64, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_u128( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: u128, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_u256( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: U256, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_bool( - &mut self, - _driver: &ValueDriver<'_, 'b, 'l>, - _value: bool, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_address( - &mut self, - _: &ValueDriver<'_, 'b, 'l>, - _: AccountAddress, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_signer( - &mut self, - _: &ValueDriver<'_, 'b, 'l>, - _: AccountAddress, - ) -> Result<(), Self::Error> { - Ok(()) - } - - fn traverse_vector(&mut self, driver: &mut VecDriver<'_, 'b, 'l>) -> Result<(), Self::Error> { - while driver.next_element(self)?.is_some() {} - Ok(()) - } - - fn traverse_struct( - &mut self, - driver: &mut StructDriver<'_, 'b, 'l>, - ) -> Result<(), Self::Error> { - while driver.next_field(self)?.is_some() {} - Ok(()) - } - - fn traverse_variant( - &mut self, - driver: &mut VariantDriver<'_, 'b, 'l>, - ) -> Result<(), Self::Error> { - while driver.next_field(self)?.is_some() {} - Ok(()) - } -} - -/// Default implementation converting any traversal into a visitor. -impl<'b, 'l, T: Traversal<'b, 'l> + ?Sized> Visitor<'b, 'l> for T { - type Value = (); - type Error = T::Error; - - fn visit_u8( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u8, - ) -> Result { - self.traverse_u8(driver, value) - } - - fn visit_u16( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u16, - ) -> Result { - self.traverse_u16(driver, value) - } - - fn visit_u32( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u32, - ) -> Result { - self.traverse_u32(driver, value) - } - - fn visit_u64( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u64, - ) -> Result { - self.traverse_u64(driver, value) - } - - fn visit_u128( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: u128, - ) -> Result { - self.traverse_u128(driver, value) - } - - fn visit_u256( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: U256, - ) -> Result { - self.traverse_u256(driver, value) - } - - fn visit_bool( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: bool, - ) -> Result { - self.traverse_bool(driver, value) - } - - fn visit_address( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: AccountAddress, - ) -> Result { - self.traverse_address(driver, value) - } - - fn visit_signer( - &mut self, - driver: &ValueDriver<'_, 'b, 'l>, - value: AccountAddress, - ) -> Result { - self.traverse_signer(driver, value) - } - - fn visit_vector( - &mut self, - driver: &mut VecDriver<'_, 'b, 'l>, - ) -> Result { - self.traverse_vector(driver) - } - - fn visit_struct( - &mut self, - driver: &mut StructDriver<'_, 'b, 'l>, - ) -> Result { - self.traverse_struct(driver) - } - - fn visit_variant( - &mut self, - driver: &mut VariantDriver<'_, 'b, 'l>, - ) -> Result { - self.traverse_variant(driver) - } -} - -/// Exposes information about the byte stream that the value being visited came -/// from, namely the bytes themselves, and the offset at which the value starts. -/// Also exposes the layout of the value being visited. -pub struct ValueDriver<'c, 'b, 'l> { - bytes: &'c mut Cursor<&'b [u8]>, - layout: Option<&'l MoveTypeLayout>, - start: usize, -} - -/// 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<'c, 'b, 'l> { - inner: ValueDriver<'c, 'b, 'l>, - 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<'c, 'b, 'l> { - inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveStructLayout, - off: u64, -} - -/// Exposes information about a variant being visited (its layout, details about -/// the next field to be visited, the variant's tag, and name) to a visitor -/// implementation, and allows that visitor to progress the traversal (by -/// visiting or skipping fields). -pub struct VariantDriver<'c, 'b, 'l> { - inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveEnumLayout, - tag: u16, - variant_name: &'l IdentStr, - variant_layout: &'l [MoveFieldLayout], - off: u64, -} - -#[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), - - #[error("invalid variant tag: {0}")] - UnexpectedVariantTag(usize), - - #[error("no layout available for value")] - NoValueLayout, -} - -/// 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<'b, 'l> Traversal<'b, 'l> for NullTraversal { - type Error = Error; -} - -impl<'c, 'b, 'l> ValueDriver<'c, 'b, 'l> { - pub(crate) fn new(bytes: &'c mut Cursor<&'b [u8]>, layout: Option<&'l MoveTypeLayout>) -> Self { - let start = bytes.position() as usize; - Self { - bytes, - layout, - start, - } - } - - /// The offset at which the value being visited starts in the byte stream. - pub fn start(&self) -> usize { - self.start - } - - /// The current position in the byte stream. - pub fn position(&self) -> usize { - self.bytes.position() as usize - } - - /// All the bytes in the byte stream (including the ones that have been - /// read). - pub fn bytes(&self) -> &'b [u8] { - self.bytes.get_ref() - } - /// The bytes that haven't been consumed by the visitor yet. - pub fn remaining_bytes(&self) -> &'b [u8] { - &self.bytes.get_ref()[self.position()..] - } - - /// Type layout for the value being visited. May produce an error if a - /// layout was not supplied when the driver was created (which should - /// only happen if the driver was created for visiting a struct - /// specifically). - pub fn layout(&self) -> Result<&'l MoveTypeLayout, Error> { - self.layout.ok_or(Error::NoValueLayout) - } - - fn read_exact(&mut self) -> Result<[u8; N], Error> { - let mut buf = [0u8; N]; - self.bytes - .read_exact(&mut buf) - .map_err(|_| Error::UnexpectedEof)?; - Ok(buf) - } - - fn read_leb128(&mut self) -> Result { - leb128::read::unsigned(self.bytes).map_err(|_| Error::UnexpectedEof) - } -} - -#[allow(clippy::len_without_is_empty)] -impl<'c, 'b, 'l> VecDriver<'c, 'b, 'l> { - fn new(inner: ValueDriver<'c, 'b, 'l>, layout: &'l MoveTypeLayout, len: u64) -> Self { - Self { - inner, - layout, - len, - off: 0, - } - } - - /// The offset at which the value being visited starts in the byte stream. - pub fn start(&self) -> usize { - self.inner.start() - } - - /// The current position in the byte stream. - pub fn position(&self) -> usize { - self.inner.position() - } - - /// All the bytes in the byte stream (including the ones that have been - /// read). - pub fn bytes(&self) -> &'b [u8] { - self.inner.bytes() - } - - /// The bytes that haven't been consumed by the visitor yet. - pub fn remaining_bytes(&self) -> &'b [u8] { - self.inner.remaining_bytes() - } - - /// Type layout for the vector's inner type. - pub fn element_layout(&self) -> &'l MoveTypeLayout { - self.layout - } - - /// The number of elements in this vector that have been visited so far. - pub fn off(&self) -> u64 { - self.off - } - - /// 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 + ?Sized>( - &mut self, - visitor: &mut V, - ) -> Result, V::Error> { - Ok(if self.off >= self.len { - None - } else { - let res = visit_value(self.inner.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<'c, 'b, 'l> StructDriver<'c, 'b, 'l> { - fn new(inner: ValueDriver<'c, 'b, 'l>, layout: &'l MoveStructLayout) -> Self { - Self { - inner, - layout, - off: 0, - } - } - - /// The offset at which the value being visited starts in the byte stream. - pub fn start(&self) -> usize { - self.inner.start() - } - - /// The current position in the byte stream. - pub fn position(&self) -> usize { - self.inner.position() - } - - /// All the bytes in the byte stream (including the ones that have been - /// read). - pub fn bytes(&self) -> &'b [u8] { - self.inner.bytes() - } - - /// The bytes that haven't been consumed by the visitor yet. - pub fn remaining_bytes(&self) -> &'b [u8] { - self.inner.remaining_bytes() - } - - /// The layout of the struct being visited. - pub fn struct_layout(&self) -> &'l MoveStructLayout { - self.layout - } - - /// The number of fields in this struct that have been visited so far. - pub fn off(&self) -> u64 { - self.off - } - - /// 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 as usize) - } - - /// 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 + ?Sized>( - &mut self, - visitor: &mut V, - ) -> Result, V::Error> { - let Some(field) = self.peek_field() else { - return Ok(None); - }; - - let res = visit_value(self.inner.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)) - } -} - -impl<'c, 'b, 'l> VariantDriver<'c, 'b, 'l> { - fn new( - inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveEnumLayout, - variant_layout: &'l [MoveFieldLayout], - variant_name: &'l IdentStr, - tag: u16, - ) -> Self { - Self { - inner, - layout, - tag, - variant_name, - variant_layout, - off: 0, - } - } - - /// The offset at which the value being visited starts in the byte stream. - pub fn start(&self) -> usize { - self.inner.start() - } - - /// The current position in the byte stream. - pub fn position(&self) -> usize { - self.inner.position() - } - - /// All the bytes in the byte stream (including the ones that have been - /// read). - pub fn bytes(&self) -> &'b [u8] { - self.inner.bytes() - } - - /// The bytes that haven't been consumed by the visitor yet. - pub fn remaining_bytes(&self) -> &'b [u8] { - self.inner.remaining_bytes() - } - - /// The layout of the enum being visited. - pub fn enum_layout(&self) -> &'l MoveEnumLayout { - self.layout - } - - /// The layout of the variant being visited. - pub fn variant_layout(&self) -> &'l [MoveFieldLayout] { - self.variant_layout - } - - /// The tag of the variant being visited. - pub fn tag(&self) -> u16 { - self.tag - } - - /// The name of the enum variant being visited. - pub fn variant_name(&self) -> &'l IdentStr { - self.variant_name - } - - /// The number of elements in this vector that have been visited so far. - pub fn off(&self) -> u64 { - self.off - } - - /// 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.variant_layout.get(self.off as usize) - } - - /// Visit the next field in the variant. 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 variant. - /// - /// Returns `Ok(None)` if there are no more fields in the variant, `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 + ?Sized>( - &mut self, - visitor: &mut V, - ) -> Result, V::Error> { - let Some(field) = self.peek_field() else { - return Ok(None); - }; - - let res = visit_value(self.inner.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 -/// `annoted_value::MoveValue::visit_deserialize` for details. -pub(crate) fn visit_value<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( - bytes: &'c mut Cursor<&'b [u8]>, - layout: &'l MoveTypeLayout, - visitor: &mut V, -) -> Result { - use MoveTypeLayout as L; - - let mut driver = ValueDriver::new(bytes, Some(layout)); - match layout { - L::Bool => match driver.read_exact()? { - [0] => visitor.visit_bool(&driver, false), - [1] => visitor.visit_bool(&driver, true), - [b] => Err(Error::UnexpectedByte(b).into()), - }, - - L::U8 => { - let v = u8::from_le_bytes(driver.read_exact()?); - visitor.visit_u8(&driver, v) - } - - L::U16 => { - let v = u16::from_le_bytes(driver.read_exact()?); - visitor.visit_u16(&driver, v) - } - - L::U32 => { - let v = u32::from_le_bytes(driver.read_exact()?); - visitor.visit_u32(&driver, v) - } - - L::U64 => { - let v = u64::from_le_bytes(driver.read_exact()?); - visitor.visit_u64(&driver, v) - } - - L::U128 => { - let v = u128::from_le_bytes(driver.read_exact()?); - visitor.visit_u128(&driver, v) - } - - L::U256 => { - let v = U256::from_le_bytes(&driver.read_exact()?); - visitor.visit_u256(&driver, v) - } - - L::Address => { - let v = AccountAddress::new(driver.read_exact()?); - visitor.visit_address(&driver, v) - } - - L::Signer => { - let v = AccountAddress::new(driver.read_exact()?); - visitor.visit_signer(&driver, v) - } - - L::Vector(l) => visit_vector(driver, l.as_ref(), visitor), - L::Struct(l) => visit_struct(driver, l, visitor), - L::Enum(e) => visit_variant(driver, e, visitor), - } -} - -/// Like `visit_value` but specialized to visiting a vector (where the `bytes` -/// is known to be a serialized move vector), and the layout is the vector's -/// element's layout. -fn visit_vector<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( - mut inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveTypeLayout, - visitor: &mut V, -) -> Result { - let len = inner.read_leb128()?; - let mut driver = VecDriver::new(inner, layout, len); - let res = visitor.visit_vector(&mut driver)?; - while driver.skip_element()? {} - Ok(res) - } - -/// 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<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( - inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveStructLayout, - visitor: &mut V, -) -> Result { - let mut driver = StructDriver::new(inner, layout); - let res = visitor.visit_struct(&mut driver)?; - while driver.skip_field()?.is_some() {} - Ok(res) -} - -/// Like `visit_struct` but specialized to visiting a variant (where the `bytes` -/// is known to be a serialized move variant), and the layout is an enum layout. -fn visit_variant<'c, 'b, 'l, V: Visitor<'b, 'l> + ?Sized>( - mut inner: ValueDriver<'c, 'b, 'l>, - layout: &'l MoveEnumLayout, - visitor: &mut V, -) -> Result { - // Since variants are bounded at 127, we can read the tag as a single byte. - // When we add true ULEB encoding for enum variants switch to this: - // let tag = inner.read_leb128()?; - let [tag] = inner.read_exact()?; - if tag >= VARIANT_COUNT_MAX as u8 { - return Err(Error::UnexpectedVariantTag(tag as usize).into()); - } - let variant_layout = layout - .variants - .iter() - .find(|((_, vtag), _)| *vtag == tag as u16) - .ok_or(Error::UnexpectedVariantTag(tag as usize))?; - - let mut driver = VariantDriver::new( - inner, - layout, - variant_layout.1, - &variant_layout.0.0, - tag as u16, - ); - let res = visitor.visit_variant(&mut driver)?; - while driver.skip_field()?.is_some() {} - Ok(res) -} 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 deleted file mode 100644 index 82c6f3d0d..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/identifier.rs +++ /dev/null @@ -1,243 +0,0 @@ -// 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); - } - } - - /// Creates a new `Identifier` from a string without checking if it is a - /// valid identifier. This should not be used under normal - /// circumstances, but is used in cases where we need to - /// preserve backwards compatibility. - /// - /// # Safety - /// - /// Only use this function when preserving backwards compatibility. - pub unsafe fn new_unchecked(s: impl Into>) -> Self { - Self(s.into()) - } - - /// 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() - } - - // AbstractMemorySize is not available for wasm32 - // - // /// Returns the abstract size of the struct - // /// TODO (ade): use macro to enfornce determinism - // pub fn abstract_size_for_gas_metering(&self) -> AbstractMemorySize { - // AbstractMemorySize::new((self.len()) as u64) - // } -} - -impl Borrow for Identifier { - fn borrow(&self) -> &IdentStr { - self - } -} - -impl Borrow for Identifier { - fn borrow(&self) -> &str { - &self.0 - } -} - -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 deleted file mode 100644 index 8d9b677a2..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/language_storage.rs +++ /dev/null @@ -1,350 +0,0 @@ -// 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, - identifier::{IdentStr, Identifier}, - parsing::types::{ParsedModuleId, ParsedStructType, ParsedType}, -}; - -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: - /// - /// - "bool", "u8", "u16", "u32", "u64", "u128", "u256", "address", - /// "signer", "vector" for ground types. - /// - /// - Structs are represented as fully qualified type names, with or without - /// the prefix "0x" depending on the `with_prefix` flag, e.g. - /// `0x000...0001::string::String` or - /// `0x000...000a::m::T<0x000...000a::n::U>`. - /// - /// - Addresses are hex-encoded lowercase values of length 32 (zero-padded). - /// - /// Note: this function is guaranteed to be stable -- suitable for use - /// inside Move native functions or the VM. By contrast, this type's - /// `Display` implementation is subject to change and should be used - /// inside code that needs to return a stable output (e.g. that might be - /// committed to effects on-chain). - 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 type with optional - /// 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 { - ParsedType::parse(s)?.into_type_tag(&|_| None) - } -} - -#[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. - /// - /// - Structs are represented as fully qualified type names, with or without - /// the prefix "0x" depending on the `with_prefix` flag, e.g. - /// `0x000...0001::string::String` or - /// `0x000...000a::m::T<0x000...000a::n::U>`. - /// - /// - Addresses are hex-encoded lowercase values of length 32 (zero-padded). - /// - /// Note: this function is guaranteed to be stable -- suitable for use - /// inside Move native functions or the VM. By contrast, this type's - /// `Display` implementation is subject to change and should be used - /// inside code that needs to return a stable output (e.g. that might be - /// committed to effects on-chain). - 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 - /// optional 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 { - ParsedStructType::parse(s)?.into_struct_tag(&|_| None) - } -} - -/// 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 FromStr for ModuleId { - type Err = anyhow::Error; - fn from_str(s: &str) -> Result { - ParsedModuleId::parse(s)?.into_module_id(&|_| None) - } -} - -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 From for TypeTag { - fn from(t: StructTag) -> TypeTag { - TypeTag::Struct(Box::new(t)) - } -} - 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 deleted file mode 100644 index c6278bedd..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -// 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 runtime_value; -pub mod u256; -pub mod parsing; - -use std::fmt; - -pub const VARIANT_COUNT_MAX: u64 = 127; - -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(()) -} diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs deleted file mode 100644 index 0ac720345..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/address.rs +++ /dev/null @@ -1,202 +0,0 @@ -// 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::{self, Some, None}; -use std::string::String; - -use anyhow::anyhow; - -use super::super::account_address::AccountAddress; -use super::super::u256::U256; -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: 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 = U256::from_be_bytes(&self.bytes); - 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 PartialEq for NumericalAddress { - fn eq(&self, other: &AccountAddress) -> bool { - let Self { - bytes: self_bytes, - format: _, - } = self; - self_bytes == other - } -} - -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_core_types/parsing/mod.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/mod.rs deleted file mode 100644 index bdb2683bc..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#![forbid(unsafe_code)] - -pub mod address; -pub mod parser; -pub mod types; -pub mod values; diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs deleted file mode 100644 index becea624d..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/parser.rs +++ /dev/null @@ -1,470 +0,0 @@ -// 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::{ - account_address::AccountAddress, - u256::{U256FromStrError, U256}, -}; - -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; -// See: https://stackoverflow.com/questions/43787672/the-max-number-of-digits-in-an-int-based-on-number-of-bits -const U256_MAX_DECIMAL_DIGITS: usize = 241 * AccountAddress::LENGTH / 100 + 1; - -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()) - } -} - -pub(crate) 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()) { - if allow_trailing_delim { - break; - } else { - bail!("Invalid type list: trailing delimiter '{}'", delim) - } - } - } - 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)?; - if type_args.is_empty() { - bail!("expected at least one type argument") - } - 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<(AccountAddress, NumberFormat)> { - let (txt, base) = determine_num_text_and_base(s); - let txt = txt.replace('_', ""); - let max_len = match base { - NumberFormat::Hex => AccountAddress::LENGTH * 2, - NumberFormat::Decimal => U256_MAX_DECIMAL_DIGITS, - }; - if txt.len() > max_len { - return None; - } - let parsed = U256::from_str_radix(&txt, match base { - NumberFormat::Hex => 16, - NumberFormat::Decimal => 10, - }) - .ok()?; - Some((AccountAddress::new(parsed.to_be_bytes()), base)) -} diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs deleted file mode 100644 index 2dc1142ba..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/types.rs +++ /dev/null @@ -1,195 +0,0 @@ -// 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::{ - account_address::AccountAddress, - identifier::{self, Identifier}, - 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')) => { - chars.next().unwrap(); - match chars.next() { - Some(c) if c.is_ascii_hexdigit() => { - // 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(|c| c.is_ascii_digit() || *c == '_') - .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 == '_' - && chars - .peek() - .is_some_and(|c| identifier::is_valid_identifier_char(*c))) => - { - // c + remaining - let len = 1 + chars - .take_while(|c| identifier::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)?)), - }) - } -} diff --git a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs b/identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs deleted file mode 100644 index 1ad1322e2..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/parsing/values.rs +++ /dev/null @@ -1,320 +0,0 @@ -// 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::{ - account_address::AccountAddress, - identifier, - runtime_value::{MoveStruct, MoveValue}, - u256::U256, -}; - -use super::{ - address::ParsedAddress, - parser::{Parser, Token}, -}; - -#[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), // Imported at the top of this file - U8(u8), - U16(u16), - U32(u32), - U64(u64), - U128(u128), - U256(U256), // Imported at the top of this file - 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| identifier::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), - } - } -} 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 deleted file mode 100644 index ad6cfc115..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/runtime_value.rs +++ /dev/null @@ -1,584 +0,0 @@ -// Copyright (c) The Diem Core Contributors -// Copyright (c) The Move Contributors -// Modifications Copyright (c) 2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use super::{ - account_address::AccountAddress, annotated_value as A, fmt_list, u256, VARIANT_COUNT_MAX, -}; -use anyhow::{anyhow, Result as AResult}; -// use move_proc_macros::test_variant_order; -use serde::{ - de::Error as DeError, - ser::{SerializeSeq, SerializeTuple}, - Deserialize, Serialize, -}; -use std::fmt::{self, Debug}; - -/// 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 struct MoveVariant { - pub tag: u16, - pub fields: 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), - Variant(MoveVariant), -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MoveStructLayout(pub Box>); - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MoveEnumLayout(pub Box>>); - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum MoveDatatypeLayout { - Struct(Box), - Enum(Box), -} - -impl MoveDatatypeLayout { - pub fn into_layout(self) -> MoveTypeLayout { - match self { - MoveDatatypeLayout::Struct(layout) => MoveTypeLayout::Struct(layout), - MoveDatatypeLayout::Enum(layout) => MoveTypeLayout::Enum(layout), - } - } -} - -#[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(Box), - #[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, - #[serde(rename(serialize = "enum", deserialize = "enum"))] - Enum(Box), -} - -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::Variant(s), A::MoveTypeLayout::Enum(l)) => { - A::MoveValue::Variant(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.iter()) - .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 MoveVariant { - pub fn new(tag: u16, fields: Vec) -> Self { - Self { tag, fields } - } - - pub fn simple_deserialize(blob: &[u8], ty: &MoveEnumLayout) -> AResult { - Ok(bcs::from_bytes_seed(ty, blob)?) - } - - pub fn decorate(self, layout: &A::MoveEnumLayout) -> A::MoveVariant { - let MoveVariant { tag, fields } = self; - let A::MoveEnumLayout { type_, variants } = layout; - let ((v_name, _), v_layout) = variants - .iter() - .find(|((_, v_tag), _)| *v_tag == tag) - .unwrap(); - A::MoveVariant { - type_: type_.clone(), - tag, - fields: fields - .into_iter() - .zip(v_layout.iter()) - .map(|(v, l)| (l.name.clone(), v.decorate(&l.layout))) - .collect(), - variant_name: v_name.clone(), - } - } - - pub fn fields(&self) -> &[MoveValue] { - &self.fields - } - - pub fn into_fields(self) -> Vec { - self.fields - } -} - -impl MoveStructLayout { - pub fn new(types: Vec) -> Self { - Self(Box::new(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::Enum(ty) => Ok(MoveValue::Variant(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<'d> serde::de::DeserializeSeed<'d> for &MoveEnumLayout { - type Value = MoveVariant; - fn deserialize>( - self, - deserializer: D, - ) -> Result { - deserializer.deserialize_tuple(2, EnumFieldVisitor(&self.0)) - } -} - -struct EnumFieldVisitor<'a>(&'a Vec>); - -impl<'d, 'a> serde::de::Visitor<'d> for EnumFieldVisitor<'a> { - type Value = MoveVariant; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("Enum") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'d>, - { - let tag = match seq.next_element_seed(&MoveTypeLayout::U8)? { - Some(MoveValue::U8(tag)) if tag as u64 <= VARIANT_COUNT_MAX => tag as u16, - Some(MoveValue::U8(tag)) => return Err(A::Error::invalid_length(tag as usize, &self)), - Some(val) => { - return Err(A::Error::invalid_type( - serde::de::Unexpected::Other(&format!("{val:?}")), - &self, - )); - } - None => return Err(A::Error::invalid_length(0, &self)), - }; - - let Some(variant_layout) = self.0.get(tag as usize) else { - return Err(A::Error::invalid_length(tag as usize, &self)); - }; - - let Some(fields) = seq.next_element_seed(&MoveVariantFieldLayout(variant_layout))? else { - return Err(A::Error::invalid_length(1, &self)); - }; - - Ok(MoveVariant { tag, fields }) - } -} - -struct MoveVariantFieldLayout<'a>(&'a [MoveTypeLayout]); - -impl<'d, 'a> serde::de::DeserializeSeed<'d> for &MoveVariantFieldLayout<'a> { - type Value = Vec; - - fn deserialize>( - self, - deserializer: D, - ) -> Result { - 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::Variant(v) => v.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 serde::Serialize for MoveVariant { - // Serialize a variant as: (tag, [fields...]) - // Since we restrict tags to be less than or equal to 127, the tag will always - // be a single byte in uleb encoding and we don't actually need to uleb - // encode it, but we can at a later date if we want/need to. - fn serialize(&self, serializer: S) -> Result { - let tag = if self.tag as u64 > VARIANT_COUNT_MAX { - return Err(serde::ser::Error::custom(format!( - "Variant tag {} is greater than the maximum allowed value of {}", - self.tag, VARIANT_COUNT_MAX - ))); - } else { - self.tag as u8 - }; - - let mut t = serializer.serialize_tuple(2)?; - - t.serialize_element(&tag)?; - t.serialize_element(&MoveFields(&self.fields))?; - - t.end() - } -} - -struct MoveFields<'a>(&'a [MoveValue]); - -impl<'a> serde::Serialize for MoveFields<'a> { - 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}"), - Enum(e) if f.alternate() => write!(f, "{e:#}"), - Enum(e) => write!(f, "{e}"), - } - } -} - -/// 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 MoveEnumLayout { - fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result { - write!(f, "enum ")?; - for (tag, variant) in self.0.iter().enumerate() { - write!(f, "variant_tag: {} {{ ", tag)?; - for (i, l) in variant.iter().enumerate() { - write!(f, "{}: {}, ", i, l)? - } - write!(f, " }} ")?; - } - Ok(()) - } -} - -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), - MoveValue::Variant(v) => fmt::Display::fmt(v, f), - } - } -} - -impl fmt::Display for MoveStruct { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_list(f, "struct[", &self.0, "]") - } -} - -impl fmt::Display for MoveVariant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt_list( - f, - &format!("variant(tag = {})[", self.tag), - &self.fields, - "]", - ) - } -} 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 deleted file mode 100644 index 82556bd04..000000000 --- a/identity_iota_interaction/src/sdk_types/move_core_types/u256.rs +++ /dev/null @@ -1,391 +0,0 @@ -// 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, - }, -}; - -// This U256 impl was chosen for now but we are open to changing it as needed -use primitive_types::U256 as PrimitiveU256; - -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use uint::FromStrRadixErr; - -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 from 32 big endian bytes - pub fn from_be_bytes(slice: &[u8; U256_NUM_BYTES]) -> Self { - Self(PrimitiveU256::from_big_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 - } - - /// U256 to 32 big endian bytes - pub fn to_be_bytes(self) -> [u8; U256_NUM_BYTES] { - let mut bytes = [0u8; U256_NUM_BYTES]; - self.0.to_big_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 deleted file mode 100644 index 03ff73711..000000000 --- a/identity_iota_interaction/src/sdk_types/shared_crypto/intent.rs +++ /dev/null @@ -1,204 +0,0 @@ -// 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., 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, - Consensus = 1, -} - -// 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 key. */ - BridgeEventUnused = 6, // for bridge purposes but it's currently not included in messages. - ConsensusBlock = 7, // 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 Intent { - pub fn to_bytes(&self) -> [u8; INTENT_PREFIX_LENGTH] { - [self.scope as u8, self.version as u8, self.app_id as u8] - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != INTENT_PREFIX_LENGTH { - return Err(eyre!("Invalid Intent")); - } - Ok(Self { - scope: bytes[0].try_into()?, - version: bytes[1].try_into()?, - app_id: bytes[2].try_into()?, - }) - } -} - -impl FromStr for Intent { - type Err = eyre::Report; - fn from_str(s: &str) -> Result { - let bytes: Vec = decode_bytes_hex(s).map_err(|_| eyre!("Invalid Intent"))?; - Self::from_bytes(bytes.as_slice()) - } -} - -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 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, -} diff --git a/identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs b/identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs deleted file mode 100644 index db49c84ed..000000000 --- a/identity_iota_interaction/src/sdk_types/shared_crypto/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// 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 deleted file mode 100644 index e6bdcc9f2..000000000 --- a/identity_iota_interaction/src/transaction_builder_trait.rs +++ /dev/null @@ -1,15 +0,0 @@ -// 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_iota_interaction/Cargo.toml b/identity_iota_move_calls/Cargo.toml similarity index 98% rename from identity_iota_interaction/Cargo.toml rename to identity_iota_move_calls/Cargo.toml index a580760c6..351bff14a 100644 --- a/identity_iota_interaction/Cargo.toml +++ b/identity_iota_move_calls/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "identity_iota_interaction" +name = "identity_iota_move_calls" version = "1.6.0-alpha" authors.workspace = true edition.workspace = true diff --git a/identity_iota_move_calls/README.md b/identity_iota_move_calls/README.md new file mode 100644 index 000000000..f37ad48a5 --- /dev/null +++ b/identity_iota_move_calls/README.md @@ -0,0 +1,8 @@ +# Platform Agnostic Interfaces for Identity Move Call + +This crate gathers interfaces that provide platform specific implementations to +interact with the identity Move package. + +Platform specific adapters (implementing the cross-platform-traits defined in this crate) are contained in +the crates [bindings/wasm/iota_move_calls](../bindings/wasm/iota_move_calls_ts) +and [iota_move_calls_rust](../identity_iota_core/src/iota_move_calls_rust). \ No newline at end of file diff --git a/identity_iota_interaction/src/lib.rs b/identity_iota_move_calls/src/lib.rs similarity index 100% rename from identity_iota_interaction/src/lib.rs rename to identity_iota_move_calls/src/lib.rs diff --git a/identity_iota_interaction/src/move_call_traits.rs b/identity_iota_move_calls/src/move_call_traits.rs similarity index 100% rename from identity_iota_interaction/src/move_call_traits.rs rename to identity_iota_move_calls/src/move_call_traits.rs diff --git a/identity_storage/Cargo.toml b/identity_storage/Cargo.toml index ff8b17af4..be0b1e765 100644 --- a/identity_storage/Cargo.toml +++ b/identity_storage/Cargo.toml @@ -37,10 +37,10 @@ tokio = { version = "1.43", default-features = false, features = ["macros", "syn zkryptium = { workspace = true, optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -identity_iota_interaction = { version = "=1.6.0-alpha", path = "../identity_iota_interaction", optional = true } +identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -identity_iota_interaction = { version = "=1.6.0-alpha", path = "../identity_iota_interaction", default-features = false, optional = true } +identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", default-features = false, optional = true } [dev-dependencies] identity_credential = { version = "=1.6.0-alpha", path = "../identity_credential", features = ["revocation-bitmap"] } @@ -61,7 +61,7 @@ iota-document = ["dep:identity_iota_core"] storage-signer = [ "identity_iota_core?/iota-client", "dep:secret-storage", - "dep:identity_iota_interaction", + "dep:identity_iota_move_calls", "dep:fastcrypto", "dep:bcs", ] @@ -74,8 +74,8 @@ jpt-bbs-plus = [ ] # Enables integration with IOTA Keytool keytool = [ - "dep:identity_iota_interaction", - "identity_iota_interaction/keytool", + "dep:identity_iota_move_calls", + "iota_interaction/keytool", "k256", "p256", "dep:fastcrypto", From 4f6ebed287493795885365446623da630b427037 Mon Sep 17 00:00:00 2001 From: chrisgitiota Date: Thu, 24 Apr 2025 19:54:30 +0200 Subject: [PATCH 04/10] Fixed cargo.toml issues and added product-core dependencies --- bindings/wasm/README.md | 4 ++-- bindings/wasm/identity_wasm/Cargo.toml | 12 ++++++------ bindings/wasm/iota_move_calls_ts/Cargo.toml | 6 ++++-- identity_iota_core/Cargo.toml | 16 ++++++++++++++-- identity_storage/Cargo.toml | 10 ++++++---- 5 files changed, 32 insertions(+), 16 deletions(-) diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 4b2507674..8cf5ee71f 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -10,9 +10,9 @@ Here is an overview of the existing artifacts: * `identity_wasm`
Exports the IdentityClient to TypeScript using wasm-bindgen generated wasm bindings -* `iota_interaction_ts`
+* `iota_move_calls_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. + and implements identity_iota_move_calls traits for wasm32 platforms. ## Building an Artifact diff --git a/bindings/wasm/identity_wasm/Cargo.toml b/bindings/wasm/identity_wasm/Cargo.toml index 8d49c83a4..a4d59e39b 100644 --- a/bindings/wasm/identity_wasm/Cargo.toml +++ b/bindings/wasm/identity_wasm/Cargo.toml @@ -39,6 +39,11 @@ tsify = "0.4.5" wasm-bindgen = { version = "0.2.100", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } +# dummy-client dependencies +#iota_interaction_ts = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_ts", optional = true } +iota_interaction_ts = { path = "../../../../product-core/bindings/wasm/iota_interaction_ts", optional = true } +iota_move_calls_ts = { version = "=1.6.0-alpha", path = "../iota_move_calls_ts", optional = true } + [dependencies.identity_iota] path = "../../../identity_iota" default-features = false @@ -53,11 +58,6 @@ 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"] } @@ -81,5 +81,5 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(wasm_bindgen_unstable_test [features] default = ["dummy-client"] -dummy-client = ["dep:iota_interaction_ts"] +dummy-client = ["dep:iota_interaction_ts", "iota_move_calls_ts"] # TODO: Rename this feature or remove it keytool = ["dep:iota_interaction_ts", "iota_interaction_ts/keytool", "identity_iota/keytool"] diff --git a/bindings/wasm/iota_move_calls_ts/Cargo.toml b/bindings/wasm/iota_move_calls_ts/Cargo.toml index c9c60c243..8aac4a97a 100644 --- a/bindings/wasm/iota_move_calls_ts/Cargo.toml +++ b/bindings/wasm/iota_move_calls_ts/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "iota_interaction_ts" +name = "iota_move_calls_ts" version = "1.6.0-alpha" authors = ["IOTA Stiftung"] edition = "2021" @@ -25,7 +25,9 @@ eyre = "0.6.12" fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", package = "fastcrypto" } futures = { version = "0.3" } identity_core = { version = "=1.6.0-alpha", path = "../../../identity_core" } -identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../../../identity_iota_move_calls", default-features = false } +identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../../../identity_iota_move_calls" } +#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false } +iota_interaction = { path = "../../../../product-core/iota_interaction", default-features = false } js-sys = { version = "0.3.61" } secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } serde = { version = "1.0", features = ["derive"] } diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index d12465989..7a0b06570 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -45,18 +45,28 @@ serde-aux = { version = "4.5.0", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", optional = true } iota-config = { git = "https://github.com/iotaledger/iota.git", package = "iota-config", tag = "v0.12.0-rc", optional = true } +#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", optional = true } +iota_interaction = { path = "../../product-core/iota_interaction", optional = true } +#iota_interaction_rust = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_rust", optional = true } +iota_interaction_rust = { path = "../../product-core/iota_interaction_rust", optional = true } iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.12.0-rc", optional = true } move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.12.0-rc", optional = true } shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "shared-crypto", tag = "v0.12.0-rc", optional = true } tokio = { version = "1.43", default-features = false, features = ["macros", "sync", "rt", "process"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", default-features = false, optional = true } +identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", optional = true } +#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false, optional = true } +iota_interaction = { path = "../../product-core/iota_interaction", default-features = false, optional = true } +#iota_interaction_rust = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_rust", optional = true } +iota_interaction_rust = { path = "../../product-core/iota_interaction_rust", optional = true } tokio = { version = "1.43", default-features = false, features = ["sync"] } # Dependency iota_interaction_ts is always used on wasm32 platform. It is not controlled by the "iota-client" feature # because it'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.6.0-alpha", path = "../bindings/wasm/iota_interaction_ts" } +#iota_interaction_ts = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_ts" } +iota_interaction_ts = { path = "../../product-core/bindings/wasm/iota_interaction_ts" } +iota_move_calls_ts = { version = "=1.6.0-alpha", path = "../bindings/wasm/iota_move_calls_ts" } [dev-dependencies] iota-crypto = { version = "0.23", default-features = false, features = ["bip39", "bip39-en"] } @@ -86,6 +96,8 @@ iota-client = [ "dep:identity_jose", "dep:iota-config", "dep:iota-crypto", + "dep:iota_interaction", + "dep:iota_interaction_rust", "dep:iota-sdk", "dep:itertools", "dep:move-core-types", diff --git a/identity_storage/Cargo.toml b/identity_storage/Cargo.toml index be0b1e765..268ee048f 100644 --- a/identity_storage/Cargo.toml +++ b/identity_storage/Cargo.toml @@ -37,10 +37,12 @@ tokio = { version = "1.43", default-features = false, features = ["macros", "syn zkryptium = { workspace = true, optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", optional = true } +#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", optional = true } +iota_interaction = { path = "../../product-core/iota_interaction", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", default-features = false, optional = true } +#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false, optional = true } +iota_interaction = { path = "../../product-core/iota_interaction", default-features = false, optional = true } [dev-dependencies] identity_credential = { version = "=1.6.0-alpha", path = "../identity_credential", features = ["revocation-bitmap"] } @@ -61,7 +63,7 @@ iota-document = ["dep:identity_iota_core"] storage-signer = [ "identity_iota_core?/iota-client", "dep:secret-storage", - "dep:identity_iota_move_calls", + "dep:iota_interaction", "dep:fastcrypto", "dep:bcs", ] @@ -74,7 +76,7 @@ jpt-bbs-plus = [ ] # Enables integration with IOTA Keytool keytool = [ - "dep:identity_iota_move_calls", + "dep:iota_interaction", "iota_interaction/keytool", "k256", "p256", From 7c7569892150d7a4dd4dd464135a307e65002198 Mon Sep 17 00:00:00 2001 From: chrisgitiota Date: Fri, 25 Apr 2025 09:58:07 +0200 Subject: [PATCH 05/10] First Rust buildable and e2e tested version using product_core --- Cargo.toml | 4 +- .../src/asset_move_calls.rs | 18 +-- .../src/bindings/keytool/signer.rs | 6 +- .../src/bindings/keytool/storage.rs | 8 +- .../src/bindings/wasm_iota_client.rs | 58 ++++---- .../src/bindings/wasm_types.rs | 26 ++-- .../iota_move_calls_ts/src/common/types.rs | 4 +- bindings/wasm/iota_move_calls_ts/src/error.rs | 8 +- .../src/identity_move_calls.rs | 22 ++-- .../src/iota_client_ts_sdk.rs | 62 ++++----- .../src/migration_move_calls.rs | 10 +- .../src/transaction_builder.rs | 4 +- identity_core/Cargo.toml | 1 + identity_core/src/common/mod.rs | 5 +- identity_iota/src/lib.rs | 2 - .../src/document/iota_document.rs | 2 +- .../src/iota_interaction_adapter.rs | 4 +- .../iota_move_calls_rust/asset_move_calls.rs | 26 ++-- .../identity_move_calls.rs | 47 +++---- .../migration_move_calls.rs | 20 +-- .../src/rebased/assets/asset.rs | 40 +++--- .../src/rebased/assets/public_available_vc.rs | 8 +- .../src/rebased/client/full_client.rs | 28 ++-- identity_iota_core/src/rebased/client/mod.rs | 2 +- .../src/rebased/client/read_only.rs | 36 ++--- identity_iota_core/src/rebased/error.rs | 17 +-- .../rebased/iota/move_calls/asset/create.rs | 14 +- .../rebased/iota/move_calls/asset/delete.rs | 16 +-- .../rebased/iota/move_calls/asset/transfer.rs | 22 ++-- .../iota/move_calls/asset/try_to_argument.rs | 8 +- .../rebased/iota/move_calls/asset/update.rs | 16 +-- .../iota/move_calls/identity/borrow_asset.rs | 22 ++-- .../iota/move_calls/identity/config.rs | 18 +-- .../identity/controller_execution.rs | 18 +-- .../iota/move_calls/identity/create.rs | 14 +- .../iota/move_calls/identity/deactivate.rs | 14 +- .../iota/move_calls/identity/proposal.rs | 16 +-- .../iota/move_calls/identity/send_asset.rs | 22 ++-- .../iota/move_calls/identity/update.rs | 14 +- .../iota/move_calls/identity/upgrade.rs | 14 +- .../src/rebased/iota/move_calls/migration.rs | 16 +-- .../src/rebased/iota/move_calls/utils.rs | 24 ++-- .../src/rebased/iota/types/mod.rs | 4 +- .../src/rebased/iota/well_known_networks.rs | 4 +- .../src/rebased/migration/alias.rs | 28 ++-- .../src/rebased/migration/controller_token.rs | 8 +- .../src/rebased/migration/identity.rs | 44 ++++--- .../src/rebased/migration/multicontroller.rs | 10 +- .../src/rebased/migration/registry.rs | 8 +- identity_iota_core/src/rebased/mod.rs | 2 +- .../src/rebased/proposals/borrow.rs | 22 ++-- .../src/rebased/proposals/config_change.rs | 16 +-- .../src/rebased/proposals/controller.rs | 26 ++-- .../src/rebased/proposals/mod.rs | 30 ++--- .../src/rebased/proposals/send.rs | 12 +- .../src/rebased/proposals/update_did_doc.rs | 10 +- .../src/rebased/proposals/upgrade.rs | 10 +- identity_iota_core/src/rebased/transaction.rs | 2 +- .../src/rebased/transaction_builder.rs | 38 +++--- identity_iota_core/src/rebased/utils.rs | 4 +- identity_iota_core/tests/e2e/asset.rs | 4 +- identity_iota_core/tests/e2e/common.rs | 14 +- identity_iota_move_calls/Cargo.toml | 3 +- identity_iota_move_calls/README.md | 2 +- identity_iota_move_calls/src/lib.rs | 124 +----------------- .../src/move_call_traits.rs | 22 ++-- .../src/key_id_storage/keytool.rs | 4 +- identity_storage/src/key_storage/keytool.rs | 12 +- identity_storage/src/storage/mod.rs | 2 +- .../src/storage/storage_signer.rs | 14 +- 70 files changed, 550 insertions(+), 665 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c96c8e8ba..8e82caf0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,8 +33,8 @@ exclude = ["bindings/wasm/identity_wasm", "bindings/grpc"] bls12_381_plus = { version = "0.8.17" } #iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction" } iota_interaction = { path = "../product-core/iota_interaction" } -#iota_interaction_rust = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_rust" } -iota_interaction_rust = { path = "../product-core/iota_interaction_rust" } +#product_common = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "product_common" } +product_common = { path = "../product-core/product_common" } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } serde_json = { version = "1.0", default-features = false } strum = { version = "0.25", default-features = false, features = ["std", "derive"] } diff --git a/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs index be0b7a087..11c8269b4 100644 --- a/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs +++ b/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs @@ -1,7 +1,7 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_iota_interaction::types::execution_status::CommandArgumentError; +use iota_interaction::types::execution_status::CommandArgumentError; use js_sys::Uint8Array; use serde::Serialize; use wasm_bindgen::prelude::wasm_bindgen; @@ -11,14 +11,14 @@ use crate::bindings::WasmObjectRef; use crate::bindings::WasmSharedObjectRef; use crate::error::TsSdkError; use crate::error::WasmError; -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; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::base_types::SequenceNumber; +use iota_interaction::types::TypeTag; +use iota_interaction::AssetMoveCalls; +use iota_interaction::MoveType; +use iota_interaction::ProgrammableTransactionBcs; #[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls/asset")] extern "C" { diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/signer.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/signer.rs index a5d77cc28..2f9179a7b 100644 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/signer.rs +++ b/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/signer.rs @@ -6,9 +6,9 @@ use std::str::FromStr; use crate::error::Result; use crate::error::WasmResult; use fastcrypto::traits::EncodeDecodeBase64 as _; -use identity_iota_interaction::types::base_types::IotaAddress; -use identity_iota_interaction::KeytoolSigner; -use identity_iota_interaction::KeytoolSignerBuilder; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::KeytoolSigner; +use iota_interaction::KeytoolSignerBuilder; use secret_storage::Signer; use serde_json::Value; use wasm_bindgen::prelude::wasm_bindgen; diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/storage.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/storage.rs index a78951a46..9f03ec277 100644 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/storage.rs +++ b/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/storage.rs @@ -3,10 +3,10 @@ use std::str::FromStr; -use identity_iota_interaction::types::base_types::IotaAddress; -use identity_iota_interaction::types::crypto::IotaKeyPair; -use identity_iota_interaction::types::crypto::SignatureScheme; -use identity_iota_interaction::KeytoolStorage; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::crypto::IotaKeyPair; +use iota_interaction::types::crypto::SignatureScheme; +use iota_interaction::KeytoolStorage; use js_sys::Array; use js_sys::JsString; use wasm_bindgen::prelude::*; diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_iota_client.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_iota_client.rs index 0bf674cfe..98d6883ac 100644 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_iota_client.rs +++ b/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_iota_client.rs @@ -1,35 +1,35 @@ // 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::WaitForTransactionParams; -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::crypto::Signature; -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::TransactionData; +use iota_interaction::error::Error as IotaRpcError; +use iota_interaction::error::IotaRpcResult; +use iota_interaction::generated_types::ExecuteTransactionBlockParams; +use iota_interaction::generated_types::GetCoinsParams; +use iota_interaction::generated_types::GetDynamicFieldObjectParams; +use iota_interaction::generated_types::GetObjectParams; +use iota_interaction::generated_types::GetOwnedObjectsParams; +use iota_interaction::generated_types::GetTransactionBlockParams; +use iota_interaction::generated_types::QueryEventsParams; +use iota_interaction::generated_types::SortOrder; +use iota_interaction::generated_types::WaitForTransactionParams; +use iota_interaction::rpc_types::CoinPage; +use iota_interaction::rpc_types::EventFilter; +use iota_interaction::rpc_types::EventPage; +use iota_interaction::rpc_types::IotaObjectDataOptions; +use iota_interaction::rpc_types::IotaObjectResponse; +use iota_interaction::rpc_types::IotaObjectResponseQuery; +use iota_interaction::rpc_types::IotaPastObjectResponse; +use iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; +use iota_interaction::rpc_types::ObjectsPage; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::SequenceNumber; +use iota_interaction::types::crypto::Signature; +use iota_interaction::types::digests::TransactionDigest; +use iota_interaction::types::dynamic_field::DynamicFieldName; +use iota_interaction::types::event::EventID; +use iota_interaction::types::quorum_driver_types::ExecuteTransactionRequestType; +use iota_interaction::types::transaction::TransactionData; use js_sys::Promise; use serde::Serialize; use wasm_bindgen::prelude::*; diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_types.rs index 7e7abda81..cc7788f31 100644 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_types.rs +++ b/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_types.rs @@ -5,19 +5,19 @@ use std::str::FromStr; use fastcrypto::encoding::Base64; use fastcrypto::encoding::Encoding; use fastcrypto::traits::EncodeDecodeBase64 as _; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -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::crypto::PublicKey; -use identity_iota_interaction::types::crypto::Signature; -use identity_iota_interaction::types::digests::TransactionDigest; -use identity_iota_interaction::types::execution_status::CommandArgumentError; -use identity_iota_interaction::types::object::Owner; -use identity_iota_interaction::types::transaction::TransactionData; -use identity_iota_interaction::ProgrammableTransactionBcs; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::base_types::SequenceNumber; +use iota_interaction::types::crypto::PublicKey; +use iota_interaction::types::crypto::Signature; +use iota_interaction::types::digests::TransactionDigest; +use iota_interaction::types::execution_status::CommandArgumentError; +use iota_interaction::types::object::Owner; +use iota_interaction::types::transaction::TransactionData; +use iota_interaction::ProgrammableTransactionBcs; use js_sys::Promise; use js_sys::Uint8Array; use serde::Deserialize; diff --git a/bindings/wasm/iota_move_calls_ts/src/common/types.rs b/bindings/wasm/iota_move_calls_ts/src/common/types.rs index d95f6dcfa..472a88bd2 100644 --- a/bindings/wasm/iota_move_calls_ts/src/common/types.rs +++ b/bindings/wasm/iota_move_calls_ts/src/common/types.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use identity_core::common::Object; -use identity_iota_interaction::types::transaction::TransactionKind; -use identity_iota_interaction::ProgrammableTransactionBcs; +use iota_interaction::types::transaction::TransactionKind; +use iota_interaction::ProgrammableTransactionBcs; use js_sys::Promise; use js_sys::Uint8Array; use wasm_bindgen::prelude::*; diff --git a/bindings/wasm/iota_move_calls_ts/src/error.rs b/bindings/wasm/iota_move_calls_ts/src/error.rs index e29a09837..ace14ccab 100644 --- a/bindings/wasm/iota_move_calls_ts/src/error.rs +++ b/bindings/wasm/iota_move_calls_ts/src/error.rs @@ -10,10 +10,10 @@ 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 iota_interaction::types::execution_status::CommandArgumentError; +use iota_interaction::types::execution_status::ExecutionFailureStatus; +use iota_interaction::types::execution_status::PackageUpgradeError; +use iota_interaction::types::execution_status::TypeArgumentError; use thiserror::Error as ThisError; /// Convenience wrapper for `Result`. diff --git a/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs index a26c1ff09..e0058faf5 100644 --- a/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs +++ b/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs @@ -18,17 +18,17 @@ use crate::common::PromiseUint8Array; use crate::error::TsSdkError; use crate::error::WasmError; use crate::transaction_builder::TransactionBuilderTsSdk; -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::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; +use iota_interaction::rpc_types::IotaObjectData; +use iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::TypeTag; +use iota_interaction::BorrowIntentFnInternalT; +use iota_interaction::ControllerIntentFnInternalT; +use iota_interaction::IdentityMoveCalls; +use iota_interaction::MoveType; +use iota_interaction::ProgrammableTransactionBcs; #[wasm_bindgen] extern "C" { diff --git a/bindings/wasm/iota_move_calls_ts/src/iota_client_ts_sdk.rs b/bindings/wasm/iota_move_calls_ts/src/iota_client_ts_sdk.rs index db6f00e00..e4b66f143 100644 --- a/bindings/wasm/iota_move_calls_ts/src/iota_client_ts_sdk.rs +++ b/bindings/wasm/iota_move_calls_ts/src/iota_client_ts_sdk.rs @@ -5,39 +5,39 @@ use std::boxed::Box; use std::option::Option; use std::result::Result; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::types::crypto::Signature; -use identity_iota_interaction::types::digests::TransactionDigest; -use identity_iota_interaction::types::dynamic_field::DynamicFieldName; -use identity_iota_interaction::types::transaction::TransactionData; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::types::crypto::Signature; +use iota_interaction::types::digests::TransactionDigest; +use iota_interaction::types::dynamic_field::DynamicFieldName; +use iota_interaction::types::transaction::TransactionData; use secret_storage::Signer; -use identity_iota_interaction::error::Error as IotaRpcError; -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::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::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::types::transaction::ProgrammableTransaction as ProgrammableTransactionSdk; -use identity_iota_interaction::types::transaction::TransactionDataAPI as _; -use identity_iota_interaction::CoinReadTrait; -use identity_iota_interaction::EventTrait; -use identity_iota_interaction::IotaClientTrait; -use identity_iota_interaction::IotaKeySignature; -use identity_iota_interaction::IotaTransactionBlockResponseT; -use identity_iota_interaction::QuorumDriverTrait; -use identity_iota_interaction::ReadTrait; +use iota_interaction::error::Error as IotaRpcError; +use iota_interaction::error::IotaRpcResult; +use iota_interaction::rpc_types::CoinPage; +use iota_interaction::rpc_types::EventFilter; +use iota_interaction::rpc_types::EventPage; +use iota_interaction::rpc_types::IotaObjectData; +use iota_interaction::rpc_types::IotaObjectDataOptions; +use iota_interaction::rpc_types::IotaObjectResponse; +use iota_interaction::rpc_types::IotaObjectResponseQuery; +use iota_interaction::rpc_types::IotaPastObjectResponse; +use iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; +use iota_interaction::rpc_types::ObjectsPage; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::SequenceNumber; +use iota_interaction::types::event::EventID; +use iota_interaction::types::quorum_driver_types::ExecuteTransactionRequestType; +use iota_interaction::types::transaction::ProgrammableTransaction as ProgrammableTransactionSdk; +use iota_interaction::types::transaction::TransactionDataAPI as _; +use iota_interaction::CoinReadTrait; +use iota_interaction::EventTrait; +use iota_interaction::IotaClientTrait; +use iota_interaction::IotaKeySignature; +use iota_interaction::IotaTransactionBlockResponseT; +use iota_interaction::QuorumDriverTrait; +use iota_interaction::ReadTrait; use crate::bindings::ManagedWasmIotaClient; use crate::bindings::WasmIotaClient; diff --git a/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs index 5000374fb..83319bd8d 100644 --- a/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs +++ b/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs @@ -1,11 +1,11 @@ // 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::ObjectID; -use identity_iota_interaction::types::base_types::ObjectRef; -use identity_iota_interaction::MigrationMoveCalls; -use identity_iota_interaction::ProgrammableTransactionBcs; +use iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::MigrationMoveCalls; +use iota_interaction::ProgrammableTransactionBcs; use js_sys::Uint8Array; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; diff --git a/bindings/wasm/iota_move_calls_ts/src/transaction_builder.rs b/bindings/wasm/iota_move_calls_ts/src/transaction_builder.rs index 0d95bb740..24643a5e5 100644 --- a/bindings/wasm/iota_move_calls_ts/src/transaction_builder.rs +++ b/bindings/wasm/iota_move_calls_ts/src/transaction_builder.rs @@ -7,8 +7,8 @@ use std::ops::DerefMut; use crate::bindings::WasmTransactionBuilder; use crate::error::TsSdkError; use crate::error::WasmError; -use identity_iota_interaction::ProgrammableTransactionBcs; -use identity_iota_interaction::TransactionBuilderT; +use iota_interaction::ProgrammableTransactionBcs; +use iota_interaction::TransactionBuilderT; pub type NativeTsTransactionBuilderBindingWrapper = WasmTransactionBuilder; diff --git a/identity_core/Cargo.toml b/identity_core/Cargo.toml index 9c0c5d60e..0ee936eec 100644 --- a/identity_core/Cargo.toml +++ b/identity_core/Cargo.toml @@ -15,6 +15,7 @@ description = "The core traits and types for the identity-rs library." # that conflicts with `iota-sdk`s `core` impl, leading to errors in `iota-sdk` crate deranged = { version = ">=0.4.0, <0.4.1", default-features = false } multibase = { version = "0.9", default-features = false, features = ["std"] } +product_common.workspace = true serde = { workspace = true, features = ["std"] } serde_json = { workspace = true, features = ["std"] } strum.workspace = true diff --git a/identity_core/src/common/mod.rs b/identity_core/src/common/mod.rs index 04568e05b..3c2e7918c 100644 --- a/identity_core/src/common/mod.rs +++ b/identity_core/src/common/mod.rs @@ -5,8 +5,8 @@ pub use self::context::Context; pub use self::key_comparable::KeyComparable; -pub use self::object::Object; -pub use self::object::Value; +pub use product_common::object::Object; +pub use product_common::object::Value; pub use self::one_or_many::OneOrMany; pub use self::one_or_set::OneOrSet; pub use self::ordered_set::OrderedSet; @@ -18,7 +18,6 @@ pub use string_or_url::StringOrUrl; mod context; mod key_comparable; -mod object; mod one_or_many; mod one_or_set; mod ordered_set; diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 0d93d605c..4f6369ff9 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -17,8 +17,6 @@ clippy::missing_errors_doc )] -pub use identity_iota_interaction as iota_interaction; - pub mod core { //! Core Traits and Types diff --git a/identity_iota_core/src/document/iota_document.rs b/identity_iota_core/src/document/iota_document.rs index b1cad01c9..289ead1ad 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 identity_iota_interaction::rpc_types::IotaObjectData; + use iota_interaction::rpc_types::IotaObjectData; use crate::rebased::migration::unpack_identity_data; use crate::rebased::migration::IdentityData; diff --git a/identity_iota_core/src/iota_interaction_adapter.rs b/identity_iota_core/src/iota_interaction_adapter.rs index c0b97a4e3..ef273ad49 100644 --- a/identity_iota_core/src/iota_interaction_adapter.rs +++ b/identity_iota_core/src/iota_interaction_adapter.rs @@ -9,7 +9,9 @@ cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { pub(crate) use iota_interaction_ts::*; + pub(crate) use iota_move_call_ts::*; } else { - pub(crate) use crate::iota_interaction_rust::*; + pub(crate) use iota_interaction_rust::*; + pub(crate) use crate::iota_move_calls_rust::*; } } diff --git a/identity_iota_core/src/iota_move_calls_rust/asset_move_calls.rs b/identity_iota_core/src/iota_move_calls_rust/asset_move_calls.rs index f5122c566..33220af8e 100644 --- a/identity_iota_core/src/iota_move_calls_rust/asset_move_calls.rs +++ b/identity_iota_core/src/iota_move_calls_rust/asset_move_calls.rs @@ -4,19 +4,19 @@ 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 identity_iota_move_calls::AssetMoveCalls; +use iota_interaction::ident_str; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::base_types::SequenceNumber; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::Command; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::TypeTag; +use iota_interaction::MoveType; +use iota_interaction::ProgrammableTransactionBcs; +use iota_interaction::TypedValue; use iota_sdk::types::transaction::Argument; use iota_sdk::types::transaction::ProgrammableMoveCall; diff --git a/identity_iota_core/src/iota_move_calls_rust/identity_move_calls.rs b/identity_iota_core/src/iota_move_calls_rust/identity_move_calls.rs index 2e5dc788f..ea9158398 100644 --- a/identity_iota_core/src/iota_move_calls_rust/identity_move_calls.rs +++ b/identity_iota_core/src/iota_move_calls_rust/identity_move_calls.rs @@ -2,33 +2,34 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; -use identity_iota_interaction::OptionalSend; +use iota_interaction::OptionalSend; 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 identity_iota_move_calls::BorrowIntentFnInternalT; +use identity_iota_move_calls::ControllerIntentFnInternalT; +use identity_iota_move_calls::IdentityMoveCalls; + +use iota_interaction::ident_str; +use iota_interaction::rpc_types::IotaObjectData; +use iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::base_types::ObjectType; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as PrgrTxBuilder; +use iota_interaction::types::transaction::Argument; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::TypeTag; +use iota_interaction::types::IOTA_FRAMEWORK_PACKAGE_ID; +use iota_interaction::MoveType; +use iota_interaction::ProgrammableTransactionBcs; +use iota_interaction::TransactionBuilderT; + +use iota_interaction_rust::transaction_builder::TransactionBuilderRustSdk; +use iota_interaction_rust::utils; use crate::rebased::proposals::BorrowAction; use crate::rebased::proposals::ControllerExecution; @@ -358,7 +359,7 @@ impl IdentityMoveCalls for IdentityMoveCallsRustSdk { utils::put_back_delegation_token(ptb, controller_cap, delegation_token, borrow, package); - internal_ptb.finish() + internal_ptb.finish().map_err(rebased_err) } fn create_and_execute_borrow>( diff --git a/identity_iota_core/src/iota_move_calls_rust/migration_move_calls.rs b/identity_iota_core/src/iota_move_calls_rust/migration_move_calls.rs index eaeb087ce..dad3588c5 100644 --- a/identity_iota_core/src/iota_move_calls_rust/migration_move_calls.rs +++ b/identity_iota_core/src/iota_move_calls_rust/migration_move_calls.rs @@ -1,20 +1,20 @@ // 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_move_calls::MigrationMoveCalls; -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 iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; +use iota_interaction::ident_str; +use iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::IOTA_FRAMEWORK_PACKAGE_ID; +use iota_interaction::ProgrammableTransactionBcs; use crate::rebased::Error; -use super::utils; +use iota_interaction_rust::utils; pub(crate) struct MigrationMoveCallsRustSdk {} diff --git a/identity_iota_core/src/rebased/assets/asset.rs b/identity_iota_core/src/rebased/assets/asset.rs index eb08c1246..edc0110f7 100644 --- a/identity_iota_core/src/rebased/assets/asset.rs +++ b/identity_iota_core/src/rebased/assets/asset.rs @@ -11,25 +11,27 @@ use crate::rebased::Error; use anyhow::anyhow; use anyhow::Context; use async_trait::async_trait; -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::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; -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::transaction::ProgrammableTransaction; -use identity_iota_interaction::types::TypeTag; -use identity_iota_interaction::AssetMoveCalls; -use identity_iota_interaction::IotaClientTrait; -use identity_iota_interaction::IotaTransactionBlockEffectsMutAPI as _; -use identity_iota_interaction::MoveType; + +use identity_iota_move_calls::AssetMoveCalls; + +use iota_interaction::ident_str; +use iota_interaction::move_types::language_storage::StructTag; +use iota_interaction::rpc_types::IotaData as _; +use iota_interaction::rpc_types::IotaExecutionStatus; +use iota_interaction::rpc_types::IotaObjectDataOptions; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::base_types::SequenceNumber; +use iota_interaction::types::id::UID; +use iota_interaction::types::object::Owner; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::types::TypeTag; +use iota_interaction::IotaClientTrait; +use iota_interaction::IotaTransactionBlockEffectsMutAPI as _; +use iota_interaction::MoveType; use serde::de::DeserializeOwned; use serde::Deserialize; use serde::Deserializer; 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 63e8a99c1..1fa11a508 100644 --- a/identity_iota_core/src/rebased/assets/public_available_vc.rs +++ b/identity_iota_core/src/rebased/assets/public_available_vc.rs @@ -7,10 +7,10 @@ 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_iota_interaction::OptionalSync; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::IotaKeySignature; +use iota_interaction::IotaVerifiableCredential; +use iota_interaction::OptionalSync; use identity_jose::jwt::JwtHeader; use identity_jose::jwu; use itertools::Itertools; diff --git a/identity_iota_core/src/rebased/client/full_client.rs b/identity_iota_core/src/rebased/client/full_client.rs index a6dde9c18..d3a3f4adc 100644 --- a/identity_iota_core/src/rebased/client/full_client.rs +++ b/identity_iota_core/src/rebased/client/full_client.rs @@ -12,16 +12,16 @@ use crate::IotaDocument; use crate::StateMetadataDocument; use crate::StateMetadataEncoding; use async_trait::async_trait; -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::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::types::base_types::IotaAddress; -use identity_iota_interaction::types::base_types::ObjectRef; -use identity_iota_interaction::types::crypto::PublicKey; -use identity_iota_interaction::types::transaction::ProgrammableTransaction; -use identity_iota_interaction::IdentityMoveCalls as _; +use identity_iota_move_calls::IdentityMoveCalls as _; +use iota_interaction::move_types::language_storage::StructTag; +use iota_interaction::rpc_types::IotaObjectData; +use iota_interaction::rpc_types::IotaObjectDataFilter; +use iota_interaction::rpc_types::IotaObjectResponseQuery; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::crypto::PublicKey; +use iota_interaction::types::transaction::ProgrammableTransaction; use identity_verification::jwk::Jwk; use secret_storage::Signer; use serde::de::DeserializeOwned; @@ -31,10 +31,10 @@ use crate::rebased::assets::AuthenticatedAssetBuilder; use crate::rebased::migration::Identity; use crate::rebased::migration::IdentityBuilder; use crate::rebased::Error; -use identity_iota_interaction::IotaClientTrait; -use identity_iota_interaction::IotaKeySignature; -use identity_iota_interaction::MoveType; -use identity_iota_interaction::OptionalSync; +use iota_interaction::IotaClientTrait; +use iota_interaction::IotaKeySignature; +use iota_interaction::MoveType; +use iota_interaction::OptionalSync; use super::get_object_id_from_did; use super::IdentityClientReadOnly; diff --git a/identity_iota_core/src/rebased/client/mod.rs b/identity_iota_core/src/rebased/client/mod.rs index d5b4f76b5..87668c383 100644 --- a/identity_iota_core/src/rebased/client/mod.rs +++ b/identity_iota_core/src/rebased/client/mod.rs @@ -7,4 +7,4 @@ mod read_only; pub use full_client::*; pub use read_only::*; -pub use identity_iota_interaction::IotaKeySignature; +pub use 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 012bdd669..8c19fadaa 100644 --- a/identity_iota_core/src/rebased/client/read_only.rs +++ b/identity_iota_core/src/rebased/client/read_only.rs @@ -7,14 +7,14 @@ use std::ops::Deref; use std::pin::Pin; use std::str::FromStr; -use crate::rebased::iota; +use crate::rebased::{iota, rebased_err}; use crate::IotaDID; use crate::IotaDocument; use crate::NetworkName; use anyhow::anyhow; use anyhow::Context as _; use futures::stream::FuturesUnordered; -use identity_iota_interaction::MoveType; +use iota_interaction::MoveType; use crate::iota_interaction_adapter::IotaClientAdapter; use crate::rebased::migration::get_alias; @@ -25,24 +25,24 @@ use crate::rebased::Error; use futures::StreamExt as _; use identity_core::common::Url; use identity_did::DID; -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::IotaParsedData; -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 iota_interaction::move_types::language_storage::StructTag; +use iota_interaction::rpc_types::EventFilter; +use iota_interaction::rpc_types::IotaData as _; +use iota_interaction::rpc_types::IotaObjectData; +use iota_interaction::rpc_types::IotaObjectDataFilter; +use iota_interaction::rpc_types::IotaObjectDataOptions; +use iota_interaction::rpc_types::IotaObjectResponseQuery; +use iota_interaction::rpc_types::IotaParsedData; +use iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::IotaClientTrait; use serde::de::DeserializeOwned; use serde::Deserialize; #[cfg(not(target_arch = "wasm32"))] -use identity_iota_interaction::IotaClient; +use iota_interaction::IotaClient; #[cfg(target_arch = "wasm32")] use iota_interaction_ts::bindings::WasmIotaClient; @@ -108,7 +108,7 @@ impl IdentityClientReadOnly { /// 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 + Self::new_internal(IotaClientAdapter::new(iota_client).map_err(rebased_err)?).await } } } @@ -148,7 +148,7 @@ impl IdentityClientReadOnly { /// 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)?, + IotaClientAdapter::new(iota_client).map_err(rebased_err)?, iota_identity_pkg_id ).await } diff --git a/identity_iota_core/src/rebased/error.rs b/identity_iota_core/src/rebased/error.rs index e3a7175a2..c35dbde19 100644 --- a/identity_iota_core/src/rebased/error.rs +++ b/identity_iota_core/src/rebased/error.rs @@ -3,8 +3,7 @@ //! Errors that may occur for the rebased logic. -#[cfg(target_arch = "wasm32")] -use iota_interaction_ts::error::TsSdkError; +use crate::iota_interaction_adapter::AdapterError; /// This type represents all possible errors that can occur in the library. #[derive(Debug, thiserror::Error, strum::IntoStaticStr)] @@ -12,7 +11,7 @@ use iota_interaction_ts::error::TsSdkError; pub enum Error { /// failed to connect to network. #[error("failed to connect to iota network node; {0:?}")] - Network(String, #[source] identity_iota_interaction::error::Error), + Network(String, #[source] iota_interaction::error::Error), /// could not lookup an object ID. #[error("failed to lookup an object; {0}")] ObjectLookup(String), @@ -42,7 +41,7 @@ pub enum Error { TransactionSigningFailed(String), /// Could not execute transaction. #[error("transaction execution failed; {0}")] - TransactionExecutionFailed(#[from] identity_iota_interaction::error::Error), + TransactionExecutionFailed(#[from] 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}")] @@ -56,7 +55,7 @@ pub enum Error { /// The raw RPC response, as received by the client. // Dev-comment: Neeeded to box this to avoid clippy complaining about the size of this variant. #[cfg(not(target_arch = "wasm32"))] - response: Box, + response: Box, /// JSON-encoded string representation for the actual execution's RPC response. #[cfg(target_arch = "wasm32")] response: String, @@ -91,10 +90,12 @@ pub enum 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 + /// Caused by an interaction with the IOTA protocol. + #[error("IOTA interaction error")] + IotaInteractionError(#[source] iota_interaction::interaction_error::Error), + /// Caused by a platform-specific adapter to interact with the IOTA protocol. #[error("TsSdkError: {0}")] - TsSdkError(#[from] TsSdkError), + IotaInteractionAdapterError(#[from] AdapterError), } /// Can be used for example like `map_err(rebased_err)` to convert other error 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 63c402deb..1846761fa 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,15 +1,15 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -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 iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::Command; +use iota_interaction::types::transaction::ProgrammableMoveCall; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::ident_str; use serde::Serialize; -use identity_iota_interaction::MoveType; +use iota_interaction::MoveType; use crate::rebased::Error; use super::try_to_argument; 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 5a2adc053..b4554c8fc 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 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 iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::Command; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::ident_str; -use identity_iota_interaction::MoveType; +use 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/transfer.rs b/identity_iota_core/src/rebased/iota/move_calls/asset/transfer.rs index 2505905a7..195350b3a 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 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 iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::base_types::SequenceNumber; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::Command; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::types::TypeTag; +use iota_interaction::ident_str; -use identity_iota_interaction::MoveType; +use 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 index 31ce4134d..0ff4214e7 100644 --- 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 @@ -2,10 +2,10 @@ // 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 iota_interaction::{ident_str, MoveType, TypedValue}; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::transaction::{Argument, Command, ProgrammableMoveCall}; use crate::rebased::Error; pub(crate) fn try_to_argument( 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 c94575e8b..9f1007f01 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 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 iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::Command; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::ident_str; use serde::Serialize; -use identity_iota_interaction::MoveType; +use 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 2071a5754..63be42b30 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 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 iota_interaction::rpc_types::IotaObjectData; +use iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::base_types::ObjectType; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::Argument; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::ident_str; use itertools::Itertools; use crate::rebased::iota::move_calls::utils; use crate::rebased::proposals::BorrowAction; -use identity_iota_interaction::MoveType; +use 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 dd560718d..659eed86d 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 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 iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::types::TypeTag; +use 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 efde7e4da..4e42819b5 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 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 iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::Argument; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::ident_str; use crate::rebased::iota::move_calls::utils; use crate::rebased::proposals::ControllerExecution; -use identity_iota_interaction::MoveType; +use 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 2d2d29b6c..68c7ec199 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 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 iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::types::TypeTag; +use iota_interaction::types::IOTA_FRAMEWORK_PACKAGE_ID; +use 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 bb7ddb918..030e15d4b 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 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 iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use 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 64b4d32e4..65554fa78 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 identity_iota_interaction::MoveType; +use iota_interaction::MoveType; use crate::rebased::Error; -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 iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use 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 9fd39d6a9..9ef3a7740 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 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 iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::Argument; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::types::TypeTag; +use iota_interaction::ident_str; use crate::rebased::iota::move_calls; use crate::rebased::proposals::SendAction; -use identity_iota_interaction::MoveType; +use 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 59f7560e0..96efa4db1 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 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 iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use 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 636e0d0a1..1b65090f2 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 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 iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use 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 6605ac025..a66038b33 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 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; +use iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::types::IOTA_FRAMEWORK_PACKAGE_ID; +use 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 a18623687..49aa5d290 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 identity_iota_interaction::MoveType; +use iota_interaction::MoveType; use crate::rebased::Error; -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 iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::STD_OPTION_MODULE_NAME; +use iota_interaction::types::object::Owner; +use iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; +use iota_interaction::types::transaction::Argument; +use iota_interaction::types::transaction::ObjectArg; +use iota_interaction::types::IOTA_CLOCK_OBJECT_ID; +use iota_interaction::types::IOTA_CLOCK_OBJECT_SHARED_VERSION; +use iota_interaction::types::MOVE_STDLIB_PACKAGE_ID; +use 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 fdf352f8f..b2fe842d0 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 identity_iota_interaction::types::base_types::ObjectID; -use identity_iota_interaction::types::id::UID; +use iota_interaction::types::base_types::ObjectID; +use 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 7d5a9e2a0..6e8b562e7 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 identity_iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectID; use phf::phf_map; use phf::Map; @@ -71,7 +71,7 @@ impl IdentityNetworkMetadata { #[cfg(test)] mod test { - use identity_iota_interaction::IotaClientBuilder; + use 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 a8c68ff62..78f657d14 100644 --- a/identity_iota_core/src/rebased/migration/alias.rs +++ b/identity_iota_core/src/rebased/migration/alias.rs @@ -4,17 +4,17 @@ use async_trait::async_trait; use identity_core::common::Url; use identity_did::DID as _; -use identity_iota_interaction::rpc_types::IotaExecutionStatus; -use identity_iota_interaction::rpc_types::IotaObjectDataOptions; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; -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::transaction::ProgrammableTransaction; -use identity_iota_interaction::types::TypeTag; -use identity_iota_interaction::types::STARDUST_PACKAGE_ID; -use identity_iota_interaction::IotaTransactionBlockEffectsMutAPI as _; +use iota_interaction::rpc_types::IotaExecutionStatus; +use iota_interaction::rpc_types::IotaObjectDataOptions; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::id::UID; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::types::TypeTag; +use iota_interaction::types::STARDUST_PACKAGE_ID; +use iota_interaction::IotaTransactionBlockEffectsMutAPI as _; use serde; use serde::Deserialize; use serde::Serialize; @@ -25,9 +25,9 @@ use crate::rebased::client::IdentityClientReadOnly; use crate::rebased::transaction_builder::Transaction; use crate::rebased::Error; use crate::IotaDID; -use identity_iota_interaction::IotaClientTrait; -use identity_iota_interaction::MigrationMoveCalls; -use identity_iota_interaction::MoveType; +use identity_iota_move_calls::MigrationMoveCalls; +use iota_interaction::IotaClientTrait; +use iota_interaction::MoveType; use super::get_identity; use super::Identity; diff --git a/identity_iota_core/src/rebased/migration/controller_token.rs b/identity_iota_core/src/rebased/migration/controller_token.rs index d87de7099..3e9204240 100644 --- a/identity_iota_core/src/rebased/migration/controller_token.rs +++ b/identity_iota_core/src/rebased/migration/controller_token.rs @@ -1,10 +1,10 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -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::MoveType; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::id::UID; +use iota_interaction::types::TypeTag; +use iota_interaction::MoveType; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; diff --git a/identity_iota_core/src/rebased/migration/identity.rs b/identity_iota_core/src/rebased/migration/identity.rs index ea99acfd4..84a41b809 100644 --- a/identity_iota_core/src/rebased/migration/identity.rs +++ b/identity_iota_core/src/rebased/migration/identity.rs @@ -7,9 +7,9 @@ use std::collections::HashSet; use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; use crate::rebased::transaction_builder::Transaction; use crate::rebased::transaction_builder::TransactionBuilder; -use identity_iota_interaction::types::transaction::ProgrammableTransaction; -use identity_iota_interaction::IdentityMoveCalls; -use identity_iota_interaction::IotaTransactionBlockEffectsMutAPI as _; +use identity_iota_move_calls::IdentityMoveCalls; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::IotaTransactionBlockEffectsMutAPI as _; use tokio::sync::OnceCell; use crate::rebased::iota::types::Number; @@ -21,21 +21,21 @@ use crate::StateMetadataDocument; use crate::StateMetadataEncoding; use async_trait::async_trait; use identity_core::common::Timestamp; -use identity_iota_interaction::ident_str; -use identity_iota_interaction::move_types::language_storage::StructTag; -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::IotaParsedData; -use identity_iota_interaction::rpc_types::IotaParsedMoveObject; -use identity_iota_interaction::rpc_types::IotaPastObjectResponse; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; -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::object::Owner; -use identity_iota_interaction::types::TypeTag; +use iota_interaction::ident_str; +use iota_interaction::move_types::language_storage::StructTag; +use iota_interaction::rpc_types::IotaExecutionStatus; +use iota_interaction::rpc_types::IotaObjectData; +use iota_interaction::rpc_types::IotaObjectDataOptions; +use iota_interaction::rpc_types::IotaParsedData; +use iota_interaction::rpc_types::IotaParsedMoveObject; +use iota_interaction::rpc_types::IotaPastObjectResponse; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::id::UID; +use iota_interaction::types::object::Owner; +use iota_interaction::types::TypeTag; use serde; use serde::Deserialize; use serde::Serialize; @@ -50,8 +50,8 @@ use crate::rebased::proposals::SendAction; use crate::rebased::proposals::UpdateDidDocument; use crate::rebased::rebased_err; use crate::rebased::Error; -use identity_iota_interaction::IotaClientTrait; -use identity_iota_interaction::MoveType; +use iota_interaction::IotaClientTrait; +use iota_interaction::MoveType; use super::ControllerToken; use super::Multicontroller; @@ -275,7 +275,9 @@ impl OnChainIdentity { } else { // no version given, this version will be included in history let version = identity_ref.version(); - let response = client.get_past_object(object_id, version).await?; + let response = client.get_past_object(object_id, version) + .await + .map_err(rebased_err)?; let latest_version = if let IotaPastObjectResponse::VersionFound(response_value) = response { response_value } else { diff --git a/identity_iota_core/src/rebased/migration/multicontroller.rs b/identity_iota_core/src/rebased/migration/multicontroller.rs index 5002fb67d..73f2f8051 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 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 iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::collection_types::Entry; +use iota_interaction::types::collection_types::VecMap; +use iota_interaction::types::collection_types::VecSet; +use 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 196cf15e1..6b358f310 100644 --- a/identity_iota_core/src/rebased/migration/registry.rs +++ b/identity_iota_core/src/rebased/migration/registry.rs @@ -1,10 +1,10 @@ // Copyright 2020-2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -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 iota_interaction::rpc_types::IotaData; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::id::ID; +use iota_interaction::IotaClientTrait; use crate::rebased::client::IdentityClientReadOnly; diff --git a/identity_iota_core/src/rebased/mod.rs b/identity_iota_core/src/rebased/mod.rs index fe1e45a56..7fa24754b 100644 --- a/identity_iota_core/src/rebased/mod.rs +++ b/identity_iota_core/src/rebased/mod.rs @@ -24,4 +24,4 @@ pub use error::*; /// Integration with IOTA's Keytool. #[cfg(feature = "keytool")] -pub use identity_iota_interaction::keytool; +pub use iota_interaction::keytool; diff --git a/identity_iota_core/src/rebased/proposals/borrow.rs b/identity_iota_core/src/rebased/proposals/borrow.rs index 05c143e4f..b0cfc6f45 100644 --- a/identity_iota_core/src/rebased/proposals/borrow.rs +++ b/identity_iota_core/src/rebased/proposals/borrow.rs @@ -9,7 +9,7 @@ use crate::rebased::client::IdentityClientReadOnly; use crate::rebased::migration::ControllerToken; use crate::rebased::transaction_builder::Transaction; use crate::rebased::transaction_builder::TransactionBuilder; -use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_move_calls::IdentityMoveCalls; use serde::Deserialize; use tokio::sync::Mutex; @@ -17,15 +17,15 @@ use crate::rebased::migration::Proposal; use crate::rebased::transaction::ProtoTransaction; use crate::rebased::Error; use async_trait::async_trait; -use identity_iota_interaction::rpc_types::IotaExecutionStatus; -use identity_iota_interaction::rpc_types::IotaObjectData; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; -use identity_iota_interaction::types::base_types::ObjectID; -use identity_iota_interaction::types::transaction::Argument; -use identity_iota_interaction::types::transaction::ProgrammableTransaction; -use identity_iota_interaction::types::TypeTag; -use identity_iota_interaction::MoveType; +use iota_interaction::rpc_types::IotaExecutionStatus; +use iota_interaction::rpc_types::IotaObjectData; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::transaction::Argument; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::types::TypeTag; +use iota_interaction::MoveType; use serde::Serialize; use super::CreateProposal; @@ -45,7 +45,7 @@ cfg_if::cfg_if! { #[allow(unreachable_pub)] pub type BorrowIntentFn = Box; } else { - use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; + use 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) {} diff --git a/identity_iota_core/src/rebased/proposals/config_change.rs b/identity_iota_core/src/rebased/proposals/config_change.rs index ae9b596db..b5a8337ea 100644 --- a/identity_iota_core/src/rebased/proposals/config_change.rs +++ b/identity_iota_core/src/rebased/proposals/config_change.rs @@ -11,23 +11,23 @@ use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; use crate::rebased::client::IdentityClientReadOnly; use crate::rebased::migration::ControllerToken; use crate::rebased::transaction_builder::TransactionBuilder; -use identity_iota_interaction::IdentityMoveCalls; +use identity_iota_move_calls::IdentityMoveCalls; use crate::rebased::migration::Proposal; use async_trait::async_trait; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -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 iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::collection_types::Entry; +use iota_interaction::types::collection_types::VecMap; +use iota_interaction::types::TypeTag; use serde::Deserialize; use serde::Serialize; use crate::rebased::iota::types::Number; use crate::rebased::migration::OnChainIdentity; use crate::rebased::Error; -use identity_iota_interaction::MoveType; +use iota_interaction::MoveType; use super::CreateProposal; use super::ExecuteProposal; diff --git a/identity_iota_core/src/rebased/proposals/controller.rs b/identity_iota_core/src/rebased/proposals/controller.rs index a18b21177..00d8acc8c 100644 --- a/identity_iota_core/src/rebased/proposals/controller.rs +++ b/identity_iota_core/src/rebased/proposals/controller.rs @@ -8,24 +8,24 @@ use crate::rebased::client::IdentityClientReadOnly; use crate::rebased::migration::ControllerToken; use crate::rebased::transaction_builder::Transaction; use crate::rebased::transaction_builder::TransactionBuilder; -use identity_iota_interaction::rpc_types::IotaExecutionStatus; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; -use identity_iota_interaction::types::transaction::ProgrammableTransaction; -use identity_iota_interaction::IdentityMoveCalls; +use iota_interaction::rpc_types::IotaExecutionStatus; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; +use iota_interaction::types::transaction::ProgrammableTransaction; +use identity_iota_move_calls::IdentityMoveCalls; use tokio::sync::Mutex; use crate::rebased::migration::Proposal; use crate::rebased::transaction::ProtoTransaction; use crate::rebased::Error; use async_trait::async_trait; -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 iota_interaction::rpc_types::IotaObjectRef; +use iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::transaction::Argument; +use iota_interaction::types::TypeTag; +use iota_interaction::MoveType; use serde::Deserialize; use serde::Serialize; @@ -46,7 +46,7 @@ cfg_if::cfg_if! { /// Boxed dynamic trait object of {@link ControllerIntentFnT} pub type ControllerIntentFn = Box; } else { - use identity_iota_interaction::types::programmable_transaction_builder::ProgrammableTransactionBuilder as Ptb; + use 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) {} diff --git a/identity_iota_core/src/rebased/proposals/mod.rs b/identity_iota_core/src/rebased/proposals/mod.rs index 93ff25058..8cd6490f8 100644 --- a/identity_iota_core/src/rebased/proposals/mod.rs +++ b/identity_iota_core/src/rebased/proposals/mod.rs @@ -14,10 +14,10 @@ use std::ops::DerefMut; use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; -use identity_iota_interaction::IdentityMoveCalls; -use identity_iota_interaction::IotaClientTrait; -use identity_iota_interaction::OptionalSend; -use identity_iota_interaction::OptionalSync; +use identity_iota_move_calls::IdentityMoveCalls; +use iota_interaction::IotaClientTrait; +use iota_interaction::OptionalSend; +use iota_interaction::OptionalSync; use tokio::sync::OnceCell; use crate::rebased::client::IdentityClientReadOnly; @@ -29,16 +29,16 @@ use async_trait::async_trait; pub use borrow::*; pub use config_change::*; pub use controller::*; -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::IotaTransactionBlockEffects; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; -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::transaction::ProgrammableTransaction; -use identity_iota_interaction::types::TypeTag; +use iota_interaction::rpc_types::IotaExecutionStatus; +use iota_interaction::rpc_types::IotaObjectData; +use iota_interaction::rpc_types::IotaObjectDataOptions; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::base_types::ObjectType; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::types::TypeTag; pub use send::*; use serde::de::DeserializeOwned; pub use update_did_doc::*; @@ -47,7 +47,7 @@ pub use upgrade::*; use crate::rebased::migration::OnChainIdentity; use crate::rebased::migration::Proposal; use crate::rebased::Error; -use identity_iota_interaction::MoveType; +use iota_interaction::MoveType; use super::migration::ControllerToken; diff --git a/identity_iota_core/src/rebased/proposals/send.rs b/identity_iota_core/src/rebased/proposals/send.rs index bac20489c..daf826311 100644 --- a/identity_iota_core/src/rebased/proposals/send.rs +++ b/identity_iota_core/src/rebased/proposals/send.rs @@ -4,12 +4,12 @@ use std::marker::PhantomData; use async_trait::async_trait; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::types::base_types::IotaAddress; -use identity_iota_interaction::types::base_types::ObjectID; -use identity_iota_interaction::types::TypeTag; -use identity_iota_interaction::IdentityMoveCalls; -use identity_iota_interaction::MoveType; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::TypeTag; +use identity_iota_move_calls::IdentityMoveCalls; +use iota_interaction::MoveType; use serde::Deserialize; use serde::Serialize; 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 52caeb2ac..0c8277f09 100644 --- a/identity_iota_core/src/rebased/proposals/update_did_doc.rs +++ b/identity_iota_core/src/rebased/proposals/update_did_doc.rs @@ -3,8 +3,8 @@ use std::marker::PhantomData; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::IdentityMoveCalls; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use identity_iota_move_calls::IdentityMoveCalls; use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; use crate::rebased::client::IdentityClientReadOnly; @@ -12,15 +12,15 @@ use crate::rebased::migration::ControllerToken; use crate::rebased::transaction_builder::TransactionBuilder; use crate::IotaDocument; use async_trait::async_trait; -use identity_iota_interaction::types::base_types::ObjectID; -use identity_iota_interaction::types::TypeTag; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::TypeTag; use serde::Deserialize; use serde::Serialize; use crate::rebased::migration::OnChainIdentity; use crate::rebased::migration::Proposal; use crate::rebased::Error; -use identity_iota_interaction::MoveType; +use iota_interaction::MoveType; use super::CreateProposal; use super::ExecuteProposal; diff --git a/identity_iota_core/src/rebased/proposals/upgrade.rs b/identity_iota_core/src/rebased/proposals/upgrade.rs index b0def21e0..90859e127 100644 --- a/identity_iota_core/src/rebased/proposals/upgrade.rs +++ b/identity_iota_core/src/rebased/proposals/upgrade.rs @@ -3,23 +3,23 @@ use std::marker::PhantomData; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::IdentityMoveCalls; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use identity_iota_move_calls::IdentityMoveCalls; use crate::iota_interaction_adapter::IdentityMoveCallsAdapter; use crate::rebased::client::IdentityClientReadOnly; use crate::rebased::migration::ControllerToken; use crate::rebased::transaction_builder::TransactionBuilder; use async_trait::async_trait; -use identity_iota_interaction::types::base_types::ObjectID; -use identity_iota_interaction::types::TypeTag; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::TypeTag; use serde::Deserialize; use serde::Serialize; use crate::rebased::migration::OnChainIdentity; use crate::rebased::migration::Proposal; use crate::rebased::Error; -use identity_iota_interaction::MoveType; +use iota_interaction::MoveType; use super::CreateProposal; use super::ExecuteProposal; diff --git a/identity_iota_core/src/rebased/transaction.rs b/identity_iota_core/src/rebased/transaction.rs index cfdc790ca..14c9db41f 100644 --- a/identity_iota_core/src/rebased/transaction.rs +++ b/identity_iota_core/src/rebased/transaction.rs @@ -4,7 +4,7 @@ use std::ops::Deref; #[cfg(not(target_arch = "wasm32"))] -use identity_iota_interaction::rpc_types::IotaTransactionBlockResponse; +use iota_interaction::rpc_types::IotaTransactionBlockResponse; use super::transaction_builder::TransactionBuilder; use crate::iota_interaction_adapter::IotaTransactionBlockResponseAdaptedTraitObj; diff --git a/identity_iota_core/src/rebased/transaction_builder.rs b/identity_iota_core/src/rebased/transaction_builder.rs index 4037ac4df..a0adf1019 100644 --- a/identity_iota_core/src/rebased/transaction_builder.rs +++ b/identity_iota_core/src/rebased/transaction_builder.rs @@ -6,25 +6,25 @@ use std::ops::Deref; use anyhow::Context as _; use async_trait::async_trait; use cfg_if::cfg_if; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; -use identity_iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; -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::ObjectRef; -use identity_iota_interaction::types::crypto::IotaSignature as _; -use identity_iota_interaction::types::crypto::PublicKey; -use identity_iota_interaction::types::crypto::Signature; -use identity_iota_interaction::types::quorum_driver_types::ExecuteTransactionRequestType; -use identity_iota_interaction::types::transaction::GasData; -use identity_iota_interaction::types::transaction::ProgrammableTransaction; -use identity_iota_interaction::types::transaction::TransactionData; -use identity_iota_interaction::types::transaction::TransactionDataAPI as _; -use identity_iota_interaction::types::transaction::TransactionKind; -use identity_iota_interaction::IotaClientTrait; -use identity_iota_interaction::IotaKeySignature; -use identity_iota_interaction::OptionalSync; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI as _; +use iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; +use iota_interaction::shared_crypto::intent::Intent; +use iota_interaction::shared_crypto::intent::IntentMessage; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::crypto::IotaSignature as _; +use iota_interaction::types::crypto::PublicKey; +use iota_interaction::types::crypto::Signature; +use iota_interaction::types::quorum_driver_types::ExecuteTransactionRequestType; +use iota_interaction::types::transaction::GasData; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::types::transaction::TransactionData; +use iota_interaction::types::transaction::TransactionDataAPI as _; +use iota_interaction::types::transaction::TransactionKind; +use iota_interaction::IotaClientTrait; +use iota_interaction::IotaKeySignature; +use iota_interaction::OptionalSync; use itertools::Itertools; use secret_storage::Signer; diff --git a/identity_iota_core/src/rebased/utils.rs b/identity_iota_core/src/rebased/utils.rs index c12905d1a..c59b3143f 100644 --- a/identity_iota_core/src/rebased/utils.rs +++ b/identity_iota_core/src/rebased/utils.rs @@ -4,7 +4,7 @@ use std::process::Output; use anyhow::Context as _; -use identity_iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectID; use iota_sdk::IotaClient; use iota_sdk::IotaClientBuilder; use serde::Deserialize; @@ -12,7 +12,7 @@ use serde::Deserialize; use tokio::process::Command; use crate::rebased::Error; -use identity_iota_interaction::types::base_types::IotaAddress; +use 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; diff --git a/identity_iota_core/tests/e2e/asset.rs b/identity_iota_core/tests/e2e/asset.rs index 28b49d331..729d62b7e 100644 --- a/identity_iota_core/tests/e2e/asset.rs +++ b/identity_iota_core/tests/e2e/asset.rs @@ -19,8 +19,8 @@ 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 iota_interaction::IotaClientTrait; +use 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 3dd576764..4fe5edeb3 100644 --- a/identity_iota_core/tests/e2e/common.rs +++ b/identity_iota_core/tests/e2e/common.rs @@ -13,12 +13,12 @@ use identity_iota_core::rebased::transaction_builder::TransactionBuilder; use identity_iota_core::rebased::utils::request_funds; use identity_iota_core::rebased::Error; use identity_iota_core::IotaDID; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffects; -use identity_iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI; -use identity_iota_interaction::types::transaction::ProgrammableTransaction; -use identity_iota_interaction::IotaKeySignature; -use identity_iota_interaction::IotaTransactionBlockEffectsMutAPI; -use identity_iota_interaction::OptionalSync; +use iota_interaction::rpc_types::IotaTransactionBlockEffects; +use iota_interaction::rpc_types::IotaTransactionBlockEffectsAPI; +use iota_interaction::types::transaction::ProgrammableTransaction; +use iota_interaction::IotaKeySignature; +use iota_interaction::IotaTransactionBlockEffectsMutAPI; +use iota_interaction::OptionalSync; use identity_jose::jwk::Jwk; use identity_jose::jws::JwsAlgorithm; use identity_storage::JwkMemStore; @@ -391,7 +391,7 @@ impl Transaction for GetTestCoin { mut effects: IotaTransactionBlockEffects, client: &IdentityClientReadOnly, ) -> (Result, IotaTransactionBlockEffects) { - use identity_iota_interaction::IotaClientTrait as _; + use iota_interaction::IotaClientTrait as _; let created_objects = effects .created() .iter() diff --git a/identity_iota_move_calls/Cargo.toml b/identity_iota_move_calls/Cargo.toml index 351bff14a..0d6121157 100644 --- a/identity_iota_move_calls/Cargo.toml +++ b/identity_iota_move_calls/Cargo.toml @@ -9,7 +9,7 @@ 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." +description = "Interfaces that can be used to provide platform-specific implementations to interact with the identity Move package" [dependencies] anyhow = "1.0.75" @@ -17,6 +17,7 @@ async-trait = { version = "0.1.81", default-features = false } bcs = "0.1.4" cfg-if = "1.0.0" fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", package = "fastcrypto", features = ["copy_key"] } +iota_interaction.workspace = true jsonpath-rust = { version = "0.5.1", optional = true } secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } serde.workspace = true diff --git a/identity_iota_move_calls/README.md b/identity_iota_move_calls/README.md index f37ad48a5..46c33a65d 100644 --- a/identity_iota_move_calls/README.md +++ b/identity_iota_move_calls/README.md @@ -1,6 +1,6 @@ # Platform Agnostic Interfaces for Identity Move Call -This crate gathers interfaces that provide platform specific implementations to +This crate gathers interfaces that can be used to provide platform-specific implementations to interact with the identity Move package. Platform specific adapters (implementing the cross-platform-traits defined in this crate) are contained in diff --git a/identity_iota_move_calls/src/lib.rs b/identity_iota_move_calls/src/lib.rs index 64ea7b62d..94f24835b 100644 --- a/identity_iota_move_calls/src/lib.rs +++ b/identity_iota_move_calls/src/lib.rs @@ -5,128 +5,6 @@ #![allow(missing_docs)] -mod effects_mut_api; -mod iota_client_trait; -mod iota_verifiable_credential; -#[cfg(feature = "keytool")] -pub mod keytool; mod move_call_traits; -mod move_type; -mod transaction_builder_trait; -pub use effects_mut_api::*; -pub use iota_client_trait::*; -pub use iota_verifiable_credential::*; -#[cfg(feature = "keytool")] -pub use keytool::*; -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: {} - } -} +pub use move_call_traits::*; \ No newline at end of file diff --git a/identity_iota_move_calls/src/move_call_traits.rs b/identity_iota_move_calls/src/move_call_traits.rs index d1d9e8e41..19e4e2aba 100644 --- a/identity_iota_move_calls/src/move_call_traits.rs +++ b/identity_iota_move_calls/src/move_call_traits.rs @@ -8,17 +8,17 @@ use std::iter::IntoIterator; use async_trait::async_trait; 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::OptionalSend; -use crate::ProgrammableTransactionBcs; +use iota_interaction::rpc_types::IotaObjectData; +use iota_interaction::rpc_types::OwnedObjectRef; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::base_types::ObjectID; +use iota_interaction::types::base_types::ObjectRef; +use iota_interaction::types::base_types::SequenceNumber; +use iota_interaction::types::transaction::Argument; +use iota_interaction::types::TypeTag; +use iota_interaction::MoveType; +use iota_interaction::OptionalSend; +use iota_interaction::ProgrammableTransactionBcs; pub trait AssetMoveCalls { type Error; diff --git a/identity_storage/src/key_id_storage/keytool.rs b/identity_storage/src/key_id_storage/keytool.rs index f0e09ad29..89924e48f 100644 --- a/identity_storage/src/key_id_storage/keytool.rs +++ b/identity_storage/src/key_id_storage/keytool.rs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; -use identity_iota_interaction::types::base_types::IotaAddress; -use identity_iota_interaction::KeytoolStorage; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::KeytoolStorage; use identity_verification::jwu::encode_b64; use crate::KeyId; diff --git a/identity_storage/src/key_storage/keytool.rs b/identity_storage/src/key_storage/keytool.rs index 9f0fc0ff6..0a83f61c1 100644 --- a/identity_storage/src/key_storage/keytool.rs +++ b/identity_storage/src/key_storage/keytool.rs @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 use async_trait::async_trait; -use identity_iota_interaction::types::base_types::IotaAddress; -use identity_iota_interaction::types::crypto::IotaKeyPair; -use identity_iota_interaction::types::crypto::PublicKey; -use identity_iota_interaction::types::crypto::SignatureScheme; -use identity_iota_interaction::KeytoolStorage; +use iota_interaction::types::base_types::IotaAddress; +use iota_interaction::types::crypto::IotaKeyPair; +use iota_interaction::types::crypto::PublicKey; +use iota_interaction::types::crypto::SignatureScheme; +use iota_interaction::KeytoolStorage; use identity_verification::jwk::Jwk; use identity_verification::jws::JwsAlgorithm; @@ -143,7 +143,7 @@ mod tests { use identity_ecdsa_verifier::EcDSAJwsVerifier; use identity_iota_core::IotaDocument; use identity_iota_core::NetworkName; - use identity_iota_interaction::KeytoolStorage as Keytool; + use iota_interaction::KeytoolStorage as Keytool; use identity_verification::jws::JwsAlgorithm; use identity_verification::MethodScope; use serde_json::Value; diff --git a/identity_storage/src/storage/mod.rs b/identity_storage/src/storage/mod.rs index 419ad27aa..b2e4ca323 100644 --- a/identity_storage/src/storage/mod.rs +++ b/identity_storage/src/storage/mod.rs @@ -59,7 +59,7 @@ impl Storage { #[cfg(feature = "keytool")] mod keytool { use super::Storage; - use identity_iota_interaction::KeytoolStorage as Keytool; + use iota_interaction::KeytoolStorage as Keytool; /// An unsecure [Storage] that leverages IOTA Keytool. pub type KeytoolStorage = Storage; diff --git a/identity_storage/src/storage/storage_signer.rs b/identity_storage/src/storage/storage_signer.rs index 810dbfa4b..ea62ebb96 100644 --- a/identity_storage/src/storage/storage_signer.rs +++ b/identity_storage/src/storage/storage_signer.rs @@ -6,13 +6,13 @@ use anyhow::Context as _; use async_trait::async_trait; use fastcrypto::hash::Blake2b256; use fastcrypto::traits::ToFromBytes; -use identity_iota_interaction::shared_crypto::intent::Intent; -use identity_iota_interaction::types::crypto::PublicKey; -use identity_iota_interaction::types::crypto::Signature; -use identity_iota_interaction::types::crypto::SignatureScheme as IotaSignatureScheme; -use identity_iota_interaction::types::transaction::TransactionData; -use identity_iota_interaction::IotaKeySignature; -use identity_iota_interaction::OptionalSync; +use iota_interaction::shared_crypto::intent::Intent; +use iota_interaction::types::crypto::PublicKey; +use iota_interaction::types::crypto::Signature; +use iota_interaction::types::crypto::SignatureScheme as IotaSignatureScheme; +use iota_interaction::types::transaction::TransactionData; +use iota_interaction::IotaKeySignature; +use iota_interaction::OptionalSync; use identity_verification::jwk::Jwk; use identity_verification::jwk::JwkParams; use identity_verification::jwk::JwkParamsEc; From 00cc42c57fd152f7a1d8250c9700deb28a4137ef Mon Sep 17 00:00:00 2001 From: chrisgitiota Date: Fri, 25 Apr 2025 11:54:17 +0200 Subject: [PATCH 06/10] First WASM32 (not NodeJS build) buildable untested version using product_core --- Cargo.toml | 2 + bindings/wasm/iota_move_calls_ts/Cargo.toml | 1 + .../src/asset_move_calls.rs | 12 +- .../src/bindings/keytool/mod.rs | 8 - .../src/bindings/keytool/signer.rs | 110 ---- .../src/bindings/keytool/storage.rs | 147 ----- .../iota_move_calls_ts/src/bindings/mod.rs | 11 - .../iota_move_calls_ts/src/bindings/types.rs | 12 - .../src/bindings/wasm_iota_client.rs | 437 ------------- .../src/bindings/wasm_types.rs | 581 ------------------ .../iota_move_calls_ts/src/common/macros.rs | 62 -- .../wasm/iota_move_calls_ts/src/common/mod.rs | 11 - .../iota_move_calls_ts/src/common/types.rs | 122 ---- .../iota_move_calls_ts/src/common/utils.rs | 26 - bindings/wasm/iota_move_calls_ts/src/error.rs | 223 ------- .../src/identity_move_calls.rs | 26 +- .../src/iota_client_ts_sdk.rs | 427 ------------- bindings/wasm/iota_move_calls_ts/src/lib.rs | 41 -- .../src/migration_move_calls.rs | 10 +- .../src/transaction_builder.rs | 63 -- identity_iota_core/Cargo.toml | 10 +- .../src/iota_interaction_adapter.rs | 2 +- identity_iota_move_calls/Cargo.toml | 44 +- rustfmt.toml | 3 - 24 files changed, 37 insertions(+), 2354 deletions(-) delete mode 100644 bindings/wasm/iota_move_calls_ts/src/bindings/keytool/mod.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/bindings/keytool/signer.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/bindings/keytool/storage.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/bindings/mod.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/bindings/types.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/bindings/wasm_iota_client.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/bindings/wasm_types.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/common/macros.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/common/mod.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/common/types.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/common/utils.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/error.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/iota_client_ts_sdk.rs delete mode 100644 bindings/wasm/iota_move_calls_ts/src/transaction_builder.rs diff --git a/Cargo.toml b/Cargo.toml index 8e82caf0b..1b46d62fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,8 @@ exclude = ["bindings/wasm/identity_wasm", "bindings/grpc"] bls12_381_plus = { version = "0.8.17" } #iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction" } iota_interaction = { path = "../product-core/iota_interaction" } +#iota_interaction_ts = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_ts" } +iota_interaction_ts = { path = "../product-core/bindings/wasm/iota_interaction_ts" } #product_common = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "product_common" } product_common = { path = "../product-core/product_common" } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } diff --git a/bindings/wasm/iota_move_calls_ts/Cargo.toml b/bindings/wasm/iota_move_calls_ts/Cargo.toml index 8aac4a97a..5c6740922 100644 --- a/bindings/wasm/iota_move_calls_ts/Cargo.toml +++ b/bindings/wasm/iota_move_calls_ts/Cargo.toml @@ -28,6 +28,7 @@ identity_core = { version = "=1.6.0-alpha", path = "../../../identity_core" } identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../../../identity_iota_move_calls" } #iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false } iota_interaction = { path = "../../../../product-core/iota_interaction", default-features = false } +iota_interaction_ts.workspace = true js-sys = { version = "0.3.61" } secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } serde = { version = "1.0", features = ["derive"] } diff --git a/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs index 11c8269b4..629b82274 100644 --- a/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs +++ b/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs @@ -7,16 +7,18 @@ use serde::Serialize; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; -use crate::bindings::WasmObjectRef; -use crate::bindings::WasmSharedObjectRef; -use crate::error::TsSdkError; -use crate::error::WasmError; +use iota_interaction_ts::bindings::WasmObjectRef; +use iota_interaction_ts::bindings::WasmSharedObjectRef; +use iota_interaction_ts::error::TsSdkError; +use iota_interaction_ts::error::WasmError; + +use identity_iota_move_calls::AssetMoveCalls; + use iota_interaction::types::base_types::IotaAddress; use iota_interaction::types::base_types::ObjectID; use iota_interaction::types::base_types::ObjectRef; use iota_interaction::types::base_types::SequenceNumber; use iota_interaction::types::TypeTag; -use iota_interaction::AssetMoveCalls; use iota_interaction::MoveType; use iota_interaction::ProgrammableTransactionBcs; diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/mod.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/mod.rs deleted file mode 100644 index 4bc1a321a..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -mod signer; -mod storage; - -pub use signer::*; -pub use storage::*; diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/signer.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/signer.rs deleted file mode 100644 index 2f9179a7b..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/signer.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::str::FromStr; - -use crate::error::Result; -use crate::error::WasmResult; -use fastcrypto::traits::EncodeDecodeBase64 as _; -use iota_interaction::types::base_types::IotaAddress; -use iota_interaction::KeytoolSigner; -use iota_interaction::KeytoolSignerBuilder; -use secret_storage::Signer; -use serde_json::Value; -use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsError; - -use crate::WasmPublicKey; - -#[wasm_bindgen(module = buffer)] -extern "C" { - #[wasm_bindgen(typescript_type = Buffer)] - type NodeBuffer; - #[wasm_bindgen(method, js_name = toString)] - fn to_string(this: &NodeBuffer) -> String; -} - -#[wasm_bindgen(module = child_process)] -extern "C" { - #[wasm_bindgen(js_name = execSync, catch)] - fn exec_cli_cmd(cmd: &str) -> Result; -} - -/// An implementation of the Signer interface that relies on -/// IOTA Keytool. -#[wasm_bindgen(js_name = KeytoolSigner)] -pub struct WasmKeytoolSigner(pub(crate) KeytoolSigner); - -#[wasm_bindgen(js_class = KeytoolSigner)] -impl WasmKeytoolSigner { - /// Returns a new {@link KeytoolSigner}. The optional parameters respectively - /// allow to set the signing address and the `iota` binary to use. Defaults - /// to use the current active address and the binary found in $PATH. - #[wasm_bindgen(constructor)] - pub fn new(address: Option, iota_bin_location: Option) -> Result { - let address = address - .as_deref() - .map(IotaAddress::from_str) - .transpose() - .wasm_result()?; - - let builder = address - .map(|address| KeytoolSignerBuilder::new().with_address(address)) - .unwrap_or_default(); - let builder = if let Some(iota_bin_location) = iota_bin_location { - builder.iota_bin_location(iota_bin_location) - } else { - builder - }; - - Ok(WasmKeytoolSigner(builder.build().wasm_result()?)) - } - - /// Returns the signing address. - #[wasm_bindgen] - pub fn address(&self) -> String { - self.0.address().to_string() - } - - // These method definition are needed to make sure `KeytoolSigner` - // implements `Signer` interface. - - #[wasm_bindgen(js_name = keyId)] - pub fn key_id(&self) -> String { - self.address() - } - - #[wasm_bindgen(js_name = publicKey)] - pub async fn public_key(&self) -> Result { - self.0.public_key().try_into() - } - - #[wasm_bindgen] - pub async fn sign(&self, tx_data_bcs: &[u8]) -> Result { - let tx_data = bcs::from_bytes(tx_data_bcs).map_err(|e| JsError::new(&e.to_string()))?; - self - .0 - .sign(&tx_data) - .await - .map(|sig| sig.encode_base64()) - .map_err(|e| JsError::new(&e.to_string()).into()) - } - - #[wasm_bindgen(js_name = iotaPublicKeyBytes)] - pub async fn iota_public_key_bytes(&self) -> Vec { - let pk = self.0.public_key(); - let mut bytes = vec![pk.flag()]; - bytes.extend_from_slice(pk.as_ref()); - - bytes - } -} - -// This is used in KeytoolSigner implementation to issue CLI commands. -#[no_mangle] -pub extern "Rust" fn __wasm_exec_iota_cmd(cmd: &str) -> anyhow::Result { - let output = exec_cli_cmd(cmd) - .map_err(|e| anyhow::anyhow!("exec failed: {e:?}"))? - .to_string(); - serde_json::from_str(&output).map_err(|_| anyhow::anyhow!("failed to deserialize JSON object from command output")) -} diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/storage.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/storage.rs deleted file mode 100644 index 9f03ec277..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/keytool/storage.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::str::FromStr; - -use iota_interaction::types::base_types::IotaAddress; -use iota_interaction::types::crypto::IotaKeyPair; -use iota_interaction::types::crypto::SignatureScheme; -use iota_interaction::KeytoolStorage; -use js_sys::Array; -use js_sys::JsString; -use wasm_bindgen::prelude::*; - -use crate::error::Result; -use crate::error::WasmResult; -use crate::WasmPublicKey; - -use super::signer::WasmKeytoolSigner; - -const __TS_IMPORTS: &str = r#" -import { PublicKey } from "@iota/iota-sdk/cryptography"; -"#; - -fn make_pk_alias_tuple(pk: &WasmPublicKey, alias: &str) -> Array { - let arr = Array::new(); - arr.push(pk.as_ref()); - arr.push(JsString::from(alias).as_ref()); - - arr -} - -/// IOTA Keytool CLI wrapper. -#[derive(Default, Clone)] -#[wasm_bindgen(js_name = KeytoolStorage)] -pub struct WasmKeytoolStorage(pub(crate) KeytoolStorage); - -impl AsRef for WasmKeytoolStorage { - fn as_ref(&self) -> &KeytoolStorage { - &self.0 - } -} - -#[wasm_bindgen(js_class = KeytoolStorage)] -impl WasmKeytoolStorage { - /// Creates a new {@link KeytoolStorage} that wraps the given iota binary. - /// Attempts to use the one in PATH if none is provided. - #[wasm_bindgen(constructor)] - pub fn new(iota_bin: Option) -> Self { - iota_bin - .as_deref() - .map(KeytoolStorage::new_with_custom_bin) - .map(Self) - .unwrap_or_default() - } - - /// Returns a {@link KeytoolSigner} that will use the provided `address` - /// to sign transactions. If no address is provided the current active - /// one will be used. - pub fn signer(&self, address: Option) -> Result { - let address = address.map(|s| IotaAddress::from_str(&s)).transpose().wasm_result()?; - let mut signer_builder = self.0.signer(); - if let Some(address) = address { - signer_builder = signer_builder.with_address(address); - } - - signer_builder.build().map(WasmKeytoolSigner).wasm_result() - } - - /// Creates a new key of type `key_scheme`. - /// Returns the tuple ([`PublicKey`](https://docs.iota.org/ts-sdk/api/cryptography/classes/PublicKey), alias). - #[wasm_bindgen( - js_name = generateKey, - unchecked_return_type = "[PublicKey, string]", - )] - pub fn generate_key( - &self, - #[wasm_bindgen(unchecked_param_type = "'ed25519' | 'secp256r1' | 'secp256k1'")] key_scheme: &str, - ) -> Result { - let key_scheme = match key_scheme { - "ed25519" => SignatureScheme::ED25519, - "secp256r1" => SignatureScheme::Secp256r1, - "secp256k1" => SignatureScheme::Secp256k1, - _ => return Err(JsError::new("invalid key type").into()), - }; - - self - .0 - .generate_key(key_scheme) - .wasm_result() - .map(|(pk, alias)| make_pk_alias_tuple(&WasmPublicKey::try_from(&pk).unwrap(), &alias)) - } - - /// Inserts a Bech32-encoded private key in the keystore. - /// The key must use the prefix `iotaprivkey`. - /// - /// Returns the key's alias. - #[wasm_bindgen(js_name = insertKey)] - pub fn insert_key(&self, bech32_secret_key: &str) -> Result { - let key = IotaKeyPair::decode(bech32_secret_key) - .map_err(|e| anyhow::anyhow!("{e:?}")) - .wasm_result()?; - self.0.insert_key(key).wasm_result() - } - - /// Signs `data` with `address`'s secret key. - #[wasm_bindgen(js_name = signRaw)] - pub fn sign_raw(&self, address: &str, data: &[u8]) -> Result> { - let address = address.parse().wasm_result()?; - self.0.sign_raw(address, data).wasm_result() - } - - /// Updates an alias from `old_alias` to `new_alias` - /// If no value for `new_alias` is provided, a randomly generated one will be used. - #[wasm_bindgen(js_name = updateAlias)] - pub fn update_alias(&self, old_alias: &str, new_alias: Option) -> Result<()> { - let new_alias = new_alias.as_deref(); - self.0.update_alias(old_alias, new_alias).wasm_result() - } - - /// Returns the [`PublicKey`](https://docs.iota.org/ts-sdk/api/cryptography/classes/PublicKey) for the given address together with its alias. - #[wasm_bindgen( - js_name = getKey, - unchecked_return_type = "[PublicKey, string]", - )] - pub fn get_key(&self, address: &str) -> Result { - let address = address.parse().wasm_result()?; - self - .0 - .get_key(address) - .wasm_result()? - .map(|(pk, alias)| make_pk_alias_tuple(&WasmPublicKey::try_from(&pk).unwrap(), &alias)) - .ok_or_else(|| anyhow::anyhow!("the requested address is not in the keystore")) - .wasm_result() - } - - /// Returns the [`PublicKey`](https://docs.iota.org/ts-sdk/api/cryptography/classes/PublicKey) that has the given alias. - #[wasm_bindgen(js_name = getKeyByAlias)] - pub fn get_key_by_alias(&self, alias: &str) -> Result { - self - .0 - .get_key_by_alias(alias) - .wasm_result()? - .map(|pk| WasmPublicKey::try_from(&pk).unwrap()) - .ok_or_else(|| anyhow::anyhow!("the requested alias is not in the keystore")) - .wasm_result() - } -} diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/mod.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/mod.rs deleted file mode 100644 index 095591007..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -#[cfg(feature = "keytool")] -pub mod keytool; -mod types; -mod wasm_iota_client; -mod wasm_types; - -pub use types::*; -pub use wasm_iota_client::*; diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/types.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/types.rs deleted file mode 100644 index fe0645f61..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/types.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use wasm_bindgen::prelude::*; - -pub use super::wasm_types::*; - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseBalance; -} diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_iota_client.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_iota_client.rs deleted file mode 100644 index 98d6883ac..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_iota_client.rs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use iota_interaction::error::Error as IotaRpcError; -use iota_interaction::error::IotaRpcResult; -use iota_interaction::generated_types::ExecuteTransactionBlockParams; -use iota_interaction::generated_types::GetCoinsParams; -use iota_interaction::generated_types::GetDynamicFieldObjectParams; -use iota_interaction::generated_types::GetObjectParams; -use iota_interaction::generated_types::GetOwnedObjectsParams; -use iota_interaction::generated_types::GetTransactionBlockParams; -use iota_interaction::generated_types::QueryEventsParams; -use iota_interaction::generated_types::SortOrder; -use iota_interaction::generated_types::WaitForTransactionParams; -use iota_interaction::rpc_types::CoinPage; -use iota_interaction::rpc_types::EventFilter; -use iota_interaction::rpc_types::EventPage; -use iota_interaction::rpc_types::IotaObjectDataOptions; -use iota_interaction::rpc_types::IotaObjectResponse; -use iota_interaction::rpc_types::IotaObjectResponseQuery; -use iota_interaction::rpc_types::IotaPastObjectResponse; -use iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; -use iota_interaction::rpc_types::ObjectsPage; -use iota_interaction::types::base_types::IotaAddress; -use iota_interaction::types::base_types::ObjectID; -use iota_interaction::types::base_types::SequenceNumber; -use iota_interaction::types::crypto::Signature; -use iota_interaction::types::digests::TransactionDigest; -use iota_interaction::types::dynamic_field::DynamicFieldName; -use iota_interaction::types::event::EventID; -use iota_interaction::types::quorum_driver_types::ExecuteTransactionRequestType; -use iota_interaction::types::transaction::TransactionData; -use js_sys::Promise; -use serde::Serialize; -use wasm_bindgen::prelude::*; -use wasm_bindgen_futures::JsFuture; - -use super::wasm_types::PromiseIotaTransactionBlockResponse; -use super::wasm_types::WasmExecuteTransactionBlockParams; -use super::wasm_types::WasmIotaTransactionBlockResponseWrapper; -use super::PromiseDryRunTransactionBlockResponse; -use super::WasmDryRunTransactionBlockParams; -use super::WasmWaitForTransactionParams; - -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 used as arguments for rust functions via the typescript_type annotation. -// In other words: The typescript_type "IotaClient" is imported here to be bound -// to the WasmIotaClient functions below. -// TODO: check why this isn't done by `module` macro attribute for `WasmIotaClient` -#[wasm_bindgen(typescript_custom_section)] -const IOTA_CLIENT_TYPE: &'static str = r#" - import { IotaClient } from "@iota/iota-sdk/client"; -"#; - -#[wasm_bindgen(module = "@iota/iota-sdk/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 = dryRunTransactionBlock)] - pub fn dry_run_transaction_block( - this: &WasmIotaClient, - params: &WasmDryRunTransactionBlockParams, - ) -> PromiseDryRunTransactionBlockResponse; - - #[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; - - #[wasm_bindgen(method, js_name = waitForTransaction)] - pub fn wait_for_transaction( - this: &WasmIotaClient, - input: &WasmWaitForTransactionParams, - ) -> PromiseIotaTransactionBlockResponse; -} - -// Helper struct used to convert TYPESCRIPT types to RUST types -#[derive(Clone)] -pub struct ManagedWasmIotaClient(pub(crate) 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: TransactionData, - signatures: Vec, - options: Option, - request_type: Option, - ) -> IotaRpcResult { - let params = ExecuteTransactionBlockParams::new(tx_data, signatures, options, request_type); - let wasm_params = params - .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .map_err(|e| { - IotaRpcError::FfiError(format!( - "failed to convert ExecuteTransactionBlockParams to JS value: {e}" - )) - })? - .into(); - - let promise: Promise = Promise::resolve(&WasmIotaClient::execute_transaction_block(&self.0, &wasm_params)); - let result: JsValue = JsFuture::from(promise).await.map_err(|e| { - console_log!("Error executing JsFuture::from(promise): {:?}", e); - IotaRpcError::FfiError(format!("{:?}", e)) - })?; - Ok(WasmIotaTransactionBlockResponseWrapper::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)) - })?; - - #[allow(deprecated)] // will be refactored - 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)) - })?; - - #[allow(deprecated)] // will be refactored - 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)) - })?; - - #[allow(deprecated)] // will be refactored - 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(WasmIotaTransactionBlockResponseWrapper::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)) - })?; - - serde_wasm_bindgen::from_value(result) - .map_err(|e| IotaRpcError::FfiError(format!("failed to deserialize gas price from JS value: {e}"))) - } - - 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)) - })?; - - #[allow(deprecated)] // will be refactored - 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)) - })?; - - #[allow(deprecated)] // will be refactored - Ok(result.into_serde()?) - } - - /// Wait for a transaction block result to be available over the API. - /// This can be used in conjunction with `execute_transaction_block` to wait for the transaction to - /// be available via the API. - /// This currently polls the `getTransactionBlock` API to check for the transaction. - /// - /// # Arguments - /// - /// * `digest` - The digest of the queried transaction. - /// * `options` - Options for specifying the content to be returned. - /// * `timeout` - The amount of time to wait for a transaction block. Defaults to one minute. - /// * `poll_interval` - The amount of time to wait between checks for the transaction block. Defaults to 2 seconds. - pub async fn wait_for_transaction( - &self, - digest: TransactionDigest, - options: Option, - timeout: Option, - poll_interval: Option, - ) -> IotaRpcResult { - let params_object = WaitForTransactionParams::new(digest.to_string(), options, timeout, poll_interval); - let params: WasmWaitForTransactionParams = serde_json::to_value(¶ms_object) - .map_err(|e| { - console_log!("Error serializing WaitForTransactionParams to Value: {:?}", e); - IotaRpcError::FfiError(format!("{:?}", e)) - }) - .and_then(|v| { - v.serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .map_err(|e| { - console_log!("Error serializing Value to WasmWaitForTransactionParams: {:?}", e); - IotaRpcError::FfiError(format!("{:?}", e)) - }) - })? - .into(); - - let promise: Promise = Promise::resolve(&WasmIotaClient::wait_for_transaction(&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(WasmIotaTransactionBlockResponseWrapper::new(result.into())) - } -} diff --git a/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_types.rs b/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_types.rs deleted file mode 100644 index cc7788f31..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/bindings/wasm_types.rs +++ /dev/null @@ -1,581 +0,0 @@ -// Copyright 2020-2025 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 -use std::str::FromStr; - -use fastcrypto::encoding::Base64; -use fastcrypto::encoding::Encoding; -use fastcrypto::traits::EncodeDecodeBase64 as _; -use iota_interaction::rpc_types::IotaTransactionBlockEffects; -use iota_interaction::rpc_types::OwnedObjectRef; -use iota_interaction::types::base_types::IotaAddress; -use iota_interaction::types::base_types::ObjectID; -use iota_interaction::types::base_types::ObjectRef; -use iota_interaction::types::base_types::SequenceNumber; -use iota_interaction::types::crypto::PublicKey; -use iota_interaction::types::crypto::Signature; -use iota_interaction::types::digests::TransactionDigest; -use iota_interaction::types::execution_status::CommandArgumentError; -use iota_interaction::types::object::Owner; -use iota_interaction::types::transaction::TransactionData; -use iota_interaction::ProgrammableTransactionBcs; -use js_sys::Promise; -use js_sys::Uint8Array; -use serde::Deserialize; -use serde::Serialize; -use wasm_bindgen::prelude::wasm_bindgen; -use wasm_bindgen::JsCast; -use wasm_bindgen::JsError; -use wasm_bindgen::JsValue; -use wasm_bindgen_futures::JsFuture; - -use crate::bindings::WasmIotaClient; -use crate::console_log; -use crate::error::TsSdkError; -use crate::error::WasmError; - -// TODO: fix/add signer or remove functions relying on it -type WasmStorageSigner = (); - -#[wasm_bindgen(typescript_custom_section)] -const TS_SDK_TYPES: &str = r#" - import { - Balance, - ExecuteTransactionBlockParams, - GetCoinsParams, - GetDynamicFieldObjectParams, - GetObjectParams, - GetOwnedObjectsParams, - GetTransactionBlockParams, - IotaClient, - IotaObjectData, - IotaObjectResponse, - IotaTransactionBlockResponse, - IotaTransactionBlockResponseOptions, - ObjectRead, - PaginatedCoins, - PaginatedEvents, - PaginatedObjectsResponse, - QueryEventsParams, - TryGetPastObjectParams, - } from "@iota/iota-sdk/client"; - import { bcs } from "@iota/iota-sdk/bcs"; - import { - executeTransaction, - WasmIotaTransactionBlockResponseWrapper, - } from "./iota_client_helpers" -"#; - -#[wasm_bindgen(module = "@iota/iota-sdk/client")] -extern "C" { - #[wasm_bindgen(typescript_type = "Promise")] - pub type PromiseBalance; - - #[wasm_bindgen(typescript_type = "TransactionArgument")] - pub type WasmTransactionArgument; - - #[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 = "Promise")] - pub type PromiseDryRunTransactionBlockResponse; - - #[wasm_bindgen(typescript_type = "DryRunTransactionBlockResponse")] - #[derive(Clone)] - pub type WasmDryRunTransactionBlockResponse; - - #[wasm_bindgen(typescript_type = "DryRunTransactionBlockParams")] - #[derive(Clone)] - pub type WasmDryRunTransactionBlockParams; - - #[derive(Clone)] - #[wasm_bindgen( - typescript_type = "TransactionEffects", - extends = js_sys::Object, - )] - pub type WasmIotaTransactionBlockEffects; - - #[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 = "IotaObjectRef")] - #[derive(Clone)] - pub type WasmObjectRef; - - #[wasm_bindgen(method, getter, js_name = objectId)] - pub fn object_id(this: &WasmObjectRef) -> String; - - #[wasm_bindgen(method, getter, js_name = digest)] - pub fn digest(this: &WasmObjectRef) -> String; - - #[wasm_bindgen(method, getter, js_name = version)] - pub fn version(this: &WasmObjectRef) -> String; - - #[wasm_bindgen(typescript_type = "SharedObjectRef")] - #[derive(Clone)] - pub type WasmSharedObjectRef; - - #[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 PromiseIotaTransactionBlockResponseWrapper; - - #[wasm_bindgen(typescript_type = "Signature")] - pub type WasmIotaSignature; - - #[wasm_bindgen(typescript_type = "Parameters")] - #[derive(Clone, Debug)] - pub type WasmWaitForTransactionParams; -} - -impl From for IotaTransactionBlockEffects { - fn from(value: WasmIotaTransactionBlockEffects) -> Self { - serde_wasm_bindgen::from_value(value.into()).expect("have the same repr") - } -} - -impl From<&'_ IotaTransactionBlockEffects> for WasmIotaTransactionBlockEffects { - fn from(value: &'_ IotaTransactionBlockEffects) -> Self { - value - .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .expect("same representation") - .unchecked_into() - } -} - -#[derive(Serialize, Deserialize)] -enum IotaSignatureHelper { - Ed25519IotaSignature(String), - Secp256k1IotaSignature(String), - Secp256r1IotaSignature(String), -} - -impl TryFrom for WasmIotaSignature { - type Error = JsValue; - fn try_from(sig: Signature) -> Result { - let base64sig = Base64::encode(&sig); - let json_signature = match sig { - Signature::Ed25519IotaSignature(_) => IotaSignatureHelper::Ed25519IotaSignature(base64sig), - Signature::Secp256r1IotaSignature(_) => IotaSignatureHelper::Secp256r1IotaSignature(base64sig), - Signature::Secp256k1IotaSignature(_) => IotaSignatureHelper::Secp256k1IotaSignature(base64sig), - }; - - json_signature - .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .map(JsCast::unchecked_into) - .map_err(|e| e.into()) - } -} - -impl TryFrom for Signature { - type Error = JsValue; - fn try_from(sig: WasmIotaSignature) -> Result { - let sig_helper = serde_wasm_bindgen::from_value(sig.into())?; - let base64sig = match sig_helper { - IotaSignatureHelper::Ed25519IotaSignature(s) => s, - IotaSignatureHelper::Secp256k1IotaSignature(s) => s, - IotaSignatureHelper::Secp256r1IotaSignature(s) => s, - }; - - base64sig - .parse() - .map_err(|e: eyre::Report| JsError::new(&e.to_string()).into()) - } -} - -#[wasm_bindgen(module = "@iota/iota-sdk/transactions")] -extern "C" { - #[derive(Clone)] - #[wasm_bindgen(typescript_type = "Transaction")] - pub type WasmTransaction; - - #[wasm_bindgen(js_name = "from", js_class = "Transaction", static_method_of = WasmTransaction, catch)] - pub fn from_bcs_bytes(bytes: Uint8Array) -> Result; - - #[wasm_bindgen(method, structural, catch)] - pub async fn build( - this: &WasmTransaction, - options: Option, - ) -> Result; - - #[wasm_bindgen(typescript_type = BuildTransactionOptions)] - pub type WasmBuildTransactionOptions; - - #[derive(Clone)] - #[wasm_bindgen(typescript_type = "TransactionData")] - pub type WasmTransactionData; - - #[wasm_bindgen(typescript_type = "Transaction")] - pub type WasmTransactionBuilder; - - #[wasm_bindgen(js_name = "from", js_class = "Transaction", static_method_of = WasmTransactionBuilder, catch)] - pub fn from_bcs_bytes(bytes: Uint8Array) -> Result; - - #[wasm_bindgen(method, structural, catch)] - pub async fn build(this: &WasmTransactionBuilder) -> Result; - - #[derive(Clone)] - #[wasm_bindgen(typescript_type = TransactionDataBuilder)] - pub type WasmTransactionDataBuilder; - - #[wasm_bindgen( - js_name = fromBytes, - js_class = TransactionDataBuilder, - static_method_of = WasmTransactionDataBuilder, - catch - )] - pub fn from_bcs_bytes(bytes: Uint8Array) -> Result; - - #[wasm_bindgen( - js_name = fromKindBytes, - js_class = TransactionDataBuilder, - static_method_of = WasmTransactionDataBuilder, - catch - )] - pub fn from_kind_bcs_bytes(bytes: Uint8Array) -> Result; - - #[wasm_bindgen(method, catch)] - pub fn build(this: &WasmTransactionDataBuilder) -> Result; - // TODO: decide if we need the following functions: "yagni" or not? - - // #[wasm_bindgen(js_name = "setSender", method, catch)] - // pub fn set_sender(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; - - // #[wasm_bindgen(js_name = "setGasOwner", method, catch)] - // pub fn set_gas_owner(this: &WasmTransactionBuilder, address: String) -> Result<(), JsValue>; - - // #[wasm_bindgen(js_name = "setGasPrice", method, catch)] - // pub fn set_gas_price(this: &WasmTransactionBuilder, price: u64) -> Result<(), JsValue>; - - // #[wasm_bindgen(js_name = "setGasPayment", method, catch)] - // pub fn set_gas_payment(this: &WasmTransactionBuilder, payments: Vec) -> Result<(), JsValue>; - - // #[wasm_bindgen(js_name = "setGasBudget", method, catch)] - // pub fn set_gas_budget(this: &WasmTransactionBuilder, budget: u64) -> Result<(), JsValue>; - - // #[wasm_bindgen(js_name = "getData", method, catch)] - // pub fn get_data(this: &WasmTransactionBuilder) -> Result; -} - -impl TryFrom for WasmTransactionData { - type Error = serde_wasm_bindgen::Error; - fn try_from(value: TransactionData) -> Result { - let js_value = serde_wasm_bindgen::to_value(&value)?; - Ok(js_value.unchecked_into()) - } -} - -impl TryFrom for TransactionData { - type Error = serde_wasm_bindgen::Error; - fn try_from(value: WasmTransactionData) -> Result { - serde_wasm_bindgen::from_value(value.into()) - } -} - -#[wasm_bindgen(module = "@iota/iota-sdk/cryptography")] -extern "C" { - #[derive(Clone)] - #[wasm_bindgen(typescript_type = PublicKey)] - pub type WasmPublicKey; - - #[wasm_bindgen(js_name = toIotaPublicKey, method)] - pub fn to_iota_public_key(this: &WasmPublicKey) -> String; - - #[wasm_bindgen(js_name = toRawBytes, method)] - pub fn to_raw_bytes(this: &WasmPublicKey) -> Vec; - - #[wasm_bindgen(js_name = toIotaAddress, method)] - pub fn to_iota_address(this: &WasmPublicKey) -> String; - - #[wasm_bindgen(method)] - pub fn flag(this: &WasmPublicKey) -> u8; -} - -#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/ed25519")] -extern "C" { - #[wasm_bindgen(extends = WasmPublicKey)] - pub type Ed25519PublicKey; - - #[wasm_bindgen(constructor, catch)] - pub fn new_ed25519_pk(bytes: &[u8]) -> Result; -} - -#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/secp256r1")] -extern "C" { - #[wasm_bindgen(extends = WasmPublicKey)] - pub type Secp256r1PublicKey; - - #[wasm_bindgen(constructor, catch)] - pub fn new_secp256r1_pk(bytes: &[u8]) -> Result; -} - -#[wasm_bindgen(module = "@iota/iota-sdk/keypairs/secp256k1")] -extern "C" { - #[wasm_bindgen(extends = WasmPublicKey)] - pub type Secp256k1PublicKey; - - #[wasm_bindgen(constructor, catch)] - pub fn new_secp256k1_pk(bytes: &[u8]) -> Result; -} - -impl TryFrom<&'_ PublicKey> for WasmPublicKey { - type Error = JsValue; - fn try_from(pk: &PublicKey) -> Result { - let pk_bytes = pk.as_ref(); - let wasm_pk: WasmPublicKey = match pk { - PublicKey::Ed25519(_) => Ed25519PublicKey::new_ed25519_pk(pk_bytes)?.into(), - PublicKey::Secp256r1(_) => Secp256r1PublicKey::new_secp256r1_pk(pk_bytes)?.into(), - PublicKey::Secp256k1(_) => Secp256k1PublicKey::new_secp256k1_pk(pk_bytes)?.into(), - _ => return Err(JsError::new("unsupported PublicKey type").into()), - }; - - assert_eq!(pk_bytes, &wasm_pk.to_raw_bytes()); - assert_eq!( - IotaAddress::from(pk), - wasm_pk.to_iota_address().parse().expect("valid iota address") - ); - - Ok(wasm_pk) - } -} - -impl TryFrom for PublicKey { - type Error = JsValue; - fn try_from(wasm_pk: WasmPublicKey) -> Result { - let pk = PublicKey::decode_base64(&wasm_pk.to_iota_public_key()) - .map_err(|_| JsError::new("failed to decode base64 JS PublicKey"))?; - - assert_eq!(&wasm_pk.to_raw_bytes(), pk.as_ref()); - assert_eq!( - IotaAddress::from(&pk), - wasm_pk.to_iota_address().parse().expect("valid iota address") - ); - - Ok(pk) - } -} - -impl TryFrom for ObjectRef { - type Error = anyhow::Error; - fn try_from(value: WasmObjectRef) -> Result { - let digest = serde_json::from_value(serde_json::Value::String(value.digest()))?; - let version = { - let version_number = serde_json::Number::from_str(&value.version())?; - serde_json::from_value(serde_json::Value::Number(version_number))? - }; - let object_id = value.object_id().parse()?; - - Ok((object_id, version, digest)) - } -} - -impl From for WasmObjectRef { - fn from(value: ObjectRef) -> Self { - let json_obj = serde_json::json!({ - "objectId": value.0, - "version": value.1, - "digest": value.2, - }); - - json_obj - .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .expect("a JSON object is a JS value") - // safety: `json_obj` was constructed following TS ObjectRef's interface. - .unchecked_into() - } -} - -impl From<(ObjectID, SequenceNumber, bool)> for WasmSharedObjectRef { - fn from(value: (ObjectID, SequenceNumber, bool)) -> Self { - let json_obj = serde_json::json!({ - "objectId": value.0, - "initialSharedVersion": value.1, - "mutable": value.2, - }); - - json_obj - .serialize(&serde_wasm_bindgen::Serializer::json_compatible()) - .expect("a JSON object is a JS value") - // safety: `json_obj` was constructed following TS SharedObjectRef's interface. - .unchecked_into() - } -} - -impl TryFrom for WasmSharedObjectRef { - type Error = TsSdkError; - fn try_from(value: OwnedObjectRef) -> Result { - let Owner::Shared { initial_shared_version } = value.owner else { - return Err(TsSdkError::CommandArgumentError(CommandArgumentError::TypeMismatch)); - }; - let obj_id = value.object_id(); - - Ok((obj_id, initial_shared_version, true).into()) - } -} - -impl WasmSharedObjectRef { - #[allow(dead_code)] - pub(crate) fn immutable(self) -> Self { - const JS_FALSE: JsValue = JsValue::from_bool(false); - - let _ = js_sys::Reflect::set(&self, &JsValue::from_str("mutable"), &JS_FALSE); - self - } -} - -#[wasm_bindgen(module = "@iota/iota-interaction-ts/iota_client_helpers")] -extern "C" { - // Please note: For unclear reasons the `typescript_type` name and the `pub type` name defined - // in wasm_bindgen extern "C" scopes must be equal. Otherwise, the JS constructor will not be - // found in the generated js code. - #[wasm_bindgen(typescript_type = "WasmIotaTransactionBlockResponseWrapper")] - #[derive(Clone)] - pub type WasmIotaTransactionBlockResponseWrapper; - - #[wasm_bindgen(constructor)] - pub fn new(response: WasmIotaTransactionBlockResponse) -> WasmIotaTransactionBlockResponseWrapper; - - #[wasm_bindgen(method, js_name = get_effects)] - pub fn effects(this: &WasmIotaTransactionBlockResponseWrapper) -> Option; - - #[wasm_bindgen(method)] - pub fn to_string(this: &WasmIotaTransactionBlockResponseWrapper) -> String; - - #[wasm_bindgen(method, js_name = "get_digest")] - fn digest_inner(this: &WasmIotaTransactionBlockResponseWrapper) -> String; - - #[wasm_bindgen(method, js_name = "get_response")] - pub fn response(this: &WasmIotaTransactionBlockResponseWrapper) -> WasmIotaTransactionBlockResponse; - - #[wasm_bindgen(js_name = executeTransaction)] - fn execute_transaction_inner( - iota_client: &WasmIotaClient, // --> TypeScript: IotaClient - sender_address: String, // --> TypeScript: string - tx_bcs: Vec, // --> TypeScript: Uint8Array, - signer: WasmStorageSigner, // --> TypeScript: Signer (iota_client_helpers module) - gas_budget: Option, // --> TypeScript: optional bigint - ) -> PromiseIotaTransactionBlockResponseWrapper; - - #[wasm_bindgen(js_name = "sleep")] - fn sleep_inner(ms: i32) -> Promise; -} - -/// Helper function to pause execution. -pub async fn sleep(duration_ms: i32) -> Result<(), JsValue> { - let promise = sleep_inner(duration_ms); - let js_fut = JsFuture::from(promise); - js_fut.await?; - Ok(()) -} - -impl WasmIotaTransactionBlockResponseWrapper { - pub fn digest(&self) -> Result { - TransactionDigest::from_str(&self.digest_inner()) - .map_err(|err| TsSdkError::WasmError("Failed to parse transaction block digest".to_string(), err.to_string())) - } -} - -pub async fn execute_transaction( - iota_client: &WasmIotaClient, // --> Binding: WasmIotaClient - sender_address: IotaAddress, // --> Binding: String - 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(), - tx_bcs, - signer, - gas_budget, - )); - let result: JsValue = JsFuture::from(promise).await.map_err(|e| { - let message = "Error executing JsFuture::from(promise) for `execute_transaction`"; - let details = format!("{e:?}"); - console_log!("{message}; {details}"); - TsSdkError::WasmError(message.to_string(), details.to_string()) - })?; - - Ok(WasmIotaTransactionBlockResponseWrapper::new(result.into())) -} - -#[derive(Deserialize)] -#[serde(try_from = "Vec")] -pub struct ProgrammableTransaction(#[allow(dead_code)] pub(crate) WasmTransactionBuilder); -impl TryFrom> for ProgrammableTransaction { - type Error = TsSdkError; - fn try_from(value: Vec) -> Result { - let uint8array: Uint8Array = value.as_slice().into(); - WasmTransactionBuilder::from_bcs_bytes(uint8array) - .map(Self) - .map_err(WasmError::from) - .map_err(TsSdkError::from) - } -} diff --git a/bindings/wasm/iota_move_calls_ts/src/common/macros.rs b/bindings/wasm/iota_move_calls_ts/src/common/macros.rs deleted file mode 100644 index 259fc5417..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/common/macros.rs +++ /dev/null @@ -1,62 +0,0 @@ -// 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_move_calls_ts/src/common/mod.rs b/bindings/wasm/iota_move_calls_ts/src/common/mod.rs deleted file mode 100644 index f764977e2..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/common/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -// 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_move_calls_ts/src/common/types.rs b/bindings/wasm/iota_move_calls_ts/src/common/types.rs deleted file mode 100644 index 472a88bd2..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/common/types.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use identity_core::common::Object; -use iota_interaction::types::transaction::TransactionKind; -use iota_interaction::ProgrammableTransactionBcs; -use js_sys::Promise; -use js_sys::Uint8Array; -use wasm_bindgen::prelude::*; -use wasm_bindgen::JsCast; -use wasm_bindgen_futures::JsFuture; - -use crate::error::TsSdkError; -use crate::error::TsSdkResult; -use crate::error::WasmError; -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()), - #[allow(deprecated)] // will be refactored - &JsValue::from_serde(&value).wasm_result()?, - ); - } - Ok(map.unchecked_into::()) - } -} - -impl Default for MapStringAny { - fn default() -> Self { - js_sys::Map::new().unchecked_into() - } -} - -impl PromiseUint8Array { - /// Helper function to convert Uint8 arrays from contract calls to the internal `ProgrammableTransactionBcs` type. - pub async fn to_programmable_transaction_bcs(&self) -> TsSdkResult { - let promise: Promise = Promise::resolve(self); - let tx_kind_bcs = JsFuture::from(promise) - .await - .map(|v| Uint8Array::from(v).to_vec()) - .map_err(WasmError::from)?; - - let tx_kind = bcs::from_bytes(&tx_kind_bcs).map_err(|e| { - TsSdkError::TransactionSerializationError(format!("failed to deserialize BCS TransactionKind: {e}")) - })?; - - #[allow(irrefutable_let_patterns)] - let TransactionKind::ProgrammableTransaction(pt) = tx_kind - else { - return Err(TsSdkError::WasmError( - "TransactionKind variant".to_owned(), - "only programmable transactions can be used within this library".to_owned(), - )); - }; - - bcs::to_bytes(&pt).map_err(|e| { - TsSdkError::TransactionSerializationError(format!("failed to BCS serialize programmable transaction: {e}")) - }) - } -} diff --git a/bindings/wasm/iota_move_calls_ts/src/common/utils.rs b/bindings/wasm/iota_move_calls_ts/src/common/utils.rs deleted file mode 100644 index 3ed8447a1..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/common/utils.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020-2023 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use serde::de::DeserializeOwned; -use wasm_bindgen::prelude::*; - -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_move_calls_ts/src/error.rs b/bindings/wasm/iota_move_calls_ts/src/error.rs deleted file mode 100644 index ace14ccab..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/error.rs +++ /dev/null @@ -1,223 +0,0 @@ -// 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 iota_interaction::types::execution_status::CommandArgumentError; -use iota_interaction::types::execution_status::ExecutionFailureStatus; -use iota_interaction::types::execution_status::PackageUpgradeError; -use 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()), - } - } -} - -impl From for WasmError<'_> { - fn from(error: anyhow::Error) -> Self { - Self { - name: Cow::Borrowed("anyhow::Error"), - message: Cow::Owned(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(#[from] PackageUpgradeError), - #[error("[TsSdkError] CommandArgumentError: {0}")] - CommandArgumentError(#[from] CommandArgumentError), - #[error("[TsSdkError] ExecutionFailureStatus: {0}")] - ExecutionFailureStatus(#[from] ExecutionFailureStatus), - #[error("[TsSdkError] TypeArgumentError: {0}")] - TypeArgumentError(#[from] TypeArgumentError), - #[error("[TsSdkError] WasmError:{{\n name: {0},\n message: {1}\n}}")] - WasmError(String, String), - #[error("[TsSdkError] JsSysError: {0}")] - JsSysError(String), - #[error("[TsSdkError] TransactionSerializationError: {0}")] - TransactionSerializationError(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_move_calls_ts/src/identity_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs index e0058faf5..f0278fda9 100644 --- a/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs +++ b/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs @@ -9,24 +9,26 @@ use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsCast; use wasm_bindgen::JsValue; -use crate::bindings::WasmIotaObjectData; -use crate::bindings::WasmObjectRef; -use crate::bindings::WasmSharedObjectRef; -use crate::bindings::WasmTransactionArgument; -use crate::bindings::WasmTransactionBuilder; -use crate::common::PromiseUint8Array; -use crate::error::TsSdkError; -use crate::error::WasmError; -use crate::transaction_builder::TransactionBuilderTsSdk; +use iota_interaction_ts::bindings::WasmIotaObjectData; +use iota_interaction_ts::bindings::WasmObjectRef; +use iota_interaction_ts::bindings::WasmSharedObjectRef; +use iota_interaction_ts::bindings::WasmTransactionArgument; +use iota_interaction_ts::bindings::WasmTransactionBuilder; +use iota_interaction_ts::common::PromiseUint8Array; +use iota_interaction_ts::error::TsSdkError; +use iota_interaction_ts::error::WasmError; +use iota_interaction_ts::transaction_builder::TransactionBuilderTsSdk; + +use identity_iota_move_calls::BorrowIntentFnInternalT; +use identity_iota_move_calls::ControllerIntentFnInternalT; +use identity_iota_move_calls::IdentityMoveCalls; + use iota_interaction::rpc_types::IotaObjectData; use iota_interaction::rpc_types::OwnedObjectRef; use iota_interaction::types::base_types::IotaAddress; use iota_interaction::types::base_types::ObjectID; use iota_interaction::types::base_types::ObjectRef; use iota_interaction::types::TypeTag; -use iota_interaction::BorrowIntentFnInternalT; -use iota_interaction::ControllerIntentFnInternalT; -use iota_interaction::IdentityMoveCalls; use iota_interaction::MoveType; use iota_interaction::ProgrammableTransactionBcs; diff --git a/bindings/wasm/iota_move_calls_ts/src/iota_client_ts_sdk.rs b/bindings/wasm/iota_move_calls_ts/src/iota_client_ts_sdk.rs deleted file mode 100644 index e4b66f143..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/iota_client_ts_sdk.rs +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::boxed::Box; -use std::option::Option; -use std::result::Result; - -use iota_interaction::rpc_types::IotaTransactionBlockEffects; -use iota_interaction::types::crypto::Signature; -use iota_interaction::types::digests::TransactionDigest; -use iota_interaction::types::dynamic_field::DynamicFieldName; -use iota_interaction::types::transaction::TransactionData; -use secret_storage::Signer; - -use iota_interaction::error::Error as IotaRpcError; -use iota_interaction::error::IotaRpcResult; -use iota_interaction::rpc_types::CoinPage; -use iota_interaction::rpc_types::EventFilter; -use iota_interaction::rpc_types::EventPage; -use iota_interaction::rpc_types::IotaObjectData; -use iota_interaction::rpc_types::IotaObjectDataOptions; -use iota_interaction::rpc_types::IotaObjectResponse; -use iota_interaction::rpc_types::IotaObjectResponseQuery; -use iota_interaction::rpc_types::IotaPastObjectResponse; -use iota_interaction::rpc_types::IotaTransactionBlockResponseOptions; -use iota_interaction::rpc_types::ObjectsPage; -use iota_interaction::types::base_types::IotaAddress; -use iota_interaction::types::base_types::ObjectID; -use iota_interaction::types::base_types::SequenceNumber; -use iota_interaction::types::event::EventID; -use iota_interaction::types::quorum_driver_types::ExecuteTransactionRequestType; -use iota_interaction::types::transaction::ProgrammableTransaction as ProgrammableTransactionSdk; -use iota_interaction::types::transaction::TransactionDataAPI as _; -use iota_interaction::CoinReadTrait; -use iota_interaction::EventTrait; -use iota_interaction::IotaClientTrait; -use iota_interaction::IotaKeySignature; -use iota_interaction::IotaTransactionBlockResponseT; -use iota_interaction::QuorumDriverTrait; -use iota_interaction::ReadTrait; - -use crate::bindings::ManagedWasmIotaClient; -use crate::bindings::WasmIotaClient; -use crate::bindings::WasmIotaTransactionBlockResponseWrapper; -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: WasmIotaTransactionBlockResponseWrapper, - effects: Option, -} - -impl IotaTransactionBlockResponseProvider { - pub fn new(response: WasmIotaTransactionBlockResponseWrapper) -> Self { - let effects = response.effects().map(Into::into); - IotaTransactionBlockResponseProvider { response, effects } - } -} - -#[async_trait::async_trait(?Send)] -impl IotaTransactionBlockResponseT for IotaTransactionBlockResponseProvider { - type Error = TsSdkError; - type NativeResponse = WasmIotaTransactionBlockResponseWrapper; - - fn to_string(&self) -> String { - format!("{:?}", self.response.to_string()) - } - - fn effects(&self) -> Option<&IotaTransactionBlockEffects> { - self.effects.as_ref() - } - - 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() - } - - fn digest(&self) -> Result { - self.response.digest() - } -} - -pub struct ReadAdapter { - client: ManagedWasmIotaClient, -} - -#[async_trait::async_trait(?Send)] -impl ReadTrait for ReadAdapter { - type Error = TsSdkError; - type NativeResponse = WasmIotaTransactionBlockResponseWrapper; - - 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 = WasmIotaTransactionBlockResponseWrapper; - - async fn execute_transaction_block( - &self, - tx_data: TransactionData, - signatures: Vec, - options: Option, - request_type: Option, - ) -> IotaRpcResult { - let wasm_response = self - .client - .execute_transaction_block(tx_data, signatures, options, request_type) - .await?; - - let digest = wasm_response - .digest() - .map_err(|e| IotaRpcError::FfiError(e.to_string()))?; - - self - .client - .wait_for_transaction(digest, Some(IotaTransactionBlockResponseOptions::new()), None, None) - .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 = WasmIotaTransactionBlockResponseWrapper; - - 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, - tx_data: TransactionData, - signer: &S, - ) -> Result< - Box>, - Self::Error, - > { - let response = self.sdk_execute_transaction(tx_data, signer).await?; - - // wait until new transaction block is available - self - .iota_client - .wait_for_transaction( - response.digest()?, - Some(IotaTransactionBlockResponseOptions::new()), - None, - None, - ) - .await - .unwrap(); - - Ok(Box::new(response)) - } - - async fn default_gas_budget( - &self, - _sender_address: IotaAddress, - _tx: &ProgrammableTransactionSdk, - ) -> Result { - Ok(50_000_000) - } - - 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), - }) - } - - pub fn into_inner(self) -> WasmIotaClient { - self.iota_client.clone().0 - } - - // Submit tx to IOTA client, also: - // - signs tx - // - calls execute_transaction_block to submit tx (with signatures created here) - async fn sdk_execute_transaction>( - &self, - tx: TransactionData, - signer: &S, - ) -> Result { - let sender_public_key = signer - .public_key() - .await - .map_err(|e| TsSdkError::WasmError(String::from("SecretStorage"), e.to_string()))?; - let sender_address = IotaAddress::from(&sender_public_key); - if sender_address != tx.sender() { - return Err(TsSdkError::WasmError("SDK".to_owned(), format!("transaction data needs to be signed by address {}, but client can only provide signature for address {sender_address}", tx.sender()))); - } - let signature = signer - .sign(&tx) - .await - .map_err(|e| TsSdkError::WasmError("SecretStorage".to_owned(), e.to_string()))?; - - let wasm_response = self - .quorum_driver_api() - .execute_transaction_block( - tx, - vec![signature], - Some(IotaTransactionBlockResponseOptions::full_content()), - Some(ExecuteTransactionRequestType::WaitForLocalExecution), - ) - .await - .unwrap(); - let native = wasm_response.clone_native_response(); - - Ok(IotaTransactionBlockResponseProvider::new(native)) - } -} diff --git a/bindings/wasm/iota_move_calls_ts/src/lib.rs b/bindings/wasm/iota_move_calls_ts/src/lib.rs index 218525657..3a4d47077 100644 --- a/bindings/wasm/iota_move_calls_ts/src/lib.rs +++ b/bindings/wasm/iota_move_calls_ts/src/lib.rs @@ -1,58 +1,17 @@ // 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_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { - #[allow(unused_imports)] pub use error::TsSdkError as AdapterError; #[allow(unused_imports)] pub use asset_move_calls::AssetMoveCallsTsSdk as AssetMoveCallsAdapter; #[allow(unused_imports)] pub use identity_move_calls::IdentityMoveCallsTsSdk as IdentityMoveCallsAdapter; - #[allow(unused_imports)] pub use iota_client_ts_sdk::IotaClientTsSdk as IotaClientAdapter; - #[allow(unused_imports)] pub use iota_client_ts_sdk::IotaTransactionBlockResponseProvider as IotaTransactionBlockResponseAdapter; - #[allow(unused_imports)] pub use bindings::WasmIotaTransactionBlockResponseWrapper as NativeTransactionBlockResponse; #[allow(unused_imports)] pub use migration_move_calls::MigrationMoveCallsTsSdk as MigrationMoveCallsAdapter; - #[allow(unused_imports)] pub use transaction_builder::TransactionBuilderTsSdk as TransactionBuilderAdapter; - - #[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 bindings::ProgrammableTransaction; - #[allow(unused_imports)] pub use bindings::WasmPublicKey; - #[allow(unused_imports)] pub use bindings::Ed25519PublicKey as WasmEd25519PublicKey; - #[allow(unused_imports)] pub use bindings::Secp256r1PublicKey as WasmSecp256r1PublicKey; - #[allow(unused_imports)] pub use bindings::Secp256k1PublicKey as WasmSecp256k1PublicKey; - #[allow(unused_imports)] pub use bindings::WasmIotaSignature; - #[cfg(feature = "keytool")] - #[allow(unused_imports)] - pub use bindings::keytool::*; - - #[allow(unused_imports)] pub use transaction_builder::NativeTsTransactionBuilderBindingWrapper; } } diff --git a/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs index 83319bd8d..06b220b05 100644 --- a/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs +++ b/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs @@ -1,19 +1,19 @@ // Copyright 2020-2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_iota_move_calls::MigrationMoveCalls; use iota_interaction::rpc_types::OwnedObjectRef; use iota_interaction::types::base_types::ObjectID; use iota_interaction::types::base_types::ObjectRef; -use iota_interaction::MigrationMoveCalls; use iota_interaction::ProgrammableTransactionBcs; use js_sys::Uint8Array; use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; -use crate::bindings::WasmObjectRef; -use crate::bindings::WasmSharedObjectRef; -use crate::error::TsSdkError; -use crate::error::WasmError; +use iota_interaction_ts::bindings::WasmObjectRef; +use iota_interaction_ts::bindings::WasmSharedObjectRef; +use iota_interaction_ts::error::TsSdkError; +use iota_interaction_ts::error::WasmError; #[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls")] extern "C" { diff --git a/bindings/wasm/iota_move_calls_ts/src/transaction_builder.rs b/bindings/wasm/iota_move_calls_ts/src/transaction_builder.rs deleted file mode 100644 index 24643a5e5..000000000 --- a/bindings/wasm/iota_move_calls_ts/src/transaction_builder.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::ops::Deref; -use std::ops::DerefMut; - -use crate::bindings::WasmTransactionBuilder; -use crate::error::TsSdkError; -use crate::error::WasmError; -use iota_interaction::ProgrammableTransactionBcs; -use iota_interaction::TransactionBuilderT; - -pub type NativeTsTransactionBuilderBindingWrapper = WasmTransactionBuilder; - -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 { - futures::executor::block_on(self.builder.build()) - .map(|js_arr| js_arr.to_vec()) - .map_err(WasmError::from) - .map_err(Self::Error::from) - } - - fn as_native_tx_builder(&mut self) -> &mut Self::NativeTxBuilder { - &mut self.builder - } - - fn into_native_tx_builder(self) -> Self::NativeTxBuilder { - self.builder - } -} - -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/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 7a0b06570..924151500 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -34,6 +34,7 @@ thiserror.workspace = true bcs = { version = "0.1.4", optional = true } fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", package = "fastcrypto", optional = true } identity_eddsa_verifier = { version = "=1.6.0-alpha", path = "../identity_eddsa_verifier", optional = true } +identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", optional = true } identity_jose = { version = "=1.6.0-alpha", path = "../identity_jose", optional = true } iota-crypto = { version = "0.23", optional = true } itertools = { version = "0.13.0", optional = true } @@ -43,7 +44,6 @@ secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", tag serde-aux = { version = "4.5.0", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", optional = true } iota-config = { git = "https://github.com/iotaledger/iota.git", package = "iota-config", tag = "v0.12.0-rc", optional = true } #iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", optional = true } iota_interaction = { path = "../../product-core/iota_interaction", optional = true } @@ -55,18 +55,14 @@ shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "sha tokio = { version = "1.43", default-features = false, features = ["macros", "sync", "rt", "process"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", optional = true } #iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false, optional = true } iota_interaction = { path = "../../product-core/iota_interaction", default-features = false, optional = true } -#iota_interaction_rust = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_rust", optional = true } -iota_interaction_rust = { path = "../../product-core/iota_interaction_rust", optional = true } -tokio = { version = "1.43", default-features = false, features = ["sync"] } # Dependency iota_interaction_ts is always used on wasm32 platform. It is not controlled by the "iota-client" feature # because it'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 = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_ts" } -iota_interaction_ts = { path = "../../product-core/bindings/wasm/iota_interaction_ts" } +iota_interaction_ts.workspace = true iota_move_calls_ts = { version = "=1.6.0-alpha", path = "../bindings/wasm/iota_move_calls_ts" } +tokio = { version = "1.43", default-features = false, features = ["sync"] } [dev-dependencies] iota-crypto = { version = "0.23", default-features = false, features = ["bip39", "bip39-en"] } diff --git a/identity_iota_core/src/iota_interaction_adapter.rs b/identity_iota_core/src/iota_interaction_adapter.rs index ef273ad49..dff4afe96 100644 --- a/identity_iota_core/src/iota_interaction_adapter.rs +++ b/identity_iota_core/src/iota_interaction_adapter.rs @@ -9,7 +9,7 @@ cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { pub(crate) use iota_interaction_ts::*; - pub(crate) use iota_move_call_ts::*; + pub(crate) use iota_move_calls_ts::*; } else { pub(crate) use iota_interaction_rust::*; pub(crate) use crate::iota_move_calls_rust::*; diff --git a/identity_iota_move_calls/Cargo.toml b/identity_iota_move_calls/Cargo.toml index 0d6121157..ca1c8cccc 100644 --- a/identity_iota_move_calls/Cargo.toml +++ b/identity_iota_move_calls/Cargo.toml @@ -14,45 +14,14 @@ description = "Interfaces that can be used to provide platform-specific implemen [dependencies] anyhow = "1.0.75" async-trait = { version = "0.1.81", default-features = false } -bcs = "0.1.4" -cfg-if = "1.0.0" -fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", package = "fastcrypto", features = ["copy_key"] } -iota_interaction.workspace = true -jsonpath-rust = { version = "0.5.1", optional = true } -secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } serde.workspace = true -serde_json.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.12.0-rc" } -move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.12.0-rc" } -tokio = { version = "1", optional = true, default-features = false, features = ["process"] } - -shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "shared-crypto", tag = "v0.12.0-rc" } - +iota_interaction.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] -eyre = { version = "0.6" } -fastcrypto-zkp = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8570fe4e9cff36eea5bbd6fef22002898", 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_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" } -derive_more = "0.99.18" -enum_dispatch = "0.3.13" -schemars = "0.8.21" -tap = "1" -nonempty = "0.11" +#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false } +iota_interaction = { path = "../../product-core/iota_interaction", default-features = false } + [package.metadata.docs.rs] # To build locally: @@ -60,11 +29,6 @@ nonempty = "0.11" all-features = true rustdoc-args = ["--cfg", "docsrs"] -[features] -default = ["send-sync-transaction", "secret-storage/send-sync-storage"] -send-sync-transaction = ["secret-storage/send-sync-storage"] -keytool = ["dep:tokio", "dep:jsonpath-rust"] - [lints.clippy] result_large_err = "allow" diff --git a/rustfmt.toml b/rustfmt.toml index 103ed48b3..c0842c211 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -6,6 +6,3 @@ normalize_doc_attributes = false tab_spaces = 2 wrap_comments = true imports_granularity = "Item" -ignore = [ - "identity_iota_interaction/src/sdk_types", -] From 9e60199f61092cbb298faf39f33393a07db456fc Mon Sep 17 00:00:00 2001 From: chrisgitiota Date: Fri, 25 Apr 2025 14:40:45 +0200 Subject: [PATCH 07/10] iota_move_calls_ts buildable with build:nodejs --- bindings/wasm/iota_move_calls_ts/lib/index.ts | 3 +- .../lib/iota_client_helpers.ts | 181 --- .../wasm/iota_move_calls_ts/lib/tsconfig.json | 6 +- .../iota_move_calls_ts/lib/tsconfig.web.json | 6 +- .../wasm/iota_move_calls_ts/package-lock.json | 1068 ----------------- bindings/wasm/iota_move_calls_ts/package.json | 8 +- 6 files changed, 11 insertions(+), 1261 deletions(-) delete mode 100644 bindings/wasm/iota_move_calls_ts/lib/iota_client_helpers.ts delete mode 100644 bindings/wasm/iota_move_calls_ts/package-lock.json diff --git a/bindings/wasm/iota_move_calls_ts/lib/index.ts b/bindings/wasm/iota_move_calls_ts/lib/index.ts index 1a201cc25..f90713e62 100644 --- a/bindings/wasm/iota_move_calls_ts/lib/index.ts +++ b/bindings/wasm/iota_move_calls_ts/lib/index.ts @@ -1,6 +1,5 @@ // Copyright 2020-2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -export * from "~iota_interaction_ts"; -export * as iota_client_helpers from "./iota_client_helpers"; +export * from "~iota_move_calls_ts"; export * as move_calls from "./move_calls"; diff --git a/bindings/wasm/iota_move_calls_ts/lib/iota_client_helpers.ts b/bindings/wasm/iota_move_calls_ts/lib/iota_client_helpers.ts deleted file mode 100644 index 1eafe9e97..000000000 --- a/bindings/wasm/iota_move_calls_ts/lib/iota_client_helpers.ts +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2020-2024 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -import { CoinStruct, IotaClient, IotaTransactionBlockResponse, TransactionEffects } from "@iota/iota-sdk/client"; -import { GasData, TransactionDataBuilder } from "@iota/iota-sdk/transactions"; - -export type Signer = { sign(data: Uint8Array): Promise }; - -const MINIMUM_BALANCE_FOR_COIN = BigInt(1_000_000_000); - -export class WasmIotaTransactionBlockResponseWrapper { - response: IotaTransactionBlockResponse; - - constructor(response: IotaTransactionBlockResponse) { - this.response = response; - } - - to_string(): string { - return JSON.stringify(this.response); - } - - get_effects(): TransactionEffects | null | undefined { - return this.response.effects; - } - - get_response(): IotaTransactionBlockResponse { - return this.response; - } - - get_digest(): string { - return this.response.digest; - } -} - -function byHighestBalance({ balance: a }: T, { balance: b }: T) { - if (a > b) { - return -1; - } - if (a < b) { - return 1; - } - return 0; -} - -async function getCoinForTransaction(iotaClient: IotaClient, senderAddress: string): Promise { - let cursor: string | null | undefined = undefined; - do { - const response = await iotaClient.getCoins({ owner: senderAddress, cursor }); - if (response.data.length === 0) { - throw new Error( - `no coin found with minimum required balance of ${MINIMUM_BALANCE_FOR_COIN} for address ${senderAddress}"`, - ); - } - - let sortedValidCoins = response.data - .map((coin) => ({ coin, balance: BigInt(coin.balance) })) - .filter(({ balance }) => balance >= MINIMUM_BALANCE_FOR_COIN) - .sort(byHighestBalance); - - if (sortedValidCoins.length >= 1) { - return sortedValidCoins[0].coin; - } - - cursor = response.nextCursor; - } while (cursor); - - throw new Error( - `no coin found with minimum required balance of ${MINIMUM_BALANCE_FOR_COIN} for address ${senderAddress}"`, - ); -} - -/** - * Inserts these values into the transaction and replaces placeholder values. - * - * - sender (overwritten as we assume a placeholder to be used in prepared transaction) - * - gas budget (value determined automatically if not provided) - * - gas price (value determined automatically) - * - gas coin / payment object (fetched automatically) - * - gas owner (equals sender) - * - * @param iotaClient client instance - * @param senderAddress transaction sender (and the one paying for it) - * @param txBcs transaction data serialized to bcs, most probably having placeholder values - * @param gasBudget optional fixed gas budget, determined automatically with a dry run if not provided - * @returns updated transaction data - */ -export 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: GasData = { - budget: gasBudget ? gasBudget.toString() : "50000000", // 50_000_000 - owner: senderAddress, - payment: [{ - objectId: gasCoin.coinObjectId, - version: gasCoin.version, - digest: gasCoin.digest, - }], - price: gasPrice.toString(), - }; - const overrides = { - gasData, - sender: senderAddress, - }; - // TODO: check why `.build` with `overrides` doesn't override these values - txData.sender = overrides.sender; - txData.gasData = overrides.gasData; - let builtTx = txData.build({ overrides }); - - 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; - - overrides.gasData.budget = budget.toString(); - txData.gasData.budget = budget.toString(); - - builtTx = txData.build({ overrides }); - } - - return builtTx; -} - -// estimate gas, get coin, execute tx here -export async function executeTransaction( - iotaClient: IotaClient, - senderAddress: string, - txBcs: Uint8Array, - signer: Signer, - gasBudget?: bigint, -): Promise { - const txWithGasData = await addGasDataToTransaction(iotaClient, senderAddress, txBcs, gasBudget); - const signature = await signer.sign(txWithGasData); - - const response = await iotaClient.executeTransactionBlock({ - transactionBlock: txWithGasData, - signature, - options: { // equivalent of `IotaTransactionBlockResponseOptions::full_content()` - showEffects: true, - showInput: true, - showRawInput: true, - showEvents: true, - showObjectChanges: true, - showBalanceChanges: true, - showRawEffects: false, - }, - }); - - if (response?.effects?.status.status === "failure") { - throw new Error(`transaction returned an unexpected response; ${response?.effects?.status.error}`); - } - - return new WasmIotaTransactionBlockResponseWrapper(response); -} - -/** - * Helper function to pause execution. - * - * @param durationMs time to sleep in ms - */ -export function sleep(durationMs: number) { - return new Promise(resolve => setTimeout(resolve, durationMs)); -} diff --git a/bindings/wasm/iota_move_calls_ts/lib/tsconfig.json b/bindings/wasm/iota_move_calls_ts/lib/tsconfig.json index 9bee396af..f332fa4cd 100644 --- a/bindings/wasm/iota_move_calls_ts/lib/tsconfig.json +++ b/bindings/wasm/iota_move_calls_ts/lib/tsconfig.json @@ -6,9 +6,9 @@ "../lib": [ "." ], - "~iota_interaction_ts": [ - "../node/iota_interaction_ts", - "./iota_interaction_ts.js" + "~iota_move_calls_ts": [ + "../node/iota_move_calls_ts", + "./iota_move_calls_ts.js" ] }, "outDir": "../node", diff --git a/bindings/wasm/iota_move_calls_ts/lib/tsconfig.web.json b/bindings/wasm/iota_move_calls_ts/lib/tsconfig.web.json index b1937c9cb..de2255897 100644 --- a/bindings/wasm/iota_move_calls_ts/lib/tsconfig.web.json +++ b/bindings/wasm/iota_move_calls_ts/lib/tsconfig.web.json @@ -7,9 +7,9 @@ "../lib": [ "." ], - "~iota_interaction_ts": [ - "../web/iota_interaction_ts", - "./iota_interaction_ts.js" + "~iota_move_calls_ts": [ + "../web/iota_move_calls_ts", + "./iota_move_calls_ts.js" ] }, "outDir": "../web", diff --git a/bindings/wasm/iota_move_calls_ts/package-lock.json b/bindings/wasm/iota_move_calls_ts/package-lock.json deleted file mode 100644 index ff0c6f9bd..000000000 --- a/bindings/wasm/iota_move_calls_ts/package-lock.json +++ /dev/null @@ -1,1068 +0,0 @@ -{ - "name": "@iota/iota-interaction-ts", - "version": "0.3.3", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@iota/iota-interaction-ts", - "version": "0.3.3", - "license": "Apache-2.0", - "devDependencies": { - "@types/node": "^22.0.0", - "dprint": "^0.33.0", - "rimraf": "^6.0.1", - "tsconfig-paths": "^4.1.0", - "typescript": "^5.7.3", - "wasm-opt": "^1.4.0" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@iota/iota-sdk": "^0.6.0" - } - }, - "node_modules/@0no-co/graphql.web": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@0no-co/graphql.web/-/graphql.web-1.0.13.tgz", - "integrity": "sha512-jqYxOevheVTU1S36ZdzAkJIdvRp2m3OYIG5SEoKDw5NI8eVwkoI0D/Q3DYNGmXCxkA6CQuoa7zvMiDPTLqUNuw==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" - }, - "peerDependenciesMeta": { - "graphql": { - "optional": true - } - } - }, - "node_modules/@0no-co/graphqlsp": { - "version": "1.12.16", - "resolved": "https://registry.npmjs.org/@0no-co/graphqlsp/-/graphqlsp-1.12.16.tgz", - "integrity": "sha512-B5pyYVH93Etv7xjT6IfB7QtMBdaaC07yjbhN6v8H7KgFStMkPvi+oWYBTibMFRMY89qwc9H8YixXg8SXDVgYWw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@gql.tada/internal": "^1.0.0", - "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" - }, - "peerDependencies": { - "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", - "typescript": "^5.0.0" - } - }, - "node_modules/@gql.tada/cli-utils": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@gql.tada/cli-utils/-/cli-utils-1.6.3.tgz", - "integrity": "sha512-jFFSY8OxYeBxdKi58UzeMXG1tdm4FVjXa8WHIi66Gzu9JWtCE6mqom3a8xkmSw+mVaybFW5EN2WXf1WztJVNyQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@0no-co/graphqlsp": "^1.12.13", - "@gql.tada/internal": "1.0.8", - "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0" - }, - "peerDependencies": { - "@0no-co/graphqlsp": "^1.12.13", - "@gql.tada/svelte-support": "1.0.1", - "@gql.tada/vue-support": "1.0.1", - "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "@gql.tada/svelte-support": { - "optional": true - }, - "@gql.tada/vue-support": { - "optional": true - } - } - }, - "node_modules/@gql.tada/internal": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@gql.tada/internal/-/internal-1.0.8.tgz", - "integrity": "sha512-XYdxJhtHC5WtZfdDqtKjcQ4d7R1s0d1rnlSs3OcBEUbYiPoJJfZU7tWsVXuv047Z6msvmr4ompJ7eLSK5Km57g==", - "license": "MIT", - "peer": true, - "dependencies": { - "@0no-co/graphql.web": "^1.0.5" - }, - "peerDependencies": { - "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0", - "typescript": "^5.0.0" - } - }, - "node_modules/@graphql-typed-document-node/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", - "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, - "node_modules/@iota/bcs": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@iota/bcs/-/bcs-0.2.1.tgz", - "integrity": "sha512-T+iv5gZhUZP7BiDY7+Ir4MA2rYmyGNZA2b+nxjv219Fp8klFt+l38OWA+1RgJXrCmzuZ+M4hbMAeHhHziURX6Q==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "bs58": "^6.0.0" - } - }, - "node_modules/@iota/iota-sdk": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@iota/iota-sdk/-/iota-sdk-0.6.0.tgz", - "integrity": "sha512-NEYiE7bdWw2DA3vLV7dO2EnoLDljN9NPhYrjfDGefTbIS9XpqX0JZTHMi//Q/K0aO4NwSHR5gu7n/ywoOTzTKg==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@graphql-typed-document-node/core": "^3.2.0", - "@iota/bcs": "0.2.1", - "@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" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/@iota/iota-sdk/node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "license": "Unlicense", - "peer": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@noble/curves": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", - "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@noble/hashes": "1.7.1" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", - "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/base": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz", - "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz", - "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@noble/curves": "~1.8.1", - "@noble/hashes": "~1.7.1", - "@scure/base": "~1.2.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip39": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz", - "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@noble/hashes": "~1.7.1", - "@scure/base": "~1.2.4" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@suchipi/femver": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@suchipi/femver/-/femver-1.0.0.tgz", - "integrity": "sha512-bprE8+K5V+DPX7q2e2K57ImqNBdfGHDIWaGI5xHxZoxbKOuQZn4wzPiUxOAHnsUr3w3xHrWXwN7gnG/iIuEMIg==", - "license": "MIT", - "peer": true - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "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 - }, - "node_modules/base-x": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", - "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==", - "license": "MIT", - "peer": true - }, - "node_modules/bech32": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", - "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==", - "license": "MIT", - "peer": true - }, - "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, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/bs58": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", - "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", - "license": "MIT", - "peer": true, - "dependencies": { - "base-x": "^5.0.0" - } - }, - "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/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "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, - "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 - }, - "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, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "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/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "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/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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, - "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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/gql.tada": { - "version": "1.8.10", - "resolved": "https://registry.npmjs.org/gql.tada/-/gql.tada-1.8.10.tgz", - "integrity": "sha512-FrvSxgz838FYVPgZHGOSgbpOjhR+yq44rCzww3oOPJYi0OvBJjAgCiP6LEokZIYND2fUTXzQAyLgcvgw1yNP5A==", - "license": "MIT", - "peer": true, - "dependencies": { - "@0no-co/graphql.web": "^1.0.5", - "@0no-co/graphqlsp": "^1.12.13", - "@gql.tada/cli-utils": "1.6.3", - "@gql.tada/internal": "1.0.8" - }, - "bin": { - "gql-tada": "bin/cli.js", - "gql.tada": "bin/cli.js" - }, - "peerDependencies": { - "typescript": "^5.0.0" - } - }, - "node_modules/graphql": { - "version": "16.10.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", - "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.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, - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jackspeak": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", - "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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/lru-cache": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", - "dev": true, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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, - "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, - "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, - "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, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "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==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "dev": true, - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "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/rimraf": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", - "dev": true, - "dependencies": { - "glob": "^11.0.0", - "package-json-from-dist": "^1.0.0" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/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 - }, - "node_modules/string-width-cjs/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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/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, - "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/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, - "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/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, - "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/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/valibot": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.36.0.tgz", - "integrity": "sha512-CjF1XN4sUce8sBK9TixrDqFM7RwNkuXdJu174/AwmQUB62QbCQADg5lLe8ldBalFgtj1uKj+pKwDJiNo4Mn+eQ==", - "license": "MIT", - "peer": true - }, - "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, - "dependencies": { - "node-fetch": "^2.6.9", - "tar": "^6.1.13" - }, - "bin": { - "wasm-opt": "bin/wasm-opt.js" - } - }, - "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==", - "dev": true - }, - "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==", - "dev": true, - "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, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "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, - "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/wrap-ansi-cjs/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/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 - }, - "node_modules/wrap-ansi-cjs/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, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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 - }, - "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" - } - } - } -} diff --git a/bindings/wasm/iota_move_calls_ts/package.json b/bindings/wasm/iota_move_calls_ts/package.json index 3f4eadcb6..6e97c024b 100644 --- a/bindings/wasm/iota_move_calls_ts/package.json +++ b/bindings/wasm/iota_move_calls_ts/package.json @@ -13,11 +13,11 @@ "build:src": "cargo build --lib --release --target wasm32-unknown-unknown --target-dir ../target", "build:src:node": "cargo build --lib --release --target wasm32-unknown-unknown --features keytool --target-dir ../target", "prebundle:nodejs": "rimraf node", - "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:nodejs": "wasm-bindgen ../target/wasm32-unknown-unknown/release/iota_move_calls_ts.wasm --typescript --weak-refs --target nodejs --out-dir node && node ../build/node iota_move_calls_ts && tsc --project ./lib/tsconfig.json && node ../build/replace_paths ./lib/tsconfig.json node iota_move_calls_ts", "prebundle:web": "rimraf web", - "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:node && 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", + "bundle:web": "wasm-bindgen ../target/wasm32-unknown-unknown/release/iota_move_calls_ts.wasm --typescript --target web --out-dir web && node ../build/web iota_move_calls_ts && tsc --project ./lib/tsconfig.web.json && node ../build/replace_paths ./lib/tsconfig.web.json web iota_move_calls_ts", + "build:nodejs": "npm run build:src:node && npm run bundle:nodejs && wasm-opt -O node/iota_move_calls_ts_bg.wasm -o node/iota_move_calls_ts_bg.wasm", + "build:web": "npm run build:src && npm run bundle:web && wasm-opt -O web/iota_move_calls_ts_bg.wasm -o web/iota_move_calls_ts_bg.wasm", "build": "npm run build:web && npm run build:nodejs", "fmt": "dprint fmt" }, From 2821f20c45dcea9d2919c9a4b431cdfa831ef78b Mon Sep 17 00:00:00 2001 From: chrisgitiota Date: Fri, 25 Apr 2025 17:08:19 +0200 Subject: [PATCH 08/10] First attempt to accomplish build:nodejs for identity_wasm --- bindings/wasm/identity_wasm/lib/tsconfig.json | 3 +++ bindings/wasm/identity_wasm/lib/tsconfig.web.json | 3 +++ bindings/wasm/identity_wasm/package.json | 3 ++- bindings/wasm/iota_move_calls_ts/package.json | 2 +- bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs | 2 +- bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs | 2 +- bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs | 2 +- bindings/wasm/iota_move_calls_ts/tsconfig.json | 2 +- identity_iota/Cargo.toml | 3 +++ identity_iota/src/lib.rs | 2 ++ 10 files changed, 18 insertions(+), 6 deletions(-) diff --git a/bindings/wasm/identity_wasm/lib/tsconfig.json b/bindings/wasm/identity_wasm/lib/tsconfig.json index 49e2616ec..98fa0c303 100644 --- a/bindings/wasm/identity_wasm/lib/tsconfig.json +++ b/bindings/wasm/identity_wasm/lib/tsconfig.json @@ -10,6 +10,9 @@ "@iota/iota-interaction-ts/": [ "@iota/iota-interaction-ts/node/" ], + "@iota/iota-move-calls-identity/": [ + "@iota/iota-move-calls-identity/node/" + ], "../lib": [ "." ] diff --git a/bindings/wasm/identity_wasm/lib/tsconfig.web.json b/bindings/wasm/identity_wasm/lib/tsconfig.web.json index edbccd05a..2e63a0615 100644 --- a/bindings/wasm/identity_wasm/lib/tsconfig.web.json +++ b/bindings/wasm/identity_wasm/lib/tsconfig.web.json @@ -10,6 +10,9 @@ "@iota/iota-interaction-ts/": [ "@iota/iota-interaction-ts/web/" ], + "@iota/iota-move-calls-identity/": [ + "@iota/iota-move-calls-identity/web/" + ], "../lib": [ "." ] diff --git a/bindings/wasm/identity_wasm/package.json b/bindings/wasm/identity_wasm/package.json index 5968e878f..0ef0e6b71 100644 --- a/bindings/wasm/identity_wasm/package.json +++ b/bindings/wasm/identity_wasm/package.json @@ -74,7 +74,8 @@ "wasm-opt": "^1.4.0" }, "dependencies": { - "@iota/iota-interaction-ts": "^0.3.0", + "@iota/iota-interaction-ts": "file:../../../../product-core/bindings/wasm/iota_interaction_ts", + "@iota/iota-move-calls-identity": "file:../iota_move_calls_ts", "@noble/ed25519": "^1.7.3", "@noble/hashes": "^1.4.0", "@types/node-fetch": "^2.6.2", diff --git a/bindings/wasm/iota_move_calls_ts/package.json b/bindings/wasm/iota_move_calls_ts/package.json index 6e97c024b..2f43240b0 100644 --- a/bindings/wasm/iota_move_calls_ts/package.json +++ b/bindings/wasm/iota_move_calls_ts/package.json @@ -1,5 +1,5 @@ { - "name": "@iota/iota-interaction-ts", + "name": "@iota/iota-move-calls-identity", "author": "IOTA Foundation ", "description": "WASM bindings importing types from the IOTA Client typescript SDK to be used in Rust", "homepage": "https://www.iota.org", diff --git a/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs index 629b82274..dbaf519fc 100644 --- a/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs +++ b/bindings/wasm/iota_move_calls_ts/src/asset_move_calls.rs @@ -22,7 +22,7 @@ use iota_interaction::types::TypeTag; use iota_interaction::MoveType; use iota_interaction::ProgrammableTransactionBcs; -#[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls/asset")] +#[wasm_bindgen(module = "@iota/iota-move-calls-identity/move_calls/asset")] extern "C" { #[wasm_bindgen(js_name = "create", catch)] pub(crate) async fn new_asset( diff --git a/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs index f0278fda9..6f2c57a50 100644 --- a/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs +++ b/bindings/wasm/iota_move_calls_ts/src/identity_move_calls.rs @@ -60,7 +60,7 @@ impl From<(IotaAddress, u64)> for WasmControllerCouple { } } -#[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls/identity")] +#[wasm_bindgen(module = "@iota/iota-move-calls-identity/move_calls/identity")] extern "C" { #[wasm_bindgen(js_name = "create", catch)] fn identity_new(did: Option<&[u8]>, package: &str) -> Result; diff --git a/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs b/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs index 06b220b05..3e772938f 100644 --- a/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs +++ b/bindings/wasm/iota_move_calls_ts/src/migration_move_calls.rs @@ -15,7 +15,7 @@ use iota_interaction_ts::bindings::WasmSharedObjectRef; use iota_interaction_ts::error::TsSdkError; use iota_interaction_ts::error::WasmError; -#[wasm_bindgen(module = "@iota/iota-interaction-ts/move_calls")] +#[wasm_bindgen(module = "@iota/iota-move-calls-identity/move_calls")] extern "C" { #[wasm_bindgen(js_name = "migrateDidOutput", catch)] async fn migrate_did_output_impl( diff --git a/bindings/wasm/iota_move_calls_ts/tsconfig.json b/bindings/wasm/iota_move_calls_ts/tsconfig.json index e8a42d2a3..32a218d42 100644 --- a/bindings/wasm/iota_move_calls_ts/tsconfig.json +++ b/bindings/wasm/iota_move_calls_ts/tsconfig.json @@ -7,7 +7,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@iota/iota-interaction-ts/*": [ + "@iota/iota-move-calls-identity/*": [ "./*" ], } diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 864323d37..9d9c30d60 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -22,9 +22,12 @@ identity_verification = { version = "=1.6.0-alpha", path = "../identity_verifica [target.'cfg(not(target_arch = "wasm32"))'.dependencies] identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls" } +iota_interaction.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", default-features = false } +#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false } +iota_interaction = { path = "../../product-core/iota_interaction", default-features = false } [dev-dependencies] # required for doc test diff --git a/identity_iota/src/lib.rs b/identity_iota/src/lib.rs index 4f6369ff9..1ecbf5a26 100644 --- a/identity_iota/src/lib.rs +++ b/identity_iota/src/lib.rs @@ -17,6 +17,8 @@ clippy::missing_errors_doc )] +pub use iota_interaction as iota_interaction; + pub mod core { //! Core Traits and Types From 165887b4512f7ee9cd11df68efa0e1cae930b722 Mon Sep 17 00:00:00 2001 From: chrisgitiota Date: Mon, 28 Apr 2025 14:55:28 +0200 Subject: [PATCH 09/10] Switch product_core cargo dependencies from local neighboured folder to GH feature branch --- Cargo.toml | 12 ++++++------ bindings/wasm/identity_wasm/Cargo.toml | 4 ++-- bindings/wasm/iota_move_calls_ts/Cargo.toml | 4 ++-- identity_iota/Cargo.toml | 4 ++-- identity_iota_core/Cargo.toml | 13 +++++++------ identity_iota_core/packages/iota_identity/Move.lock | 10 +++++----- identity_iota_move_calls/Cargo.toml | 4 ++-- identity_storage/Cargo.toml | 8 ++++---- 8 files changed, 30 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1b46d62fb..362e3a836 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,12 +31,12 @@ exclude = ["bindings/wasm/identity_wasm", "bindings/grpc"] [workspace.dependencies] bls12_381_plus = { version = "0.8.17" } -#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction" } -iota_interaction = { path = "../product-core/iota_interaction" } -#iota_interaction_ts = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_ts" } -iota_interaction_ts = { path = "../product-core/bindings/wasm/iota_interaction_ts" } -#product_common = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "product_common" } -product_common = { path = "../product-core/product_common" } +iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction" } +#iota_interaction = { path = "../product-core/iota_interaction" } +iota_interaction_ts = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction_ts" } +#iota_interaction_ts = { path = "../product-core/bindings/wasm/iota_interaction_ts" } +product_common = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "product_common" } +#product_common = { path = "../product-core/product_common" } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } serde_json = { version = "1.0", default-features = false } strum = { version = "0.25", default-features = false, features = ["std", "derive"] } diff --git a/bindings/wasm/identity_wasm/Cargo.toml b/bindings/wasm/identity_wasm/Cargo.toml index a4d59e39b..530594bca 100644 --- a/bindings/wasm/identity_wasm/Cargo.toml +++ b/bindings/wasm/identity_wasm/Cargo.toml @@ -40,8 +40,8 @@ wasm-bindgen = { version = "0.2.100", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } # dummy-client dependencies -#iota_interaction_ts = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_ts", optional = true } -iota_interaction_ts = { path = "../../../../product-core/bindings/wasm/iota_interaction_ts", optional = true } +iota_interaction_ts = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction_ts", optional = true } +#iota_interaction_ts = { path = "../../../../product-core/bindings/wasm/iota_interaction_ts", optional = true } iota_move_calls_ts = { version = "=1.6.0-alpha", path = "../iota_move_calls_ts", optional = true } [dependencies.identity_iota] diff --git a/bindings/wasm/iota_move_calls_ts/Cargo.toml b/bindings/wasm/iota_move_calls_ts/Cargo.toml index 5c6740922..c39f32a04 100644 --- a/bindings/wasm/iota_move_calls_ts/Cargo.toml +++ b/bindings/wasm/iota_move_calls_ts/Cargo.toml @@ -26,8 +26,8 @@ fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8 futures = { version = "0.3" } identity_core = { version = "=1.6.0-alpha", path = "../../../identity_core" } identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../../../identity_iota_move_calls" } -#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false } -iota_interaction = { path = "../../../../product-core/iota_interaction", default-features = false } +iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false } +#iota_interaction = { path = "../../../../product-core/iota_interaction", default-features = false } iota_interaction_ts.workspace = true js-sys = { version = "0.3.61" } secret-storage = { git = "https://github.com/iotaledger/secret-storage.git", default-features = false, tag = "v0.3.0" } diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index 9d9c30d60..e05a6f30b 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -26,8 +26,8 @@ iota_interaction.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", default-features = false } -#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false } -iota_interaction = { path = "../../product-core/iota_interaction", default-features = false } +iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false } +#iota_interaction = { path = "../../product-core/iota_interaction", default-features = false } [dev-dependencies] # required for doc test diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 924151500..9d1ccae81 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -45,18 +45,19 @@ serde-aux = { version = "4.5.0", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] iota-config = { git = "https://github.com/iotaledger/iota.git", package = "iota-config", tag = "v0.12.0-rc", optional = true } -#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", optional = true } -iota_interaction = { path = "../../product-core/iota_interaction", optional = true } -#iota_interaction_rust = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction_rust", optional = true } -iota_interaction_rust = { path = "../../product-core/iota_interaction_rust", optional = true } +iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", optional = true } +#iota_interaction = { path = "../../product-core/iota_interaction", optional = true } +iota_interaction_rust = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction_rust", optional = true } +#iota_interaction_rust = { path = "../../product-core/iota_interaction_rust", optional = true } iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.12.0-rc", optional = true } move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.12.0-rc", optional = true } shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "shared-crypto", tag = "v0.12.0-rc", optional = true } tokio = { version = "1.43", default-features = false, features = ["macros", "sync", "rt", "process"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false, optional = true } -iota_interaction = { path = "../../product-core/iota_interaction", default-features = false, optional = true } +iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false, optional = true } +#iota_interaction = { path = "../../product-core/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 it'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. diff --git a/identity_iota_core/packages/iota_identity/Move.lock b/identity_iota_core/packages/iota_identity/Move.lock index 0017522ae..7f3793944 100644 --- a/identity_iota_core/packages/iota_identity/Move.lock +++ b/identity_iota_core/packages/iota_identity/Move.lock @@ -40,8 +40,8 @@ flavor = "iota" [env.testnet] chain-id = "2304aa97" -original-published-id = "0x222741bbdff74b42df48a7b4733185e9b24becb8ccfbafe8eac864ab4e4cc555" -latest-published-id = "0x222741bbdff74b42df48a7b4733185e9b24becb8ccfbafe8eac864ab4e4cc555" +original-published-id = "0xfb4494330dfd5076c14e5dd68b2206029572f5133b6ff240665e80242cbfe13d" +latest-published-id = "0xfb4494330dfd5076c14e5dd68b2206029572f5133b6ff240665e80242cbfe13d" published-version = "1" [env.devnet] @@ -51,7 +51,7 @@ latest-published-id = "0x03242ae6b87406bd0eb5d669fbe874ed4003694c0be9c6a9ee7c315 published-version = "1" [env.localnet] -chain-id = "461e4e6c" -original-published-id = "0x7b310d2af24b4d782ebb41d93b4b72bde08546e3e12a00e91b8b8f98442f6656" -latest-published-id = "0x7b310d2af24b4d782ebb41d93b4b72bde08546e3e12a00e91b8b8f98442f6656" +chain-id = "39aff582" +original-published-id = "0x6a507b5765860d7b66d4b07b77af3eb99e373ae31c1108e8b40bc223a34d5e57" +latest-published-id = "0x6a507b5765860d7b66d4b07b77af3eb99e373ae31c1108e8b40bc223a34d5e57" published-version = "1" diff --git a/identity_iota_move_calls/Cargo.toml b/identity_iota_move_calls/Cargo.toml index ca1c8cccc..abd0a6f84 100644 --- a/identity_iota_move_calls/Cargo.toml +++ b/identity_iota_move_calls/Cargo.toml @@ -19,8 +19,8 @@ serde.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] iota_interaction.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] -#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false } -iota_interaction = { path = "../../product-core/iota_interaction", default-features = false } +iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false } +#iota_interaction = { path = "../../product-core/iota_interaction", default-features = false } [package.metadata.docs.rs] diff --git a/identity_storage/Cargo.toml b/identity_storage/Cargo.toml index 268ee048f..0919862a2 100644 --- a/identity_storage/Cargo.toml +++ b/identity_storage/Cargo.toml @@ -37,12 +37,12 @@ tokio = { version = "1.43", default-features = false, features = ["macros", "syn zkryptium = { workspace = true, optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", optional = true } -iota_interaction = { path = "../../product-core/iota_interaction", optional = true } +iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", optional = true } +#iota_interaction = { path = "../../product-core/iota_interaction", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -#iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/extensions-needed-for-identity", package = "iota_interaction", default-features = false, optional = true } -iota_interaction = { path = "../../product-core/iota_interaction", default-features = false, optional = true } +iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false, optional = true } +#iota_interaction = { path = "../../product-core/iota_interaction", default-features = false, optional = true } [dev-dependencies] identity_credential = { version = "=1.6.0-alpha", path = "../identity_credential", features = ["revocation-bitmap"] } From 5d630ab3f083fd6d7e613dcdb98e6b92043812fb Mon Sep 17 00:00:00 2001 From: chrisgitiota Date: Tue, 29 Apr 2025 17:34:38 +0200 Subject: [PATCH 10/10] Switch product_core cargo dependencies from ssh to https --- Cargo.toml | 6 +++--- bindings/wasm/identity_wasm/Cargo.toml | 2 +- bindings/wasm/iota_move_calls_ts/Cargo.toml | 2 +- identity_iota/Cargo.toml | 2 +- identity_iota_core/Cargo.toml | 6 +++--- identity_iota_move_calls/Cargo.toml | 2 +- identity_storage/Cargo.toml | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 362e3a836..6e4e39c28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,11 +31,11 @@ exclude = ["bindings/wasm/identity_wasm", "bindings/grpc"] [workspace.dependencies] bls12_381_plus = { version = "0.8.17" } -iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction" } +iota_interaction = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction" } #iota_interaction = { path = "../product-core/iota_interaction" } -iota_interaction_ts = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction_ts" } +iota_interaction_ts = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction_ts" } #iota_interaction_ts = { path = "../product-core/bindings/wasm/iota_interaction_ts" } -product_common = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "product_common" } +product_common = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "product_common" } #product_common = { path = "../product-core/product_common" } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } serde_json = { version = "1.0", default-features = false } diff --git a/bindings/wasm/identity_wasm/Cargo.toml b/bindings/wasm/identity_wasm/Cargo.toml index 530594bca..91a2e6e0e 100644 --- a/bindings/wasm/identity_wasm/Cargo.toml +++ b/bindings/wasm/identity_wasm/Cargo.toml @@ -40,7 +40,7 @@ wasm-bindgen = { version = "0.2.100", features = ["serde-serialize"] } wasm-bindgen-futures = { version = "0.4", default-features = false } # dummy-client dependencies -iota_interaction_ts = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction_ts", optional = true } +iota_interaction_ts = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction_ts", optional = true } #iota_interaction_ts = { path = "../../../../product-core/bindings/wasm/iota_interaction_ts", optional = true } iota_move_calls_ts = { version = "=1.6.0-alpha", path = "../iota_move_calls_ts", optional = true } diff --git a/bindings/wasm/iota_move_calls_ts/Cargo.toml b/bindings/wasm/iota_move_calls_ts/Cargo.toml index c39f32a04..522d7f57c 100644 --- a/bindings/wasm/iota_move_calls_ts/Cargo.toml +++ b/bindings/wasm/iota_move_calls_ts/Cargo.toml @@ -26,7 +26,7 @@ fastcrypto = { git = "https://github.com/MystenLabs/fastcrypto", rev = "2f502fd8 futures = { version = "0.3" } identity_core = { version = "=1.6.0-alpha", path = "../../../identity_core" } identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../../../identity_iota_move_calls" } -iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false } +iota_interaction = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false } #iota_interaction = { path = "../../../../product-core/iota_interaction", default-features = false } iota_interaction_ts.workspace = true js-sys = { version = "0.3.61" } diff --git a/identity_iota/Cargo.toml b/identity_iota/Cargo.toml index e05a6f30b..2dfa39a56 100644 --- a/identity_iota/Cargo.toml +++ b/identity_iota/Cargo.toml @@ -26,7 +26,7 @@ iota_interaction.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] identity_iota_move_calls = { version = "=1.6.0-alpha", path = "../identity_iota_move_calls", default-features = false } -iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false } +iota_interaction = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false } #iota_interaction = { path = "../../product-core/iota_interaction", default-features = false } [dev-dependencies] diff --git a/identity_iota_core/Cargo.toml b/identity_iota_core/Cargo.toml index 9d1ccae81..1750b0dad 100644 --- a/identity_iota_core/Cargo.toml +++ b/identity_iota_core/Cargo.toml @@ -45,9 +45,9 @@ serde-aux = { version = "4.5.0", optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] iota-config = { git = "https://github.com/iotaledger/iota.git", package = "iota-config", tag = "v0.12.0-rc", optional = true } -iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", optional = true } +iota_interaction = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", optional = true } #iota_interaction = { path = "../../product-core/iota_interaction", optional = true } -iota_interaction_rust = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction_rust", optional = true } +iota_interaction_rust = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction_rust", optional = true } #iota_interaction_rust = { path = "../../product-core/iota_interaction_rust", optional = true } iota-sdk = { git = "https://github.com/iotaledger/iota.git", package = "iota-sdk", tag = "v0.12.0-rc", optional = true } move-core-types = { git = "https://github.com/iotaledger/iota.git", package = "move-core-types", tag = "v0.12.0-rc", optional = true } @@ -55,7 +55,7 @@ shared-crypto = { git = "https://github.com/iotaledger/iota.git", package = "sha tokio = { version = "1.43", default-features = false, features = ["macros", "sync", "rt", "process"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false, optional = true } +iota_interaction = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false, optional = true } #iota_interaction = { path = "../../product-core/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 diff --git a/identity_iota_move_calls/Cargo.toml b/identity_iota_move_calls/Cargo.toml index abd0a6f84..026f2bb52 100644 --- a/identity_iota_move_calls/Cargo.toml +++ b/identity_iota_move_calls/Cargo.toml @@ -19,7 +19,7 @@ serde.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] iota_interaction.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] -iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false } +iota_interaction = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false } #iota_interaction = { path = "../../product-core/iota_interaction", default-features = false } diff --git a/identity_storage/Cargo.toml b/identity_storage/Cargo.toml index 0919862a2..8594efdbe 100644 --- a/identity_storage/Cargo.toml +++ b/identity_storage/Cargo.toml @@ -37,11 +37,11 @@ tokio = { version = "1.43", default-features = false, features = ["macros", "syn zkryptium = { workspace = true, optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", optional = true } +iota_interaction = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", optional = true } #iota_interaction = { path = "../../product-core/iota_interaction", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -iota_interaction = { git = "ssh://git@github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false, optional = true } +iota_interaction = { git = "https://github.com/iotaledger/product-core.git", branch = "feat/transaction-client-core-for-identity", package = "iota_interaction", default-features = false, optional = true } #iota_interaction = { path = "../../product-core/iota_interaction", default-features = false, optional = true } [dev-dependencies]