From f58fb5379b64d6e52e102d758ec59ffeb5fd6e45 Mon Sep 17 00:00:00 2001 From: afarukcali Date: Tue, 20 Jan 2026 14:13:22 +0300 Subject: [PATCH 1/5] Merge branch 'main' into feat/wasm-r&d --- .github/actions/coverage/action.yml | 12 + .github/workflows/release.yml | 4 +- .gitignore | 4 + CHANGELOG.md | 62 +- Cargo.lock | 3707 ++++++++---- Cargo.toml | 16 +- Cross.toml | 5 + README.md | 18 +- cli/Cargo.toml | 8 +- cli/README.md | 16 +- cli/src/cli_conf.rs | 61 +- cli/src/completion/mod.rs | 14 +- cli/src/conf/conf_get.rs | 18 +- cli/src/conf/conf_set.rs | 68 +- cli/src/conf/mod.rs | 35 +- cli/src/crypto/crypto_auth.rs | 6 +- cli/src/crypto/crypto_generate_id_key.rs | 1 + cli/src/crypto/crypto_init_key.rs | 1 + cli/src/crypto/crypto_key_status.rs | 3 +- cli/src/dag/dag_execute.rs | 428 +- cli/src/dag/dag_inspect_execution.rs | 10 +- cli/src/dag/dag_publish.rs | 4 +- cli/src/dag/mod.rs | 27 +- cli/src/display.rs | 2 +- cli/src/error.rs | 2 +- cli/src/gas/gas_add_budget.rs | 6 +- cli/src/gas/mod.rs | 14 +- cli/src/gas/tickets/expiry/buy_ticket.rs | 85 +- cli/src/gas/tickets/expiry/disable.rs | 79 +- cli/src/gas/tickets/expiry/enable.rs | 85 +- .../tickets/limited_invocations/buy_ticket.rs | 85 +- .../tickets/limited_invocations/disable.rs | 80 +- .../gas/tickets/limited_invocations/enable.rs | 79 +- cli/src/main.rs | 15 +- cli/src/network/mod.rs | 2 +- cli/src/network/network_create.rs | 130 +- cli/src/prelude.rs | 48 +- cli/src/scheduler/helpers.rs | 53 + cli/src/scheduler/mod.rs | 28 + cli/src/scheduler/occurrence/mod.rs | 73 + .../scheduler/occurrence/occurrence_add.rs | 57 + cli/src/scheduler/periodic/mod.rs | 71 + .../scheduler/periodic/periodic_disable.rs | 38 + cli/src/scheduler/periodic/periodic_set.rs | 57 + cli/src/scheduler/task/mod.rs | 213 + cli/src/scheduler/task/task_create.rs | 207 + cli/src/scheduler/task/task_inspect.rs | 66 + cli/src/scheduler/task/task_metadata.rs | 46 + cli/src/scheduler/task/task_state.rs | 70 + cli/src/sui.rs | 888 +-- cli/src/tool/mod.rs | 258 +- cli/src/tool/templates/move/.gitignore.jinja | 2 + cli/src/tool/templates/move/Move.toml.jinja | 16 + cli/src/tool/templates/move/tests.move.jinja | 19 + cli/src/tool/templates/move/tool.move.jinja | 121 + cli/src/tool/templates/rust/Cargo.toml.jinja | 4 +- cli/src/tool/tool_claim_collateral.rs | 81 +- cli/src/tool/tool_list.rs | 138 +- cli/src/tool/tool_new.rs | 104 +- cli/src/tool/tool_register.rs | 312 - cli/src/tool/tool_register_offchain.rs | 262 + cli/src/tool/tool_register_onchain.rs | 1285 ++++ cli/src/tool/tool_set_invocation_cost.rs | 87 +- cli/src/tool/tool_unregister.rs | 88 +- cli/src/tool/tool_validate.rs | 45 +- cli/src/utils/secrets/aes_gcm_encryption.rs | 8 +- cli/src/utils/secrets/master_key.rs | 9 + cli/src/workflow/mod.rs | 401 ++ codecov.yml | 1 + docs/cli.md | 152 +- docs/client.md | 230 +- docs/guides/dag-construction.md | 2 +- docs/guides/onchain-tool-development.md | 376 ++ docs/guides/setup.md | 94 +- docs/index.md | 3 +- docs/toolkit-rust.md | 2 +- rust-toolchain.toml | 2 +- sdk/Cargo.toml | 105 +- sdk/README.md | 2 +- sdk/build.rs | 4 +- sdk/src/crypto/double_ratchet.rs | 78 +- sdk/src/crypto/session.rs | 128 +- sdk/src/crypto/x3dh.rs | 11 +- .../dag/_dags/conditional_onchain_tool.json | 79 + sdk/src/dag/_dags/double_onchain_tool.json | 56 + sdk/src/dag/_dags/guide_onchain_tool.json | 66 + sdk/src/dag/_dags/single_onchain_tool.json | 22 + sdk/src/dag/validator.rs | 25 +- sdk/src/events.rs | 450 -- sdk/src/events/fetching.rs | 316 + sdk/src/events/graphql/events_query.graphql | 24 + sdk/src/events/graphql/events_query.rs | 14 + sdk/src/events/graphql/mod.rs | 1 + sdk/src/events/graphql/schema-1.61.2.graphql | 5370 +++++++++++++++++ sdk/src/events/mod.rs | 650 ++ sdk/src/events/parsing.rs | 956 +++ sdk/src/idents/mod.rs | 32 +- sdk/src/idents/move_std.rs | 64 +- sdk/src/idents/primitives.rs | 160 +- sdk/src/idents/sui_framework.rs | 141 +- sdk/src/idents/tap.rs | 6 +- sdk/src/idents/workflow.rs | 721 ++- sdk/src/lib.rs | 16 +- sdk/src/nexus/client.rs | 584 +- sdk/src/nexus/crawler.rs | 917 +++ sdk/src/nexus/crypto.rs | 276 +- sdk/src/nexus/gas.rs | 130 +- sdk/src/nexus/mod.rs | 3 + sdk/src/nexus/scheduler.rs | 1464 +++++ sdk/src/nexus/signer.rs | 215 + sdk/src/nexus/workflow.rs | 593 +- sdk/src/object_crawler/fetching.rs | 233 - sdk/src/object_crawler/mod.rs | 3 - sdk/src/object_crawler/wrappers.rs | 504 -- sdk/src/onchain_schema_gen/input.rs | 201 + sdk/src/onchain_schema_gen/mod.rs | 14 + sdk/src/onchain_schema_gen/output.rs | 216 + sdk/src/onchain_schema_gen/types.rs | 450 ++ sdk/src/sui.rs | 130 +- sdk/src/test_utils/containers.rs | 121 +- sdk/src/test_utils/contracts.rs | 187 +- sdk/src/test_utils/faucet.rs | 2 +- sdk/src/test_utils/gas.rs | 47 +- sdk/src/test_utils/mod.rs | 1 - sdk/src/test_utils/nexus_mocks.rs | 48 +- sdk/src/test_utils/sui_mocks.rs | 781 ++- sdk/src/test_utils/wallet.rs | 103 - sdk/src/tool_fqn.rs | 2 +- sdk/src/tool_ref.rs | 415 ++ sdk/src/transactions/crypto.rs | 435 +- sdk/src/transactions/dag.rs | 533 +- sdk/src/transactions/gas.rs | 500 +- sdk/src/transactions/mod.rs | 3 + sdk/src/transactions/scheduler.rs | 1733 ++++++ sdk/src/transactions/tool.rs | 564 +- sdk/src/types/json_dag.rs | 8 +- sdk/src/types/mod.rs | 6 + sdk/src/types/nexus_data.rs | 297 +- sdk/src/types/nexus_data_parser.rs | 146 +- sdk/src/types/nexus_objects.rs | 193 +- sdk/src/types/ports_data.rs | 2 +- sdk/src/types/runtime_vertex.rs | 25 +- sdk/src/types/scheduler.rs | 463 ++ sdk/src/types/secret_value.rs | 75 + sdk/src/types/serde_parsers.rs | 196 +- sdk/src/types/storage_kind.rs | 2 +- sdk/src/types/tool_meta.rs | 2 +- sdk/src/types/type_name.rs | 14 + sdk/src/walrus/client.rs | 6 +- sdk/tests/move/object_crawler_test/Move.toml | 2 +- .../object_crawler_test/sources/main.move | 21 +- sdk/tests/move/onchain_tool_test/Move.toml | 10 + .../sources/onchain_tool.move | 104 + sdk/tests/object_crawler_test.rs | 445 +- sdk/tests/scheduler_test.rs | 115 + toolkit-rust/README.md | 2 +- toolkit-rust/src/runtime.rs | 14 + toolkit-rust/tests/integration.rs | 25 + tools/exchanges-coinbase/src/main.rs | 1 + .../src/tools/get_product_stats.rs | 1 + tools/http/src/errors.rs | 63 +- tools/http/src/http.rs | 41 +- tools/http/src/http_client.rs | 8 +- tools/http/src/models.rs | 9 +- .../get_conversation_messages.rs | 2 +- .../get_conversation_messages_by_id.rs | 2 +- tools/social-twitter/src/list/get_list.rs | 2 +- .../src/list/get_list_members.rs | 1 - .../src/list/get_list_tweets.rs | 2 +- .../social-twitter/src/list/get_user_lists.rs | 2 +- .../social-twitter/src/list/remove_member.rs | 1 - tools/social-twitter/src/list/update_list.rs | 1 - tools/social-twitter/src/main.rs | 10 + tools/social-twitter/src/media/models.rs | 7 +- .../social-twitter/src/media/upload_media.rs | 7 +- .../src/tweet/get_mentioned_tweets.rs | 1 + .../src/tweet/get_recent_search_tweets.rs | 2 +- .../src/tweet/get_recent_tweet_count.rs | 6 - tools/social-twitter/src/tweet/get_tweet.rs | 1 + tools/social-twitter/src/tweet/get_tweets.rs | 1 + .../src/tweet/get_user_tweets.rs | 1 + tools/social-twitter/src/tweet/models.rs | 17 +- tools/social-twitter/src/tweet/post_tweet.rs | 5 +- .../social-twitter/src/user/get_user_by_id.rs | 1 + .../src/user/get_user_by_username.rs | 1 + 185 files changed, 26795 insertions(+), 7688 deletions(-) create mode 100644 Cross.toml create mode 100644 cli/src/scheduler/helpers.rs create mode 100644 cli/src/scheduler/mod.rs create mode 100644 cli/src/scheduler/occurrence/mod.rs create mode 100644 cli/src/scheduler/occurrence/occurrence_add.rs create mode 100644 cli/src/scheduler/periodic/mod.rs create mode 100644 cli/src/scheduler/periodic/periodic_disable.rs create mode 100644 cli/src/scheduler/periodic/periodic_set.rs create mode 100644 cli/src/scheduler/task/mod.rs create mode 100644 cli/src/scheduler/task/task_create.rs create mode 100644 cli/src/scheduler/task/task_inspect.rs create mode 100644 cli/src/scheduler/task/task_metadata.rs create mode 100644 cli/src/scheduler/task/task_state.rs create mode 100644 cli/src/tool/templates/move/.gitignore.jinja create mode 100644 cli/src/tool/templates/move/Move.toml.jinja create mode 100644 cli/src/tool/templates/move/tests.move.jinja create mode 100644 cli/src/tool/templates/move/tool.move.jinja delete mode 100644 cli/src/tool/tool_register.rs create mode 100644 cli/src/tool/tool_register_offchain.rs create mode 100644 cli/src/tool/tool_register_onchain.rs create mode 100644 cli/src/workflow/mod.rs create mode 100644 docs/guides/onchain-tool-development.md create mode 100644 sdk/src/dag/_dags/conditional_onchain_tool.json create mode 100644 sdk/src/dag/_dags/double_onchain_tool.json create mode 100644 sdk/src/dag/_dags/guide_onchain_tool.json create mode 100644 sdk/src/dag/_dags/single_onchain_tool.json delete mode 100644 sdk/src/events.rs create mode 100644 sdk/src/events/fetching.rs create mode 100644 sdk/src/events/graphql/events_query.graphql create mode 100644 sdk/src/events/graphql/events_query.rs create mode 100644 sdk/src/events/graphql/mod.rs create mode 100644 sdk/src/events/graphql/schema-1.61.2.graphql create mode 100644 sdk/src/events/mod.rs create mode 100644 sdk/src/events/parsing.rs create mode 100644 sdk/src/nexus/crawler.rs create mode 100644 sdk/src/nexus/scheduler.rs create mode 100644 sdk/src/nexus/signer.rs delete mode 100644 sdk/src/object_crawler/fetching.rs delete mode 100644 sdk/src/object_crawler/mod.rs delete mode 100644 sdk/src/object_crawler/wrappers.rs create mode 100644 sdk/src/onchain_schema_gen/input.rs create mode 100644 sdk/src/onchain_schema_gen/mod.rs create mode 100644 sdk/src/onchain_schema_gen/output.rs create mode 100644 sdk/src/onchain_schema_gen/types.rs delete mode 100644 sdk/src/test_utils/wallet.rs create mode 100644 sdk/src/tool_ref.rs create mode 100644 sdk/src/transactions/scheduler.rs create mode 100644 sdk/src/types/scheduler.rs create mode 100644 sdk/src/types/secret_value.rs create mode 100644 sdk/tests/move/onchain_tool_test/Move.toml create mode 100644 sdk/tests/move/onchain_tool_test/sources/onchain_tool.move create mode 100644 sdk/tests/scheduler_test.rs diff --git a/.github/actions/coverage/action.yml b/.github/actions/coverage/action.yml index 259d89c6..efadc06f 100644 --- a/.github/actions/coverage/action.yml +++ b/.github/actions/coverage/action.yml @@ -20,9 +20,21 @@ inputs: runs: using: "composite" steps: + - name: Configure iptables for Docker + shell: bash + run: | + sudo update-alternatives --set iptables /usr/sbin/iptables-legacy + sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy + - name: Set up Docker uses: docker/setup-docker-action@v4 + - name: Restart Docker to reinitialize iptables + shell: bash + run: | + sudo systemctl restart docker + sleep 5 + - name: Install Rust Toolchain uses: actions-rust-lang/setup-rust-toolchain@v1 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 49935d2a..f3393cea 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -87,7 +87,7 @@ jobs: - name: Install build dependencies if: ${{ runner.os == 'Linux' }} run: | - sudo apt install -y libssl-dev musl-tools pkg-config + sudo apt install -y libssl-dev musl-tools pkg-config libdbus-1-dev - name: Cache Build uses: Swatinem/rust-cache@v2 @@ -109,7 +109,7 @@ jobs: - name: Package run: | - tar -cv CHANGELOG.md LICENSE.txt README.md \ + tar -cv CHANGELOG.md LICENSE README.md \ -C target/${{ matrix.target }}/release/ nexus | gzip --best > \ nexus-cli-${{ steps.get_version.outputs.value }}-${{ matrix.target }}.tar.gz diff --git a/.gitignore b/.gitignore index 5a6a27ad..e66214bc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ target sdk/tests/move/object_crawler_test/build sdk/tests/move/object_crawler_test/Move.lock +sdk/tests/move/onchain_tool_test/build +sdk/tests/move/onchain_tool_test/Move.lock # WASM build outputs cli-wasm/pkg/ @@ -16,3 +18,5 @@ cli-wasm/pkg-bundler/ # macOS .DS_Store + +**/Move.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e62c4c8..720704e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,23 +5,75 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## [`0.5.0`] - 2026-01-16 -## [`0.3.0`] - 2025-11-10 +### `nexus-cli` + +#### Added + +- `nexus scheduler` command group for on-chain task management: + - `nexus scheduler task create` / `inspect` / `metadata` / `pause` / `resume` / `cancel` + - `nexus scheduler occurrence add` + - `nexus scheduler periodic set` / `disable` +- `ToolRef` to combine offchain url and onchain move module id +- add `--verbose` flag for debug log output + +### `nexus-sdk` + +#### Changed + +- leader and crypto caps in PTB templates are now party objects +- added `ToolRegistryCreated` as tracked event +- combined some functions in tool_registry.move +- set Rust toolchain back to `stable` + +## [`0.4.0`] - 2026-01-07 ### `nexus-cli` #### Added -- `nexus dag inspect-execution` now also shows the data storage kind for each port +- `--priority-fee-per-gas-unit` flag on `nexus dag execute` to forward a priority fee with DAG executions +- `nexus tool register onchain` command to register onchain tools +- onchain tool development guide +- `nexus tool new` onchain tool move template #### Changed -- `nexus dag inspect-execution` now uses new `NexusData` implementation that supports remote storage -- `nexus dag execute` now uses new `NexusData` implementation that supports remote storage +- CLI now uses GRPC behind the scenes to communicate with the Sui blockchain +- CLI now uses the `EventFetcher` to fetche evens where necessary from Sui GraphQL + +### `nexus-sdk` + +#### Added + +- support for `scheduler` transactions and events +- onchain schema generation +- `EventFetcher` under `nexus` module to fetch events from Sui GraphQL + +#### Changed + +- `crypto auth` now uses the new handshake algorithm +- `nexus tool register` now has two subcommands for both types of tools +- wrap large numbers as JSON strings to preserve precision for u128/u256 in nexus parser +- all identifiers and transaction templates now use new `sui-rust-sdk` types +- `NexusClient` uses GRPC client under the hood +- `ObjectCrawler` moved under `nexus` module and uses GRPC +- `onchain_schema_gen` module now uses GRPC +- all types in the SDK changed to use `sui-rust-sdk` types instead of `sui-sdk` + +#### Removed + +- dependency on `sui-sdk` crate in favour of `sui-rust-sdk` + +## [`0.3.0`] - 2025-11-10 + +### `nexus-cli` #### Fixed +- `nexus dag inspect-execution` now uses new `NexusData` implementation that supports remote storage +- `nexus dag execute` now uses new `NexusData` implementation that supports remote storage - `nexus crypto init-key --force` wipes the old `crypto` state from config before rotating the key to avoid parsing errors ### `nexus-sdk` diff --git a/Cargo.lock b/Cargo.lock index fee1368b..b29ed9be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,16 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "addchain" version = "0.2.0" @@ -15,18 +25,18 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" @@ -63,6 +73,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "aes-gcm-siv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + [[package]] name = "aes-siv" version = "0.7.0" @@ -81,23 +106,23 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.2.15", + "getrandom 0.3.4", "once_cell", "serde", "version_check", - "zerocopy 0.7.35", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -124,16 +149,34 @@ dependencies = [ ] [[package]] -name = "allocator-api2" -version = "0.2.21" +name = "allocative" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +checksum = "8fac2ce611db8b8cee9b2aa886ca03c924e9da5e5295d0dbd0526e5d0b0710f7" +dependencies = [ + "allocative_derive", + "bumpalo", + "ctor", + "hashbrown 0.14.5", + "num-bigint 0.4.6", +] [[package]] -name = "android-tzdata" -version = "0.1.1" +name = "allocative_derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe233a377643e0fc1a56421d7c90acdec45c291b30345eb9f08e8d0ddce5a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android_system_properties" @@ -156,7 +199,7 @@ dependencies = [ "ed25519", "futures", "hex", - "http 1.3.1", + "http 1.4.0", "matchit 0.5.0", "pin-project-lite", "pkcs8 0.10.2", @@ -169,7 +212,7 @@ dependencies = [ "rustls-webpki", "serde", "serde_json", - "socket2", + "socket2 0.5.10", "tap", "thiserror 1.0.69", "tokio", @@ -197,11 +240,20 @@ dependencies = [ "uuid", ] +[[package]] +name = "annotate-snippets" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e" +dependencies = [ + "unicode-width 0.1.14", +] + [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -214,55 +266,71 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "antithesis_sdk" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dafc0460f582169b1414074fd82bedbda60456fb4df0a78dc7fef1306e732ea3" +dependencies = [ + "libc", + "libloading", + "linkme", "once_cell", - "windows-sys 0.59.0", + "rand 0.8.5", + "rustc_version_runtime", + "serde", + "serde_json", ] [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" dependencies = [ "backtrace", ] [[package]] -name = "arc-swap" -version = "1.7.1" +name = "ar_archive_writer" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" dependencies = [ - "serde", + "object 0.32.2", ] [[package]] @@ -303,7 +371,7 @@ dependencies = [ "blake2", "derivative", "digest 0.10.7", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -405,6 +473,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "ark-secp256k1" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c02e954eaeb4ddb29613fee20840c2bbc85ca4396d53e33837e11905363c5f2" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + [[package]] name = "ark-secp256r1" version = "0.4.0" @@ -473,6 +552,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term", +] + [[package]] name = "asn1-rs" version = "0.7.1" @@ -485,7 +573,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 2.0.12", + "thiserror 2.0.17", "time", ] @@ -497,8 +585,8 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", - "synstructure 0.13.1", + "syn 2.0.111", + "synstructure 0.13.2", ] [[package]] @@ -509,7 +597,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -524,14 +612,14 @@ dependencies = [ [[package]] name = "assert_fs" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7efdb1fdb47602827a342857666feb372712cbc64b414172bd6b167a02927674" +checksum = "a652f6cb1f516886fcfee5e7a5c078b9ade62cfcb889524efe5a64d682dd27a9" dependencies = [ "anstyle", "doc-comment", "globwalk", - "predicates", + "predicates 3.1.3", "predicates-core", "predicates-tree", "tempfile", @@ -545,18 +633,15 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.22" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a194f9d963d8099596278594b3107448656ba73831c9d8c783e613ce86da64" +checksum = "07a926debf178f2d355197f9caddb08e54a9329d44748034bba349c5848cb519" dependencies = [ - "brotli", - "flate2", + "compression-codecs", + "compression-core", "futures-core", - "memchr", "pin-project-lite", "tokio", - "zstd", - "zstd-safe", ] [[package]] @@ -577,7 +662,7 @@ dependencies = [ "secrecy", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", @@ -603,18 +688,18 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -631,42 +716,41 @@ checksum = "7460f7dd8e100147b82a63afca1a20eb6c231ee36b90ba7272e14951cb58af59" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "axum" -version = "0.7.9" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" dependencies = [ - "async-trait", "axum-core", "axum-macros", "base64 0.22.1", "bytes", + "form_urlencoded", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-util", "itoa", - "matchit 0.7.3", + "matchit 0.8.4", "memchr", "mime", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", + "serde_core", "serde_json", "serde_path_to_error", "serde_urlencoded", "sha1", "sync_wrapper", "tokio", - "tokio-tungstenite 0.24.0", + "tokio-tungstenite 0.28.0", "tower 0.5.2", "tower-layer", "tower-service", @@ -674,19 +758,17 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ - "async-trait", "bytes", - "futures-util", - "http 1.3.1", + "futures-core", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", - "rustversion", "sync_wrapper", "tower-layer", "tower-service", @@ -694,36 +776,13 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", -] - -[[package]] -name = "axum-server" -version = "0.6.1" -source = "git+https://github.com/bmwill/axum-server.git?rev=f44323e271afdd1365fd0c8b0a4c0bbdf4956cb7#f44323e271afdd1365fd0c8b0a4c0bbdf4956cb7" -dependencies = [ - "arc-swap", - "bytes", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "hyper 1.6.0", - "hyper-util", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower 0.4.13", - "tower-service", + "syn 2.0.111", ] [[package]] @@ -733,7 +792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ "futures-core", - "getrandom 0.2.15", + "getrandom 0.2.16", "instant", "pin-project-lite", "rand 0.8.5", @@ -754,17 +813,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.37.3", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -785,6 +844,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +dependencies = [ + "const-str 0.4.3", + "match-lookup", +] + [[package]] name = "base64" version = "0.13.1" @@ -805,9 +874,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "bcs" @@ -825,6 +894,12 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + [[package]] name = "bellpepper" version = "0.4.1" @@ -871,10 +946,10 @@ dependencies = [ [[package]] name = "bin-version" -version = "1.45.3" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +version = "1.61.2" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ - "const-str", + "const-str 0.5.7", "git-version", ] @@ -895,25 +970,40 @@ checksum = "b30ed1d6f8437a487a266c8293aeb95b61a23261273e3e02912cdb8b68bf798b" dependencies = [ "bs58 0.4.0", "hmac", - "k256", + "k256 0.11.6", "once_cell", "pbkdf2", "rand_core 0.6.4", "ripemd", - "sha2 0.10.8", + "sha2 0.10.9", "subtle", "zeroize", ] +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec 0.6.3", +] + [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec", + "bit-vec 0.8.0", ] +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bit-vec" version = "0.8.0" @@ -943,9 +1033,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitmaps" @@ -1053,9 +1143,9 @@ dependencies = [ [[package]] name = "blst" -version = "0.3.14" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c79a94619fade3c0b887670333513a67ac28a6a7e653eb260bf0d4103db38d" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" dependencies = [ "cc", "glob", @@ -1081,9 +1171,9 @@ dependencies = [ [[package]] name = "bnum" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f781dba93de3a5ef6dc5b17c9958b208f6f3f021623b360fb605ea51ce443f10" +checksum = "119771309b95163ec7aaf79810da82f7cd0599c19722d48b9c03894dca833966" [[package]] name = "bollard" @@ -1098,9 +1188,9 @@ dependencies = [ "futures-util", "hex", "home", - "http 1.3.1", + "http 1.4.0", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-named-pipe", "hyper-rustls", "hyper-util", @@ -1116,7 +1206,7 @@ dependencies = [ "serde_json", "serde_repr", "serde_urlencoded", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-util", "tower-service", @@ -1137,15 +1227,15 @@ dependencies = [ [[package]] name = "borrow-or-share" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eeab4423108c5d7c744f4d234de88d18d636100093ae04caf4825134b9c3a32" +checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1154,9 +1244,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -1182,9 +1272,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "serde", @@ -1192,9 +1282,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-slice-cast" @@ -1204,15 +1294,15 @@ checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" [[package]] name = "bytecount" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" [[package]] name = "byteorder" @@ -1222,13 +1312,22 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] +[[package]] +name = "bytestring" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" +dependencies = [ + "bytes", +] + [[package]] name = "cbc" version = "0.1.2" @@ -1240,10 +1339,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.18" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -1257,9 +1357,15 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "cfg_aliases" @@ -1293,17 +1399,16 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1346,19 +1451,30 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.35" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", ] +[[package]] +name = "clap-verbosity" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7bf75a8e0407a558bd7e8e7919baa352e21fb0c1c7702a63c853f2277c4c63" +dependencies = [ + "clap", + "log", + "serde", +] + [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -1369,30 +1485,39 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.48" +version = "4.5.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8c97f3a6f02b9e24cadc12aaba75201d18754b53ea0a9d99642f806ccdb4c9" +checksum = "39615915e2ece2550c0149addac32fb5bd312c657f43845bb9088cb9c8a7c992" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "clipboard-win" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] [[package]] name = "cmac" @@ -1405,6 +1530,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "cmp_any" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b18233253483ce2f65329a24072ec414db782531bdbb7d0bbc4bd2ce6b7e21" + [[package]] name = "codespan" version = "0.11.1" @@ -1428,9 +1559,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "colored" @@ -1461,10 +1592,30 @@ dependencies = [ "memchr", ] +[[package]] +name = "compression-codecs" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34a3cbbb8b6eca96f3a5c4bf6938d5b27ced3675d69f95bb51948722870bc323" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + [[package]] name = "consensus-config" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "fastcrypto", "mysten-network", @@ -1473,6 +1624,17 @@ dependencies = [ "shared-crypto", ] +[[package]] +name = "consensus-types" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" +dependencies = [ + "base64 0.21.7", + "consensus-config", + "fastcrypto", + "serde", +] + [[package]] name = "console" version = "0.15.11" @@ -1482,7 +1644,7 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.0", + "unicode-width 0.2.2", "windows-sys 0.59.0", ] @@ -1502,6 +1664,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-str" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" + [[package]] name = "const-str" version = "0.5.7" @@ -1520,6 +1688,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "convert_case" version = "0.7.1" @@ -1541,9 +1718,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -1585,13 +1762,28 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -1619,9 +1811,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-bigint" @@ -1649,9 +1841,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -1660,25 +1852,35 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ "csv-core", "itoa", "ryu", - "serde", + "serde_core", ] [[package]] name = "csv-core" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ "memchr", ] +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "ctr" version = "0.9.2" @@ -1712,7 +1914,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -1748,6 +1950,16 @@ dependencies = [ "darling_macro 0.20.11", ] +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + [[package]] name = "darling_core" version = "0.14.4" @@ -1773,7 +1985,21 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.100", + "syn 2.0.111", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.111", ] [[package]] @@ -1795,7 +2021,18 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.100", + "syn 2.0.111", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.111", ] [[package]] @@ -1813,15 +2050,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "data-encoding-macro" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9724adfcf41f45bf652b3995837669d73c4d49a1b5ac1ff82905ac7d9b5558" +checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -1829,12 +2066,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4fdb82bd54a12e42fb58a800dcae6b9e13982238ce2296dc3570b92148e1f" +checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -1864,9 +2101,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "708b509edf7889e53d7efb0ffadd994cc6c2345ccb62f55cfd6b0682165e4fa6" dependencies = [ "dbus", + "openssl", "zeroize", ] +[[package]] +name = "debugserver-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf6834a70ed14e8e4e41882df27190bea150f1f6ecf461f1033f8739cd8af4a" +dependencies = [ + "schemafy", + "serde", + "serde_json", +] + [[package]] name = "der" version = "0.6.1" @@ -1880,9 +2129,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "pem-rfc7468 0.7.0", @@ -1905,12 +2154,12 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -1953,7 +2202,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -1963,20 +2212,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -1994,12 +2243,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ + "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", "unicode-xid", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "difflib" version = "0.4.0" @@ -2095,8 +2351,8 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users 0.5.0", - "windows-sys 0.59.0", + "redox_users 0.5.2", + "windows-sys 0.61.2", ] [[package]] @@ -2110,6 +2366,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "display_container" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a110a75c96bedec8e65823dea00a1d710288b7a369d95fd8a0f5127639466fa" +dependencies = [ + "either", + "indenter", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -2118,37 +2384,63 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "doc-comment" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" [[package]] name = "docker_credential" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31951f49556e34d90ed28342e1df7e1cb7a229c4cab0aecc627b5d91edd41d07" +checksum = "1d89dfcba45b4afad7450a99b39e751590463e45c04728cf555d36bb66940de8" dependencies = [ "base64 0.21.7", "serde", "serde_json", ] +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dupe" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed2bc011db9c93fbc2b6cdb341a53737a55bafb46dbb74cf6764fc33a2fbf9c" +dependencies = [ + "dupe_derive", +] + +[[package]] +name = "dupe_derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e195b4945e88836d826124af44fdcb262ec01ef94d44f14f4fb5103f19892a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "dyn-clone" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ecdsa" @@ -2168,7 +2460,7 @@ version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.9", + "der 0.7.10", "digest 0.10.7", "elliptic-curve 0.13.8", "rfc6979 0.4.0", @@ -2204,14 +2496,14 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", "serde", - "sha2 0.10.8", + "sha2 0.10.9", "subtle", "zeroize", ] @@ -2270,6 +2562,15 @@ dependencies = [ "serde", ] +[[package]] +name = "ena" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" +dependencies = [ + "log", +] + [[package]] name = "encode_unicode" version = "1.0.0" @@ -2285,6 +2586,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "enum-as-inner" version = "0.6.1" @@ -2294,13 +2601,13 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "enum-compat-util" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "serde_yaml", ] @@ -2314,14 +2621,14 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "env_filter" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", "regex", @@ -2355,16 +2662,31 @@ dependencies = [ "typeid", ] +[[package]] +name = "erased-serde" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" +dependencies = [ + "serde", +] + [[package]] name = "errno" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + [[package]] name = "etcetera" version = "0.8.0" @@ -2378,9 +2700,9 @@ dependencies = [ [[package]] name = "ethnum" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" +checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" [[package]] name = "eventsource-stream" @@ -2395,17 +2717,17 @@ dependencies = [ [[package]] name = "exchanges-coinbase" -version = "0.3.0" +version = "0.5.0" dependencies = [ "chrono", "mockito", "nexus-sdk", "nexus-toolkit", "reqwest", - "schemars 1.0.0-alpha.17", + "schemars 1.1.0", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", ] @@ -2425,24 +2747,39 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" dependencies = [ - "bit-set", + "bit-set 0.8.0", "regex-automata", - "regex-syntax", + "regex-syntax 0.8.8", +] + +[[package]] +name = "fastbloom" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18c1ddb9231d8554c2d6bdf4cfaabf0c59251658c68b6c95cd52dd0c513a912a" +dependencies = [ + "getrandom 0.3.4", + "libm", + "rand 0.9.2", + "siphasher", ] [[package]] name = "fastcrypto" -version = "0.1.8" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=69d496c71fb37e3d22fe85e5bbfd4256d61422b9#69d496c71fb37e3d22fe85e5bbfd4256d61422b9" +version = "0.1.9" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=09f86974195ec85d8aae386b1909d341d3ccfe52#09f86974195ec85d8aae386b1909d341d3ccfe52" dependencies = [ "aes", "aes-gcm", + "aes-gcm-siv", "ark-ec", "ark-ff", + "ark-secp256k1", "ark-secp256r1", "ark-serialize", "auto_ops", "base64ct", + "bcs", "bech32", "bincode", "blake2", @@ -2451,7 +2788,7 @@ dependencies = [ "cbc", "ctr", "curve25519-dalek-ng", - "derive_more 0.99.19", + "derive_more 0.99.20", "digest 0.10.7", "ecdsa 0.16.9", "ed25519-consensus", @@ -2474,7 +2811,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sha2 0.10.8", + "sha2 0.10.9", "sha3", "signature 2.2.0", "static_assertions", @@ -2487,7 +2824,7 @@ dependencies = [ [[package]] name = "fastcrypto-derive" version = "0.1.3" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=69d496c71fb37e3d22fe85e5bbfd4256d61422b9#69d496c71fb37e3d22fe85e5bbfd4256d61422b9" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=09f86974195ec85d8aae386b1909d341d3ccfe52#09f86974195ec85d8aae386b1909d341d3ccfe52" dependencies = [ "quote", "syn 1.0.109", @@ -2496,7 +2833,7 @@ dependencies = [ [[package]] name = "fastcrypto-tbls" version = "0.1.0" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=69d496c71fb37e3d22fe85e5bbfd4256d61422b9#69d496c71fb37e3d22fe85e5bbfd4256d61422b9" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=09f86974195ec85d8aae386b1909d341d3ccfe52#09f86974195ec85d8aae386b1909d341d3ccfe52" dependencies = [ "bcs", "digest 0.10.7", @@ -2505,6 +2842,7 @@ dependencies = [ "itertools 0.10.5", "rand 0.8.5", "serde", + "serde-big-array", "sha3", "tap", "tracing", @@ -2515,7 +2853,7 @@ dependencies = [ [[package]] name = "fastcrypto-zkp" version = "0.1.3" -source = "git+https://github.com/MystenLabs/fastcrypto?rev=69d496c71fb37e3d22fe85e5bbfd4256d61422b9#69d496c71fb37e3d22fe85e5bbfd4256d61422b9" +source = "git+https://github.com/MystenLabs/fastcrypto?rev=09f86974195ec85d8aae386b1909d341d3ccfe52#09f86974195ec85d8aae386b1909d341d3ccfe52" dependencies = [ "ark-bn254", "ark-ec", @@ -2526,7 +2864,7 @@ dependencies = [ "ark-snark", "bcs", "byte-slice-cast", - "derive_more 0.99.19", + "derive_more 0.99.20", "fastcrypto", "ff 0.13.1", "im", @@ -2549,6 +2887,17 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fd-lock" +version = "4.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "ff" version = "0.12.1" @@ -2595,16 +2944,22 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" -version = "0.2.25" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + [[package]] name = "fixed-hash" version = "0.7.0" @@ -2619,9 +2974,9 @@ dependencies = [ [[package]] name = "fixedbitset" -version = "0.2.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "fixedbitset" @@ -2631,14 +2986,23 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fluent-uri" version = "0.3.2" @@ -2685,9 +3049,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -2702,6 +3066,12 @@ dependencies = [ "num", ] +[[package]] +name = "fragile" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + [[package]] name = "funty" version = "1.1.0" @@ -2770,7 +3140,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -2809,6 +3179,15 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2823,28 +3202,28 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] @@ -2860,9 +3239,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "git-version" @@ -2881,26 +3260,26 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "globset" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" dependencies = [ "aho-corasick", "bstr", "log", "regex-automata", - "regex-syntax", + "regex-syntax 0.8.8", ] [[package]] @@ -2909,7 +3288,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "ignore", "walkdir", ] @@ -2934,6 +3313,64 @@ dependencies = [ "spinning_top", ] +[[package]] +name = "graphql-introspection-query" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2a4732cf5140bd6c082434494f785a19cfb566ab07d1382c3671f5812fed6d" +dependencies = [ + "serde", +] + +[[package]] +name = "graphql-parser" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a818c0d883d7c0801df27be910917750932be279c7bc82dc541b8769425f409" +dependencies = [ + "combine", + "thiserror 1.0.69", +] + +[[package]] +name = "graphql_client" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a50cfdc7f34b7f01909d55c2dcb71d4c13cbcbb4a1605d6c8bd760d654c1144b" +dependencies = [ + "graphql_query_derive", + "serde", + "serde_json", +] + +[[package]] +name = "graphql_client_codegen" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e27ed0c2cf0c0cc52c6bcf3b45c907f433015e580879d14005386251842fb0a" +dependencies = [ + "graphql-introspection-query", + "graphql-parser", + "heck 0.4.1", + "lazy_static", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 1.0.109", +] + +[[package]] +name = "graphql_query_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83febfa838f898cfa73dfaa7a8eb69ff3409021ac06ee94cfb3d622f6eeb1a97" +dependencies = [ + "graphql_client_codegen", + "proc-macro2", + "syn 1.0.109", +] + [[package]] name = "group" version = "0.12.1" @@ -2954,15 +3391,15 @@ dependencies = [ "ff 0.13.1", "rand 0.8.5", "rand_core 0.6.4", - "rand_xorshift", + "rand_xorshift 0.3.0", "subtle", ] [[package]] name = "h2" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ "bytes", "fnv", @@ -2970,7 +3407,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.9.0", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", @@ -2979,17 +3416,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.8" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.9.0", + "http 1.4.0", + "indexmap 2.12.1", "slab", "tokio", "tokio-util", @@ -2998,12 +3435,13 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -3026,18 +3464,28 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "hdrhistogram" version = "7.5.4" @@ -3086,9 +3534,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -3107,9 +3555,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hickory-proto" -version = "0.24.4" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" +checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" dependencies = [ "async-trait", "cfg-if", @@ -3121,8 +3569,9 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.8.5", - "thiserror 1.0.69", + "rand 0.9.2", + "ring", + "thiserror 2.0.17", "tinyvec", "tokio", "tracing", @@ -3131,21 +3580,21 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.4" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" +checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" dependencies = [ "cfg-if", "futures-util", "hickory-proto", "ipconfig", - "lru-cache", + "moka", "once_cell", "parking_lot", - "rand 0.8.5", + "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -3176,22 +3625,11 @@ checksum = "77e806677ce663d0a199541030c816847b36e8dc095f70dae4a4f4ad63da5383" [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "hostname" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" -dependencies = [ - "cfg-if", - "libc", - "windows-link", + "windows-sys 0.61.2", ] [[package]] @@ -3207,7 +3645,7 @@ dependencies = [ [[package]] name = "http" -version = "0.3.0" +version = "0.5.0" dependencies = [ "backon", "base64 0.21.7", @@ -3216,22 +3654,21 @@ dependencies = [ "nexus-sdk", "nexus-toolkit", "reqwest", - "schemars 1.0.0-alpha.17", + "schemars 1.1.0", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "url", ] [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -3253,7 +3690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -3264,7 +3701,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "pin-project-lite", ] @@ -3289,9 +3726,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hyper" @@ -3303,14 +3740,14 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -3319,20 +3756,22 @@ dependencies = [ [[package]] name = "hyper" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", - "h2 0.4.8", - "http 1.3.1", + "futures-core", + "h2 0.4.12", + "http 1.4.0", "http-body 1.0.1", "httparse", "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -3345,7 +3784,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" dependencies = [ "hex", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-util", "pin-project-lite", "tokio", @@ -3355,13 +3794,12 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", - "http 1.3.1", - "hyper 1.6.0", + "http 1.4.0", + "hyper 1.8.1", "hyper-util", "log", "rustls", @@ -3379,7 +3817,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.6.0", + "hyper 1.8.1", "hyper-util", "pin-project-lite", "tokio", @@ -3394,7 +3832,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-util", "native-tls", "tokio", @@ -3404,22 +3842,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", - "hyper 1.6.0", + "hyper 1.8.1", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.1", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -3430,7 +3874,7 @@ checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" dependencies = [ "hex", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-util", "pin-project-lite", "tokio", @@ -3439,9 +3883,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -3463,21 +3907,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -3486,99 +3931,61 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", + "icu_locale_core", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -3587,9 +3994,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -3598,9 +4005,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -3608,9 +4015,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.23" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" dependencies = [ "crossbeam-deque", "globset", @@ -3662,14 +4069,14 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "indenter" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" @@ -3684,13 +4091,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.16.1", "serde", + "serde_core", ] [[package]] @@ -3702,7 +4110,7 @@ dependencies = [ "console", "number_prefix", "portable-atomic", - "unicode-width 0.2.0", + "unicode-width 0.2.2", "web-time", ] @@ -3724,16 +4132,12 @@ dependencies = [ [[package]] name = "insta" -version = "1.42.2" +version = "1.44.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084" +checksum = "b5c943d4415edd8153251b6f197de5eb1640e56d84e8d9159bea190421c73698" dependencies = [ "console", - "linked-hash-map", "once_cell", - "pest", - "pest_derive", - "pin-project", "serde", "similar", ] @@ -3747,14 +4151,23 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "inventory" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" +dependencies = [ + "rustversion", +] + [[package]] name = "ipconfig" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2", - "widestring 1.2.0", + "socket2 0.5.10", + "widestring 1.2.1", "windows-sys 0.48.0", "winreg", ] @@ -3767,19 +4180,30 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", ] +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -3825,26 +4249,26 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.6" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f33145a5cbea837164362c7bd596106eb7c5198f97d1ba6f6ebb3223952e488" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "log", "portable-atomic", "portable-atomic-util", - "serde", + "serde_core", ] [[package]] name = "jiff-static" -version = "0.2.6" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ce13c40ec6956157a3635d97a1ee2df323b263f09ea14165131289cb0f5c19" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -3871,19 +4295,19 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -3898,11 +4322,23 @@ dependencies = [ "tabled", ] +[[package]] +name = "jsonrpc" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" +dependencies = [ + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tracing", +] + [[package]] name = "jsonrpsee" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b26c20e2178756451cfeb0661fb74c47dd5988cb7e3939de7e9241fd604d42" +checksum = "e281ae70cc3b98dac15fced3366a880949e65fc66e345ce857a5682d152f3e62" dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", @@ -3916,13 +4352,13 @@ dependencies = [ [[package]] name = "jsonrpsee-client-transport" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bacb85abf4117092455e1573625e21b8f8ef4dec8aff13361140b2dc266cdff2" +checksum = "cc4280b709ac3bb5e16cf3bad5056a0ec8df55fa89edfe996361219aadc2c7ea" dependencies = [ "base64 0.22.1", "futures-util", - "http 1.3.1", + "http 1.4.0", "jsonrpsee-core", "pin-project", "rustls", @@ -3939,15 +4375,15 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456196007ca3a14db478346f58c7238028d55ee15c1df15115596e411ff27925" +checksum = "348ee569eaed52926b5e740aae20863762b16596476e943c9e415a6479021622" dependencies = [ "async-trait", "bytes", "futures-timer", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "jsonrpsee-types", @@ -3965,14 +4401,14 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c872b6c9961a4ccc543e321bb5b89f6b2d2c7fe8b61906918273a3333c95400c" +checksum = "f50c389d6e6a52eb7c3548a6600c90cf74d9b71cb5912209833f00a5479e9a01" dependencies = [ "async-trait", "base64 0.22.1", "http-body 1.0.1", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-rustls", "hyper-util", "jsonrpsee-core", @@ -3990,28 +4426,28 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e65763c942dfc9358146571911b0cd1c361c2d63e2d2305622d40d36376ca80" +checksum = "7398cddf5013cca4702862a2692b66c48a3bd6cf6ec681a47453c93d63cf8de5" dependencies = [ "heck 0.5.0", - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "jsonrpsee-server" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e363146da18e50ad2b51a0a7925fc423137a0b1371af8235b1c231a0647328" +checksum = "21429bcdda37dcf2d43b68621b994adede0e28061f816b038b0f18c70c143d51" dependencies = [ "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", @@ -4030,11 +4466,11 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a8e70baf945b6b5752fc8eb38c918a48f1234daf11355e07106d963f860089" +checksum = "b0f05e0028e55b15dbd2107163b3c744cd3bb4474f193f95d9708acbf5677e44" dependencies = [ - "http 1.3.1", + "http 1.4.0", "serde", "serde_json", "thiserror 1.0.69", @@ -4042,11 +4478,11 @@ dependencies = [ [[package]] name = "jsonrpsee-ws-client" -version = "0.24.9" +version = "0.24.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b3323d890aa384f12148e8d2a1fd18eb66e9e7e825f9de4fa53bcc19b93eef" +checksum = "78fc744f17e7926d57f478cf9ca6e1ee5d8332bf0514860b1a3cdf1742e614cc" dependencies = [ - "http 1.3.1", + "http 1.4.0", "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", @@ -4071,7 +4507,7 @@ dependencies = [ "once_cell", "percent-encoding", "referencing", - "regex-syntax", + "regex-syntax 0.8.8", "reqwest", "serde", "serde_json", @@ -4087,10 +4523,22 @@ dependencies = [ "cfg-if", "ecdsa 0.14.8", "elliptic-curve 0.12.3", - "sha2 0.10.8", + "sha2 0.10.9", "sha3", ] +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "sha2 0.10.9", +] + [[package]] name = "keccak" version = "0.1.5" @@ -4102,24 +4550,57 @@ dependencies = [ [[package]] name = "keyring" -version = "3.6.2" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1961983669d57bdfe6c0f3ef8e4c229b5ef751afcc7d87e4271d2f71f6ccfa8b" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" dependencies = [ "byteorder", "dbus-secret-service", "linux-keyutils", "log", + "openssl", "security-framework 2.11.1", - "security-framework 3.2.0", - "windows-sys 0.59.0", + "security-framework 3.5.1", + "windows-sys 0.60.2", + "zeroize", +] + +[[package]] +name = "lalrpop" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a1cbf952127589f2851ab2046af368fd20645491bb4b376f04b7f94d7a9837b" +dependencies = [ + "ascii-canvas", + "bit-set 0.5.3", + "diff", + "ena", + "is-terminal", + "itertools 0.10.5", + "lalrpop-util", + "petgraph 0.6.5", + "regex", + "regex-syntax 0.6.29", + "string_cache", + "term", + "tiny-keccak", + "unicode-xid", +] + +[[package]] +name = "lalrpop-util" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c48237b9604c5a4702de6b824e02006c3214327564636aef27c1028a8fa0ed" +dependencies = [ + "regex", ] [[package]] name = "lazy-regex" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" +checksum = "191898e17ddee19e60bccb3945aa02339e81edd4a8c50e21fd4d48cdecda7b29" dependencies = [ "lazy-regex-proc_macros", "once_cell", @@ -4128,14 +4609,14 @@ dependencies = [ [[package]] name = "lazy-regex-proc_macros" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" +checksum = "c35dc8b0da83d1a9507e12122c80dea71a9c7c613014347392483a83ea593e04" dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -4147,6 +4628,15 @@ dependencies = [ "spin", ] +[[package]] +name = "lcov" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ccfa6d5e585a884db65b37f38184e4364eaf74d884ac35d0a90fe9baf80b723" +dependencies = [ + "thiserror 1.0.69", +] + [[package]] name = "leb128" version = "0.2.5" @@ -4155,9 +4645,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libdbus-sys" @@ -4165,24 +4655,35 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cbe856efeb50e4681f010e9aaa2bf0a644e10139e54cde10fc83a307c23bd9f" dependencies = [ + "cc", "pkg-config", ] +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "libc", - "redox_syscall 0.5.11", + "redox_syscall 0.5.18", ] [[package]] @@ -4191,31 +4692,51 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linkme" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3283ed2d0e50c06dd8602e0ab319bb048b6325d0bba739db64ed8205179898" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5cec0ec4228b4853bb129c84dbf093a27e6c7a20526da046defc334a1b017f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "linux-keyutils" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "libc", ] [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "llm-openai-chat-completion" -version = "0.3.0" +version = "0.5.0" dependencies = [ "anyhow", "async-openai", @@ -4229,32 +4750,54 @@ dependencies = [ "portpicker", "reqwest", "rstest", - "schemars 1.0.0-alpha.17", + "schemars 1.1.0", "serde", "serde_json", "serial_test", - "strum 0.27.1", - "strum_macros 0.27.1", + "strum 0.27.2", + "strum_macros 0.27.2", "tokio", ] [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" dependencies = [ - "serde", + "serde_core", +] + +[[package]] +name = "logos" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf8b031682c67a8e3d5446840f9573eb7fe26efe7ec8d195c9ac4c0647c502f1" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d849148dbaf9661a6151d1ca82b13bb4c4c128146a88d05253b38d4e2f496c" +dependencies = [ + "beef", + "fnv", + "proc-macro2", + "quote", + "regex-syntax 0.6.29", + "syn 1.0.109", ] [[package]] @@ -4272,16 +4815,26 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.5", ] [[package]] -name = "lru-cache" +name = "lru-slab" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "lsp-types" +version = "0.94.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1" dependencies = [ - "linked-hash-map", + "bitflags 1.3.2", + "serde", + "serde_json", + "serde_repr", + "url", ] [[package]] @@ -4297,6 +4850,23 @@ dependencies = [ "url", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "match-lookup" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "matchit" version = "0.5.0" @@ -4305,17 +4875,17 @@ checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "math" -version = "0.3.0" +version = "0.5.0" dependencies = [ "nexus-sdk", "nexus-toolkit", - "schemars 1.0.0-alpha.17", + "schemars 1.1.0", "serde", "tokio", ] @@ -4332,9 +4902,18 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memoffset" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] [[package]] name = "mime" @@ -4354,9 +4933,9 @@ dependencies = [ [[package]] name = "minijinja" -version = "2.9.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98642a6dfca91122779a307b77cd07a4aa951fbe32232aaf5bad9febc66be754" +checksum = "0adbe6e92a6ce0fd6c4aac593fdfd3e3950b0f61b1a63aa9731eb6fd85776fa3" dependencies = [ "serde", ] @@ -4369,41 +4948,96 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "mockall" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive 0.11.4", + "predicates 2.1.5", + "predicates-tree", +] + +[[package]] +name = "mockall" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58d964098a5f9c6b63d0798e5372fd04708193510a7af313c22e9f29b7b620b" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive 0.14.0", + "predicates 3.1.3", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "mockall_derive" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca41ce716dda6a9be188b385aa78ee5260fc25cd3802cb2a8afdc6afbe6b6dbf" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] name = "mockito" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7760e0e418d9b7e5777c0374009ca4c93861b9066f18cb334a20ce50ab63aa48" +checksum = "7e0603425789b4a70fcc4ac4f5a46a566c116ee3e2a6b768dc623f7719c611de" dependencies = [ "assert-json-diff", "bytes", "colored 3.0.0", - "futures-util", - "http 1.3.1", + "futures-core", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-util", "log", - "rand 0.9.0", + "pin-project-lite", + "rand 0.9.2", "regex", "serde_json", "serde_urlencoded", @@ -4412,26 +5046,42 @@ dependencies = [ ] [[package]] -name = "move-abstract-interpreter" -version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +name = "moka" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" dependencies = [ - "move-binary-format", - "move-bytecode-verifier-meter", + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "equivalent", + "parking_lot", + "portable-atomic", + "rustc_version", + "smallvec", + "tagptr", + "uuid", ] +[[package]] +name = "move-abstract-interpreter" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" + [[package]] name = "move-abstract-stack" version = "0.0.1" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" [[package]] name = "move-binary-format" version = "0.0.3" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "enum-compat-util", + "indexmap 2.12.1", + "move-abstract-interpreter", "move-core-types", "move-proc-macros", "ref-cast", @@ -4442,12 +5092,12 @@ dependencies = [ [[package]] name = "move-borrow-graph" version = "0.0.1" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" [[package]] name = "move-bytecode-source-map" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "bcs", @@ -4463,20 +5113,20 @@ dependencies = [ [[package]] name = "move-bytecode-utils" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", - "indexmap 2.9.0", + "indexmap 2.12.1", "move-binary-format", "move-core-types", - "petgraph 0.5.1", + "petgraph 0.8.3", "serde-reflection", ] [[package]] name = "move-bytecode-verifier" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "move-abstract-interpreter", "move-abstract-stack", @@ -4484,14 +5134,15 @@ dependencies = [ "move-borrow-graph", "move-bytecode-verifier-meter", "move-core-types", + "move-regex-borrow-graph", "move-vm-config", - "petgraph 0.5.1", + "petgraph 0.8.3", ] [[package]] name = "move-bytecode-verifier-meter" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "move-binary-format", "move-core-types", @@ -4501,7 +5152,7 @@ dependencies = [ [[package]] name = "move-command-line-common" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "bcs", @@ -4512,6 +5163,7 @@ dependencies = [ "move-binary-format", "move-core-types", "once_cell", + "packed_struct", "serde", "sha2 0.9.9", "vfs", @@ -4521,7 +5173,7 @@ dependencies = [ [[package]] name = "move-compiler" version = "0.0.1" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "bcs", @@ -4530,7 +5182,8 @@ dependencies = [ "dunce", "hex", "insta", - "lsp-types", + "lsp-types 0.95.1", + "move-abstract-interpreter", "move-binary-format", "move-borrow-graph", "move-bytecode-source-map", @@ -4542,7 +5195,7 @@ dependencies = [ "move-proc-macros", "move-symbol-pool", "once_cell", - "petgraph 0.5.1", + "petgraph 0.8.3", "rayon", "regex", "serde", @@ -4556,13 +5209,14 @@ dependencies = [ [[package]] name = "move-core-types" version = "0.0.4" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "bcs", "enum-compat-util", "ethnum", "hex", + "indexmap 2.12.1", "leb128", "move-proc-macros", "num", @@ -4580,28 +5234,32 @@ dependencies = [ [[package]] name = "move-coverage" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "bcs", "clap", "codespan", "colored 2.2.0", - "indexmap 2.9.0", + "indexmap 2.12.1", + "lcov", "move-abstract-interpreter", "move-binary-format", "move-bytecode-source-map", + "move-bytecode-verifier", "move-command-line-common", + "move-compiler", "move-core-types", "move-ir-types", - "petgraph 0.5.1", + "move-trace-format", + "petgraph 0.8.3", "serde", ] [[package]] name = "move-disassembler" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "bcs", @@ -4622,7 +5280,7 @@ dependencies = [ [[package]] name = "move-docgen" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "clap", @@ -4645,7 +5303,7 @@ dependencies = [ [[package]] name = "move-ir-to-bytecode" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "codespan-reporting", @@ -4663,7 +5321,7 @@ dependencies = [ [[package]] name = "move-ir-to-bytecode-syntax" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "hex", @@ -4676,7 +5334,7 @@ dependencies = [ [[package]] name = "move-ir-types" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "hex", "move-command-line-common", @@ -4689,12 +5347,13 @@ dependencies = [ [[package]] name = "move-model-2" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "bcs", "codespan", "codespan-reporting", + "indexmap 2.12.1", "move-binary-format", "move-bytecode-source-map", "move-command-line-common", @@ -4704,6 +5363,7 @@ dependencies = [ "move-ir-types", "move-symbol-pool", "num", + "pretty_simple", "serde", "vfs", ] @@ -4711,11 +5371,12 @@ dependencies = [ [[package]] name = "move-package" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "clap", "colored 2.2.0", + "dunce", "itertools 0.10.5", "move-binary-format", "move-bytecode-source-map", @@ -4729,14 +5390,14 @@ dependencies = [ "move-symbol-pool", "named-lock", "once_cell", - "petgraph 0.5.1", + "petgraph 0.8.3", "regex", "serde", "serde_yaml", "sha2 0.9.9", "tempfile", "toml 0.5.11", - "toml_edit 0.14.4", + "toml_edit 0.22.27", "treeline", "vfs", "walkdir", @@ -4746,27 +5407,51 @@ dependencies = [ [[package]] name = "move-proc-macros" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "enum-compat-util", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] -name = "move-symbol-pool" -version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +name = "move-regex-borrow-graph" +version = "0.0.1" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ - "once_cell", - "phf", + "itertools 0.10.5", + "move-binary-format", + "move-core-types", + "petgraph 0.8.3", + "proptest", +] + +[[package]] +name = "move-symbol-pool" +version = "0.1.0" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" +dependencies = [ + "once_cell", + "phf", + "serde", +] + +[[package]] +name = "move-trace-format" +version = "0.0.1" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" +dependencies = [ + "move-binary-format", + "move-core-types", "serde", + "serde_json", + "zstd", ] [[package]] name = "move-vm-config" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "move-binary-format", "once_cell", @@ -4775,8 +5460,9 @@ dependencies = [ [[package]] name = "move-vm-profiler" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ + "move-trace-format", "move-vm-config", "once_cell", "serde", @@ -4787,7 +5473,7 @@ dependencies = [ [[package]] name = "move-vm-test-utils" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "move-binary-format", @@ -4801,7 +5487,7 @@ dependencies = [ [[package]] name = "move-vm-types" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "bcs", "move-binary-format", @@ -4814,7 +5500,7 @@ dependencies = [ [[package]] name = "msim-macros" version = "0.1.0" -source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=9f175e3517812929ad6bdd8db443f1bbd8107965#9f175e3517812929ad6bdd8db443f1bbd8107965" +source = "git+https://github.com/MystenLabs/mysten-sim.git?rev=427147994705914a2f5afa42bc140794e31113b9#427147994705914a2f5afa42bc140794e31113b9" dependencies = [ "darling 0.14.4", "proc-macro2", @@ -4861,11 +5547,12 @@ dependencies = [ [[package]] name = "multibase" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" dependencies = [ "base-x", + "base256emoji", "data-encoding", "data-encoding-macro", ] @@ -4898,18 +5585,22 @@ dependencies = [ [[package]] name = "mysten-common" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ + "antithesis_sdk", "anyhow", + "either", "fastcrypto", "futures", "mysten-metrics", + "once_cell", "parking_lot", - "prometheus", + "rand 0.8.5", "reqwest", + "serde_json", "snap", - "sui-tls", - "sui-types", + "sui-macros", + "tempfile", "tokio", "tracing", ] @@ -4917,7 +5608,7 @@ dependencies = [ [[package]] name = "mysten-metrics" version = "0.7.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "async-trait", "axum", @@ -4938,23 +5629,30 @@ dependencies = [ [[package]] name = "mysten-network" version = "0.2.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anemo", "anemo-tower", + "anyhow", "async-stream", "bcs", "bytes", + "dashmap", "eyre", + "fastcrypto", "futures", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "hyper-rustls", "hyper-util", "multiaddr", + "mysten-metrics", "once_cell", "pin-project-lite", "prometheus", + "quinn-proto", + "rand 0.8.5", + "rustls", "serde", "snap", "sui-http", @@ -4963,8 +5661,8 @@ dependencies = [ "tokio-stream", "tonic", "tonic-health", - "tower 0.4.13", - "tower-http", + "tower 0.5.2", + "tower-http 0.5.2", "tracing", ] @@ -5018,22 +5716,31 @@ dependencies = [ "trait-set", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nexus-cli" -version = "0.3.0" +version = "0.5.0" dependencies = [ "aes-gcm", "anyhow", "argon2", "assert_fs", "assert_matches", + "base64 0.21.7", "bincode", "chrono", "clap", + "clap-verbosity", "clap_complete", "colored 3.0.0", "convert_case 0.7.1", "directories", + "env_logger", "hex", "home", "indicatif", @@ -5048,14 +5755,14 @@ dependencies = [ "reqwest", "rpassword", "rstest", - "schemars 1.0.0-alpha.17", + "schemars 1.1.0", "serde", "serde_json", "serial_test", "tempfile", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", - "toml 0.8.20", + "toml 0.8.23", "warp", "x25519-dalek", "zeroize", @@ -5063,7 +5770,7 @@ dependencies = [ [[package]] name = "nexus-sdk" -version = "0.3.0" +version = "0.5.0" dependencies = [ "aead", "aes-gcm", @@ -5071,6 +5778,7 @@ dependencies = [ "anyhow", "assert_matches", "base64 0.21.7", + "bcs", "bincode", "blake3", "chacha20poly1305", @@ -5080,14 +5788,18 @@ dependencies = [ "enum_dispatch", "futures", "futures-util", + "graphql_client", "hex", "hkdf", "hmac", "lazy-regex", "lru 0.12.5", + "mockall 0.14.0", "mockito", - "move-core-types", + "move-package", "petgraph 0.7.1", + "portpicker", + "prost-types", "rand 0.8.5", "rand_core 0.6.4", "regex", @@ -5096,20 +5808,22 @@ dependencies = [ "serde-big-array", "serde_bytes", "serde_json", - "sha2 0.10.8", - "shared-crypto", + "serde_repr", + "sha2 0.10.9", "subtle", - "sui-config", - "sui-keys", + "sui-crypto", "sui-move-build", "sui-package-management", - "sui-sdk", + "sui-rpc 0.1.0", + "sui-sdk-types 0.1.0", + "sui-transaction-builder 0.1.0", "tempfile", "testcontainers-modules", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-retry", - "toml 0.8.20", + "toml 0.8.23", + "tonic", "x25519-dalek", "xeddsa", "zeroize", @@ -5143,14 +5857,14 @@ dependencies = [ [[package]] name = "nexus-toolkit" -version = "0.3.0" +version = "0.5.0" dependencies = [ "anyhow", "env_logger", "log", "nexus-sdk", "reqwest", - "schemars 1.0.0-alpha.17", + "schemars 1.1.0", "serde", "serde_json", "serde_path_to_error", @@ -5158,6 +5872,27 @@ dependencies = [ "warp", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases 0.1.1", + "libc", +] + [[package]] name = "no-std-compat" version = "0.4.1" @@ -5186,6 +5921,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "num" version = "0.4.3" @@ -5224,11 +5965,10 @@ dependencies = [ [[package]] name = "num-bigint-dig" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" dependencies = [ - "byteorder", "lazy_static", "libm", "num-integer", @@ -5303,9 +6043,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ "hermit-abi", "libc", @@ -5329,7 +6069,7 @@ dependencies = [ "proc-macro-crate 1.1.3", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -5368,18 +6108,27 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dfe4c99deecc1a12b90d134256879897827028925bce89cea2f51469599dd91" dependencies = [ - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "object" -version = "0.36.7" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] @@ -5397,7 +6146,7 @@ dependencies = [ "futures", "httparse", "humantime", - "hyper 1.6.0", + "hyper 1.8.1", "itertools 0.13.0", "md-5", "parking_lot", @@ -5430,6 +6179,16 @@ name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "critical-section", + "portable-atomic", +] + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "opaque-debug" @@ -5439,11 +6198,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "cfg-if", "foreign-types", "libc", @@ -5460,7 +6219,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -5471,18 +6230,18 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.0+3.5.0" +version = "300.5.4+3.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -5518,7 +6277,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -5536,7 +6295,7 @@ dependencies = [ "ecdsa 0.16.9", "elliptic-curve 0.13.8", "primeorder", - "sha2 0.10.8", + "sha2 0.10.9", ] [[package]] @@ -5548,7 +6307,29 @@ dependencies = [ "ecdsa 0.16.9", "elliptic-curve 0.13.8", "primeorder", - "sha2 0.10.8", + "sha2 0.10.9", +] + +[[package]] +name = "packed_struct" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36b29691432cc9eff8b282278473b63df73bea49bc3ec5e67f31a3ae9c3ec190" +dependencies = [ + "bitvec 1.0.1", + "packed_struct_codegen", + "serde", +] + +[[package]] +name = "packed_struct_codegen" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd6706dfe50d53e0f6aa09e12c034c44faacd23e966ae5a209e8bdb8f179f98" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -5599,9 +6380,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -5609,15 +6390,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.11", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -5628,7 +6409,7 @@ checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" dependencies = [ "parse-display-derive", "regex", - "regex-syntax", + "regex-syntax 0.8.8", ] [[package]] @@ -5640,9 +6421,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "regex-syntax", + "regex-syntax 0.8.8", "structmeta", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -5651,17 +6432,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77144664f6aac5f629d7efa815f5098a054beeeca6ccafee5ec453fd2b0c53f9" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "ciborium", "coset", "data-encoding", - "getrandom 0.2.15", + "getrandom 0.2.16", "hmac", - "indexmap 2.9.0", + "indexmap 2.12.1", "rand 0.8.5", "serde", "serde_json", - "sha2 0.10.8", + "sha2 0.10.9", "strum 0.25.0", "typeshare", "zeroize", @@ -5712,12 +6493,12 @@ dependencies = [ [[package]] name = "pem" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ "base64 0.22.1", - "serde", + "serde_core", ] [[package]] @@ -5740,73 +6521,40 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pest" -version = "2.8.0" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" -dependencies = [ - "memchr", - "thiserror 2.0.12", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.100", -] +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] -name = "pest_meta" -version = "2.8.0" +name = "petgraph" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "once_cell", - "pest", - "sha2 0.10.8", + "fixedbitset 0.4.2", + "indexmap 2.12.1", ] [[package]] name = "petgraph" -version = "0.5.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ - "fixedbitset 0.2.0", - "indexmap 1.9.3", + "fixedbitset 0.5.7", + "indexmap 2.12.1", ] [[package]] name = "petgraph" -version = "0.7.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset 0.5.7", - "indexmap 2.9.0", + "hashbrown 0.15.5", + "indexmap 2.12.1", + "serde", ] [[package]] @@ -5839,7 +6587,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -5868,7 +6616,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -5911,7 +6659,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.9", + "der 0.7.10", "spki 0.7.3", ] @@ -5946,9 +6694,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -5968,6 +6716,15 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -5980,7 +6737,27 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.24", + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools 0.10.5", + "normalize-line-endings", + "predicates-core", + "regex", ] [[package]] @@ -6010,6 +6787,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "pretty_simple" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce5f33f1e15ede98d6bf380779f1d4cd90ca3f0943c0aab6637c60fcdd7d9024" +dependencies = [ + "insta", + "once_cell", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -6043,11 +6830,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.22.24", + "toml_edit 0.23.9", ] [[package]] @@ -6076,9 +6863,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -6101,7 +6888,7 @@ dependencies = [ [[package]] name = "prometheus-closure-metric" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "prometheus", @@ -6110,19 +6897,18 @@ dependencies = [ [[package]] name = "proptest" -version = "1.6.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.9.0", - "lazy_static", + "bit-set 0.8.0", + "bit-vec 0.8.0", + "bitflags 2.10.0", "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", - "rand_xorshift", - "regex-syntax", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift 0.4.0", + "regex-syntax 0.8.8", "rusty-fork", "tempfile", "unarray", @@ -6136,14 +6922,14 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "prost" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" dependencies = [ "bytes", "prost-derive", @@ -6151,22 +6937,22 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" dependencies = [ "anyhow", "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "prost-types" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" dependencies = [ "prost", ] @@ -6182,24 +6968,25 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.25" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58e5423e24c18cc840e1c98370b3993c6649cd1678b4d24318bcf0a083cbe88" +checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" dependencies = [ + "ar_archive_writer", "cc", ] [[package]] name = "quanta" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" dependencies = [ "crossbeam-utils", "libc", "once_cell", "raw-cpuid", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "web-sys", "winapi", ] @@ -6212,9 +6999,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quick-xml" -version = "0.37.4" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", "serde", @@ -6222,20 +7009,20 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.7" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bd15a6f2967aef83887dcb9fec0014580467e33720d073560cf015a5683012" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", - "cfg_aliases", + "cfg_aliases 0.2.1", "futures-io", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", "rustls", - "socket2", - "thiserror 2.0.12", + "socket2 0.6.1", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -6243,19 +7030,21 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.10" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.2", - "rand 0.9.0", + "fastbloom", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", "ring", "rustc-hash 2.1.1", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -6263,32 +7052,32 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.11" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ - "cfg_aliases", + "cfg_aliases 0.2.1", "libc", "once_cell", - "socket2", + "socket2 0.6.1", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radium" @@ -6302,6 +7091,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -6315,13 +7114,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.24", ] [[package]] @@ -6350,7 +7148,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -6359,7 +7157,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", ] [[package]] @@ -6371,6 +7169,15 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.3", +] + [[package]] name = "rand_xoshiro" version = "0.6.0" @@ -6382,18 +7189,18 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.5.0" +version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", ] [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -6401,9 +7208,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -6430,7 +7237,7 @@ checksum = "f2a62d85ed81ca5305dc544bd42c8804c5060b78ffa5ad3c64b0fb6a8c13d062" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -6444,11 +7251,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", ] [[package]] @@ -6457,40 +7264,40 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", "thiserror 1.0.69", ] [[package]] name = "redox_users" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -6508,32 +7315,38 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax", + "regex-syntax 0.8.8", ] [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.8", ] [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "relative-path" @@ -6543,9 +7356,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "b6eff9328d40131d43bd911d42d79eb6a47312002a4daefc9e37f17e74a7701a" dependencies = [ "base64 0.22.1", "bytes", @@ -6553,16 +7366,15 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.8", + "h2 0.4.12", "hickory-resolver", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-rustls", "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", "mime", @@ -6574,18 +7386,17 @@ dependencies = [ "quinn", "rustls", "rustls-native-certs", - "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls", "tokio-util", "tower 0.5.2", + "tower-http 0.6.8", "tower-service", "url", "wasm-bindgen", @@ -6593,7 +7404,6 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots", - "windows-registry", ] [[package]] @@ -6614,12 +7424,9 @@ dependencies = [ [[package]] name = "resolv-conf" -version = "0.7.1" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48375394603e3dd4b2d64371f7148fd8c7baa2680e28741f2cb8d23b59e3d4c4" -dependencies = [ - "hostname", -] +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "rfc6979" @@ -6650,7 +7457,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -6675,6 +7482,16 @@ dependencies = [ "byteorder", ] +[[package]] +name = "roaring" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f08d6a905edb32d74a5d5737a0c9d7e950c312f3c46cb0ca0a2ca09ea11878a0" +dependencies = [ + "bytemuck", + "byteorder", +] + [[package]] name = "route-recognizer" version = "0.3.1" @@ -6707,7 +7524,7 @@ dependencies = [ "pkcs1", "pkcs8 0.9.0", "rand_core 0.6.4", - "sha2 0.10.8", + "sha2 0.10.9", "signature 2.2.0", "subtle", "zeroize", @@ -6733,13 +7550,13 @@ checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" dependencies = [ "cfg-if", "glob", - "proc-macro-crate 3.3.0", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", "regex", "relative-path", "rustc_version", - "syn 2.0.100", + "syn 2.0.111", "unicode-ident", ] @@ -6755,9 +7572,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -6787,32 +7604,42 @@ dependencies = [ ] [[package]] -name = "rusticata-macros" -version = "4.1.0" +name = "rustc_version_runtime" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +checksum = "2dd18cd2bae1820af0b6ad5e54f4a51d0f3fcc53b05f845675074efcc7af071d" +dependencies = [ + "rustc_version", + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" dependencies = [ "nom", ] [[package]] name = "rustix" -version = "1.0.5" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.25" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "log", "once_cell", @@ -6825,14 +7652,14 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.2.0", + "security-framework 3.5.1", ] [[package]] @@ -6846,20 +7673,21 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" dependencies = [ "web-time", + "zeroize", ] [[package]] name = "rustls-platform-verifier" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5467026f437b4cb2a533865eaa73eb840019a0916f4b9ec563c6e617e086c9" +checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ - "core-foundation 0.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "jni", "log", @@ -6868,9 +7696,9 @@ dependencies = [ "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", - "security-framework 3.2.0", + "security-framework 3.5.1", "security-framework-sys", - "webpki-root-certs", + "webpki-root-certs 0.26.11", "windows-sys 0.59.0", ] @@ -6882,9 +7710,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", @@ -6893,15 +7721,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -6909,6 +7737,28 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "rustyline" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "unicode-segmentation", + "unicode-width 0.1.14", + "utf8parse", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.20" @@ -6935,11 +7785,53 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "schemafy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aea5ba40287dae331f2c48b64dbc8138541f5e97ee8793caa7948c1f31d86d5" +dependencies = [ + "Inflector", + "schemafy_core", + "schemafy_lib", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "syn 1.0.109", +] + +[[package]] +name = "schemafy_core" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41781ae092f4fd52c9287efb74456aea0d3b90032d2ecad272bd14dbbcb0511b" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "schemafy_lib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e953db32579999ca98c451d80801b6f6a7ecba6127196c5387ec0774c528befa" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "schemafy_core", + "serde", + "serde_derive", + "serde_json", + "syn 1.0.109", ] [[package]] @@ -6957,13 +7849,25 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.0-alpha.17" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ef2a6523400a2228db974a8ddc9e1d3deaa04f51bddd7832ef8d7e531bafcc" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" dependencies = [ "dyn-clone", "ref-cast", - "schemars_derive 1.0.0-alpha.17", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" +dependencies = [ + "dyn-clone", + "ref-cast", + "schemars_derive 1.1.0", "serde", "serde_json", ] @@ -6977,19 +7881,19 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "schemars_derive" -version = "1.0.0-alpha.17" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6d4e1945a3c9e58edaa708449b026519f7f4197185e1b5dbc689615c1ad724d" +checksum = "301858a4023d78debd2353c7426dc486001bddc91ae31a76fb1f55132f7e2633" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -7006,9 +7910,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdd" -version = "3.0.8" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" +checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" [[package]] name = "sec1" @@ -7030,7 +7934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.9", + "der 0.7.10", "generic-array", "pkcs8 0.10.2", "subtle", @@ -7050,9 +7954,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +checksum = "4473013577ec77b4ee3668179ef1186df3146e2cf2d927bd200974c6fe60fd99" dependencies = [ "cc", ] @@ -7073,7 +7977,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -7082,12 +7986,12 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.2.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.0", - "core-foundation 0.10.0", + "bitflags 2.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -7095,9 +7999,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -7105,16 +8009,17 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] @@ -7149,13 +8054,14 @@ dependencies = [ [[package]] name = "serde-reflection" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bef77b40d103fda6c10d29c21f5c78c980e8570e1a290a648a9ff5011f96e1" +checksum = "bfe23e63efbe7af1bc1859ead4a05014bdd5478be550762a40f6fcf91ad5473c" dependencies = [ "erased-discriminant", "once_cell", "serde", + "serde_json", "thiserror 1.0.69", "typeid", ] @@ -7173,22 +8079,32 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.17" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" dependencies = [ "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -7199,30 +8115,32 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.12.1", "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "serde_path_to_error" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" dependencies = [ "itoa", "serde", + "serde_core", ] [[package]] @@ -7233,14 +8151,14 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -7259,17 +8177,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", - "serde", - "serde_derive", + "indexmap 2.12.1", + "schemars 0.9.0", + "schemars 1.1.0", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -7277,14 +8196,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ - "darling 0.20.11", + "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -7321,7 +8240,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -7350,9 +8269,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -7372,7 +8291,7 @@ dependencies = [ [[package]] name = "shared-crypto" version = "0.0.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "bcs", "eyre", @@ -7389,9 +8308,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -7416,6 +8335,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "similar" version = "2.7.0" @@ -7446,12 +8371,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slip10_ed25519" @@ -7464,29 +8386,29 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "snafu" -version = "0.8.5" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +checksum = "6e84b3f4eacbf3a1ce05eac6763b4d629d60cbc94d632e4092c54ade71f1e1a2" dependencies = [ "snafu-derive", ] [[package]] name = "snafu-derive" -version = "0.8.5" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -7497,7 +8419,7 @@ checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "social-twitter" -version = "0.3.0" +version = "0.5.0" dependencies = [ "base64 0.13.1", "chrono", @@ -7506,23 +8428,33 @@ dependencies = [ "nexus-toolkit", "oauth1-request", "reqwest", - "schemars 1.0.0-alpha.17", + "schemars 1.1.0", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", ] [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + [[package]] name = "soketto" version = "0.8.1" @@ -7532,7 +8464,7 @@ dependencies = [ "base64 0.22.1", "bytes", "futures", - "http 1.3.1", + "http 1.4.0", "httparse", "log", "rand 0.8.5", @@ -7571,20 +8503,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.9", + "der 0.7.10", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.20" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" +checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" dependencies = [ "cc", "cfg-if", @@ -7593,12 +8525,114 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "starlark" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f53849859f05d9db705b221bd92eede93877fd426c1b4a3c3061403a5912a8f" +dependencies = [ + "allocative", + "anyhow", + "bumpalo", + "cmp_any", + "debugserver-types", + "derivative", + "derive_more 1.0.0", + "display_container", + "dupe", + "either", + "erased-serde", + "hashbrown 0.14.5", + "inventory", + "itertools 0.13.0", + "maplit", + "memoffset", + "num-bigint 0.4.6", + "num-traits", + "once_cell", + "paste", + "ref-cast", + "regex", + "rustyline", + "serde", + "serde_json", + "starlark_derive", + "starlark_map", + "starlark_syntax", + "static_assertions", + "strsim 0.10.0", + "textwrap", + "thiserror 1.0.69", +] + +[[package]] +name = "starlark_derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe58bc6c8b7980a1fe4c9f8f48200c3212db42ebfe21ae6a0336385ab53f082a" +dependencies = [ + "dupe", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "starlark_map" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92659970f120df0cc1c0bb220b33587b7a9a90e80d4eecc5c5af5debb950173d" +dependencies = [ + "allocative", + "dupe", + "equivalent", + "fxhash", + "hashbrown 0.14.5", + "serde", +] + +[[package]] +name = "starlark_syntax" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe53b3690d776aafd7cb6b9fed62d94f83280e3b87d88e3719cc0024638461b3" +dependencies = [ + "allocative", + "annotate-snippets", + "anyhow", + "derivative", + "derive_more 1.0.0", + "dupe", + "lalrpop", + "lalrpop-util", + "logos", + "lsp-types 0.94.1", + "memchr", + "num-bigint 0.4.6", + "num-traits", + "once_cell", + "starlark_map", + "thiserror 1.0.69", +] + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + [[package]] name = "strsim" version = "0.10.0" @@ -7620,7 +8654,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -7631,16 +8665,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", -] - -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" -dependencies = [ - "strum_macros 0.24.3", + "syn 2.0.111", ] [[package]] @@ -7654,21 +8679,11 @@ dependencies = [ [[package]] name = "strum" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" - -[[package]] -name = "strum_macros" -version = "0.24.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 1.0.109", + "strum_macros 0.27.2", ] [[package]] @@ -7681,20 +8696,19 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "strum_macros" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "rustversion", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -7712,7 +8726,7 @@ checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" [[package]] name = "sui-config" version = "0.0.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anemo", "anyhow", @@ -7731,19 +8745,37 @@ dependencies = [ "rand 0.8.5", "reqwest", "serde", + "serde_json", "serde_with", "serde_yaml", + "starlark", "sui-keys", "sui-protocol-config", - "sui-rpc-api", "sui-types", + "thiserror 1.0.69", "tracing", ] +[[package]] +name = "sui-crypto" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7129cfd679e3f2c0e6a163c706c926d14900fd2e05d29529175e34644850b4d5" +dependencies = [ + "ed25519-dalek", + "k256 0.13.4", + "p256", + "pem-rfc7468 0.7.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "signature 2.2.0", + "sui-sdk-types 0.1.0", +] + [[package]] name = "sui-enum-compat-util" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "serde_yaml", ] @@ -7751,7 +8783,7 @@ dependencies = [ [[package]] name = "sui-framework" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "bcs", "move-binary-format", @@ -7764,8 +8796,8 @@ dependencies = [ [[package]] name = "sui-framework-snapshot" -version = "1.45.3" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +version = "1.61.2" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "bcs", @@ -7780,27 +8812,27 @@ dependencies = [ [[package]] name = "sui-http" version = "0.0.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-util", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tokio-rustls", "tokio-util", - "tower 0.4.13", + "tower 0.5.2", "tracing", ] [[package]] name = "sui-json" version = "0.0.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "bcs", @@ -7817,7 +8849,7 @@ dependencies = [ [[package]] name = "sui-json-rpc-api" version = "0.0.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "fastcrypto", @@ -7837,7 +8869,7 @@ dependencies = [ [[package]] name = "sui-json-rpc-types" version = "0.0.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "bcs", @@ -7848,6 +8880,7 @@ dependencies = [ "json_to_table", "move-binary-format", "move-bytecode-utils", + "move-command-line-common", "move-core-types", "move-disassembler", "move-ir-types", @@ -7869,11 +8902,17 @@ dependencies = [ [[package]] name = "sui-keys" version = "0.0.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", + "async-trait", + "base64 0.21.7", + "bcs", "bip32", + "colored 2.2.0", "fastcrypto", + "jsonrpc", + "mockall 0.11.4", "rand 0.8.5", "regex", "serde", @@ -7883,12 +8922,13 @@ dependencies = [ "slip10_ed25519", "sui-types", "tiny-bip39", + "tokio", ] [[package]] name = "sui-macros" version = "0.7.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "futures", "once_cell", @@ -7898,8 +8938,8 @@ dependencies = [ [[package]] name = "sui-move-build" -version = "1.45.3" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +version = "1.61.2" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "fastcrypto", @@ -7912,6 +8952,7 @@ dependencies = [ "move-ir-types", "move-package", "move-symbol-pool", + "mysten-common", "serde-reflection", "sui-package-management", "sui-protocol-config", @@ -7922,8 +8963,8 @@ dependencies = [ [[package]] name = "sui-open-rpc" -version = "1.45.3" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +version = "1.61.2" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "bcs", "schemars 0.8.22", @@ -7935,7 +8976,7 @@ dependencies = [ [[package]] name = "sui-open-rpc-macros" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "derive-syn-parse", "itertools 0.13.0", @@ -7947,8 +8988,8 @@ dependencies = [ [[package]] name = "sui-package-management" -version = "1.45.3" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +version = "1.61.2" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "move-core-types", @@ -7966,7 +9007,7 @@ dependencies = [ [[package]] name = "sui-package-resolver" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "async-trait", "bcs", @@ -7976,7 +9017,6 @@ dependencies = [ "move-command-line-common", "move-core-types", "serde", - "sui-rpc-api", "sui-types", "thiserror 1.0.69", "tokio", @@ -7985,22 +9025,24 @@ dependencies = [ [[package]] name = "sui-proc-macros" version = "0.7.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "msim-macros", "proc-macro2", "quote", "sui-enum-compat-util", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "sui-protocol-config" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "clap", - "insta", + "fastcrypto", + "move-binary-format", + "move-core-types", "move-vm-config", "schemars 0.8.22", "serde", @@ -8013,7 +9055,7 @@ dependencies = [ [[package]] name = "sui-protocol-config-macros" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "proc-macro2", "quote", @@ -8021,52 +9063,52 @@ dependencies = [ ] [[package]] -name = "sui-rpc-api" +name = "sui-rpc" +version = "0.0.8" +source = "git+https://github.com/MystenLabs/sui-rust-sdk.git?rev=fb62af78b30f5dc64eeaec0094ab95b5ce5b7ce2#fb62af78b30f5dc64eeaec0094ab95b5ce5b7ce2" +dependencies = [ + "base64 0.22.1", + "bcs", + "bytes", + "futures", + "http 1.4.0", + "prost", + "prost-types", + "serde", + "serde_json", + "sui-sdk-types 0.0.8", + "tap", + "tokio", + "tonic", + "tonic-prost", +] + +[[package]] +name = "sui-rpc" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a931ef96b890a118f12b6fa1dda727b8b6df82af3bdb5568c5257078a54c5ba8" dependencies = [ - "anyhow", - "async-stream", - "async-trait", - "axum", - "base64 0.21.7", + "base64 0.22.1", "bcs", "bytes", - "fastcrypto", - "http 1.3.1", - "itertools 0.13.0", - "mime", - "move-binary-format", - "move-core-types", - "mysten-network", - "prometheus", + "futures", + "http 1.4.0", "prost", "prost-types", - "rand 0.8.5", - "roaring", "serde", "serde_json", - "serde_with", - "sui-protocol-config", - "sui-sdk-types", - "sui-transaction-builder 0.1.0", - "sui-types", + "sui-sdk-types 0.1.0", "tap", - "thiserror 1.0.69", "tokio", - "tokio-stream", "tonic", - "tonic-health", - "tonic-reflection", - "tower 0.4.13", - "tracing", - "url", + "tonic-prost", ] [[package]] name = "sui-sdk" -version = "1.45.3" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +version = "1.61.2" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "async-trait", @@ -8098,16 +9140,18 @@ dependencies = [ [[package]] name = "sui-sdk-types" -version = "0.0.2" -source = "git+https://github.com/MystenLabs/sui-rust-sdk.git?rev=5e11579031793f086178332219f5847ec94da0c4#5e11579031793f086178332219f5847ec94da0c4" +version = "0.0.8" +source = "git+https://github.com/MystenLabs/sui-rust-sdk.git?rev=fb62af78b30f5dc64eeaec0094ab95b5ce5b7ce2#fb62af78b30f5dc64eeaec0094ab95b5ce5b7ce2" dependencies = [ "base64ct", "bcs", "blake2", "bnum", "bs58 0.5.1", - "hex", - "roaring", + "bytes", + "bytestring", + "itertools 0.14.0", + "roaring 0.11.2", "serde", "serde_derive", "serde_json", @@ -8116,31 +9160,32 @@ dependencies = [ ] [[package]] -name = "sui-tls" -version = "0.0.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" -dependencies = [ - "anyhow", - "arc-swap", - "axum", - "axum-server", - "ed25519", - "fastcrypto", - "pkcs8 0.10.2", - "rcgen", - "reqwest", - "rustls", - "rustls-webpki", - "tokio", - "tokio-rustls", - "tower-layer", - "x509-parser", +name = "sui-sdk-types" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9497600faf5a1fb07a2db85902515867ad30474abf959de1a7d1fa473c4a28" +dependencies = [ + "base64ct", + "bcs", + "blake2", + "bnum", + "bs58 0.5.1", + "bytes", + "bytestring", + "itertools 0.14.0", + "rand_core 0.6.4", + "roaring 0.11.2", + "serde", + "serde_derive", + "serde_json", + "serde_with", + "winnow", ] [[package]] name = "sui-transaction-builder" version = "0.0.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anyhow", "async-trait", @@ -8157,32 +9202,36 @@ dependencies = [ [[package]] name = "sui-transaction-builder" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui-rust-sdk.git?rev=5e11579031793f086178332219f5847ec94da0c4#5e11579031793f086178332219f5847ec94da0c4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2fbae254130adbcfc9a29ceaecc4b278e85358a60c977c83c47b8eab7b3180" dependencies = [ "base64ct", "bcs", "serde", "serde_json", "serde_with", - "sui-sdk-types", - "thiserror 2.0.12", + "sui-sdk-types 0.1.0", + "thiserror 2.0.17", ] [[package]] name = "sui-types" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "anemo", "anyhow", "async-trait", + "base64 0.21.7", "bcs", "better_any", "bincode", "byteorder", + "bytes", "chrono", "ciborium", "consensus-config", + "consensus-types", "derive_more 1.0.0", "enum_dispatch", "eyre", @@ -8190,14 +9239,16 @@ dependencies = [ "fastcrypto-tbls", "fastcrypto-zkp", "im", - "indexmap 2.9.0", + "indexmap 2.12.1", "itertools 0.13.0", "lru 0.10.1", "move-binary-format", "move-bytecode-utils", "move-core-types", + "move-trace-format", "move-vm-profiler", "move-vm-test-utils", + "mysten-common", "mysten-metrics", "mysten-network", "nonempty", @@ -8211,8 +9262,10 @@ dependencies = [ "prometheus", "proptest", "proptest-derive", + "prost", + "prost-types", "rand 0.8.5", - "roaring", + "roaring 0.10.12", "rustls-pemfile", "schemars 0.8.22", "serde", @@ -8222,12 +9275,13 @@ dependencies = [ "shared-crypto", "signature 1.6.4", "static_assertions", - "strum 0.24.1", - "strum_macros 0.24.3", + "strum 0.27.2", + "strum_macros 0.27.2", "sui-enum-compat-util", "sui-macros", "sui-protocol-config", - "sui-sdk-types", + "sui-rpc 0.0.8", + "sui-sdk-types 0.0.8", "tap", "thiserror 1.0.69", "tonic", @@ -8239,12 +9293,12 @@ dependencies = [ [[package]] name = "sui-verifier-latest" version = "0.1.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ - "move-abstract-interpreter", "move-abstract-stack", "move-binary-format", "move-bytecode-utils", + "move-bytecode-verifier", "move-bytecode-verifier-meter", "move-core-types", "move-vm-config", @@ -8265,9 +9319,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -8297,13 +9351,13 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -8312,7 +9366,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -8351,6 +9405,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tap" version = "1.0.1" @@ -8359,30 +9419,41 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "templating-jinja" -version = "0.3.0" +version = "0.5.0" dependencies = [ "minijinja", "nexus-sdk", "nexus-toolkit", - "schemars 1.0.0-alpha.17", + "schemars 1.1.0", "serde", "serde_json", "tokio", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -8394,12 +9465,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -8430,7 +9501,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-tar", @@ -8441,12 +9512,21 @@ dependencies = [ [[package]] name = "testcontainers-modules" version = "0.11.6" -source = "git+https://github.com/Talus-Network/testcontainers-rs-modules-community?branch=feat%2Fadd-sui-support#2a59cac5f48ff020d02d4a1d6fada8100e960690" +source = "git+https://github.com/Talus-Network/testcontainers-rs-modules-community?branch=feat%2Fadd-sui-support#d49fb6d4bd73abb8fb768cd1ceefc303d4d84e1a" dependencies = [ "reqwest", "testcontainers", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width 0.1.14", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -8458,11 +9538,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -8473,18 +9553,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -8498,9 +9578,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -8513,15 +9593,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -8539,18 +9619,27 @@ dependencies = [ "pbkdf2", "rand 0.8.5", "rustc-hash 1.1.0", - "sha2 0.10.8", + "sha2 0.10.9", "thiserror 1.0.69", "unicode-normalization", "wasm-bindgen", "zeroize", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -8558,9 +9647,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -8573,31 +9662,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.44.2" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -8623,9 +9711,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -8672,21 +9760,21 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.24.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.24.0", + "tungstenite 0.28.0", ] [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -8707,77 +9795,100 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", - "toml_datetime", - "toml_edit 0.22.24", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] -name = "toml_edit" -version = "0.14.4" +name = "toml_datetime" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ - "combine", - "indexmap 1.9.3", - "itertools 0.10.5", - "serde", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.12.1", "serde", "serde_spanned", - "toml_datetime", + "toml_datetime 0.6.11", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.23.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" +dependencies = [ + "indexmap 2.12.1", + "toml_datetime 0.7.3", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tonic" -version = "0.12.3" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203" dependencies = [ - "async-stream", "async-trait", "axum", "base64 0.22.1", "bytes", - "h2 0.4.8", - "http 1.3.1", + "h2 0.4.12", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project", - "prost", - "rustls-pemfile", - "socket2", + "socket2 0.6.1", + "sync_wrapper", "tokio", "tokio-rustls", "tokio-stream", - "tower 0.4.13", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -8787,27 +9898,25 @@ dependencies = [ [[package]] name = "tonic-health" -version = "0.12.3" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eaf34ddb812120f5c601162d5429933c9b527d901ab0e7f930d3147e33a09b2" +checksum = "2a82868bf299e0a1d2e8dce0dc33a46c02d6f045b2c1f1d6cc8dc3d0bf1812ef" dependencies = [ - "async-stream", "prost", "tokio", "tokio-stream", "tonic", + "tonic-prost", ] [[package]] -name = "tonic-reflection" -version = "0.12.3" +name = "tonic-prost" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "878d81f52e7fcfd80026b7fdb6a9b578b3c3653ba987f87f0dce4b64043cba27" +checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" dependencies = [ + "bytes", "prost", - "prost-types", - "tokio", - "tokio-stream", "tonic", ] @@ -8840,11 +9949,16 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", + "hdrhistogram", + "indexmap 2.12.1", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -8855,11 +9969,11 @@ checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "async-compression", "base64 0.21.7", - "bitflags 2.9.0", + "bitflags 2.10.0", "bytes", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "http-range-header", @@ -8878,6 +9992,24 @@ dependencies = [ "uuid", ] +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "iri-string", + "pin-project-lite", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -8892,9 +10024,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -8904,20 +10036,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", ] @@ -8954,7 +10086,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.3.1", + "http 1.4.0", "httparse", "log", "rand 0.8.5", @@ -8966,26 +10098,25 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.24.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ - "byteorder", "bytes", "data-encoding", - "http 1.3.1", + "http 1.4.0", "httparse", "log", - "rand 0.8.5", + "rand 0.9.2", "sha1", - "thiserror 1.0.69", + "thiserror 2.0.17", "utf-8", ] [[package]] name = "typed-store-error" version = "0.4.0" -source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.45.3#643cae13d71392a164f427024d899ea49fc97a23" +source = "git+https://github.com/MystenLabs/sui?tag=mainnet-v1.61.2#6c229fe6c04fb21ec98751c798e57e9a00e028e2" dependencies = [ "serde", "thiserror 1.0.69", @@ -8999,9 +10130,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "typeshare" @@ -9022,15 +10153,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a615d6c2764852a2e88a4f16e9ce1ea49bb776b5872956309e170d63a042a34f" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.111", ] -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - [[package]] name = "uint" version = "0.9.5" @@ -9063,15 +10188,15 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] @@ -9090,9 +10215,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -9124,9 +10249,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -9140,12 +10265,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -9160,12 +10279,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ - "getrandom 0.3.2", - "rand 0.9.0", + "getrandom 0.3.4", + "js-sys", + "rand 0.9.2", + "wasm-bindgen", ] [[package]] @@ -9181,12 +10302,13 @@ dependencies = [ [[package]] name = "variant_count" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae2faf80ac463422992abf4de234731279c058aaf33171ca70277c98406b124" +checksum = "a1935e10c6f04d22688d07c0790f2fc0e1b1c5c2c55bc0cc87ed67656e587dd8" dependencies = [ + "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.111", ] [[package]] @@ -9244,7 +10366,7 @@ dependencies = [ [[package]] name = "walrus" -version = "0.3.0" +version = "0.5.0" dependencies = [ "anyhow", "jsonschema", @@ -9252,10 +10374,10 @@ dependencies = [ "nexus-sdk", "nexus-toolkit", "reqwest", - "schemars 1.0.0-alpha.17", + "schemars 1.1.0", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", ] @@ -9299,17 +10421,17 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -9320,35 +10442,22 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.100", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -9359,9 +10468,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9369,22 +10478,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.100", - "wasm-bindgen-backend", + "syn 2.0.111", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -9404,9 +10513,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -9424,29 +10533,38 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "0.26.8" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09aed61f5e8d2c18344b3faa33a4c837855fe56642757754775548fee21386c4" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.4", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" dependencies = [ "rustls-pki-types", ] [[package]] name = "webpki-roots" -version = "0.26.8" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] [[package]] name = "whoami" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" dependencies = [ - "redox_syscall 0.5.11", + "libredox", "wasite", "web-sys", ] @@ -9459,9 +10577,9 @@ checksum = "17882f045410753661207383517a6f62ec3dbeb6a4ed2acce01f0728238d1983" [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi" @@ -9481,11 +10599,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -9496,79 +10614,70 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.61.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.0", + "windows-strings", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-registry" -version = "0.4.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ + "windows-link", "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-strings", ] [[package]] name = "windows-result" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ "windows-link", ] @@ -9609,6 +10718,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -9657,18 +10784,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -9691,9 +10819,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -9715,9 +10843,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -9739,9 +10867,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -9751,9 +10879,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -9775,9 +10903,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -9799,9 +10927,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -9823,9 +10951,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -9847,15 +10975,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.6" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -9871,25 +10999,16 @@ dependencies = [ ] [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.0", -] - -[[package]] -name = "write16" -version = "1.0.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wyz" @@ -9932,15 +11051,15 @@ dependencies = [ "oid-registry", "ring", "rusticata-macros", - "thiserror 2.0.12", + "thiserror 2.0.17", "time", ] [[package]] name = "xattr" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", "rustix", @@ -9953,11 +11072,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2460c9a9c9d1331ff6801e87badb517faa6b6758e5fb585eb27daf7622c6d5ad" dependencies = [ "curve25519-dalek", - "derive_more 0.99.19", + "derive_more 0.99.20", "ed25519", "ed25519-dalek", "rand 0.8.5", - "sha2 0.10.8", + "sha2 0.10.9", "x25519-dalek", "zeroize", ] @@ -9982,11 +11101,10 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -9994,54 +11112,34 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", - "synstructure 0.13.1", -] - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "zerocopy-derive 0.7.35", + "syn 2.0.111", + "synstructure 0.13.2", ] [[package]] name = "zerocopy" -version = "0.8.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" -dependencies = [ - "zerocopy-derive 0.8.24", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", + "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -10061,15 +11159,15 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", - "synstructure 0.13.1", + "syn 2.0.111", + "synstructure 0.13.2", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -10082,14 +11180,25 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", ] [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -10098,13 +11207,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.111", ] [[package]] @@ -10127,9 +11236,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 33b6827d..84f3db3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = ["toolkit-rust", "cli", "sdk-wasm", "sdk", "tools/*"] [workspace.package] edition = "2021" -version = "0.3.0" +version = "0.5.0" repository = "https://github.com/Talus-Network/nexus-sdk" homepage = "https://talus.network" license = "Apache-2.0" @@ -23,17 +23,20 @@ aes-siv = "0.7" anyhow = "1.0.97" argon2 = "0.5" base64 = "0.21" +bcs = "0.1.6" bip32 = "0.4.0" bincode = "1" chrono = { version = "0.4", features = ["serde"] } clap = { version = "4.5.32", features = ["derive"] } clap_complete = "4.5.48" +clap-verbosity = "2.1.0" colored = "3.0.0" convert_case = "0.7.1" directories = "5" enum_dispatch = "0.3.13" env_logger = "0.11.7" futures = "0.3.31" +graphql_client = "0.14" hex = "0.4" home = "0.5.11" indicatif = "0.17.11" @@ -42,29 +45,32 @@ keyring = "3.6" lazy-regex = "3.4.1" log = "^0.4.26" minijinja = "2.8.0" +mockall = "0.14.0" mockito = "1.7.0" openssl = { version = "0.10", features = ["vendored"] } petgraph = "0.7.1" portpicker = "0.1.1" +prost-types = "0.14.1" rand = { version = "0.8", default-features = false, features = ["std"] } regex = "1.11.1" -reqwest = "0.12.14" +reqwest = { version = "0.12.14", features = ["json", "stream"] } rpassword = "7.4.0" rstest = "0.25.0" # Keep an eye for the full release here. We need v1 for draft2020-12 schema. schemars = "1.0.0-alpha.17" -serde = { version = "1.0.219", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } serde-big-array = "0.5.1" -serde_json = "1.0.140" +serde_json = "1.0" serde_path_to_error = "0.1.17" serial_test = "3.2.0" strum = "0.27" strum_macros = "0.27" tempfile = "3.19.0" thiserror = "2.0.12" -tokio = { version = "1.44.1", features = ["full"] } +tokio = { version = "1.48.0", features = ["full"] } tokio-retry = "0.3.0" toml = "0.8.20" +tonic = "0.14" warp = "0.3.7" zeroize = "1.7" diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 00000000..91c03ceb --- /dev/null +++ b/Cross.toml @@ -0,0 +1,5 @@ +[target.x86_64-unknown-linux-gnu] +pre-build = [ + "dpkg --add-architecture $CROSS_DEB_ARCH", + "apt-get update && apt-get install -y pkg-config libssl-dev:$CROSS_DEB_ARCH libdbus-1-dev:$CROSS_DEB_ARCH", +] diff --git a/README.md b/README.md index fe9ed105..73a4f667 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Nexus SDK [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) +[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/Talus-Network/nexus-sdk/blob/main/LICENSE) +[![Actions](https://img.shields.io/badge/GitHub_Actions-Active-brightgreen)](https://github.com/Talus-Network/nexus-sdk/actions) [![codecov](https://codecov.io/gh/Talus-Network/nexus-sdk/graph/badge.svg?token=Q9I01BXJSE)](https://codecov.io/gh/Talus-Network/nexus-sdk) The **Nexus SDK** is a collection of tools that simplifies building with **Nexus**, the Agentic Workflow Engine. Developers can quickly create [Talus agents][talus-agents] or [Talus tools][talus-tools]. @@ -50,7 +52,7 @@ To install directly from the source using `cargo`, run: ```bash cargo install nexus-cli \ --git https://github.com/talus-network/nexus-sdk \ - --tag v0.3.0 \ + --tag v0.5.0 \ --locked ``` @@ -65,11 +67,15 @@ Nexus CLI Usage: nexus [OPTIONS] Commands: - tool Manage Nexus Tools - conf Manage Nexus Configuration - dag Validate, publish and execute Nexus DAGs - network Mange Nexus networks and leader caps - help Print this message or the help of the given subcommand(s) + tool Manage Nexus Tools + conf Manage Nexus Configuration + dag Validate, publish and execute Nexus DAGs + scheduler Manage scheduler tasks and occurrences + gas Manage Nexus gas budgets and tickets + network Manage Nexus networks and leader caps + crypto Manage Nexus crypto + completion Provide shell completions + help Print this message or the help of the given subcommand(s) Options: --json Change the output format to JSON diff --git a/cli/Cargo.toml b/cli/Cargo.toml index c79bdd45..58ab1154 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -18,11 +18,14 @@ path = "src/main.rs" [dependencies] anyhow.workspace = true +base64.workspace = true chrono.workspace = true clap.workspace = true clap_complete.workspace = true +clap-verbosity.workspace = true colored.workspace = true convert_case.workspace = true +env_logger.workspace = true home.workspace = true indicatif.workspace = true minijinja.workspace = true @@ -58,7 +61,10 @@ keyring = { workspace = true, features = ["windows-native"] } [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))'.dependencies] # `-sync-persistent` requires `apt-get install -y libdbus-1-dev` in CI -keyring = { workspace = true, features = ["linux-native-sync-persistent"] } +keyring = { workspace = true, features = [ + "linux-native-sync-persistent", + "vendored", +] } # === Nexus deps === [dependencies.nexus-sdk] diff --git a/cli/README.md b/cli/README.md index b6d6cc28..7e646a42 100644 --- a/cli/README.md +++ b/cli/README.md @@ -36,7 +36,7 @@ To install directly from the source using `cargo`, run: ```bash cargo install nexus-cli \ --git https://github.com/talus-network/nexus-sdk \ - --tag v0.3.0 \ + --tag v0.5.0 \ --locked ``` @@ -51,11 +51,15 @@ Nexus CLI Usage: nexus [OPTIONS] Commands: - tool Manage Nexus Tools - conf Manage Nexus Configuration - dag Validate, publish and execute Nexus DAGs - network Mange Nexus networks and leader caps - help Print this message or the help of the given subcommand(s) + tool Manage Nexus Tools + conf Manage Nexus Configuration + dag Validate, publish and execute Nexus DAGs + scheduler Manage scheduler tasks and occurrences + gas Manage Nexus gas budgets and tickets + network Manage Nexus networks and leader caps + crypto Manage Nexus crypto + completion Provide shell completions + help Print this message or the help of the given subcommand(s) Options: --json Change the output format to JSON diff --git a/cli/src/cli_conf.rs b/cli/src/cli_conf.rs index 9027fdf4..53c9f9fb 100644 --- a/cli/src/cli_conf.rs +++ b/cli/src/cli_conf.rs @@ -1,6 +1,6 @@ use { crate::prelude::*, - nexus_sdk::types::{StorageConf, StorageKind}, + nexus_sdk::types::{SecretValue, StorageConf, StorageKind}, std::sync::Arc, tokio::sync::Mutex, }; @@ -46,24 +46,15 @@ impl CliConf { } } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] pub(crate) struct SuiConf { + /// Sui private key base64 encoded bytes. #[serde(default)] - pub(crate) net: SuiNet, - #[serde(default = "default_sui_wallet_path")] - pub(crate) wallet_path: PathBuf, + pub(crate) pk: Option, #[serde(default)] pub(crate) rpc_url: Option, -} - -impl Default for SuiConf { - fn default() -> Self { - Self { - net: SuiNet::Localnet, - wallet_path: default_sui_wallet_path(), - rpc_url: None, - } - } + #[serde(default)] + pub(crate) gql_url: Option, } /// Remote data storage configuration. @@ -79,12 +70,12 @@ pub(crate) struct DataStorageConf { pub(crate) preferred_remote_storage: Option, } -impl Into for DataStorageConf { - fn into(self) -> StorageConf { +impl From for StorageConf { + fn from(val: DataStorageConf) -> StorageConf { StorageConf { - walrus_aggregator_url: self.walrus_aggregator_url.map(|url| url.to_string()), - walrus_publisher_url: self.walrus_publisher_url.map(|url| url.to_string()), - walrus_save_for_epochs: self.walrus_save_for_epochs, + walrus_aggregator_url: val.walrus_aggregator_url.map(|url| url.to_string()), + walrus_publisher_url: val.walrus_publisher_url.map(|url| url.to_string()), + walrus_save_for_epochs: val.walrus_save_for_epochs, } } } @@ -124,9 +115,9 @@ impl CryptoConf { let conf_path = path.unwrap_or(&default_path); let crypto_conf = CryptoConf::load_from_path(conf_path).await?; - Ok(crypto_conf + crypto_conf .identity_key - .ok_or_else(|| anyhow!("No identity key found"))?) + .ok_or_else(|| anyhow!("No identity key found")) } /// Update the identity key in the configuration. @@ -220,27 +211,33 @@ impl CryptoConf { } } -// == Used by serde == - -fn default_sui_wallet_path() -> PathBuf { - let config_dir = sui::config_dir().expect("Unable to determine SUI config directory"); - config_dir.join(sui::CLIENT_CONFIG) -} - #[cfg(test)] +#[allow(clippy::single_component_path_imports)] mod tests { - use {super::*, nexus_sdk::crypto::x3dh::PreKeyBundle, serial_test::serial, tempfile}; + use {super::*, nexus_sdk::crypto::x3dh::PreKeyBundle, serial_test::serial, std::fs}; fn setup_env() -> tempfile::TempDir { std::env::set_var("NEXUS_CLI_STORE_PASSPHRASE", "test_passphrase"); let secret_home = tempfile::tempdir().unwrap(); - std::env::set_var("XDG_CONFIG_HOME", secret_home.path()); - std::env::set_var("XDG_DATA_HOME", secret_home.path()); + + // Use dedicated sub-directories to avoid interfering with the caller's real home. + let home_dir = secret_home.path().join("home"); + let xdg_config_dir = secret_home.path().join("xdg_config"); + let xdg_data_dir = secret_home.path().join("xdg_data"); + + fs::create_dir_all(&home_dir).unwrap(); + fs::create_dir_all(&xdg_config_dir).unwrap(); + fs::create_dir_all(&xdg_data_dir).unwrap(); + + std::env::set_var("HOME", &home_dir); + std::env::set_var("XDG_CONFIG_HOME", &xdg_config_dir); + std::env::set_var("XDG_DATA_HOME", &xdg_data_dir); secret_home } fn cleanup_env() { std::env::remove_var("NEXUS_CLI_STORE_PASSPHRASE"); + std::env::remove_var("HOME"); std::env::remove_var("XDG_CONFIG_HOME"); std::env::remove_var("XDG_DATA_HOME"); } diff --git a/cli/src/completion/mod.rs b/cli/src/completion/mod.rs index 63c02d82..e47dd782 100644 --- a/cli/src/completion/mod.rs +++ b/cli/src/completion/mod.rs @@ -1,6 +1,6 @@ use { crate::{prelude::*, Cli}, - std::io::Write, + std::io::{self, Write}, }; #[derive(Args)] @@ -10,6 +10,13 @@ pub(crate) struct CompletionCommand { } pub(crate) fn handle(command: CompletionCommand) -> AnyResult<(), NexusCliError> { + handle_with_writer(command, &mut io::stdout()) +} + +fn handle_with_writer( + command: CompletionCommand, + writer: &mut dyn Write, +) -> AnyResult<(), NexusCliError> { let mut cli_command = Cli::command(); let bin_name = env!("CARGO_CRATE_NAME").to_string(); @@ -18,7 +25,7 @@ pub(crate) fn handle(command: CompletionCommand) -> AnyResult<(), NexusCliError> clap_complete::generate(command.shell, &mut cli_command, bin_name, &mut buffer); // Best-effort write to stdout; ignore EPIPE/BrokenPipe to avoid crashing when the reader closes early. - let _ = std::io::stdout().write_all(&buffer); + let _ = writer.write_all(&buffer); Ok(()) } @@ -38,7 +45,8 @@ mod tests { let cli = Cli::parse_from(&args); match cli.command { Command::Completion(cc) => { - handle(cc).unwrap(); + let mut sink = std::io::sink(); + handle_with_writer(cc, &mut sink).unwrap(); } _ => unreachable!("This should have been a completion command!"), } diff --git a/cli/src/conf/conf_get.rs b/cli/src/conf/conf_get.rs index 282bcb54..35bdb84d 100644 --- a/cli/src/conf/conf_get.rs +++ b/cli/src/conf/conf_get.rs @@ -4,9 +4,8 @@ use crate::{command_title, prelude::*}; pub(crate) async fn get_nexus_conf(conf_path: PathBuf) -> AnyResult { let conf = CliConf::load_from_path(&conf_path).await.map_err(|e| { NexusCliError::Any(anyhow!( - "Failed to load Nexus CLI configuration from {}: {}", + "Failed to load Nexus CLI configuration from {}: {e}", conf_path.display(), - e )) })?; @@ -21,16 +20,17 @@ mod tests { #[tokio::test] async fn test_get_nexus_conf() { - let tempdir = tempfile::tempdir().unwrap().into_path(); + let mut rng = rand::thread_rng(); + let tempdir = tempfile::tempdir().unwrap().keep(); let path = tempdir.join("conf.toml"); assert!(!tokio::fs::try_exists(&path).await.unwrap()); let nexus_objects = NexusObjects { - workflow_pkg_id: sui::ObjectID::random(), - primitives_pkg_id: sui::ObjectID::random(), - interface_pkg_id: sui::ObjectID::random(), - network_id: sui::ObjectID::random(), + workflow_pkg_id: sui::types::Address::generate(&mut rng), + primitives_pkg_id: sui::types::Address::generate(&mut rng), + interface_pkg_id: sui::types::Address::generate(&mut rng), + network_id: sui::types::Address::generate(&mut rng), tool_registry: sui_mocks::mock_sui_object_ref(), default_tap: sui_mocks::mock_sui_object_ref(), gas_service: sui_mocks::mock_sui_object_ref(), @@ -38,9 +38,9 @@ mod tests { }; let sui_conf = SuiConf { - net: SuiNet::Mainnet, - wallet_path: tempdir.join("wallet"), + pk: Some("123".to_string().into()), rpc_url: Some(reqwest::Url::parse("https://mainnet.sui.io").unwrap()), + gql_url: Some(reqwest::Url::parse("https://mainnet.sui.io/graphql").unwrap()), }; let tools = HashMap::new(); diff --git a/cli/src/conf/conf_set.rs b/cli/src/conf/conf_set.rs index 524b3ed8..0cc4d038 100644 --- a/cli/src/conf/conf_set.rs +++ b/cli/src/conf/conf_set.rs @@ -1,16 +1,17 @@ use { - crate::{command_title, display::json_output, loading, prelude::*, sui::resolve_wallet_path}, + crate::{command_title, display::json_output, loading, prelude::*}, nexus_sdk::{ - types::StorageKind, + types::{SecretValue, StorageKind}, walrus::{WALRUS_AGGREGATOR_URL, WALRUS_PUBLISHER_URL}, }, }; /// Set the Nexus CLI configuration from the provided arguments. +#[allow(clippy::too_many_arguments)] pub(crate) async fn set_nexus_conf( - sui_net: Option, - sui_wallet_path: Option, + sui_pk: Option, sui_rpc_url: Option, + sui_gql_url: Option, nexus_objects_path: Option, data_storage_walrus_aggregator_url: Option, data_storage_walrus_publisher_url: Option, @@ -30,26 +31,24 @@ pub(crate) async fn set_nexus_conf( if let Some(objects_path) = nexus_objects_path { let content = std::fs::read_to_string(&objects_path).map_err(|e| { NexusCliError::Any(anyhow!( - "Failed to read objects file {}: {}", + "Failed to read objects file {}: {e}", objects_path.display(), - e )) })?; let objects: NexusObjects = toml::from_str(&content).map_err(|e| { NexusCliError::Any(anyhow!( - "Failed to parse objects file {}: {}", + "Failed to parse objects file {}: {e}", objects_path.display(), - e )) })?; conf.nexus = Some(objects); } - conf.sui.net = sui_net.unwrap_or(conf.sui.net); - conf.sui.wallet_path = resolve_wallet_path(sui_wallet_path, &conf.sui)?; + conf.sui.pk = sui_pk.map(SecretValue::from).or(conf.sui.pk); conf.sui.rpc_url = sui_rpc_url.or(conf.sui.rpc_url); + conf.sui.gql_url = sui_gql_url.or(conf.sui.gql_url); // Preferred remote storage cannot be inline. if matches!( @@ -101,17 +100,18 @@ mod tests { #[tokio::test] async fn test_conf_loads_and_saves() { - let tempdir = tempfile::tempdir().unwrap().into_path(); + let tempdir = tempfile::tempdir().unwrap().keep(); let path = tempdir.join("conf.toml"); let objects_path = tempdir.join("objects.toml"); + let mut rng = rand::thread_rng(); assert!(!tokio::fs::try_exists(&path).await.unwrap()); let nexus_objects_instance = NexusObjects { - workflow_pkg_id: sui::ObjectID::random(), - primitives_pkg_id: sui::ObjectID::random(), - interface_pkg_id: sui::ObjectID::random(), - network_id: sui::ObjectID::random(), + workflow_pkg_id: sui::types::Address::generate(&mut rng), + primitives_pkg_id: sui::types::Address::generate(&mut rng), + interface_pkg_id: sui::types::Address::generate(&mut rng), + network_id: sui::types::Address::generate(&mut rng), tool_registry: sui_mocks::mock_sui_object_ref(), default_tap: sui_mocks::mock_sui_object_ref(), gas_service: sui_mocks::mock_sui_object_ref(), @@ -129,10 +129,10 @@ mod tests { // Command saves values. let result = set_nexus_conf( - Some(SuiNet::Mainnet), - Some(tempdir.join("wallet")), + Some("123".to_string().into()), Some(reqwest::Url::parse("https://mainnet.sui.io").unwrap()), - Some(tempdir.join("objects.toml")), + Some(reqwest::Url::parse("https://mainnet.sui.io/graphql").unwrap()), + Some(objects_path), Some(reqwest::Url::parse("https://aggregator.url").unwrap()), Some(reqwest::Url::parse("https://publisher.url").unwrap()), Some(42), @@ -148,12 +148,15 @@ mod tests { let conf = CliConf::load_from_path(&path).await.unwrap(); let objects = conf.nexus.unwrap(); - assert_eq!(conf.sui.net, SuiNet::Mainnet); - assert_eq!(conf.sui.wallet_path, tempdir.join("wallet")); + assert_eq!(conf.sui.pk, Some("123".to_string().into())); assert_eq!( conf.sui.rpc_url, Some(reqwest::Url::parse("https://mainnet.sui.io").unwrap()) ); + assert_eq!( + conf.sui.gql_url, + Some(reqwest::Url::parse("https://mainnet.sui.io/graphql").unwrap()) + ); assert_eq!(objects, nexus_objects_instance); assert_eq!( conf.data_storage.walrus_aggregator_url, @@ -171,8 +174,8 @@ mod tests { // Overriding one value will save that one value and leave other values intact. let result = set_nexus_conf( - Some(SuiNet::Testnet), None, + Some(reqwest::Url::parse("https://testnet.sui.io").unwrap()), None, None, None, @@ -189,11 +192,14 @@ mod tests { let conf = CliConf::load_from_path(&path).await.unwrap(); let objects = conf.nexus.unwrap(); - assert_eq!(conf.sui.net, SuiNet::Testnet); - assert_eq!(conf.sui.wallet_path, tempdir.join("wallet")); + assert_eq!(conf.sui.pk, Some("123".to_string().into())); assert_eq!( conf.sui.rpc_url, - Some(reqwest::Url::parse("https://mainnet.sui.io").unwrap()) + Some(reqwest::Url::parse("https://testnet.sui.io").unwrap()) + ); + assert_eq!( + conf.sui.gql_url, + Some(reqwest::Url::parse("https://mainnet.sui.io/graphql").unwrap()) ); assert_eq!(objects, nexus_objects_instance); assert_eq!( @@ -213,14 +219,14 @@ mod tests { #[tokio::test] async fn test_data_storage_testnet_preset() { - let tempdir = tempfile::tempdir().unwrap().into_path(); + let tempdir = tempfile::tempdir().unwrap().keep(); let path = tempdir.join("conf_testnet.toml"); // Run with data_storage_testnet = true let result = set_nexus_conf( - Some(SuiNet::Testnet), - Some(tempdir.join("wallet")), + None, Some(reqwest::Url::parse("https://testnet.sui.io").unwrap()), + Some(reqwest::Url::parse("https://testnet.sui.io/graphql").unwrap()), None, None, None, @@ -251,13 +257,13 @@ mod tests { #[tokio::test] async fn test_inline_preferred_storage_error() { - let tempdir = tempfile::tempdir().unwrap().into_path(); + let tempdir = tempfile::tempdir().unwrap().keep(); let path = tempdir.join("conf_inline.toml"); let result = set_nexus_conf( - Some(SuiNet::Mainnet), - Some(tempdir.join("wallet")), - Some(reqwest::Url::parse("https://mainnet.sui.io").unwrap()), + None, + Some(reqwest::Url::parse("https://testnet.sui.io").unwrap()), + Some(reqwest::Url::parse("https://testnet.sui.io/graphql").unwrap()), None, None, None, diff --git a/cli/src/conf/mod.rs b/cli/src/conf/mod.rs index f0679580..8bde4d43 100644 --- a/cli/src/conf/mod.rs +++ b/cli/src/conf/mod.rs @@ -8,6 +8,7 @@ use { nexus_sdk::{types::StorageKind, walrus::WALRUS_MAX_EPOCHS}, }; +#[allow(clippy::large_enum_variant)] #[derive(Subcommand, Clone, Debug)] pub(crate) enum ConfCommand { #[command(about = "Print the current Nexus CLI configuration")] @@ -26,25 +27,23 @@ pub(crate) enum ConfCommand { #[command(about = "Update the Nexus CLI configuration")] Set { #[arg( - long = "sui.net", - help = "Set the Sui network", - value_enum, - value_name = "NET" - )] - sui_net: Option, - #[arg( - long = "sui.wallet-path", - help = "Set the Sui wallet path", - value_name = "PATH", - value_parser = ValueParser::from(expand_tilde) + long = "sui.pk", + help = "Set the Sui private key base64 encoded bytes", + value_name = "BASE64" )] - sui_wallet_path: Option, + sui_pk: Option, #[arg( long = "sui.rpc-url", - help = "Set a custom RPC URL for the Sui node", + help = "Set a Sui node RPC URL", value_name = "URL" )] sui_rpc_url: Option, + #[arg( + long = "sui.gql-url", + help = "Set a Sui indexer GraphQL URL", + value_name = "URL" + )] + sui_gql_url: Option, #[arg( long = "nexus.objects", help = "Path to a TOML file containing Nexus objects", @@ -107,7 +106,7 @@ pub(crate) async fn handle(command: ConfCommand) -> AnyResult<(), NexusCliError> if !JSON_MODE.load(std::sync::atomic::Ordering::Relaxed) { let conf = toml::to_string_pretty(&conf).map_err(|e| { - NexusCliError::Any(anyhow!("Failed to serialize configuration to JSON: {}", e)) + NexusCliError::Any(anyhow!("Failed to serialize configuration to JSON: {e}")) })?; println!("{conf}"); @@ -116,9 +115,9 @@ pub(crate) async fn handle(command: ConfCommand) -> AnyResult<(), NexusCliError> Ok(()) } ConfCommand::Set { - sui_net, - sui_wallet_path, + sui_pk, sui_rpc_url, + sui_gql_url, nexus_objects_path, data_storage_walrus_aggregator_url, data_storage_walrus_publisher_url, @@ -128,9 +127,9 @@ pub(crate) async fn handle(command: ConfCommand) -> AnyResult<(), NexusCliError> conf_path, } => { set_nexus_conf( - sui_net, - sui_wallet_path, + sui_pk, sui_rpc_url, + sui_gql_url, nexus_objects_path, data_storage_walrus_aggregator_url, data_storage_walrus_publisher_url, diff --git a/cli/src/crypto/crypto_auth.rs b/cli/src/crypto/crypto_auth.rs index 3aa798b4..2c3b01d4 100644 --- a/cli/src/crypto/crypto_auth.rs +++ b/cli/src/crypto/crypto_auth.rs @@ -6,18 +6,18 @@ use { pub(crate) async fn crypto_auth(gas: GasArgs) -> AnyResult<(), NexusCliError> { command_title!("Establishing a secure session with the network"); - let (nexus_client, _) = get_nexus_client(gas.sui_gas_coin, gas.sui_gas_budget).await?; + let nexus_client = get_nexus_client(gas.sui_gas_coin, gas.sui_gas_budget).await?; // Fetch or create an identity key. if CryptoConf::get_identity_key(None).await.is_err() { CryptoConf::set_identity_key(IdentityKey::generate(), None) .await - .map_err(|e| NexusCliError::Any(e))?; + .map_err(NexusCliError::Any)?; } let ik = CryptoConf::get_identity_key(None) .await - .map_err(|e| NexusCliError::Any(e))?; + .map_err(NexusCliError::Any)?; // Perform the handshake. let handshake_handle = loading!("Establishing secure session..."); diff --git a/cli/src/crypto/crypto_generate_id_key.rs b/cli/src/crypto/crypto_generate_id_key.rs index 0732ccda..f628e4d0 100644 --- a/cli/src/crypto/crypto_generate_id_key.rs +++ b/cli/src/crypto/crypto_generate_id_key.rs @@ -45,6 +45,7 @@ mod tests { // Isolate XDG config so salt lives under the temp dir let tmp_xdg = TempDir::new().expect("temp xdg"); + env::set_var("XDG_CONFIG_HOME", tmp_xdg.path()); // Ensure no lingering keyring entries diff --git a/cli/src/crypto/crypto_init_key.rs b/cli/src/crypto/crypto_init_key.rs index 5139dc92..9c821dc8 100644 --- a/cli/src/crypto/crypto_init_key.rs +++ b/cli/src/crypto/crypto_init_key.rs @@ -94,6 +94,7 @@ mod tests { // Isolate XDG config so salt lives under the temp dir let tmp_xdg = TempDir::new().expect("temp xdg"); + env::set_var("XDG_CONFIG_HOME", tmp_xdg.path()); // Ensure no lingering keyring entries diff --git a/cli/src/crypto/crypto_key_status.rs b/cli/src/crypto/crypto_key_status.rs index cdd5abc7..160a7287 100644 --- a/cli/src/crypto/crypto_key_status.rs +++ b/cli/src/crypto/crypto_key_status.rs @@ -17,9 +17,10 @@ pub fn crypto_key_status() -> AnyResult<(), NexusCliError> { let status = if std::env::var("NEXUS_CLI_STORE_PASSPHRASE").is_ok() { "source: ENV var" - } else if let Ok(_) = Entry::new(SERVICE, "passphrase") + } else if Entry::new(SERVICE, "passphrase") .map_err(|e| NexusCliError::Any(e.into()))? .get_password() + .is_ok() { "source: key-ring pass-phrase" } else if let Ok(hex) = Entry::new(SERVICE, USER) diff --git a/cli/src/dag/dag_execute.rs b/cli/src/dag/dag_execute.rs index ec399c09..38333984 100644 --- a/cli/src/dag/dag_execute.rs +++ b/cli/src/dag/dag_execute.rs @@ -7,33 +7,32 @@ use { notify_success, prelude::*, sui::*, + workflow, }, anyhow::anyhow, - nexus_sdk::{ - nexus::error::NexusError, - object_crawler::{fetch_one, Structure, VecMap, VecSet}, - types::{hint_remote_fields, PortsData, StorageKind, TypeName}, - }, + nexus_sdk::{nexus::error::NexusError, types::EncryptionMode}, std::sync::Arc, }; /// Execute a Nexus DAG based on the provided object ID and initial input data. +#[allow(clippy::too_many_arguments)] pub(crate) async fn execute_dag( - dag_id: sui::ObjectID, + dag_id: sui::types::Address, entry_group: String, input_json: serde_json::Value, remote: Vec, inspect: bool, - sui_gas_coin: Option, + priority_fee_per_gas_unit: u64, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Executing Nexus DAG '{dag_id}'"); - let (nexus_client, sui) = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; // Build the remote storage conf. let conf = CliConf::load().await.unwrap_or_default(); - let preferred_remote_storage = conf.data_storage.preferred_remote_storage.clone(); + let preferred_remote_storage = conf.data_storage.preferred_remote_storage; let storage_conf = conf.data_storage.clone().into(); // Get the active session for potential encryption @@ -49,12 +48,20 @@ pub(crate) async fn execute_dag( )?; // Fetch information about entry ports that need to be encrypted. - let encrypt = fetch_encrypted_entry_ports(&sui, entry_group.clone(), &dag_id).await?; + let encrypt = + workflow::fetch_encrypted_entry_ports(nexus_client.crawler(), entry_group.clone(), &dag_id) + .await?; // Encrypt ports that need to be encrypted and store ports remote if they // need to be stored remotely. - let input_data = - process_entry_ports(&input_json, preferred_remote_storage, &encrypt, &remote).await?; + let input_data = workflow::process_entry_ports( + &input_json, + preferred_remote_storage, + &encrypt, + &remote, + EncryptionMode::Standard, + ) + .await?; let tx_handle = loading!("Crafting and executing transaction..."); @@ -63,6 +70,7 @@ pub(crate) async fn execute_dag( .execute( dag_id, input_data, + priority_fee_per_gas_unit, Some(&entry_group), &storage_conf, Arc::clone(&session), @@ -105,10 +113,10 @@ pub(crate) async fn execute_dag( // Update the session in the configuration. CryptoConf::release_session(session, None) .await - .map_err(|e| NexusCliError::Any(anyhow!("Failed to release session: {}", e)))?; + .map_err(|e| NexusCliError::Any(anyhow!("Failed to release session: {e}")))?; if inspect { - inspect_dag_execution(result.execution_object_id, result.tx_digest).await?; + inspect_dag_execution(result.execution_object_id, result.tx_checkpoint).await?; } else { json_output( &json!({ "digest": result.tx_digest, "execution_id": result.execution_object_id }), @@ -117,395 +125,3 @@ pub(crate) async fn execute_dag( Ok(()) } - -/// Process entry ports: encrypt and/or store remotely as needed. `input` must -/// be at least a 2-level JSON object (vertex -> port -> value). -async fn process_entry_ports( - input: &serde_json::Value, - preferred_remote_storage: Option, - encrypt: &HashMap>, - remote: &Vec, -) -> Result, NexusCliError> { - let Some(vertices) = input.as_object() else { - return Err(NexusCliError::Any(anyhow!( - "Input JSON must be an object with vertex names as keys." - ))); - }; - - let mut result = HashMap::new(); - - for (vertex, data) in vertices { - let Some(ports) = data.as_object() else { - return Err(NexusCliError::Any(anyhow!( - "Input JSON for vertex '{vertex}' must be an object with port names as keys." - ))); - }; - - // Figure out which ports need to be encrypted and stored remotely for - // this vertex. - let encrypt_fields = encrypt.get(vertex); - let remote_fields = ports - .iter() - .filter_map(|(port, _)| { - let handle = format!("{vertex}.{port}"); - if remote.contains(&handle) { - Some(port.clone()) - } else { - None - } - }) - .collect::>(); - - // Convert this json into a map of port -> NexusData. - let nexus_data_map = types::json_to_nexus_data_map( - data, - encrypt_fields.unwrap_or(&vec![]), - &remote_fields, - preferred_remote_storage.clone(), - ) - .map_err(NexusCliError::Any)?; - - result.insert(vertex.clone(), PortsData::from_map(nexus_data_map)); - } - - // Hint the user if they should use remote storage and for what fields. - let flattened = vertices - .iter() - .flat_map(|(vertex, ports)| { - ports - .as_object() - .expect("Must be an object") - .iter() - .map(|(port, data)| (format!("{}.{}", vertex.clone(), port.clone()), data)) - }) - .collect::>(); - - let remote_hints = hint_remote_fields(&json!(flattened)).unwrap_or_default(); - - if !remote_hints.is_empty() { - return Err(NexusCliError::Any(anyhow!( - "Some input fields must be stored remotely to fit within transaction size limits. Please add the following argument to your command:\n\n{command} {fields}", - command = "--remote", - fields = remote_hints.join(",") - ))); - } - - Ok(result) -} - -/// Fetches the encrypted entry ports for a DAG. -async fn fetch_encrypted_entry_ports( - sui: &sui::Client, - entry_group: String, - dag_id: &sui::ObjectID, -) -> AnyResult>, NexusCliError> { - #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)] - struct EntryPort { - name: String, - encrypted: bool, - } - - #[derive(Clone, Debug, Deserialize)] - struct Dag { - entry_groups: - VecMap, VecMap, VecSet>>>, - } - - let result = fetch_one::>(sui, *dag_id) - .await - .map_err(|e| NexusCliError::Any(anyhow!(e)))?; - - // Get the relevant entry group. - let group: TypeName = TypeName { - name: entry_group.clone(), - }; - - let entry_group = result - .data - .into_inner() - .entry_groups - .into_inner() - .remove(&group.into()) - .ok_or_else(|| { - NexusCliError::Any(anyhow!("Entry group '{entry_group}' not found in DAG")) - })?; - - // Collapse into a more readable format. - Ok(entry_group - .into_inner() - .into_iter() - .filter_map(|(vertex, ports)| { - let encrypted_ports: Vec = ports - .into_inner() - .into_iter() - .filter_map(|port| { - let port = port.into_inner(); - - if port.encrypted { - Some(port.name) - } else { - None - } - }) - .collect(); - - if encrypted_ports.is_empty() { - return None; // Skip vertices with no encrypted ports - } - - Some((vertex.into_inner().name, encrypted_ports)) - }) - .collect::>()) -} - -#[cfg(test)] -mod tests { - use { - super::*, - assert_matches::assert_matches, - mockito::{Server, ServerGuard}, - nexus_sdk::{ - test_utils::nexus_mocks, - types::{DataStorage, Storable, StorageConf}, - walrus::{BlobObject, BlobStorage, NewlyCreated, StorageInfo}, - }, - serde_json::json, - std::collections::HashMap, - }; - - /// Setup mock server for Walrus testing - async fn setup_mock_server_and_conf() -> anyhow::Result<(ServerGuard, StorageConf)> { - // Create mock server - let server = Server::new_async().await; - let server_url = server.url(); - - // Create a Walrus client that points to our mock server - let storage_conf = StorageConf { - walrus_publisher_url: Some(server_url.clone()), - walrus_aggregator_url: Some(server_url), - walrus_save_for_epochs: Some(2), - }; - - Ok((server, storage_conf)) - } - - #[tokio::test] - async fn test_process_entry_ports_success_no_encrypt_no_remote() { - let input = json!({ - "vertex1": { - "port1": "value1", - "port2": "value2" - } - }); - let (_, storage_conf) = setup_mock_server_and_conf() - .await - .expect("Server must start"); - let (session, _) = nexus_mocks::mock_session(); - let encrypt = HashMap::new(); - let remote = vec![]; - - let result = process_entry_ports(&input, None, &encrypt, &remote) - .await - .expect("Should succeed"); - - let vertex = result - .get("vertex1") - .expect("vertex1 missing") - .clone() - .commit_all(&storage_conf, Arc::clone(&session)) - .await - .expect("commit_all failed"); - let port1 = vertex.get("port1").expect("port1 missing"); - let port2 = vertex.get("port2").expect("port2 missing"); - - // Both should be Inline and not encrypted - assert_matches!(port1, DataStorage::Inline(_)); - assert_eq!(port1.as_json(), &json!("value1")); - assert!(!port1.is_encrypted()); - - assert_matches!(port2, DataStorage::Inline(_)); - assert_eq!(port2.as_json(), &json!("value2")); - assert!(!port2.is_encrypted()); - } - - #[tokio::test] - async fn test_process_entry_ports_encrypt_only() { - let input = json!({ - "vertex1": { - "port1": "secret_value", - "port2": "plain_value" - } - }); - let (_, storage_conf) = setup_mock_server_and_conf() - .await - .expect("Server must start"); - let (session, _) = nexus_mocks::mock_session(); - let mut encrypt = HashMap::new(); - encrypt.insert("vertex1".to_string(), vec!["port1".to_string()]); - let remote = vec![]; - - let result = process_entry_ports(&input, None, &encrypt, &remote) - .await - .expect("Should succeed"); - - let vertex = result - .get("vertex1") - .expect("vertex1 missing") - .clone() - .commit_all(&storage_conf, Arc::clone(&session)) - .await - .expect("commit_all failed"); - let port1 = vertex.get("port1").expect("port1 missing"); - let port2 = vertex.get("port2").expect("port2 missing"); - - // port1 should be encrypted - assert!(port1.is_encrypted()); - // port2 should not be encrypted - assert!(!port2.is_encrypted()); - } - - #[tokio::test] - async fn test_process_entry_ports_remote_only() { - let input = json!({ - "vertex1": { - "port1": "remote_value", - "port2": "local_value" - } - }); - let (mut server, storage_conf) = setup_mock_server_and_conf() - .await - .expect("Server must start"); - let (session, _) = nexus_mocks::mock_session(); - let encrypt = HashMap::new(); - let remote = vec!["vertex1.port1".to_string()]; - - // Setup mock Walrus response - let mock_put_response = StorageInfo { - newly_created: Some(NewlyCreated { - blob_object: BlobObject { - blob_id: "json_blob_id".to_string(), - id: "json_object_id".to_string(), - storage: BlobStorage { end_epoch: 200 }, - }, - }), - already_certified: None, - }; - - let mock_put = server - .mock("PUT", "/v1/blobs?epochs=2") - .with_status(200) - .with_header("content-type", "application/json") - .with_body(serde_json::to_string(&mock_put_response).expect("Must serialize")) - .create_async() - .await; - - let result = process_entry_ports(&input, None, &encrypt, &remote) - .await - .expect("Should succeed"); - - let vertex = result - .get("vertex1") - .expect("vertex1 missing") - .clone() - .commit_all(&storage_conf, Arc::clone(&session)) - .await - .expect("commit_all failed"); - let port1 = vertex.get("port1").expect("port1 missing"); - let port2 = vertex.get("port2").expect("port2 missing"); - - // port1 should be walrus - assert_matches!(port1, DataStorage::Walrus(_)); - // port2 should be Inline - assert_matches!(port2, DataStorage::Inline(_)); - - // Verify the request was made - mock_put.assert_async().await; - } - - #[tokio::test] - async fn test_process_entry_ports_encrypt_and_remote() { - let input = json!({ - "vertex1": { - "port1": "secret_remote_value", - "port2": "plain_local_value" - } - }); - let (mut server, storage_conf) = setup_mock_server_and_conf() - .await - .expect("Server must start"); - let (session, _) = nexus_mocks::mock_session(); - let mut encrypt = HashMap::new(); - encrypt.insert("vertex1".to_string(), vec!["port1".to_string()]); - let remote = vec!["vertex1.port1".to_string()]; - - // Setup mock Walrus response - let mock_put_response = StorageInfo { - newly_created: Some(NewlyCreated { - blob_object: BlobObject { - blob_id: "json_blob_id".to_string(), - id: "json_object_id".to_string(), - storage: BlobStorage { end_epoch: 200 }, - }, - }), - already_certified: None, - }; - - let mock_put = server - .mock("PUT", "/v1/blobs?epochs=2") - .with_status(200) - .with_header("content-type", "application/json") - .with_body(serde_json::to_string(&mock_put_response).expect("Must serialize")) - .create_async() - .await; - - let result = process_entry_ports(&input, None, &encrypt, &remote) - .await - .expect("Should succeed"); - - let vertex = result - .get("vertex1") - .expect("vertex1 missing") - .clone() - .commit_all(&storage_conf, Arc::clone(&session)) - .await - .expect("commit_all failed"); - let port1 = vertex.get("port1").expect("port1 missing"); - let port2 = vertex.get("port2").expect("port2 missing"); - - // port1 should be encrypted and walrus - assert!(port1.is_encrypted()); - assert_matches!(port1, DataStorage::Walrus(_)); - // port2 should be Inline and not encrypted - assert_matches!(port2, DataStorage::Inline(_)); - assert!(!port2.is_encrypted()); - - // Verify the request was made - mock_put.assert_async().await; - } - - #[tokio::test] - async fn test_process_entry_ports_invalid_input_not_object() { - let input = json!("not_an_object"); - let encrypt = HashMap::new(); - let remote = vec![]; - - let result = process_entry_ports(&input, None, &encrypt, &remote).await; - assert!(result.is_err()); - let err_msg = format!("{}", result.unwrap_err()); - assert!(err_msg.contains("Input JSON must be an object")); - } - - #[tokio::test] - async fn test_process_entry_ports_invalid_vertex_not_object() { - let input = json!({ - "vertex1": "not_an_object" - }); - let encrypt = HashMap::new(); - let remote = vec![]; - - let result = process_entry_ports(&input, None, &encrypt, &remote).await; - assert!(result.is_err()); - let err_msg = format!("{}", result.unwrap_err()); - assert!(err_msg.contains("must be an object with port names as keys")); - } -} diff --git a/cli/src/dag/dag_inspect_execution.rs b/cli/src/dag/dag_inspect_execution.rs index ae97fdde..254a0efc 100644 --- a/cli/src/dag/dag_inspect_execution.rs +++ b/cli/src/dag/dag_inspect_execution.rs @@ -15,16 +15,16 @@ use { /// Inspect a Nexus DAG execution process based on the provided object ID and /// execution digest. pub(crate) async fn inspect_dag_execution( - dag_execution_id: sui::ObjectID, - execution_digest: sui::TransactionDigest, + dag_execution_id: sui::types::Address, + execution_checkpoint: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Inspecting Nexus DAG Execution '{dag_execution_id}'"); - let (nexus_client, _) = get_nexus_client(None, sui::MIST_PER_SUI / 10).await?; + let nexus_client = get_nexus_client(None, DEFAULT_GAS_BUDGET).await?; let mut result = nexus_client .workflow() - .inspect_execution(dag_execution_id, execution_digest, None) + .inspect_execution(dag_execution_id, execution_checkpoint, None) .await .map_err(NexusCliError::Nexus)?; @@ -162,7 +162,7 @@ pub(crate) async fn inspect_dag_execution( // Update the session in the configuration. CryptoConf::release_session(session, None) .await - .map_err(|e| NexusCliError::Any(anyhow!("Failed to release session: {}", e)))?; + .map_err(|e| NexusCliError::Any(anyhow!("Failed to release session: {e}")))?; json_output(&json_trace)?; diff --git a/cli/src/dag/dag_publish.rs b/cli/src/dag/dag_publish.rs index 65662854..631623c7 100644 --- a/cli/src/dag/dag_publish.rs +++ b/cli/src/dag/dag_publish.rs @@ -12,14 +12,14 @@ use crate::{ /// performs validation on the DAG before publishing. pub(crate) async fn publish_dag( path: PathBuf, - sui_gas_coin: Option, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { let dag = validate_dag(path).await?; command_title!("Publishing Nexus DAG"); - let (nexus_client, _) = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; let tx_handle = loading!("Crafting and executing transaction..."); diff --git a/cli/src/dag/mod.rs b/cli/src/dag/mod.rs index 0ccb7fcc..948f775b 100644 --- a/cli/src/dag/mod.rs +++ b/cli/src/dag/mod.rs @@ -53,7 +53,7 @@ pub(crate) enum DagCommand { help = "The object ID of the Nexus DAG", value_name = "OBJECT_ID" )] - dag_id: sui::ObjectID, + dag_id: sui::types::Address, /// The entry group to invoke. #[arg( long = "entry-group", @@ -88,6 +88,14 @@ pub(crate) enum DagCommand { help = "Whether to inspect the DAG execution process. If not provided, command returns after submitting the transaction." )] inspect: bool, + /// Priority fee per gas unit for the DAG execution. + #[arg( + long = "priority-fee-per-gas-unit", + help = "Priority fee per gas unit to pass to the DAG execution. Defaults to 0 when omitted.", + value_name = "AMOUNT", + default_value_t = 0u64 + )] + priority_fee_per_gas_unit: u64, #[command(flatten)] gas: GasArgs, }, @@ -103,15 +111,14 @@ pub(crate) enum DagCommand { help = "The object ID of the Nexus DAGExecution object.", value_name = "OBJECT_ID" )] - dag_execution_id: sui::ObjectID, + dag_execution_id: sui::types::Address, /// The entry group to invoke. #[arg( - long = "execution-digest", - short = 'd', - help = "The transaction digest of the execution.", - value_name = "DIGEST" + long = "execution-checkpoint", + short = 'c', + help = "The checkpoint of the transaction that triggered the execution." )] - execution_digest: sui::TransactionDigest, + execution_checkpoint: u64, }, } @@ -134,6 +141,7 @@ pub(crate) async fn handle(command: DagCommand) -> AnyResult<(), NexusCliError> input_json, remote, inspect, + priority_fee_per_gas_unit, gas, } => { // Optional: Check auth at CLI level instead of inside execute_dag @@ -145,6 +153,7 @@ pub(crate) async fn handle(command: DagCommand) -> AnyResult<(), NexusCliError> input_json, remote, inspect, + priority_fee_per_gas_unit, gas.sui_gas_coin, gas.sui_gas_budget, ) @@ -154,7 +163,7 @@ pub(crate) async fn handle(command: DagCommand) -> AnyResult<(), NexusCliError> // == `$ nexus dag inspect-execution` == DagCommand::InspectExecution { dag_execution_id, - execution_digest, - } => inspect_dag_execution(dag_execution_id, execution_digest).await, + execution_checkpoint, + } => inspect_dag_execution(dag_execution_id, execution_checkpoint).await, } } diff --git a/cli/src/display.rs b/cli/src/display.rs index 65990eba..233e1a52 100644 --- a/cli/src/display.rs +++ b/cli/src/display.rs @@ -156,7 +156,7 @@ pub(crate) fn json_output(data: &T) -> AnyResult<(), NexusCliError match serde_json::to_string_pretty(data) { Ok(json) => { - println!("{}", json); + println!("{json}"); Ok(()) } diff --git a/cli/src/error.rs b/cli/src/error.rs index 5830677e..7bc3486d 100644 --- a/cli/src/error.rs +++ b/cli/src/error.rs @@ -17,7 +17,7 @@ pub(crate) enum NexusCliError { #[error("{error}{separator}\n{0}", error = "HTTP Error".red().bold(), separator = separator())] Http(reqwest::Error), #[error("{error}{separator}\n{0}", error = "Sui Error".red().bold(), separator = separator())] - Sui(sui::Error), + Rpc(anyhow::Error), #[error("{error}{separator}\n{0}", error = "Nexus Client Error".red().bold(), separator = separator())] Nexus(NexusError), } diff --git a/cli/src/gas/gas_add_budget.rs b/cli/src/gas/gas_add_budget.rs index 0041c24c..3c96f4d6 100644 --- a/cli/src/gas/gas_add_budget.rs +++ b/cli/src/gas/gas_add_budget.rs @@ -2,8 +2,8 @@ use crate::{command_title, display::json_output, loading, notify_success, prelud /// Upload `coin` as a gas budget for the Nexus workflow. pub(crate) async fn add_gas_budget( - coin: sui::ObjectID, - sui_gas_coin: Option, + coin: sui::types::Address, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Adding '{coin}' as gas budget for Nexus"); @@ -14,7 +14,7 @@ pub(crate) async fn add_gas_budget( ))); } - let (nexus_client, _) = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; let tx_handle = loading!("Crafting and executing transaction..."); diff --git a/cli/src/gas/mod.rs b/cli/src/gas/mod.rs index a2243583..3a52a7f2 100644 --- a/cli/src/gas/mod.rs +++ b/cli/src/gas/mod.rs @@ -17,7 +17,7 @@ pub(crate) enum GasCommand { help = "Owned SUI coin object ID to use as budget", value_name = "OBJECT_ID" )] - coin: sui::ObjectID, + coin: sui::types::Address, #[command(flatten)] gas: GasArgs, }, @@ -49,7 +49,7 @@ pub(crate) enum ExpiryCommand { help = "The OwnerCap object ID that must be owned by the sender.", value_name = "OBJECT_ID" )] - owner_cap: Option, + owner_cap: Option, #[arg( long = "cost-per-minute", short = 'c', @@ -76,7 +76,7 @@ pub(crate) enum ExpiryCommand { help = "The OwnerCap object ID that must be owned by the sender.", value_name = "OBJECT_ID" )] - owner_cap: Option, + owner_cap: Option, #[command(flatten)] gas: GasArgs, }, @@ -103,7 +103,7 @@ pub(crate) enum ExpiryCommand { help = "Owned SUI coin object ID to use to pay for the ticket", value_name = "OBJECT_ID" )] - coin: sui::ObjectID, + coin: sui::types::Address, #[command(flatten)] gas: GasArgs, }, @@ -126,7 +126,7 @@ pub(crate) enum LimitedInvocationsCommand { help = "The OwnerCap object ID that must be owned by the sender.", value_name = "OBJECT_ID" )] - owner_cap: Option, + owner_cap: Option, #[arg( long = "cost-per-invocation", short = 'c', @@ -165,7 +165,7 @@ pub(crate) enum LimitedInvocationsCommand { help = "The OwnerCap object ID that must be owned by the sender.", value_name = "OBJECT_ID" )] - owner_cap: Option, + owner_cap: Option, #[command(flatten)] gas: GasArgs, }, @@ -192,7 +192,7 @@ pub(crate) enum LimitedInvocationsCommand { help = "Owned SUI coin object ID to use to pay for the ticket", value_name = "OBJECT_ID" )] - coin: sui::ObjectID, + coin: sui::types::Address, #[command(flatten)] gas: GasArgs, }, diff --git a/cli/src/gas/tickets/expiry/buy_ticket.rs b/cli/src/gas/tickets/expiry/buy_ticket.rs index 6b08632c..b9536515 100644 --- a/cli/src/gas/tickets/expiry/buy_ticket.rs +++ b/cli/src/gas/tickets/expiry/buy_ticket.rs @@ -1,5 +1,5 @@ use { - crate::{command_title, display::json_output, loading, prelude::*, sui::*}, + crate::{command_title, display::json_output, loading, notify_success, prelude::*, sui::*}, nexus_sdk::transactions::gas, }; @@ -8,44 +8,42 @@ use { pub(crate) async fn buy_expiry_gas_ticket( tool_fqn: ToolFqn, minutes: u64, - coin: sui::ObjectID, - sui_gas_coin: Option, + coin: sui::types::Address, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Buying an expiry gas ticket for '{minutes}' minutes for tool '{tool_fqn}'"); - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); - - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui, address, sui_gas_coin).await?; - - // Fetch the coin to pay for the ticket with. - let pay_with_coin = fetch_object_by_id(&sui, coin).await?; - - if pay_with_coin.object_id == gas_coin.coin_object_id { + if Some(coin) == sui_gas_coin { return Err(NexusCliError::Any(anyhow!( - "Gas and payment coins must be different." + "The coin used to pay for the ticket cannot be the same as the gas coin." ))); } - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let crawler = nexus_client.crawler(); + + let pay_with_coin = crawler + .get_object_metadata(coin) + .await + .map(|resp| resp.object_ref()) + .map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to fetch coin object metadata for '{coin}': {e}" + )) + })?; // Craft the transaction. let tx_handle = loading!("Crafting transaction..."); - let mut tx = sui::ProgrammableTransactionBuilder::new(); + let mut tx = sui::tx::TransactionBuilder::new(); - if let Err(e) = gas::buy_expiry_gas_ticket(&mut tx, objects, &tool_fqn, &pay_with_coin, minutes) + if let Err(e) = + gas::buy_expiry_gas_ticket(&mut tx, nexus_objects, &tool_fqn, &pay_with_coin, minutes) { tx_handle.error(); @@ -54,16 +52,33 @@ pub(crate) async fn buy_expiry_gas_ticket( tx_handle.success(); - let tx_data = sui::TransactionData::new_programmable( - address, - vec![gas_coin.object_ref()], - tx.finish(), - sui_gas_budget, - reference_gas_price, - ); + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); - // Sign and send the TX. - let response = sign_and_execute_transaction(&sui, &wallet, tx_data).await?; + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); + + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + let response = signer + .execute_tx(tx, signature, &mut gas_coin) + .await + .map_err(NexusCliError::Nexus)?; + + gas_config.release_gas_coin(gas_coin).await; + + notify_success!( + "Transaction digest: {digest}", + digest = response.digest.to_string().truecolor(100, 100, 100) + ); json_output(&json!({ "digest": response.digest }))?; diff --git a/cli/src/gas/tickets/expiry/disable.rs b/cli/src/gas/tickets/expiry/disable.rs index 7a6bd4e3..5512e289 100644 --- a/cli/src/gas/tickets/expiry/disable.rs +++ b/cli/src/gas/tickets/expiry/disable.rs @@ -1,49 +1,49 @@ use { - crate::{command_title, display::json_output, loading, prelude::*, sui::*}, + crate::{command_title, display::json_output, loading, notify_success, prelude::*, sui::*}, nexus_sdk::transactions::gas, }; /// Disable the expiry gas extension for the specified tool. pub(crate) async fn disable_expiry_extension( tool_fqn: ToolFqn, - owner_cap: Option, - sui_gas_coin: Option, + owner_cap: Option, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Disabling the expiry gas extension for tool '{tool_fqn}'"); - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let crawler = nexus_client.crawler(); - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui, address, sui_gas_coin).await?; + let conf = CliConf::load().await.unwrap_or_default(); // Use the provided or saved `owner_cap` object ID and fetch the object. - let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).map(|t| t.over_gas)) else { + let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).and_then(|t| t.over_gas)) else { return Err(NexusCliError::Any(anyhow!( "No OwnerCap object ID found for tool '{tool_fqn}'." ))); }; - let owner_cap = fetch_object_by_id(&sui, owner_cap).await?; - - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; + let owner_cap = crawler + .get_object_metadata(owner_cap) + .await + .map(|resp| resp.object_ref()) + .map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to fetch OwnerCap object metadata for '{owner_cap}': {e}" + )) + })?; // Craft the transaction. let tx_handle = loading!("Crafting transaction..."); - let mut tx = sui::ProgrammableTransactionBuilder::new(); + let mut tx = sui::tx::TransactionBuilder::new(); - if let Err(e) = gas::disable_expiry(&mut tx, objects, &tool_fqn, &owner_cap) { + if let Err(e) = gas::disable_expiry(&mut tx, nexus_objects, &tool_fqn, &owner_cap) { tx_handle.error(); return Err(NexusCliError::Any(e)); @@ -51,16 +51,33 @@ pub(crate) async fn disable_expiry_extension( tx_handle.success(); - let tx_data = sui::TransactionData::new_programmable( - address, - vec![gas_coin.object_ref()], - tx.finish(), - sui_gas_budget, - reference_gas_price, - ); + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); + + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); - // Sign and send the TX. - let response = sign_and_execute_transaction(&sui, &wallet, tx_data).await?; + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + let response = signer + .execute_tx(tx, signature, &mut gas_coin) + .await + .map_err(NexusCliError::Nexus)?; + + gas_config.release_gas_coin(gas_coin).await; + + notify_success!( + "Transaction digest: {digest}", + digest = response.digest.to_string().truecolor(100, 100, 100) + ); json_output(&json!({ "digest": response.digest }))?; diff --git a/cli/src/gas/tickets/expiry/enable.rs b/cli/src/gas/tickets/expiry/enable.rs index 313831ca..5359e4d7 100644 --- a/cli/src/gas/tickets/expiry/enable.rs +++ b/cli/src/gas/tickets/expiry/enable.rs @@ -1,50 +1,56 @@ use { - crate::{command_title, display::json_output, loading, prelude::*, sui::*}, + crate::{command_title, display::json_output, loading, notify_success, prelude::*, sui::*}, nexus_sdk::transactions::gas, }; /// Enable the expiry gas extension for the specified tool. pub(crate) async fn enable_expiry_extension( tool_fqn: ToolFqn, - owner_cap: Option, + owner_cap: Option, cost_per_minute: u64, - sui_gas_coin: Option, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Enabling the expiry gas extension for tool '{tool_fqn}' with cost '{cost_per_minute}' MIST per minute"); - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let crawler = nexus_client.crawler(); - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui, address, sui_gas_coin).await?; + let conf = CliConf::load().await.unwrap_or_default(); // Use the provided or saved `owner_cap` object ID and fetch the object. - let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).map(|t| t.over_gas)) else { + let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).and_then(|t| t.over_gas)) else { return Err(NexusCliError::Any(anyhow!( "No OwnerCap object ID found for tool '{tool_fqn}'." ))); }; - let owner_cap = fetch_object_by_id(&sui, owner_cap).await?; - - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; + let owner_cap = crawler + .get_object_metadata(owner_cap) + .await + .map(|resp| resp.object_ref()) + .map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to fetch OwnerCap object metadata for '{owner_cap}': {e}" + )) + })?; // Craft the transaction. let tx_handle = loading!("Crafting transaction..."); - let mut tx = sui::ProgrammableTransactionBuilder::new(); + let mut tx = sui::tx::TransactionBuilder::new(); - if let Err(e) = gas::enable_expiry(&mut tx, objects, &tool_fqn, &owner_cap, cost_per_minute) { + if let Err(e) = gas::enable_expiry( + &mut tx, + nexus_objects, + &tool_fqn, + &owner_cap, + cost_per_minute, + ) { tx_handle.error(); return Err(NexusCliError::Any(e)); @@ -52,16 +58,33 @@ pub(crate) async fn enable_expiry_extension( tx_handle.success(); - let tx_data = sui::TransactionData::new_programmable( - address, - vec![gas_coin.object_ref()], - tx.finish(), - sui_gas_budget, - reference_gas_price, - ); + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); + + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); - // Sign and send the TX. - let response = sign_and_execute_transaction(&sui, &wallet, tx_data).await?; + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + let response = signer + .execute_tx(tx, signature, &mut gas_coin) + .await + .map_err(NexusCliError::Nexus)?; + + gas_config.release_gas_coin(gas_coin).await; + + notify_success!( + "Transaction digest: {digest}", + digest = response.digest.to_string().truecolor(100, 100, 100) + ); json_output(&json!({ "digest": response.digest }))?; diff --git a/cli/src/gas/tickets/limited_invocations/buy_ticket.rs b/cli/src/gas/tickets/limited_invocations/buy_ticket.rs index 0390285b..d1d9e05c 100644 --- a/cli/src/gas/tickets/limited_invocations/buy_ticket.rs +++ b/cli/src/gas/tickets/limited_invocations/buy_ticket.rs @@ -1,5 +1,5 @@ use { - crate::{command_title, display::json_output, loading, prelude::*, sui::*}, + crate::{command_title, display::json_output, loading, notify_success, prelude::*, sui::*}, nexus_sdk::transactions::gas, }; @@ -8,46 +8,44 @@ use { pub(crate) async fn buy_limited_invocations_gas_ticket( tool_fqn: ToolFqn, invocations: u64, - coin: sui::ObjectID, - sui_gas_coin: Option, + coin: sui::types::Address, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Buying a limited invocations gas ticket for '{invocations}' invocations for tool '{tool_fqn}'"); - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); - - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui, address, sui_gas_coin).await?; - - // Fetch the coin to pay for the ticket with. - let pay_with_coin = fetch_object_by_id(&sui, coin).await?; - - if pay_with_coin.object_id == gas_coin.coin_object_id { + if Some(coin) == sui_gas_coin { return Err(NexusCliError::Any(anyhow!( - "Gas and payment coins must be different." + "The coin used to pay for the ticket cannot be the same as the gas coin." ))); } - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let crawler = nexus_client.crawler(); + + // Fetch the coin to pay for the ticket with. + let pay_with_coin = crawler + .get_object_metadata(coin) + .await + .map(|resp| resp.object_ref()) + .map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to fetch coin object metadata for '{coin}': {e}" + )) + })?; // Craft the transaction. let tx_handle = loading!("Crafting transaction..."); - let mut tx = sui::ProgrammableTransactionBuilder::new(); + let mut tx = sui::tx::TransactionBuilder::new(); if let Err(e) = gas::buy_limited_invocations_gas_ticket( &mut tx, - objects, + nexus_objects, &tool_fqn, &pay_with_coin, invocations, @@ -59,16 +57,33 @@ pub(crate) async fn buy_limited_invocations_gas_ticket( tx_handle.success(); - let tx_data = sui::TransactionData::new_programmable( - address, - vec![gas_coin.object_ref()], - tx.finish(), - sui_gas_budget, - reference_gas_price, - ); + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); + + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); + + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; - // Sign and send the TX. - let response = sign_and_execute_transaction(&sui, &wallet, tx_data).await?; + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + let response = signer + .execute_tx(tx, signature, &mut gas_coin) + .await + .map_err(NexusCliError::Nexus)?; + + gas_config.release_gas_coin(gas_coin).await; + + notify_success!( + "Transaction digest: {digest}", + digest = response.digest.to_string().truecolor(100, 100, 100) + ); json_output(&json!({ "digest": response.digest }))?; diff --git a/cli/src/gas/tickets/limited_invocations/disable.rs b/cli/src/gas/tickets/limited_invocations/disable.rs index 2951205a..a82cfaa7 100644 --- a/cli/src/gas/tickets/limited_invocations/disable.rs +++ b/cli/src/gas/tickets/limited_invocations/disable.rs @@ -1,5 +1,5 @@ use { - crate::{command_title, display::json_output, loading, prelude::*, sui::*}, + crate::{command_title, display::json_output, loading, notify_success, prelude::*, sui::*}, nexus_sdk::transactions::gas, }; @@ -7,44 +7,45 @@ use { /// TODO: https://github.com/Talus-Network/nexus/issues/418 pub(crate) async fn disable_limited_invocations_extension( tool_fqn: ToolFqn, - owner_cap: Option, - sui_gas_coin: Option, + owner_cap: Option, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Disabling the limited invocations gas extension for tool '{tool_fqn}'"); - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let crawler = nexus_client.crawler(); - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui, address, sui_gas_coin).await?; + let conf = CliConf::load().await.unwrap_or_default(); // Use the provided or saved `owner_cap` object ID and fetch the object. - let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).map(|t| t.over_gas)) else { + let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).and_then(|t| t.over_gas)) else { return Err(NexusCliError::Any(anyhow!( "No OwnerCap object ID found for tool '{tool_fqn}'." ))); }; - let owner_cap = fetch_object_by_id(&sui, owner_cap).await?; - - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; + let owner_cap = crawler + .get_object_metadata(owner_cap) + .await + .map(|resp| resp.object_ref()) + .map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to fetch OwnerCap object metadata for '{owner_cap}': {e}" + )) + })?; // Craft the transaction. let tx_handle = loading!("Crafting transaction..."); - let mut tx = sui::ProgrammableTransactionBuilder::new(); + let mut tx = sui::tx::TransactionBuilder::new(); - if let Err(e) = gas::disable_limited_invocations(&mut tx, objects, &tool_fqn, &owner_cap) { + if let Err(e) = gas::disable_limited_invocations(&mut tx, nexus_objects, &tool_fqn, &owner_cap) + { tx_handle.error(); return Err(NexusCliError::Any(e)); @@ -52,16 +53,33 @@ pub(crate) async fn disable_limited_invocations_extension( tx_handle.success(); - let tx_data = sui::TransactionData::new_programmable( - address, - vec![gas_coin.object_ref()], - tx.finish(), - sui_gas_budget, - reference_gas_price, - ); + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); + + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); - // Sign and send the TX. - let response = sign_and_execute_transaction(&sui, &wallet, tx_data).await?; + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + let response = signer + .execute_tx(tx, signature, &mut gas_coin) + .await + .map_err(NexusCliError::Nexus)?; + + gas_config.release_gas_coin(gas_coin).await; + + notify_success!( + "Transaction digest: {digest}", + digest = response.digest.to_string().truecolor(100, 100, 100) + ); json_output(&json!({ "digest": response.digest }))?; diff --git a/cli/src/gas/tickets/limited_invocations/enable.rs b/cli/src/gas/tickets/limited_invocations/enable.rs index 4d97dbf2..8d4cf283 100644 --- a/cli/src/gas/tickets/limited_invocations/enable.rs +++ b/cli/src/gas/tickets/limited_invocations/enable.rs @@ -1,5 +1,5 @@ use { - crate::{command_title, display::json_output, loading, prelude::*, sui::*}, + crate::{command_title, display::json_output, loading, notify_success, prelude::*, sui::*}, nexus_sdk::transactions::gas, }; @@ -7,49 +7,49 @@ use { /// TODO: https://github.com/Talus-Network/nexus/issues/418 pub(crate) async fn enable_limited_invocations_extension( tool_fqn: ToolFqn, - owner_cap: Option, + owner_cap: Option, cost_per_invocation: u64, min_invocations: u64, max_invocations: u64, - sui_gas_coin: Option, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Enabling the limited invocations gas extension for tool '{tool_fqn}' with cost '{cost_per_invocation}' MIST per invocation (min: {min_invocations}, max: {max_invocations})"); - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let crawler = nexus_client.crawler(); - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui, address, sui_gas_coin).await?; + let conf = CliConf::load().await.unwrap_or_default(); // Use the provided or saved `owner_cap` object ID and fetch the object. - let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).map(|t| t.over_gas)) else { + let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).and_then(|t| t.over_gas)) else { return Err(NexusCliError::Any(anyhow!( "No OwnerCap object ID found for tool '{tool_fqn}'." ))); }; - let owner_cap = fetch_object_by_id(&sui, owner_cap).await?; - - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; + let owner_cap = crawler + .get_object_metadata(owner_cap) + .await + .map(|resp| resp.object_ref()) + .map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to fetch OwnerCap object metadata for '{owner_cap}': {e}" + )) + })?; // Craft the transaction. let tx_handle = loading!("Crafting transaction..."); - let mut tx = sui::ProgrammableTransactionBuilder::new(); + let mut tx = sui::tx::TransactionBuilder::new(); if let Err(e) = gas::enable_limited_invocations( &mut tx, - objects, + nexus_objects, &tool_fqn, &owner_cap, cost_per_invocation, @@ -63,16 +63,33 @@ pub(crate) async fn enable_limited_invocations_extension( tx_handle.success(); - let tx_data = sui::TransactionData::new_programmable( - address, - vec![gas_coin.object_ref()], - tx.finish(), - sui_gas_budget, - reference_gas_price, - ); + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); + + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); - // Sign and send the TX. - let response = sign_and_execute_transaction(&sui, &wallet, tx_data).await?; + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + let response = signer + .execute_tx(tx, signature, &mut gas_coin) + .await + .map_err(NexusCliError::Nexus)?; + + gas_config.release_gas_coin(gas_coin).await; + + notify_success!( + "Transaction digest: {digest}", + digest = response.digest.to_string().truecolor(100, 100, 100) + ); json_output(&json!({ "digest": response.digest }))?; diff --git a/cli/src/main.rs b/cli/src/main.rs index bcf4684b..c0f058e9 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -8,9 +8,11 @@ mod error; mod gas; mod network; mod prelude; +mod scheduler; mod sui; mod tool; mod utils; +mod workflow; use crate::prelude::*; @@ -24,6 +26,10 @@ struct Cli { help = "Change the output format to JSON" )] json: bool, + + #[command(flatten)] + verbose: clap_verbosity::Verbosity, + #[command(subcommand)] command: Command, } @@ -36,6 +42,8 @@ enum Command { Conf(conf::ConfCommand), #[command(subcommand, about = "Validate, publish and execute Nexus DAGs")] Dag(dag::DagCommand), + #[command(subcommand, about = "Manage scheduler tasks and occurrences")] + Scheduler(scheduler::SchedulerCommand), #[command(subcommand, about = "Manage Nexus gas budgets and tickets")] Gas(gas::GasCommand), #[command(subcommand, about = "Manage Nexus networks and leader caps")] @@ -56,7 +64,7 @@ async fn main() { // to display the CLI help or version. match e.kind() { clap::error::ErrorKind::DisplayHelp | clap::error::ErrorKind::DisplayVersion => { - println!("{}", e); + println!("{e}"); std::process::exit(0); } @@ -73,6 +81,10 @@ async fn main() { } }; + env_logger::builder() + .filter(None, cli.verbose.log_level_filter()) + .init(); + JSON_MODE.store(cli.json, Ordering::Relaxed); // Send each sub-command to the respective handler. @@ -82,6 +94,7 @@ async fn main() { Command::Dag(dag) => dag::handle(dag).await, Command::Network(network) => network::handle(network).await, Command::Gas(gas) => gas::handle(gas).await, + Command::Scheduler(scheduler) => scheduler::handle(scheduler).await, Command::Crypto(crypto) => crypto::handle(crypto).await, Command::Completion(completion) => completion::handle(completion), }; diff --git a/cli/src/network/mod.rs b/cli/src/network/mod.rs index f8f2b142..f44c7417 100644 --- a/cli/src/network/mod.rs +++ b/cli/src/network/mod.rs @@ -16,7 +16,7 @@ pub(crate) enum NetworkCommand { num_args = 0.., value_name = "ADDRESSES" )] - addresses: Vec, + addresses: Vec, /// How many leader caps to assign to each address #[arg( long = "count-leader-caps", diff --git a/cli/src/network/network_create.rs b/cli/src/network/network_create.rs index 8414f4f9..8d3d8655 100644 --- a/cli/src/network/network_create.rs +++ b/cli/src/network/network_create.rs @@ -1,17 +1,17 @@ use { crate::{command_title, display::json_output, loading, notify_success, prelude::*, sui::*}, nexus_sdk::{ - events::{NexusEvent, NexusEventKind}, - idents::workflow, + events::NexusEventKind, + idents::{sui_framework, workflow}, }, }; /// Create a new Nexus network and assign `count_leader_caps` leader caps to /// the provided addresses. pub(crate) async fn create_network( - addresses: Vec, + addresses: Vec, count_leader_caps: u32, - sui_gas_coin: Option, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!( @@ -19,97 +19,91 @@ pub(crate) async fn create_network( addresses.len() ); - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); - - // Nexus objects must be present in the configuration. - let NexusObjects { - workflow_pkg_id, .. - } = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui, address, sui_gas_coin).await?; - - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); // Craft a TX to create a new network. let tx_handle = loading!("Crafting transaction..."); - let addresses = match serde_json::to_value(addresses).map(sui::SuiJsonValue::new) { - Ok(Ok(addrs)) => addrs, - _ => { - tx_handle.error(); - - return Err(NexusCliError::Any(anyhow!("Failed to serialize addresses"))); - } - }; + let mut tx = sui::tx::TransactionBuilder::new(); - let count_leader_caps = match sui::SuiJsonValue::new(count_leader_caps.to_string().into()) { - Ok(count) => count, + let addresses = match addresses + .iter() + .map(|addr| sui_framework::Address::address_from_type(&mut tx, *addr)) + .collect::, _>>() + { + Ok(addresses) => addresses, Err(e) => { tx_handle.error(); - return Err(NexusCliError::Any(e)); + return Err(NexusCliError::Any(anyhow!( + "Failed to serialize addresses: {e}" + ))); } }; - let tx_data = match sui - .transaction_builder() - .move_call( - address, - *workflow_pkg_id, - workflow::LeaderCap::CREATE_FOR_SELF_AND_ADDRESSES - .module - .as_str(), - workflow::LeaderCap::CREATE_FOR_SELF_AND_ADDRESSES - .name - .as_str(), - vec![], - vec![count_leader_caps, addresses], - Some(gas_coin.coin_object_id), - sui_gas_budget, - Some(reference_gas_price), - ) - .await - { - Ok(tx_data) => tx_data, + let addresses = tx.make_move_vec(Some(sui::types::TypeTag::Address), addresses); + + let count_leader_caps = match idents::pure_arg(&count_leader_caps) { + Ok(count_leader_caps) => tx.input(count_leader_caps), Err(e) => { tx_handle.error(); - return Err(NexusCliError::Any(e)); + return Err(NexusCliError::Any(anyhow!( + "Failed to serialize count_leader_caps: {e}" + ))); } }; + tx.move_call( + sui::tx::Function::new( + nexus_objects.workflow_pkg_id, + workflow::LeaderCap::CREATE_FOR_SELF_AND_ADDRESSES.module, + workflow::LeaderCap::CREATE_FOR_SELF_AND_ADDRESSES.name, + vec![], + ), + vec![count_leader_caps, addresses], + ); + tx_handle.success(); - // Sign the transaction and send it to the network. - let response = sign_and_execute_transaction(&sui, &wallet, tx_data).await?; + let mut gas_coin = gas_config.acquire_gas_coin().await; - // Parse network ID from the response. - let Some(events) = response.events else { - return Err(NexusCliError::Any(anyhow!("No events in the response"))); - }; + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); - let Some(network_id) = events.data.into_iter().find_map(|event| { - let nexus_event: NexusEvent = event.try_into().ok()?; + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); - match nexus_event.data { - NexusEventKind::FoundingLeaderCapCreated(e) => Some(e.network), - _ => None, - } + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + let response = signer + .execute_tx(tx, signature, &mut gas_coin) + .await + .map_err(NexusCliError::Nexus)?; + + gas_config.release_gas_coin(gas_coin).await; + + let Some(network_id) = response.events.iter().find_map(|event| match &event.data { + NexusEventKind::FoundingLeaderCapCreated(e) => Some(e.network), + _ => None, }) else { return Err(NexusCliError::Any(anyhow!("No network ID in the events"))); }; notify_success!( - "New Nexus network created with ID: {id}", - id = network_id.to_string().truecolor(100, 100, 100) + "New Nexus network created with ID: {id}; digest: {digest}", + id = network_id.to_string().truecolor(100, 100, 100), + digest = response.digest.to_string().truecolor(100, 100, 100), ); json_output(&json!({ "digest": response.digest, "network_id": network_id }))?; diff --git a/cli/src/prelude.rs b/cli/src/prelude.rs index ac434662..5c4a84e3 100644 --- a/cli/src/prelude.rs +++ b/cli/src/prelude.rs @@ -13,21 +13,35 @@ pub(crate) use { serde_json::json, std::{ collections::HashMap, - path::{Path, PathBuf}, - sync::atomic::{AtomicBool, Ordering}, + path::PathBuf, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, }, + tokio::sync::Mutex, }; /// Where to find config files. pub(crate) const CLI_CONF_PATH: &str = "~/.nexus/conf.toml"; pub(crate) const CRYPTO_CONF_PATH: &str = "~/.nexus/crypto.toml"; +/// Various Nexus RPC URLs. +pub(crate) const DEVNET_NEXUS_RPC_URL: &str = "https://rpc.ssfn.devnet.production.taluslabs.dev"; + /// objects.toml locations for each network. -pub(crate) const DEVNET_OBJECTS_TOML: &str = - "https://storage.googleapis.com/production-talus-sui-packages/objects.devnet.toml"; +pub(crate) const DEVNET_OBJECTS_TOML: &str = concat!( + "https://storage.googleapis.com/production-talus-sui-objects/v", + env!("CARGO_PKG_VERSION"), + "/objects.devnet.toml" +); + pub(crate) const _TESTNET_OBJECTS_TOML: &str = ""; pub(crate) const _MAINNET_OBJECTS_TOML: &str = ""; +/// What is the default gas budget to use? (0.1 SUI) +pub(crate) const DEFAULT_GAS_BUDGET: u64 = sui::MIST_PER_SUI / 10; + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum, Serialize, Deserialize)] pub(crate) enum SuiNet { #[default] @@ -50,8 +64,8 @@ impl std::fmt::Display for SuiNet { #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub(crate) struct ToolOwnerCaps { - pub(crate) over_tool: sui::ObjectID, - pub(crate) over_gas: sui::ObjectID, + pub(crate) over_tool: sui::types::Address, + pub(crate) over_gas: Option, } /// Reusable Sui gas command args. @@ -63,13 +77,13 @@ pub(crate) struct GasArgs { help = "The gas coin object ID. First coin object is chosen if not present.", value_name = "OBJECT_ID" )] - pub(crate) sui_gas_coin: Option, + pub(crate) sui_gas_coin: Option, #[arg( long = "sui-gas-budget", short = 'b', help = "The gas budget for the transaction.", value_name = "AMOUNT", - default_value_t = sui::MIST_PER_SUI / 10 + default_value_t = DEFAULT_GAS_BUDGET )] pub(crate) sui_gas_budget: u64, } @@ -98,14 +112,24 @@ pub(crate) fn parse_json_string(json: &str) -> AnyResult { #[cfg(test)] mod tests { - use super::*; + use {super::*, serial_test::serial}; #[test] + #[serial] fn test_expand_tilde() { - let path = "~/test"; - let expanded = expand_tilde(path).unwrap(); + let original_home = std::env::var_os("HOME"); + let temp_home = tempfile::tempdir().expect("temp home directory"); + let temp_home_path = temp_home.path().to_path_buf(); - assert_eq!(expanded, home::home_dir().unwrap().join("test")); + std::env::set_var("HOME", &temp_home_path); + + let expanded = expand_tilde("~/test").unwrap(); + assert_eq!(expanded, temp_home_path.join("test")); + + match original_home { + Some(value) => std::env::set_var("HOME", value), + None => std::env::remove_var("HOME"), + } } #[test] diff --git a/cli/src/scheduler/helpers.rs b/cli/src/scheduler/helpers.rs new file mode 100644 index 00000000..660f67a5 --- /dev/null +++ b/cli/src/scheduler/helpers.rs @@ -0,0 +1,53 @@ +use { + crate::{prelude::*, workflow}, + nexus_sdk::nexus::crawler::Crawler, + std::collections::HashMap, +}; + +/// Parse metadata pairs provided as `key=value` strings. +pub(crate) fn parse_metadata(pairs: &[String]) -> AnyResult, NexusCliError> { + let mut result = Vec::with_capacity(pairs.len()); + for pair in pairs { + let Some((key, value)) = pair.split_once('=') else { + return Err(NexusCliError::Any(anyhow!( + "Invalid metadata entry '{pair}'. Expected format key=value" + ))); + }; + if key.trim().is_empty() { + return Err(NexusCliError::Any(anyhow!( + "Metadata key in '{pair}' cannot be empty" + ))); + } + result.push((key.trim().to_owned(), value.trim().to_owned())); + } + Ok(result) +} + +/// Fetch the encrypted entry port mapping for the provided DAG entry group. +pub(crate) async fn fetch_encryption_targets( + crawler: &Crawler, + dag_id: &sui::types::Address, + entry_group: &str, +) -> AnyResult>, NexusCliError> { + workflow::fetch_encrypted_entry_ports(crawler, entry_group.to_owned(), dag_id).await +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_metadata_splits_pairs() { + let result = parse_metadata(&["a=b".to_string(), "c=d".to_string()]).unwrap(); + assert_eq!( + result, + vec![("a".into(), "b".into()), ("c".into(), "d".into())] + ); + } + + #[test] + fn parse_metadata_rejects_missing_equals() { + let err = parse_metadata(&["invalid".to_string()]).unwrap_err(); + assert!(err.to_string().contains("Invalid metadata entry")); + } +} diff --git a/cli/src/scheduler/mod.rs b/cli/src/scheduler/mod.rs new file mode 100644 index 00000000..7d983cd5 --- /dev/null +++ b/cli/src/scheduler/mod.rs @@ -0,0 +1,28 @@ +mod helpers; +mod occurrence; +mod periodic; +mod task; + +use crate::prelude::*; + +#[derive(Subcommand)] +pub(crate) enum SchedulerCommand { + #[command(subcommand, about = "Manage scheduler tasks")] + Task(task::TaskCommand), + #[command(subcommand, about = "Manage sporadic occurrences for a task")] + Occurrence(occurrence::OccurrenceCommand), + #[command(subcommand, about = "Manage periodic scheduling for a task")] + Periodic(periodic::PeriodicCommand), +} + +/// Handle scheduler commands dispatched from the CLI root. +pub(crate) async fn handle(command: SchedulerCommand) -> AnyResult<(), NexusCliError> { + match command { + // == `$ nexus scheduler task ...` == + SchedulerCommand::Task(cmd) => task::handle(cmd).await, + // == `$ nexus scheduler occurrence ...` == + SchedulerCommand::Occurrence(cmd) => occurrence::handle(cmd).await, + // == `$ nexus scheduler periodic ...` == + SchedulerCommand::Periodic(cmd) => periodic::handle(cmd).await, + } +} diff --git a/cli/src/scheduler/occurrence/mod.rs b/cli/src/scheduler/occurrence/mod.rs new file mode 100644 index 00000000..bd410cc0 --- /dev/null +++ b/cli/src/scheduler/occurrence/mod.rs @@ -0,0 +1,73 @@ +mod occurrence_add; + +use crate::prelude::*; + +#[derive(Args, Debug, Clone)] +#[group(id = "occurrence-start", multiple = false)] +pub(crate) struct OccurrenceStartOptions { + /// Absolute start time in milliseconds since epoch. + #[arg(long = "start-ms", value_name = "MILLIS")] + start_ms: Option, + /// Start offset in milliseconds from now. + #[arg(long = "start-offset-ms", value_name = "MILLIS")] + start_offset_ms: Option, +} + +#[derive(Args, Debug, Clone)] +#[group(id = "occurrence-deadline", multiple = false)] +pub(crate) struct OccurrenceDeadlineOptions { + /// Deadline offset in milliseconds after the scheduled start. + #[arg(long = "deadline-offset-ms", value_name = "MILLIS")] + deadline_offset_ms: Option, +} + +#[derive(Subcommand)] +pub(crate) enum OccurrenceCommand { + #[command(about = "Add a sporadic occurrence to a task")] + Add { + /// Task object ID receiving the occurrence. + #[arg(long = "task-id", short = 't', value_name = "OBJECT_ID")] + task_id: sui::types::Address, + #[command(flatten)] + start: OccurrenceStartOptions, + #[command(flatten)] + deadline: OccurrenceDeadlineOptions, + /// Priority fee per gas unit applied to the occurrence. + #[arg( + long = "priority-fee-per-gas-unit", + value_name = "AMOUNT", + default_value_t = 0u64 + )] + priority_fee_per_gas_unit: u64, + #[command(flatten)] + gas: GasArgs, + }, +} + +pub(crate) async fn handle(command: OccurrenceCommand) -> AnyResult<(), NexusCliError> { + match command { + OccurrenceCommand::Add { + task_id, + start, + deadline, + priority_fee_per_gas_unit, + gas, + } => { + let OccurrenceStartOptions { + start_ms, + start_offset_ms, + } = start; + let OccurrenceDeadlineOptions { deadline_offset_ms } = deadline; + + occurrence_add::add_occurrence_to_task( + task_id, + start_ms, + start_offset_ms, + deadline_offset_ms, + priority_fee_per_gas_unit, + gas, + ) + .await + } + } +} diff --git a/cli/src/scheduler/occurrence/occurrence_add.rs b/cli/src/scheduler/occurrence/occurrence_add.rs new file mode 100644 index 00000000..33ba9796 --- /dev/null +++ b/cli/src/scheduler/occurrence/occurrence_add.rs @@ -0,0 +1,57 @@ +use { + crate::{ + command_title, + display::json_output, + notify_success, + prelude::*, + sui::get_nexus_client, + }, + nexus_sdk::nexus::scheduler::OccurrenceRequest, + serde_json::json, +}; + +/// Schedule a one-off occurrence for a scheduler task. +pub(crate) async fn add_occurrence_to_task( + task_id: sui::types::Address, + start_ms: Option, + start_offset_ms: Option, + deadline_offset_ms: Option, + priority_fee_per_gas_unit: u64, + gas: GasArgs, +) -> AnyResult<(), NexusCliError> { + command_title!( + "Scheduling occurrence for task '{task_id}'", + task_id = task_id + ); + + let schedule = OccurrenceRequest::new( + start_ms, + None, + start_offset_ms, + deadline_offset_ms, + priority_fee_per_gas_unit, + true, + ) + .map_err(NexusCliError::Nexus)?; + + let nexus_client = get_nexus_client(gas.sui_gas_coin, gas.sui_gas_budget).await?; + + let result = nexus_client + .scheduler() + .add_occurrence(task_id, schedule) + .await + .map_err(NexusCliError::Nexus)?; + + notify_success!("Occurrence scheduled"); + + json_output(&json!({ + "digest": result.tx_digest, + "task_id": task_id, + "start_ms": start_ms, + "start_offset_ms": start_offset_ms, + "deadline_offset_ms": deadline_offset_ms, + "priority_fee_per_gas_unit": priority_fee_per_gas_unit, + }))?; + + Ok(()) +} diff --git a/cli/src/scheduler/periodic/mod.rs b/cli/src/scheduler/periodic/mod.rs new file mode 100644 index 00000000..802626bd --- /dev/null +++ b/cli/src/scheduler/periodic/mod.rs @@ -0,0 +1,71 @@ +mod periodic_disable; +mod periodic_set; + +use crate::prelude::*; + +#[derive(Subcommand)] +pub(crate) enum PeriodicCommand { + #[command(about = "Configure or update periodic scheduling")] + Set { + /// Task object ID. + #[arg(long = "task-id", short = 't', value_name = "OBJECT_ID")] + task_id: sui::types::Address, + /// Absolute start time in milliseconds since epoch for the next periodic occurrence. + #[arg(long = "first-start-ms", value_name = "MILLIS")] + first_start_ms: u64, + /// Period between occurrences in milliseconds. + #[arg(long = "period-ms", value_name = "MILLIS")] + period_ms: u64, + /// Deadline offset from each start time in milliseconds. + #[arg(long = "deadline-offset-ms", value_name = "MILLIS")] + deadline_offset_ms: Option, + /// Maximum number of generated occurrences (None for infinite). + #[arg(long = "max-iterations", value_name = "COUNT")] + max_iterations: Option, + /// Priority fee per gas unit associated with occurrences. + #[arg( + long = "priority-fee-per-gas-unit", + value_name = "AMOUNT", + default_value_t = 0u64 + )] + priority_fee_per_gas_unit: u64, + #[command(flatten)] + gas: GasArgs, + }, + #[command(about = "Disable periodic scheduling")] + Disable { + /// Task object ID to update. + #[arg(long = "task-id", short = 't', value_name = "OBJECT_ID")] + task_id: sui::types::Address, + #[command(flatten)] + gas: GasArgs, + }, +} + +pub(crate) async fn handle(command: PeriodicCommand) -> AnyResult<(), NexusCliError> { + match command { + PeriodicCommand::Set { + task_id, + first_start_ms, + period_ms, + deadline_offset_ms, + max_iterations, + priority_fee_per_gas_unit, + gas, + } => { + periodic_set::set_periodic_task( + task_id, + first_start_ms, + period_ms, + deadline_offset_ms, + max_iterations, + priority_fee_per_gas_unit, + gas, + ) + .await + } + PeriodicCommand::Disable { task_id, gas } => { + periodic_disable::disable_periodic_task(task_id, gas).await + } + } +} diff --git a/cli/src/scheduler/periodic/periodic_disable.rs b/cli/src/scheduler/periodic/periodic_disable.rs new file mode 100644 index 00000000..47bd4958 --- /dev/null +++ b/cli/src/scheduler/periodic/periodic_disable.rs @@ -0,0 +1,38 @@ +use { + crate::{ + command_title, + display::json_output, + notify_success, + prelude::*, + sui::get_nexus_client, + }, + serde_json::json, +}; + +/// Disable the periodic schedule for a scheduler task. +pub(crate) async fn disable_periodic_task( + task_id: sui::types::Address, + gas: GasArgs, +) -> AnyResult<(), NexusCliError> { + command_title!( + "Disabling periodic schedule for task '{task_id}'", + task_id = task_id + ); + + let nexus_client = get_nexus_client(gas.sui_gas_coin, gas.sui_gas_budget).await?; + + let result = nexus_client + .scheduler() + .disable_periodic(task_id) + .await + .map_err(NexusCliError::Nexus)?; + + notify_success!("Periodic schedule disabled"); + + json_output(&json!({ + "digest": result.tx_digest, + "task_id": task_id, + }))?; + + Ok(()) +} diff --git a/cli/src/scheduler/periodic/periodic_set.rs b/cli/src/scheduler/periodic/periodic_set.rs new file mode 100644 index 00000000..ff9dbb7b --- /dev/null +++ b/cli/src/scheduler/periodic/periodic_set.rs @@ -0,0 +1,57 @@ +use { + crate::{ + command_title, + display::json_output, + notify_success, + prelude::*, + sui::get_nexus_client, + }, + nexus_sdk::nexus::scheduler::PeriodicScheduleConfig, + serde_json::json, +}; + +/// Configure or update the periodic schedule for a scheduler task. +pub(crate) async fn set_periodic_task( + task_id: sui::types::Address, + first_start_ms: u64, + period_ms: u64, + deadline_offset_ms: Option, + max_iterations: Option, + priority_fee_per_gas_unit: u64, + gas: GasArgs, +) -> AnyResult<(), NexusCliError> { + command_title!( + "Configuring periodic schedule for task '{task_id}'", + task_id = task_id + ); + + let nexus_client = get_nexus_client(gas.sui_gas_coin, gas.sui_gas_budget).await?; + + let result = nexus_client + .scheduler() + .configure_periodic( + task_id, + PeriodicScheduleConfig { + first_start_ms, + period_ms, + deadline_offset_ms, + max_iterations, + priority_fee_per_gas_unit, + }, + ) + .await + .map_err(NexusCliError::Nexus)?; + + notify_success!("Periodic schedule set"); + + json_output(&json!({ + "digest": result.tx_digest, + "task_id": task_id, + "period_ms": period_ms, + "deadline_offset_ms": deadline_offset_ms, + "max_iterations": max_iterations, + "priority_fee_per_gas_unit": priority_fee_per_gas_unit, + }))?; + + Ok(()) +} diff --git a/cli/src/scheduler/task/mod.rs b/cli/src/scheduler/task/mod.rs new file mode 100644 index 00000000..4e015b4e --- /dev/null +++ b/cli/src/scheduler/task/mod.rs @@ -0,0 +1,213 @@ +mod task_create; +mod task_inspect; +mod task_metadata; +mod task_state; + +use { + self::task_state::TaskStateRequest, + crate::prelude::*, + nexus_sdk::{nexus::scheduler::GeneratorKind, types::DEFAULT_ENTRY_GROUP}, +}; + +#[derive(Copy, Clone, Debug, ValueEnum)] +pub(crate) enum TaskGeneratorArg { + Queue, + Periodic, +} + +impl From for GeneratorKind { + fn from(value: TaskGeneratorArg) -> Self { + match value { + TaskGeneratorArg::Queue => GeneratorKind::Queue, + TaskGeneratorArg::Periodic => GeneratorKind::Periodic, + } + } +} + +#[derive(Args, Debug, Clone)] +#[group(id = "schedule-start", multiple = false)] +pub(crate) struct ScheduleStartOptions { + /// Absolute start time in milliseconds since epoch for the first occurrence. + #[arg(long = "schedule-start-ms", value_name = "MILLIS")] + schedule_start_ms: Option, + /// Start offset in milliseconds from now for the first occurrence. + #[arg(long = "schedule-start-offset-ms", value_name = "MILLIS")] + schedule_start_offset_ms: Option, +} + +#[derive(Args, Debug, Clone)] +#[group(id = "schedule-deadline", multiple = false)] +pub(crate) struct ScheduleDeadlineOptions { + /// Deadline offset in milliseconds after the scheduled start for the first occurrence. + #[arg(long = "schedule-deadline-offset-ms", value_name = "MILLIS")] + schedule_deadline_offset_ms: Option, +} + +#[derive(Subcommand)] +pub(crate) enum TaskCommand { + #[command(about = "Create a new scheduler task")] + Create { + /// DAG object ID providing the execution definition. + #[arg(long = "dag-id", short = 'd', value_name = "OBJECT_ID")] + dag_id: sui::types::Address, + /// Entry group to invoke when executing the DAG. + #[arg( + long = "entry-group", + short = 'e', + default_value = DEFAULT_ENTRY_GROUP, + value_name = "NAME", + )] + entry_group: String, + /// Initial input JSON for the DAG execution. + #[arg( + long = "input-json", + short = 'i', + value_parser = ValueParser::from(parse_json_string), + value_name = "JSON", + )] + input_json: Option, + /// Which input json keys should be stored remotely. + #[arg( + long = "remote", + short = 'r', + help = "Which input json keys should be stored remotely. Provide a comma-separated list of {vertex}.{port} values. By default, all fields are stored inline.", + value_delimiter = ',', + value_name = "VERTEX.PORT" + )] + remote: Vec, + /// Metadata entries to attach to the task as key=value pairs. + #[arg(long = "metadata", short = 'm', value_name = "KEY=VALUE")] + metadata: Vec, + /// Priority fee per gas unit for DAG executions launched by the task. + #[arg( + long = "execution-priority-fee-per-gas-unit", + value_name = "AMOUNT", + default_value_t = 0u64 + )] + execution_priority_fee_per_gas_unit: u64, + #[command(flatten)] + schedule_start: ScheduleStartOptions, + #[command(flatten)] + schedule_deadline: ScheduleDeadlineOptions, + /// Priority fee per gas unit for the initial occurrence. + #[arg( + long = "schedule-priority-fee-per-gas-unit", + value_name = "AMOUNT", + default_value_t = 0u64 + )] + schedule_priority_fee_per_gas_unit: u64, + /// Generator responsible for producing future occurrences for this task. + #[arg( + long = "generator", + value_enum, + default_value_t = TaskGeneratorArg::Queue, + value_name = "KIND" + )] + generator: TaskGeneratorArg, + #[command(flatten)] + gas: GasArgs, + }, + #[command(about = "Inspect a scheduler task")] + Inspect { + /// Task object ID to inspect. + #[arg(long = "task-id", short = 't', value_name = "OBJECT_ID")] + task_id: sui::types::Address, + }, + #[command(about = "Update scheduler task metadata")] + Metadata { + /// Task object ID to update. + #[arg(long = "task-id", short = 't', value_name = "OBJECT_ID")] + task_id: sui::types::Address, + /// Metadata entries to write as key=value pairs. + #[arg( + long = "metadata", + short = 'm', + value_name = "KEY=VALUE", + required = true + )] + metadata: Vec, + #[command(flatten)] + gas: GasArgs, + }, + #[command(about = "Pause task scheduling")] + Pause { + /// Task object ID to mutate. + #[arg(long = "task-id", short = 't', value_name = "OBJECT_ID")] + task_id: sui::types::Address, + #[command(flatten)] + gas: GasArgs, + }, + #[command(about = "Resume task scheduling")] + Resume { + /// Task object ID to mutate. + #[arg(long = "task-id", short = 't', value_name = "OBJECT_ID")] + task_id: sui::types::Address, + #[command(flatten)] + gas: GasArgs, + }, + #[command(about = "Cancel task scheduling")] + Cancel { + /// Task object ID to mutate. + #[arg(long = "task-id", short = 't', value_name = "OBJECT_ID")] + task_id: sui::types::Address, + #[command(flatten)] + gas: GasArgs, + }, +} + +pub(crate) async fn handle(command: TaskCommand) -> AnyResult<(), NexusCliError> { + match command { + TaskCommand::Create { + dag_id, + entry_group, + input_json, + remote, + metadata, + execution_priority_fee_per_gas_unit, + schedule_start, + schedule_deadline, + schedule_priority_fee_per_gas_unit, + generator, + gas, + } => { + let ScheduleStartOptions { + schedule_start_ms, + schedule_start_offset_ms, + } = schedule_start; + let ScheduleDeadlineOptions { + schedule_deadline_offset_ms, + } = schedule_deadline; + + task_create::create_task( + dag_id, + entry_group, + input_json, + remote, + metadata, + execution_priority_fee_per_gas_unit, + schedule_start_ms, + schedule_start_offset_ms, + schedule_deadline_offset_ms, + schedule_priority_fee_per_gas_unit, + generator.into(), + gas, + ) + .await + } + TaskCommand::Inspect { task_id } => task_inspect::inspect_task(task_id).await, + TaskCommand::Metadata { + task_id, + metadata, + gas, + } => task_metadata::update_task_metadata(task_id, metadata, gas).await, + TaskCommand::Pause { task_id, gas } => { + task_state::set_task_state(task_id, gas, TaskStateRequest::Pause).await + } + TaskCommand::Resume { task_id, gas } => { + task_state::set_task_state(task_id, gas, TaskStateRequest::Resume).await + } + TaskCommand::Cancel { task_id, gas } => { + task_state::set_task_state(task_id, gas, TaskStateRequest::Cancel).await + } + } +} diff --git a/cli/src/scheduler/task/task_create.rs b/cli/src/scheduler/task/task_create.rs new file mode 100644 index 00000000..bc08f969 --- /dev/null +++ b/cli/src/scheduler/task/task_create.rs @@ -0,0 +1,207 @@ +use { + crate::{ + command_title, + display::json_output, + loading, + notify_success, + prelude::*, + scheduler::helpers, + sui::get_nexus_client, + workflow, + }, + nexus_sdk::{ + events::NexusEventKind, + nexus::scheduler::{CreateTaskParams, GeneratorKind, OccurrenceRequest}, + types::{EncryptionMode, PolicySymbol, StorageConf}, + }, + std::{collections::HashMap, sync::Arc}, +}; + +/// Create a scheduler task and optionally enqueue the initial occurrence. +#[allow(clippy::too_many_arguments)] +pub(crate) async fn create_task( + dag_id: sui::types::Address, + entry_group: String, + mut input_json: Option, + remote: Vec, + metadata: Vec, + execution_priority_fee_per_gas_unit: u64, + schedule_start_ms: Option, + schedule_start_offset_ms: Option, + schedule_deadline_offset_ms: Option, + schedule_priority_fee_per_gas_unit: u64, + generator: GeneratorKind, + gas: GasArgs, +) -> AnyResult<(), NexusCliError> { + command_title!( + "Creating scheduler task for DAG '{dag_id}'", + dag_id = dag_id + ); + + // Load CLI configuration. + let conf = CliConf::load().await.unwrap_or_default(); + + let nexus_client = get_nexus_client(gas.sui_gas_coin, gas.sui_gas_budget).await?; + + // Parse metadata arguments and prepare the DAG input payload. + let metadata_pairs = helpers::parse_metadata(&metadata)?; + let input_json = input_json.take().unwrap_or_else(|| serde_json::json!({})); + + // Fetch encrypted entry ports. + let encrypt_handles = + helpers::fetch_encryption_targets(nexus_client.crawler(), &dag_id, &entry_group).await?; + + // Build the remote storage configuration. + let preferred_remote_storage = conf.data_storage.preferred_remote_storage; + let storage_conf: StorageConf = conf.data_storage.clone().into(); + + // Acquire a session for potential encryption/remote storage commits. + let session = CryptoConf::get_active_session(None).await.map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to get active session: {}.\nPlease initiate a session first.\n\n{init_key}\n{crypto_auth}", + e, + init_key = "$ nexus crypto init-key --force", + crypto_auth = "$ nexus crypto auth" + )) + })?; + + let ports_data = workflow::process_entry_ports( + &input_json, + preferred_remote_storage, + &encrypt_handles, + &remote, + EncryptionMode::LimitedPersistent, + ) + .await?; + + let mut input_data = HashMap::new(); + for (vertex, data) in ports_data { + let committed = data + .commit_all(&storage_conf, Arc::clone(&session)) + .await + .map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to store data: {e}.\nEnsure remote storage is configured.\n\n{command}\n{testnet_command}", + e = e, + command = "$ nexus conf set --data-storage.walrus-publisher-url --data-storage.walrus-save-for-epochs ", + testnet_command = "Or for testnet simply: $ nexus conf set --data-storage.testnet" + )) + })?; + input_data.insert(vertex, committed); + } + + if encrypt_handles.values().any(|ports| !ports.is_empty()) { + session.lock().await.commit_sender(None); + } + + CryptoConf::release_session(session, None) + .await + .map_err(|e| NexusCliError::Any(anyhow!("Failed to release session: {e}")))?; + + let schedule_requested = schedule_start_ms.is_some() + || schedule_start_offset_ms.is_some() + || schedule_deadline_offset_ms.is_some(); + + let initial_schedule = if schedule_requested { + Some( + OccurrenceRequest::new( + schedule_start_ms, + None, + schedule_start_offset_ms, + schedule_deadline_offset_ms, + schedule_priority_fee_per_gas_unit, + true, + ) + .map_err(NexusCliError::Nexus)?, + ) + } else { + None + }; + + if matches!(generator, GeneratorKind::Periodic) && initial_schedule.is_some() { + return Err(NexusCliError::Any(anyhow!( + "Periodic tasks cannot enqueue an initial occurrence. Configure scheduling with `nexus scheduler periodic set`." + ))); + } + + let tx_handle = loading!("Submitting scheduler task transaction..."); + + let result = nexus_client + .scheduler() + .create_task(CreateTaskParams { + dag_id, + entry_group: entry_group.clone(), + input_data, + metadata: metadata_pairs, + execution_priority_fee_per_gas_unit, + initial_schedule, + generator, + }) + .await + .map_err(NexusCliError::Nexus)?; + + tx_handle.success(); + + let mut result_json = serde_json::json!({ + "digest": result.tx_digest, + "task_id": result.task_id, + }); + + let mut scheduled_event_display = None; + + if let Some(schedule) = result.initial_schedule.as_ref() { + result_json["schedule_digest"] = serde_json::json!(schedule.tx_digest); + if let Some(event) = schedule.event.clone() { + result_json["scheduled"] = serde_json::to_value(&event).unwrap_or_default(); + scheduled_event_display = describe_occurrence_event(&event); + } + } + + notify_success!( + "Scheduler task created: {task_id}", + task_id = result.task_id.to_string().truecolor(100, 100, 100) + ); + + if let Some(description) = scheduled_event_display { + notify_success!( + "Initial occurrence scheduled: {event}", + event = description.truecolor(100, 100, 100) + ); + } + + // Always save the updated config. + conf.save().await.map_err(NexusCliError::Any)?; + + json_output(&result_json)?; + + Ok(()) +} + +fn describe_occurrence_event(event: &NexusEventKind) -> Option { + match event { + NexusEventKind::RequestScheduledOccurrence(env) => Some(format!( + "task={} start_ms={} (generator={}, priority={})", + env.request.task, + env.start_ms, + describe_generator(&env.request.generator), + env.priority + )), + NexusEventKind::RequestScheduledWalk(env) => Some(format!( + "walk for dag execution start_ms={} (priority={})", + env.start_ms, env.priority + )), + NexusEventKind::OccurrenceScheduled(e) => Some(format!( + "task={} (generator={})", + e.task, + describe_generator(&e.generator) + )), + _ => None, + } +} + +fn describe_generator(symbol: &PolicySymbol) -> String { + match symbol { + PolicySymbol::Witness(name) => name.name.clone(), + PolicySymbol::Uid(uid) => uid.to_string(), + } +} diff --git a/cli/src/scheduler/task/task_inspect.rs b/cli/src/scheduler/task/task_inspect.rs new file mode 100644 index 00000000..905c8d13 --- /dev/null +++ b/cli/src/scheduler/task/task_inspect.rs @@ -0,0 +1,66 @@ +use { + crate::{ + command_title, + display::json_output, + item, + loading, + notify_success, + prelude::*, + sui::*, + }, + nexus_sdk::types::Task, + serde_json::json, +}; + +/// Inspect a scheduler task and display metadata plus raw JSON output. +pub(crate) async fn inspect_task(task_id: sui::types::Address) -> AnyResult<(), NexusCliError> { + command_title!("Inspecting scheduler task '{task_id}'", task_id = task_id); + + let nexus_client = get_nexus_client(None, DEFAULT_GAS_BUDGET).await?; + let crawler = nexus_client.crawler(); + + let objects_handle = loading!("Fetching task object..."); + + // Fetch the task object from chain. + let task = crawler + .get_object::(task_id) + .await + .map_err(|e| NexusCliError::Any(anyhow!(e)))?; + + objects_handle.success(); + + let task_ref = task.object_ref(); + let task_data = task.data; + + notify_success!( + "Task owner: {owner}", + owner = task_data.owner.to_string().truecolor(100, 100, 100) + ); + + let metadata = task_data.metadata.values.inner(); + item!("Metadata entries: {count}", count = metadata.len()); + for (key, value) in metadata.iter().take(10) { + item!( + " {key}: {value}", + key = key.truecolor(100, 100, 100), + value = value + ); + } + if metadata.len() > 10 { + item!( + " ... ({remain} more entries)", + remain = metadata.len() - 10 + ); + } + + json_output(&json!({ + "task_ref": { + "object_id": task_ref.object_id(), + "version": task_ref.version(), + "digest": task_ref.digest(), + }, + "task": task_data, + }))?; + + Ok(()) +} diff --git a/cli/src/scheduler/task/task_metadata.rs b/cli/src/scheduler/task/task_metadata.rs new file mode 100644 index 00000000..2be90f48 --- /dev/null +++ b/cli/src/scheduler/task/task_metadata.rs @@ -0,0 +1,46 @@ +use { + crate::{ + command_title, + display::json_output, + notify_success, + prelude::*, + scheduler::helpers, + sui::get_nexus_client, + }, + serde_json::json, +}; + +/// Update all metadata entries for a scheduler task. +pub(crate) async fn update_task_metadata( + task_id: sui::types::Address, + metadata: Vec, + gas: GasArgs, +) -> AnyResult<(), NexusCliError> { + command_title!( + "Updating scheduler metadata for task '{task_id}'", + task_id = task_id + ); + + let metadata_pairs = helpers::parse_metadata(&metadata)?; + + let nexus_client = get_nexus_client(gas.sui_gas_coin, gas.sui_gas_budget).await?; + + let result = nexus_client + .scheduler() + .update_metadata(task_id, metadata_pairs.clone()) + .await + .map_err(NexusCliError::Nexus)?; + + notify_success!( + "Metadata updated for task {task_id}", + task_id = task_id.to_string().truecolor(100, 100, 100) + ); + + json_output(&json!({ + "digest": result.tx_digest, + "task_id": task_id, + "metadata_entries": metadata_pairs.len(), + }))?; + + Ok(()) +} diff --git a/cli/src/scheduler/task/task_state.rs b/cli/src/scheduler/task/task_state.rs new file mode 100644 index 00000000..823d5a9c --- /dev/null +++ b/cli/src/scheduler/task/task_state.rs @@ -0,0 +1,70 @@ +use { + crate::{ + command_title, + display::json_output, + notify_success, + prelude::*, + sui::get_nexus_client, + }, + nexus_sdk::nexus::scheduler::TaskStateAction, + serde_json::json, +}; + +#[derive(Clone, Copy, Debug)] +pub(crate) enum TaskStateRequest { + Pause, + Resume, + Cancel, +} + +impl std::fmt::Display for TaskStateRequest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let verb = match self { + TaskStateRequest::Pause => "Pausing", + TaskStateRequest::Resume => "Resuming", + TaskStateRequest::Cancel => "Canceling", + }; + write!(f, "{verb}") + } +} + +/// Toggle scheduler task state between paused, resumed, or canceled. +pub(crate) async fn set_task_state( + task_id: sui::types::Address, + gas: GasArgs, + request: TaskStateRequest, +) -> AnyResult<(), NexusCliError> { + command_title!("{request} scheduler task '{task_id}'"); + + let nexus_client = get_nexus_client(gas.sui_gas_coin, gas.sui_gas_budget).await?; + + let action = match request { + TaskStateRequest::Pause => TaskStateAction::Pause, + TaskStateRequest::Resume => TaskStateAction::Resume, + TaskStateRequest::Cancel => TaskStateAction::Cancel, + }; + + let result = nexus_client + .scheduler() + .set_task_state(task_id, action) + .await + .map_err(NexusCliError::Nexus)?; + + match request { + TaskStateRequest::Pause => notify_success!("Task paused"), + TaskStateRequest::Resume => notify_success!("Task resumed"), + TaskStateRequest::Cancel => notify_success!("Task canceled"), + } + + json_output(&json!({ + "digest": result.tx_digest, + "task_id": task_id, + "state": match request { + TaskStateRequest::Pause => "paused", + TaskStateRequest::Resume => "resumed", + TaskStateRequest::Cancel => "canceled", + }, + }))?; + + Ok(()) +} diff --git a/cli/src/sui.rs b/cli/src/sui.rs index a480cbbf..17f54b4e 100644 --- a/cli/src/sui.rs +++ b/cli/src/sui.rs @@ -1,225 +1,162 @@ use { - crate::{loading, notify_success, prelude::*}, - nexus_sdk::{nexus::client::NexusClient, object_crawler::fetch_one, sui}, + crate::{loading, prelude::*}, + base64::{prelude::BASE64_STANDARD, Engine}, + nexus_sdk::{nexus::client::NexusClient, sui}, }; /// Build Sui client for the provided Sui net. -pub(crate) async fn build_sui_client(conf: &SuiConf) -> AnyResult { - let building_handle = loading!("Building Sui client..."); - let client; - - let builder = sui::ClientBuilder::default(); - - if let Ok(sui_rpc_url) = std::env::var("SUI_RPC_URL") { - client = builder.build(sui_rpc_url).await - } else if let Some(sui_rpc_url) = &conf.rpc_url { - client = builder.build(sui_rpc_url).await - } else { - client = match conf.net { - SuiNet::Localnet => builder.build_localnet().await, - SuiNet::Devnet => builder.build_devnet().await, - SuiNet::Testnet => builder.build_testnet().await, - SuiNet::Mainnet => todo!("Mainnet not yet supported"), - }; - } +pub(crate) async fn build_sui_grpc_client( + conf: &CliConf, +) -> AnyResult>, NexusCliError> { + let client_handle = loading!("Building Sui client..."); + + // Try to get the `SUI_RPC_URL` from the environment, otherwise use + // the configuration. + let Some(url) = std::env::var("SUI_RPC_URL") + .ok() + .or_else(|| conf.sui.rpc_url.as_ref().map(|u| u.to_string())) + else { + client_handle.error(); + + return Err(NexusCliError::Any(anyhow!( + "{message}\n\n{command}", + message = "The Sui RPC URL is not configured. Please set it via the environment variable or the CLI configuration.", + command = "$ nexus conf --sui.rpc-url ".to_string().bold(), + ))); + }; - match client { + match sui::grpc::Client::new(url) { Ok(client) => { - building_handle.success(); + client_handle.success(); - Ok(client) + Ok(Arc::new(Mutex::new(client))) } Err(e) => { - building_handle.error(); + client_handle.error(); - Err(NexusCliError::Sui(e)) + Err(NexusCliError::Rpc(e.into())) } } } -/// Create a wallet context from the provided path. -pub(crate) async fn create_wallet_context( - path: &Path, - net: SuiNet, -) -> AnyResult { - let wallet_handle = loading!("Initiating SUI wallet..."); - - let request_timeout = None; - let max_concurrent_requests = None; - - let wallet = match sui::WalletContext::new(path, request_timeout, max_concurrent_requests) { - Ok(wallet) => wallet, - Err(e) => { - wallet_handle.error(); - - return Err(NexusCliError::Any(e)); - } - }; - - // Check that the Sui net matches. - if wallet.config.active_env != get_sui_env(net).map(|env| env.alias) { - wallet_handle.error(); - - if let Some(active_env) = wallet.config.active_env.as_ref() { - return Err(NexusCliError::Any(anyhow!( - "{message}\n\n{command}", - message = "The Sui net of the wallet does not match the provided Sui net. Either use a different wallet or run:", - command = format!("$ nexus conf --sui.net {active_env}").bold(), - ))); - } - - return Err(NexusCliError::Any(anyhow!( - "The Sui net of the wallet is not set. Please fix the Sui client configuration." - ))); +/// Parses an Ed25519 private key from base64. +/// +/// Tries formats in order (like Sui's keytool import): +/// 1. Base64 33 bytes (flag + key) - Sui format, flag must be 0x00 (ed25519) +/// 2. Base64 32 bytes (raw key) - assumes Ed25519 +fn parse_ed25519_private_key( + pk_encoded: &str, +) -> AnyResult { + let pk_bytes = BASE64_STANDARD + .decode(pk_encoded) + .map_err(|e| format!("Failed to decode Sui private key from base64: {e}"))?; + + // Try Sui format: 33 bytes (flag + key) + if let Ok(bytes) = <[u8; 33]>::try_from(pk_bytes.as_slice()) { + const ED25519_FLAG: u8 = 0x00; + return match bytes[0] { + ED25519_FLAG => Ok(sui::crypto::Ed25519PrivateKey::new( + bytes[1..].try_into().unwrap(), + )), + flag => Err(format!( + "unsupported key scheme flag 0x{flag:02x}, only ed25519 (0x00) is supported" + )), + }; } - wallet_handle.success(); + // Try raw Ed25519: 32 bytes + if let Ok(bytes) = <[u8; 32]>::try_from(pk_bytes.as_slice()) { + return Ok(sui::crypto::Ed25519PrivateKey::new(bytes)); + } - Ok(wallet) + Err(format!( + "invalid private key length {}, expected 32 (raw ed25519) or 33 (sui format with flag)", + pk_bytes.len() + )) } -/// Fetch all coins owned by the provided address. -pub(crate) async fn fetch_all_coins_for_address( - sui: &sui::Client, - addr: sui::Address, -) -> AnyResult, NexusCliError> { - let coins_handle = loading!("Fetching coins..."); - - let limit = None; - let mut cursor = None; - let mut results = Vec::new(); - - // Keep fetching gas coins until there are no more pages. - loop { - let default_to_sui_coin_type = None; - - let response = match sui - .coin_read_api() - .get_coins(addr, default_to_sui_coin_type, cursor, limit) - .await - { - Ok(response) => response, - Err(e) => { - coins_handle.error(); - - return Err(NexusCliError::Sui(e)); - } - }; +/// Create a wallet context from the provided path. +pub(crate) async fn get_signing_key( + conf: &CliConf, +) -> AnyResult { + let key_handle = loading!("Retrieving Sui signing key..."); + + // Try to get the `SUI_PK` from the environment, otherwise use the + // configuration. This value is a base64 encoded string of the private key + // bytes. + let Some(pk_encoded) = std::env::var("SUI_PK") + .ok() + .or_else(|| conf.sui.pk.clone().map(|pk| pk.peek().to_string())) + else { + key_handle.error(); - cursor = response.next_cursor; - results.extend(response.data); + return Err(NexusCliError::Any(anyhow!( + "{message}\n\n{command}", + message = "The Sui private key is not configured. Please set it via environment or the CLI configuration.", + command = "$ nexus conf --sui.pk ".to_string().bold(), + ))); + }; - if !response.has_next_page { - break; + match parse_ed25519_private_key(&pk_encoded) { + Ok(key) => { + key_handle.success(); + Ok(key) } - } - - coins_handle.success(); - - Ok(results) -} - -/// Fetch reference gas price from Sui. -pub(crate) async fn fetch_reference_gas_price(sui: &sui::Client) -> AnyResult { - let gas_price_handle = loading!("Fetching reference gas price..."); - - let response = match sui.read_api().get_reference_gas_price().await { - Ok(response) => response, Err(e) => { - gas_price_handle.error(); - - return Err(NexusCliError::Sui(e)); + key_handle.error(); + Err(NexusCliError::Any(anyhow!("{e}"))) } - }; - - gas_price_handle.success(); - - Ok(response) + } } -/// Sign a transaction with the provided wallet and execute it. -/// -/// Returns `Ok` with the transaction block response if successful, or `Err` if -/// the signing or the execution fails, or if the response contains errors. -pub(crate) async fn sign_and_execute_transaction( - sui: &sui::Client, - wallet: &sui::WalletContext, - tx_data: sui::TransactionData, -) -> AnyResult { - let signing_handle = loading!("Signing transaction..."); - - let envelope = wallet.sign_transaction(&tx_data); - - let resp_options = sui::TransactionBlockResponseOptions::new() - .with_balance_changes() - .with_effects() - .with_object_changes() - .with_events(); - - // We want to confirm that the tx was executed (the name of this variant is - // misleading). - let resp_finality = sui::ExecuteTransactionRequestType::WaitForLocalExecution; - - let response = match sui - .quorum_driver_api() - .execute_transaction_block(envelope, resp_options, Some(resp_finality)) +/// Fetch all coins owned by the provided address. +pub(crate) async fn fetch_coins_for_address( + client: Arc>, + owner: sui::types::Address, +) -> AnyResult, NexusCliError> { + let coins_handle = loading!("Fetching coins..."); + + let request = sui::grpc::ListOwnedObjectsRequest::default() + .with_owner(owner) + .with_page_size(1000) + .with_object_type(sui::types::StructTag::gas_coin()) + .with_read_mask(sui::grpc::FieldMask::from_paths([ + "object_id", + "version", + "digest", + ])); + + let mut client = client.lock().await; + + let response = match client + .state_client() + .list_owned_objects(request) .await + .map(|resp| resp.into_inner()) { Ok(response) => response, Err(e) => { - signing_handle.error(); + coins_handle.error(); - return Err(NexusCliError::Sui(e)); + return Err(NexusCliError::Rpc(e.into())); } }; - if !response.errors.is_empty() { - signing_handle.error(); - - return Err(NexusCliError::Any(anyhow!( - "Transaction failed with errors: {errors:?}", - errors = response.errors - ))); - } + drop(client); - // Check if any effects failed in the TX. - if let Some(sui::TransactionBlockEffects::V1(effect)) = &response.effects { - if let sui::ExecutionStatus::Failure { error } = effect.clone().into_status() { - signing_handle.error(); - - return Err(NexusCliError::Any(anyhow!(error))); - } - }; - - signing_handle.success(); - - notify_success!( - "Transaction digest: {digest}", - digest = response.digest.to_string().truecolor(100, 100, 100) - ); - - Ok(response) -} - -/// Fetch a single object from Sui by its ID. -pub(crate) async fn fetch_object_by_id( - sui: &sui::Client, - object_id: sui::ObjectID, -) -> AnyResult { - let object_handle = loading!("Fetching object {object_id}..."); - - match fetch_one::(sui, object_id).await { - Ok(response) => { - object_handle.success(); - - Ok(response.object_ref()) - } - Err(e) => { - object_handle.error(); + coins_handle.success(); - Err(NexusCliError::Any(e)) - } - } + Ok(response + .objects() + .iter() + .filter_map(|object| { + Some(sui::types::ObjectReference::new( + object.object_id_opt()?.parse().ok()?, + object.version_opt()?, + object.digest_opt()?.parse().ok()?, + )) + }) + .collect()) } /// Wrapping some conf parsing functionality used around the CLI. @@ -236,8 +173,10 @@ pub(crate) async fn get_nexus_objects( } // For some networks, we attempt to load the objects from public endpoints. - let response = match conf.sui.net { - SuiNet::Devnet => fetch_objects_from_url(DEVNET_OBJECTS_TOML).await, + let response = match conf.sui.rpc_url.as_ref() { + Some(url) if url.as_str() == DEVNET_NEXUS_RPC_URL => { + fetch_objects_from_url(DEVNET_OBJECTS_TOML).await + } _ => Err(anyhow!( "Nexus objects are not configured for this network." )), @@ -277,15 +216,18 @@ async fn fetch_objects_from_url(url: &str) -> AnyResult { Ok(objects) } -/// Fetch the gas coin from the Sui client. On Localnet, Devnet and Testnet, we -/// can use the faucet to get the coin. On Mainnet, this fails if the coin is -/// not present. -pub(crate) async fn fetch_gas_coin( - sui: &sui::Client, - addr: sui::Address, - sui_gas_coin: Option, -) -> AnyResult { - let mut coins = fetch_all_coins_for_address(sui, addr).await?; +/// Fetch a coin from the Sui client. +/// +/// `by_address`: If specified, fetch the coin with this object ID. +/// `by_order`: If `by_address` is not specified, fetch the coin by its order in +/// the list of owned coins (0-based). +pub(crate) async fn fetch_coin( + client: Arc>, + owner: sui::types::Address, + by_address: Option, + by_order: usize, +) -> AnyResult { + let mut coins = fetch_coins_for_address(client, owner).await?; if coins.is_empty() { return Err(NexusCliError::Any(anyhow!( @@ -295,416 +237,72 @@ pub(crate) async fn fetch_gas_coin( // If object gas coing object ID was specified, use it. If it was specified // and could not be found, return error. - match sui_gas_coin { + match by_address { Some(id) => { let coin = coins .into_iter() - .find(|coin| coin.coin_object_id == id) + .find(|coin| *coin.object_id() == id) .ok_or_else(|| NexusCliError::Any(anyhow!("Coin '{id}' not found in wallet")))?; Ok(coin) } - None => Ok(coins.remove(0)), - } -} + None => { + if by_order >= coins.len() { + return Err(NexusCliError::Any(anyhow!( + "The wallet does not have enough coins to select coin #{by_order}" + ))); + } -pub(crate) fn resolve_wallet_path( - cli_wallet_path: Option, - conf: &SuiConf, -) -> Result { - if let Some(path) = cli_wallet_path { - Ok(path) - } else if let Ok(mnemonic) = std::env::var("SUI_SECRET_MNEMONIC") { - retrieve_wallet_with_mnemonic(conf.net, &mnemonic).map_err(NexusCliError::Any) - } else { - Ok(conf.wallet_path.clone()) + Ok(coins.swap_remove(by_order)) + } } } /// Create a Nexus client from CLI parameters. pub(crate) async fn get_nexus_client( - sui_gas_coin: Option, + sui_gas_coin: Option, sui_gas_budget: u64, -) -> Result<(NexusClient, sui::Client), NexusCliError> { - // Load CLI configuration. +) -> Result { let mut conf = CliConf::load().await.unwrap_or_default(); - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui_client = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui_client, address, sui_gas_coin).await?; + let client = build_sui_grpc_client(&conf).await?; + let pk = get_signing_key(&conf).await?; + let owner = pk.public_key().derive_address(); + let gas_coin = fetch_coin(client.clone(), owner, sui_gas_coin, 0).await?; + let nexus_objects = get_nexus_objects(&mut conf).await?; + let rpc_url = client.lock().await.uri().to_string(); + + // Try to get the `SUI_GQL_URL` from the environment, otherwise use + // the configuration. + let Some(gql_url) = std::env::var("SUI_GQL_URL") + .ok() + .or_else(|| conf.sui.gql_url.as_ref().map(|u| u.to_string())) + else { + return Err(NexusCliError::Any(anyhow!( + "{message}\n\n{command}", + message = + "The Sui GraphQL URL is not configured. Please set it via environment or the CLI configuration.", + command = "$ nexus conf --sui.gql-url ".to_string().bold(), + ))); + }; // Create Nexus client. let nexus_client = NexusClient::builder() - .with_wallet_context(wallet) - .with_nexus_objects(objects.clone()) - .with_gas(vec![&gas_coin], sui_gas_budget) - .map_err(NexusCliError::Nexus)? + .with_private_key(pk) + .with_nexus_objects(nexus_objects.clone()) + .with_gas(vec![gas_coin], sui_gas_budget) + .with_rpc_url(&rpc_url) + .with_gql_url(&gql_url) .build() .await .map_err(NexusCliError::Nexus)?; - Ok((nexus_client, sui_client)) -} - -fn retrieve_wallet_with_mnemonic(net: SuiNet, mnemonic: &str) -> Result { - // Determine configuration paths. - let config_dir = sui::config_dir()?; - let wallet_conf_path = config_dir.join(sui::CLIENT_CONFIG); - let keystore_path = config_dir.join(sui::KEYSTORE_FILENAME); - - // Ensure the keystore exists. - if !keystore_path.exists() { - let keystore = sui::FileBasedKeystore::new(&keystore_path)?; - keystore.save()?; - } - - // If the wallet config file does not exist, create it. - if !wallet_conf_path.exists() { - let keystore = sui::FileBasedKeystore::new(&keystore_path)?; - let mut client_config = sui::ClientConfig::new(keystore.into()); - if let Some(env) = get_sui_env(net) { - client_config.add_env(env); - } - if client_config.active_env.is_none() { - client_config.active_env = client_config.envs.first().map(|env| env.alias.clone()); - } - - client_config.save(&wallet_conf_path)?; - println!("Client config file is stored in {:?}.", &wallet_conf_path); - } - - // Import the mnemonic into the keystore. - let mut keystore = sui::FileBasedKeystore::new(&keystore_path)?; - let imported_address = - keystore.import_from_mnemonic(mnemonic, sui::SignatureScheme::ED25519, None, None)?; - - // Read the existing client configuration. - let mut client_config: sui::ClientConfig = sui::PersistedConfig::read(&wallet_conf_path)?; - - client_config.active_address = Some(imported_address); - client_config.save(&wallet_conf_path)?; - - Ok(wallet_conf_path) -} - -fn get_sui_env(net: SuiNet) -> Option { - let alias = match net { - SuiNet::Localnet => "localnet".to_string(), - SuiNet::Devnet => "devnet".to_string(), - SuiNet::Testnet => "testnet".to_string(), - SuiNet::Mainnet => todo!("Mainnet not yet supported"), - }; - - if let Ok(sui_rpc_url) = std::env::var("SUI_RPC_URL") { - Some(sui::Env { - alias, - rpc: sui_rpc_url, - ws: None, - basic_auth: None, - }) - } else { - let rpc = match net { - SuiNet::Localnet => sui::LOCAL_NETWORK_URL.into(), - SuiNet::Devnet => sui::DEVNET_URL.into(), - SuiNet::Testnet => sui::TESTNET_URL.into(), - SuiNet::Mainnet => todo!("Mainnet not yet supported"), - }; - - Some(sui::Env { - alias, - rpc, - ws: None, - basic_auth: None, - }) - } + Ok(nexus_client) } #[cfg(test)] mod tests { - use { - super::*, - assert_matches::assert_matches, - mockito::Server, - nexus_sdk::sui::Address, - rstest::rstest, - serial_test::serial, - tempfile::tempdir, - }; - - #[rstest( - cli_wallet_path, - mnemonic_env, - expected, - case( - Some(PathBuf::from("/tmp/sui/config/client.toml")), - None, - PathBuf::from("/tmp/sui/config/client.toml") - ), - case(None, None, PathBuf::from("/tmp/sui/config/client.toml")), - case( - None, - Some("include zoo tiger rural ball demand senior asthma tunnel hero ritual domain"), - PathBuf::from("/tmp/sui/config/client.yaml") - ) - )] - #[serial] - fn test_resolve_wallet_path( - cli_wallet_path: Option, - mnemonic_env: Option<&str>, - expected: PathBuf, - ) { - let sui_default_config = "/tmp/sui/config"; - // Set the default sui config folder to /tmp - std::env::set_var("SUI_CONFIG_DIR", sui_default_config); - - // Set or remove the mnemonic environment variable as needed. - if let Some(mnemonic) = mnemonic_env { - std::env::set_var("SUI_SECRET_MNEMONIC", mnemonic); - } else { - std::env::remove_var("SUI_SECRET_MNEMONIC"); - } - - // Prepare the SuiConf instance. - let conf = SuiConf { - net: SuiNet::Localnet, - wallet_path: PathBuf::from(format!("{}/client.toml", &sui_default_config)), - rpc_url: None, - }; - - // Call the function under test. - let resolved = resolve_wallet_path(cli_wallet_path, &conf).unwrap(); - assert_eq!(resolved, expected); - - // Clean up the env variable. - std::env::remove_var("SUI_SECRET_MNEMONIC"); - let _ = std::fs::remove_dir_all(sui_default_config); - } - - #[rstest( - mnemonic, - expected_address_str, - case( - "include zoo tiger rural ball demand senior asthma tunnel hero ritual domain", - "0x479c168e5ac1319a78b09eb922a26472fbad9fc9ac904b17453eb71f4d7eb831" - ), - case( - "just place income emotion clutch column pledge same pool twist finish proof", - "0xe58c2145af0546e7be946b214e908d7e08e99e907950b428dcfe1dc9d8d8c449" - ) - )] - #[serial] - fn test_active_address_set_by_mnemonic(mnemonic: &str, expected_address_str: &str) { - // Set up a clean temporary config directory. - let temp_dir = tempdir().unwrap(); - let sui_default_config = temp_dir.path().to_str().unwrap(); - // Set the default sui config folder to /tmp - std::env::set_var("SUI_CONFIG_DIR", sui_default_config); - let config_dir = sui::config_dir().expect("Failed to get config dir"); - let wallet_conf_path = config_dir.join(sui::CLIENT_CONFIG); - let keystore_path = config_dir.join(sui::KEYSTORE_FILENAME); - - // Clean up any existing files. - let _ = std::fs::remove_file(&wallet_conf_path); - let _ = std::fs::remove_file(&keystore_path); - - // Call the function under test. - let _ = retrieve_wallet_with_mnemonic(SuiNet::Localnet, mnemonic) - .expect("retrieve_wallet_with_mnemonic failed"); - - let client_config: sui::ClientConfig = - sui::PersistedConfig::read(&wallet_conf_path).expect("Failed to read client config"); - let expected_address: Address = expected_address_str.parse().expect("Invalid address"); - assert_eq!(client_config.active_address.unwrap(), expected_address); - - let _ = std::fs::remove_dir_all(&config_dir); - } - - #[rstest( - preexisting_mnemonic, - new_mnemonic, - expected_active_address_str, - case( - "include zoo tiger rural ball demand senior asthma tunnel hero ritual domain", - "just place income emotion clutch column pledge same pool twist finish proof", - "0xe58c2145af0546e7be946b214e908d7e08e99e907950b428dcfe1dc9d8d8c449" - ) - )] - #[serial] - fn test_active_address_with_preexisting_keystore( - preexisting_mnemonic: &str, - new_mnemonic: &str, - expected_active_address_str: &str, - ) { - // Create a temporary config directory. - let temp_dir = tempdir().unwrap(); - let config_dir: PathBuf = temp_dir.path().to_path_buf(); - std::env::set_var("SUI_CONFIG_DIR", config_dir.to_str().unwrap()); - - let wallet_conf_path = config_dir.join(sui::CLIENT_CONFIG); - let keystore_path = config_dir.join(sui::KEYSTORE_FILENAME); - - // Create a preexisting keystore file with an active address derived from preexisting_mnemonic. - { - // FileBasedKeystore is assumed to be your real implementation. - let mut preexisting_keystore = - sui::FileBasedKeystore::new(&keystore_path).expect("Failed to create keystore"); - preexisting_keystore - .import_from_mnemonic( - preexisting_mnemonic, - sui::SignatureScheme::ED25519, - None, - None, - ) - .expect("Failed to import preexisting mnemonic"); - preexisting_keystore - .save() - .expect("Failed to save keystore"); - } - - // Create a default client configuration if it doesn't exist. - if !wallet_conf_path.exists() { - let keystore = - sui::FileBasedKeystore::new(&keystore_path).expect("Failed to create keystore"); - let client_config = sui::ClientConfig::new(keystore.into()); - client_config - .save(&wallet_conf_path) - .expect("Failed to save client config"); - } - - // Call retrieve_wallet_with_mnemonic with the new mnemonic. - // This should import the new mnemonic into the preexisting keystore so that its derived - // address becomes the first (active) address. - let _ = retrieve_wallet_with_mnemonic(SuiNet::Localnet, new_mnemonic) - .expect("retrieve_wallet_with_mnemonic failed"); - - // Read the updated client configuration. - let updated_config: sui::ClientConfig = - sui::PersistedConfig::read(&wallet_conf_path).expect("Failed to read client config"); - - // Convert the expected address string into a SuiAddress. - let expected_active_address: Address = expected_active_address_str - .parse() - .expect("Invalid SuiAddress string"); - - // The active address in the config should match the one derived from the new mnemonic. - assert_eq!( - updated_config.active_address.unwrap(), - expected_active_address - ); - - // Clean up temporary files. - let _ = std::fs::remove_dir_all(&config_dir); - } - - #[rstest] - #[tokio::test] - #[serial] - async fn test_create_wallet_context() { - // Set up a clean temporary config directory - let temp_dir = tempdir().unwrap(); - let sui_config_dir = temp_dir.path().to_str().unwrap(); - std::env::set_var("SUI_CONFIG_DIR", sui_config_dir); - - std::env::set_var( - "SUI_SECRET_MNEMONIC", - "cost harsh bright regular skin trumpet pave about edit forget isolate monkey", - ); - - let conf = SuiConf { - net: SuiNet::Localnet, - wallet_path: PathBuf::from("/invalid"), - rpc_url: None, - }; - - let path = resolve_wallet_path(None, &conf).expect("Failed to resolve wallet path"); - - let wallet = create_wallet_context(&path, SuiNet::Localnet).await; - - match wallet { - Ok(_) => {} // Test passes - Err(e) => panic!("Expected wallet creation to succeed, but got error: {}", e), - } - - std::env::remove_var("SUI_SECRET_MNEMONIC"); - std::env::remove_var("SUI_CONFIG_DIR"); - } - - #[rstest] - #[tokio::test] - #[serial] - async fn test_create_wallet_context_net_mismatch() { - // Set up a clean temporary config directory - let temp_dir = tempdir().unwrap(); - let sui_config_dir = temp_dir.path().to_str().unwrap(); - std::env::set_var("SUI_CONFIG_DIR", sui_config_dir); - - std::env::set_var( - "SUI_SECRET_MNEMONIC", - "cost harsh bright regular skin trumpet pave about edit forget isolate monkey", - ); - - // Create wallet config for devnet - let conf = SuiConf { - net: SuiNet::Devnet, - wallet_path: PathBuf::from("/invalid"), - rpc_url: None, - }; - - let path = resolve_wallet_path(None, &conf).expect("Failed to resolve wallet path"); - - // Try to use the devnet wallet with localnet - this should fail - let err = create_wallet_context(&path, SuiNet::Localnet) - .await - .err() - .unwrap(); - - assert_matches!(err, NexusCliError::Any(e) if e.to_string().contains("The Sui net of the wallet does not match")); - - std::env::remove_var("SUI_SECRET_MNEMONIC"); - std::env::remove_var("SUI_CONFIG_DIR"); - } - - #[rstest] - #[tokio::test] - #[serial] - async fn test_create_wallet_context_rpc_url() { - // Set up a clean temporary config directory - let temp_dir = tempdir().unwrap(); - let sui_config_dir = temp_dir.path().to_str().unwrap(); - std::env::set_var("SUI_CONFIG_DIR", sui_config_dir); - - std::env::set_var( - "SUI_SECRET_MNEMONIC", - "cost harsh bright regular skin trumpet pave about edit forget isolate monkey", - ); - std::env::set_var("SUI_RPC_URL", "http://localhost:9000"); - - let conf = SuiConf { - net: SuiNet::Devnet, - wallet_path: PathBuf::from("/invalid"), - rpc_url: None, - }; - - let path = resolve_wallet_path(None, &conf).expect("Failed to resolve wallet path"); - - let wallet = create_wallet_context(&path, SuiNet::Devnet).await; - - match wallet { - Ok(_) => {} // Test passes - Err(e) => panic!("Expected wallet creation to succeed, but got error: {}", e), - } - - std::env::remove_var("SUI_SECRET_MNEMONIC"); - std::env::remove_var("SUI_RPC_URL"); - std::env::remove_var("SUI_CONFIG_DIR"); - } + use {super::*, mockito::Server, rstest::rstest}; #[rstest] #[tokio::test] @@ -718,22 +316,22 @@ mod tests { network_id = "0x4" [tool_registry] - objectId = "0x5" + object_id = "0x5" version = 1 digest = "3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv" [default_tap] - objectId = "0x6" + object_id = "0x6" version = 1 digest = "3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv" [gas_service] - objectId = "0x7" + object_id = "0x7" version = 1 digest = "3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv" [pre_key_vault] - objectId = "0x8" + object_id = "0x8" version = 1 digest = "3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv" "# @@ -764,39 +362,113 @@ mod tests { assert_eq!(objects.workflow_pkg_id, "0x2".parse().unwrap()); assert_eq!(objects.interface_pkg_id, "0x3".parse().unwrap()); assert_eq!(objects.network_id, "0x4".parse().unwrap()); - assert_eq!(objects.tool_registry.object_id, "0x5".parse().unwrap()); - assert_eq!(objects.tool_registry.version, 1.into()); assert_eq!( - objects.tool_registry.digest, - "3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv" - .parse() - .unwrap() + *objects.tool_registry.object_id(), + sui::types::Address::from_static("0x5") + ); + assert_eq!(objects.tool_registry.version(), 1); + assert_eq!( + *objects.tool_registry.digest(), + sui::types::Digest::from_static("3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv") + ); + assert_eq!( + *objects.default_tap.object_id(), + sui::types::Address::from_static("0x6") + ); + assert_eq!(objects.default_tap.version(), 1); + assert_eq!( + *objects.default_tap.digest(), + sui::types::Digest::from_static("3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv") ); - assert_eq!(objects.default_tap.object_id, "0x6".parse().unwrap()); - assert_eq!(objects.default_tap.version, 1.into()); assert_eq!( - objects.default_tap.digest, - "3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv" - .parse() - .unwrap() + *objects.gas_service.object_id(), + sui::types::Address::from_static("0x7") ); - assert_eq!(objects.gas_service.object_id, "0x7".parse().unwrap()); - assert_eq!(objects.gas_service.version, 1.into()); + assert_eq!(objects.gas_service.version(), 1); assert_eq!( - objects.gas_service.digest, - "3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv" - .parse() - .unwrap() + *objects.gas_service.digest(), + sui::types::Digest::from_static("3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv") ); - assert_eq!(objects.pre_key_vault.object_id, "0x8".parse().unwrap()); - assert_eq!(objects.pre_key_vault.version, 1.into()); assert_eq!( - objects.pre_key_vault.digest, - "3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv" - .parse() - .unwrap() + *objects.pre_key_vault.object_id(), + sui::types::Address::from_static("0x8") + ); + assert_eq!(objects.pre_key_vault.version(), 1); + assert_eq!( + *objects.pre_key_vault.digest(), + sui::types::Digest::from_static("3LFAfxPb6Q81U8wXg6qc6UyV9Hoj1VdfFfMwvGTEq5Bv") ); mock.assert_async().await; } + + mod parse_ed25519_private_key_tests { + use super::*; + + // Test key generated with: sui keytool generate ed25519 + // mnemonic: "nut garden prefer climb giggle armed snap sibling layer extra obvious fade" + const TEST_KEY_BASE64_WITH_FLAG: &str = "ADvFIUMRieVEkqG05MLT8h8QVd1xZuS6xF9KA2EumjLd"; + const TEST_KEY_BASE64_WITHOUT_FLAG: &str = "O8UhQxGJ5USSobTkwtPyHxBV3XFm5LrEX0oDYS6aMt0="; + const TEST_KEY_ADDRESS: &str = + "0x79d85606d67f3d046098d93d51b5de4c4606743267713fa0338846ec1729dce1"; + + #[test] + fn test_33_bytes_sui_format_with_ed25519_flag() { + // Sui format: 0x00 (ed25519 flag) + 32 byte key + let result = parse_ed25519_private_key(TEST_KEY_BASE64_WITH_FLAG); + assert!(result.is_ok(), "Expected Ok, got: {result:?}"); + + let pk = result.unwrap(); + assert_eq!( + pk.public_key().derive_address().to_string(), + TEST_KEY_ADDRESS + ); + } + + #[test] + fn test_32_bytes_raw_ed25519_key() { + // Raw 32-byte key without flag (leader format) + let result = parse_ed25519_private_key(TEST_KEY_BASE64_WITHOUT_FLAG); + assert!(result.is_ok(), "Expected Ok, got: {result:?}"); + + let pk = result.unwrap(); + // Same address as above - same key, just different encoding + assert_eq!( + pk.public_key().derive_address().to_string(), + TEST_KEY_ADDRESS + ); + } + + #[test] + fn test_33_bytes_with_unsupported_flag_fails() { + // 0x01 is secp256k1 flag - not supported + let mut bytes = vec![0x01]; // secp256k1 flag + bytes.extend_from_slice(&[0u8; 32]); // dummy key + let input = BASE64_STANDARD.encode(&bytes); + + let result = parse_ed25519_private_key(&input); + assert!(result.is_err()); + assert!( + result + .unwrap_err() + .contains("unsupported key scheme flag 0x01"), + "Expected unsupported flag error" + ); + } + + #[test] + fn test_invalid_length_fails() { + // 31 bytes - neither 32 nor 33 + let bytes = [0u8; 31]; + let input = BASE64_STANDARD.encode(bytes); + + let result = parse_ed25519_private_key(&input); + assert!(result.is_err()); + let err = result.unwrap_err(); + assert!( + err.contains("invalid private key length 31"), + "Expected length error, got: {err}" + ); + } + } } diff --git a/cli/src/tool/mod.rs b/cli/src/tool/mod.rs index 53bbffe2..54c8e673 100644 --- a/cli/src/tool/mod.rs +++ b/cli/src/tool/mod.rs @@ -1,7 +1,8 @@ mod tool_claim_collateral; mod tool_list; mod tool_new; -mod tool_register; +mod tool_register_offchain; +mod tool_register_onchain; mod tool_set_invocation_cost; mod tool_unregister; mod tool_validate; @@ -11,12 +12,132 @@ use { tool_claim_collateral::*, tool_list::*, tool_new::*, - tool_register::*, + tool_register_offchain::register_off_chain_tool, + tool_register_onchain::register_onchain_tool, tool_set_invocation_cost::*, tool_unregister::*, - tool_validate::*, + tool_validate::{validate_off_chain_tool, validate_on_chain_tool}, }; +#[derive(Subcommand)] +pub(crate) enum RegisterCommand { + #[command(about = "Register an offchain tool")] + Offchain { + #[arg(long = "url", short = 'u', help = "The URL of the offchain tool")] + url: reqwest::Url, + + #[arg( + long = "collateral-coin", + short = 'c', + help = "The collateral coin object ID. Second coin object is chosen if not present.", + value_name = "OBJECT_ID" + )] + collateral_coin: Option, + + #[arg( + long = "invocation-cost", + short = 'i', + help = "What is the cost of invoking this tool in MIST.", + default_value = "0", + value_name = "MIST" + )] + invocation_cost: u64, + + #[arg( + long = "batch", + help = "Should all tools on a webserver be registered at once?" + )] + batch: bool, + + #[arg( + long = "no-save", + help = "If this flag is set, the tool owner caps will not be saved to the local config file." + )] + no_save: bool, + + #[command(flatten)] + gas: GasArgs, + }, + + #[command(about = "Register an onchain tool")] + Onchain { + #[arg( + long = "package", + short = 'p', + help = "The onchain tool package address", + value_name = "ADDRESS" + )] + package: sui::types::Address, + + #[arg(long = "module", short = 'm', help = "The onchain tool module name")] + module: sui::types::Identifier, + + #[arg( + long = "tool-fqn", + short = 't', + help = "The fully qualified name (FQN) for this tool.", + value_name = "FQN" + )] + tool_fqn: ToolFqn, + + #[arg( + long = "description", + short = 'd', + help = "Description of what the tool does.", + value_name = "DESCRIPTION" + )] + description: String, + + #[arg( + long = "witness-id", + short = 'w', + help = "The witness object ID that proves the tool's identity.", + value_name = "OBJECT_ID" + )] + witness_id: sui::types::Address, + + #[arg( + long = "collateral-coin", + short = 'c', + help = "The collateral coin object ID. Second coin object is chosen if not present.", + value_name = "OBJECT_ID" + )] + collateral_coin: Option, + + #[arg( + long = "no-save", + help = "If this flag is set, the tool owner caps will not be saved to the local config file." + )] + no_save: bool, + + #[command(flatten)] + gas: GasArgs, + }, +} + +#[derive(Subcommand)] +pub(crate) enum ValidateCommand { + #[command(about = "Validate an offchain tool")] + Offchain { + #[arg( + long = "url", + short = 'u', + help = "The URL of the offchain tool to validate" + )] + url: reqwest::Url, + }, + + #[command(about = "Validate an onchain tool")] + Onchain { + #[arg( + long = "ident", + short = 'i', + help = "The identifier of the onchain tool to validate" + )] + ident: String, + }, +} + #[derive(Subcommand)] pub(crate) enum ToolCommand { #[command(about = "Create a new tool scaffolding with the specified name and template.")] @@ -45,47 +166,16 @@ pub(crate) enum ToolCommand { target: PathBuf, }, - #[command(about = "Validate a tool based on its identifier.")] + #[command(about = "Validate a tool based on its type.")] Validate { - /// The ident of the Tool to validate. - #[command(flatten)] - ident: ToolIdent, + #[command(subcommand)] + tool_type: ValidateCommand, }, - #[command(about = "Register a tool based on its identifier.")] + #[command(about = "Register a tool based on its type.")] Register { - /// The collateral coin object ID. Second coin object is chosen if not - /// present. - #[arg( - long = "collateral-coin", - short = 'c', - help = "The collateral coin object ID. Second coin object is chosen if not present.", - value_name = "OBJECT_ID" - )] - collateral_coin: Option, - #[arg( - long = "invocation-cost", - short = 'i', - help = "What is the cost of invoking this tool in MIST.", - default_value = "0", - value_name = "MIST" - )] - invocation_cost: u64, - #[arg( - long = "batch", - help = "Should all tools on a webserver be registered at once?" - )] - batch: bool, - #[arg( - long = "no-save", - help = "If this flag is set, the tool owner caps will not be saved to the local config file." - )] - no_save: bool, - /// The ident of the Tool to register. - #[command(flatten)] - ident: ToolIdent, - #[command(flatten)] - gas: GasArgs, + #[command(subcommand)] + tool_type: RegisterCommand, }, #[command(about = "Unregister a tool identified by its FQN.")] @@ -103,7 +193,7 @@ pub(crate) enum ToolCommand { help = "The OwnerCap object ID that must be owned by the sender.", value_name = "OBJECT_ID" )] - owner_cap: Option, + owner_cap: Option, /// Whether to skip the confirmation prompt. #[arg(long = "yes", short = 'y', help = "Skip the confirmation prompt")] skip_confirmation: bool, @@ -126,7 +216,7 @@ pub(crate) enum ToolCommand { help = "The OwnerCap object ID that must be owned by the sender.", value_name = "OBJECT_ID" )] - owner_cap: Option, + owner_cap: Option, #[command(flatten)] gas: GasArgs, }, @@ -146,7 +236,7 @@ pub(crate) enum ToolCommand { help = "The OwnerCap object ID that must be owned by the sender.", value_name = "OBJECT_ID" )] - owner_cap: Option, + owner_cap: Option, #[arg( long = "invocation-cost", short = 'i', @@ -165,28 +255,6 @@ pub(crate) enum ToolCommand { }, } -/// Struct holding an either on-chain or off-chain Tool identifier. Off-chain -/// tools are identified by their URL, while on-chain tools are identified by -/// a Move ident. -#[derive(Args, Clone, Debug)] -#[group(required = true, multiple = false)] -pub(crate) struct ToolIdent { - #[arg( - long = "off-chain", - short = 'f', - help = "The URL of the off-chain Tool to validate", - value_name = "URL" - )] - pub(crate) off_chain: Option, - #[arg( - long = "on-chain", - short = 'n', - help = "The ident of on-chain Tool to validate", - value_name = "IDENT" - )] - pub(crate) on_chain: Option, -} - /// Handle the provided tool command. The [ToolCommand] instance is passed from /// [crate::main]. pub(crate) async fn handle(command: ToolCommand) -> AnyResult<(), NexusCliError> { @@ -199,28 +267,56 @@ pub(crate) async fn handle(command: ToolCommand) -> AnyResult<(), NexusCliError> } => create_new_tool(name, template, target).await, // == `$ nexus tool validate` == - ToolCommand::Validate { ident } => validate_tool(ident).await.map(|_| ()), + ToolCommand::Validate { tool_type } => match tool_type { + ValidateCommand::Offchain { url } => validate_off_chain_tool(url).await.map(|_| ()), + ValidateCommand::Onchain { ident } => validate_on_chain_tool(ident).await.map(|_| ()), + }, // == `$ nexus tool register` == - ToolCommand::Register { - ident, - collateral_coin, - invocation_cost, - batch, - no_save, - gas, - } => { - register_tool( - ident, + ToolCommand::Register { tool_type } => match tool_type { + RegisterCommand::Offchain { + url, collateral_coin, invocation_cost, batch, no_save, - gas.sui_gas_coin, - gas.sui_gas_budget, - ) - .await - } + gas, + } => { + register_off_chain_tool( + url, + collateral_coin, + invocation_cost, + batch, + no_save, + gas.sui_gas_coin, + gas.sui_gas_budget, + ) + .await + } + RegisterCommand::Onchain { + package, + module, + tool_fqn, + description, + witness_id, + collateral_coin, + no_save, + gas, + } => { + register_onchain_tool( + package, + module, + tool_fqn, + description, + witness_id, + collateral_coin, + no_save, + gas.sui_gas_coin, + gas.sui_gas_budget, + ) + .await + } + }, // == `$ nexus tool unregister` == ToolCommand::Unregister { diff --git a/cli/src/tool/templates/move/.gitignore.jinja b/cli/src/tool/templates/move/.gitignore.jinja new file mode 100644 index 00000000..da639131 --- /dev/null +++ b/cli/src/tool/templates/move/.gitignore.jinja @@ -0,0 +1,2 @@ +build/* + diff --git a/cli/src/tool/templates/move/Move.toml.jinja b/cli/src/tool/templates/move/Move.toml.jinja new file mode 100644 index 00000000..33820b4d --- /dev/null +++ b/cli/src/tool/templates/move/Move.toml.jinja @@ -0,0 +1,16 @@ +[package] +name = "{{ name_snake_case }}" +edition = "2024.beta" + +[dependencies] +nexus_primitives = { local = "../../primitives"} +nexus_workflow = { local = "../../workflow"} +nexus_interface = { local = "../../interface"} + + +[addresses] +{{ name_snake_case }} = "0x0" +nexus_primitives = "0x1" +nexus_workflow = "0x2" +nexus_interface = "0x3" + diff --git a/cli/src/tool/templates/move/tests.move.jinja b/cli/src/tool/templates/move/tests.move.jinja new file mode 100644 index 00000000..7192c4a5 --- /dev/null +++ b/cli/src/tool/templates/move/tests.move.jinja @@ -0,0 +1,19 @@ +/* +#[test_only] +module {{ name_snake_case }}::{{ name_snake_case }}_tests; +// uncomment this line to import the module +// use {{ name_snake_case }}::{{ name_snake_case }}; + +const ENotImplemented: u64 = 0; + +#[test] +fun test_{{ name_snake_case }}() { + // pass +} + +#[test, expected_failure(abort_code = ::{{ name_snake_case }}::{{ name_snake_case }}_tests::ENotImplemented)] +fun test_{{ name_snake_case }}_fail() { + abort ENotImplemented +} +*/ + diff --git a/cli/src/tool/templates/move/tool.move.jinja b/cli/src/tool/templates/move/tool.move.jinja new file mode 100644 index 00000000..a7bf7f61 --- /dev/null +++ b/cli/src/tool/templates/move/tool.move.jinja @@ -0,0 +1,121 @@ +module {{ name_snake_case }}::{{ name_snake_case }}; + +use nexus_primitives::proof_of_uid::ProofOfUID; +use nexus_workflow::tool_output::{Self, ToolOutput}; +use sui::bag::{Self, Bag}; +use sui::transfer::share_object; +use std::ascii::String as AsciiString; +use sui::clock::Clock; + +/// One-time witness for package initialization. +public struct {{ name_uppercase }} has drop {} + +/// Witness object used to identify this tool. +public struct {{ name_pascal_case }}Witness has key, store { + id: UID, +} + +/// Your tool's state object (customize as needed). +public struct {{ name_pascal_case }}State has key { + id: UID, + /// Store the witness object that identifies this tool. + witness: Bag, + // Add your application-specific fields here. + // Example: value: u64, + // .. +} + +/// Tool execution output variants. +/// This enum is used for automatic schema generation during registration. +/// It's not used during execution. Only the ToolOutput object is used. +public enum Output { + Ok { + result: u64, + // Add custom fields here as needed. + }, + Err { + reason: AsciiString, + }, + // Add custom variants as needed. + CustomResult { + data: vector, + timestamp: u64, + }, +} + +/// Initialize your tool's state. +fun init(_otw: {{ name_uppercase }}, ctx: &mut TxContext) { + let state = {{ name_pascal_case }}State { + id: object::new(ctx), + witness: { + let mut bag = bag::new(ctx); + bag.add(b"witness", {{ name_pascal_case }}Witness { id: object::new(ctx) }); + bag + }, + // Initialize your fields. + // value: 0, + }; + share_object(state); +} + +/// Execute function with standardized Nexus signature. +/// +/// CRITICAL REQUIREMENTS: +/// 1. First parameter: worksheet: &mut ProofOfUID +/// 2. Last parameter: ctx: &mut TxContext +/// 3. Return type: ToolOutput +/// 4. Must stamp worksheet with witness ID +public fun execute( + worksheet: &mut ProofOfUID, + state: &mut {{ name_pascal_case }}State, + // Add your custom parameters here. + input_value: u64, + clock: &Clock, + ctx: &mut TxContext, +): ToolOutput { + // Get the witness for stamping. + let witness = state.witness(); + + // REQUIRED: Stamp the worksheet to prove execution. + worksheet.stamp_with_data(&witness.id, b"{{ name_snake_case }}_executed"); + + // Implement your tool logic here. + if (input_value == 0) { + // Return error variant. + tool_output::err(b"Input value cannot be zero") + } else if (input_value > 1000) { + // Return custom variant. + tool_output::variant(b"custom_result") + .with_field(b"data", tool_output::string_value(b"large_value_processed")) + .with_field(b"timestamp", tool_output::string_value(sui::clock::timestamp_ms(clock).to_string().into_bytes())) + } else { + // Return success variant. + let result = input_value * 2; + tool_output::ok() + .with_field(b"result", tool_output::number_value(result.to_string().into_bytes())) + } +} + +// === Getters and Helper Functions === + +/// Helper function to get the witness object. +fun witness(self: &{{ name_pascal_case }}State): &{{ name_pascal_case }}Witness { + self.witness.borrow(b"witness") +} + +/// Get the witness ID for external reference. +/// Useful for registering the tool onchain. +public fun witness_id(self: &{{ name_pascal_case }}State): ID { + self.witness().id.to_inner() +} + +// Add getters for your custom fields. +// public fun value(self: &{{ name_pascal_case }}State): u64 { +// self.value +// } + +#[test_only] +public fun init_for_test(otw: {{ name_uppercase }}, ctx: &mut TxContext) { + init(otw, ctx); +} + diff --git a/cli/src/tool/templates/rust/Cargo.toml.jinja b/cli/src/tool/templates/rust/Cargo.toml.jinja index cedd8c7c..c77bc5cf 100644 --- a/cli/src/tool/templates/rust/Cargo.toml.jinja +++ b/cli/src/tool/templates/rust/Cargo.toml.jinja @@ -10,10 +10,10 @@ tokio = { version = "1", features = ["full"] } [dependencies.nexus-sdk] git = "https://github.com/Talus-Network/nexus-sdk.git" -tag = "v0.3.0" +tag = "v0.5.0" package = "nexus-sdk" [dependencies.nexus-toolkit] git = "https://github.com/Talus-Network/nexus-sdk.git" -tag = "v0.3.0" +tag = "v0.5.0" package = "nexus-toolkit" diff --git a/cli/src/tool/tool_claim_collateral.rs b/cli/src/tool/tool_claim_collateral.rs index 4af10d11..a2520ace 100644 --- a/cli/src/tool/tool_claim_collateral.rs +++ b/cli/src/tool/tool_claim_collateral.rs @@ -1,33 +1,25 @@ use { - crate::{command_title, display::json_output, loading, prelude::*, sui::*}, + crate::{command_title, display::json_output, loading, notify_success, prelude::*, sui::*}, nexus_sdk::transactions::tool, }; /// Claim collateral for a Tool based on the provided FQN. pub(crate) async fn claim_collateral( tool_fqn: ToolFqn, - owner_cap: Option, - sui_gas_coin: Option, + owner_cap: Option, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Claiming collateral for Tool '{tool_fqn}'"); - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let crawler = nexus_client.crawler(); - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui, address, sui_gas_coin).await?; - - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; + let conf = CliConf::load().await.unwrap_or_default(); // Use the provided or saved `owner_cap` object ID and fetch the object. let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).map(|t| t.over_tool)) else { @@ -36,14 +28,22 @@ pub(crate) async fn claim_collateral( ))); }; - let owner_cap = fetch_object_by_id(&sui, owner_cap).await?; - - // Craft a TX to claim the collaters for a Tool. + let owner_cap = crawler + .get_object_metadata(owner_cap) + .await + .map(|resp| resp.object_ref()) + .map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to fetch OwnerCap object metadata for '{owner_cap}': {e}" + )) + })?; + + // Craft a TX to claim the collateral for a Tool. let tx_handle = loading!("Crafting transaction..."); - let mut tx = sui::ProgrammableTransactionBuilder::new(); + let mut tx = sui::tx::TransactionBuilder::new(); - if let Err(e) = tool::claim_collateral_for_self(&mut tx, objects, &tool_fqn, &owner_cap) { + if let Err(e) = tool::claim_collateral_for_self(&mut tx, nexus_objects, &tool_fqn, &owner_cap) { tx_handle.error(); return Err(NexusCliError::Any(e)); @@ -51,16 +51,33 @@ pub(crate) async fn claim_collateral( tx_handle.success(); - let tx_data = sui::TransactionData::new_programmable( - address, - vec![gas_coin.object_ref()], - tx.finish(), - sui_gas_budget, - reference_gas_price, - ); + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); - // Sign and submit the TX. - let response = sign_and_execute_transaction(&sui, &wallet, tx_data).await?; + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); + + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + let response = signer + .execute_tx(tx, signature, &mut gas_coin) + .await + .map_err(NexusCliError::Nexus)?; + + gas_config.release_gas_coin(gas_coin).await; + + notify_success!( + "Transaction digest: {digest}", + digest = response.digest.to_string().truecolor(100, 100, 100) + ); json_output(&json!({ "digest": response.digest }))?; diff --git a/cli/src/tool/tool_list.rs b/cli/src/tool/tool_list.rs index a5639eb4..16cd7e75 100644 --- a/cli/src/tool/tool_list.rs +++ b/cli/src/tool/tool_list.rs @@ -1,41 +1,42 @@ use { crate::{command_title, display::json_output, item, loading, prelude::*, sui::*}, nexus_sdk::{ - object_crawler::{fetch_one, ObjectBag, Structure}, + nexus::crawler::DynamicObjectMap, types::{ - deserialize_bytes_to_lossy_utf8, + deserialize_bytes_to_string, deserialize_bytes_to_url, deserialize_string_to_datetime, }, + ToolRef, }, }; /// List tools available in the tool registry. pub(crate) async fn list_tools() -> AnyResult<(), NexusCliError> { - command_title!("Listing all available Neuxs tools"); + command_title!("Listing all available Nexus tools"); - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); - - // Nexus objects must be present in the configuration. - let NexusObjects { tool_registry, .. } = &get_nexus_objects(&mut conf).await?; - - // Build the Sui client. - let sui = build_sui_client(&conf.sui).await?; + let nexus_client = get_nexus_client(None, DEFAULT_GAS_BUDGET).await?; + let nexus_objects = &*nexus_client.get_nexus_objects(); + let crawler = nexus_client.crawler(); let tools_handle = loading!("Fetching tools from the tool registry..."); - let tool_registry = - match fetch_one::>(&sui, tool_registry.object_id).await { - Ok(tool_registry) => tool_registry.data.into_inner(), - Err(e) => { - tools_handle.error(); + let tool_registry = match crawler + .get_object::(*nexus_objects.tool_registry.object_id()) + .await + { + Ok(tool_registry) => tool_registry.data, + Err(e) => { + tools_handle.error(); - return Err(NexusCliError::Any(e)); - } - }; + return Err(NexusCliError::Any(e)); + } + }; - let tools = match tool_registry.tools.fetch_all(&sui).await { + let tools = match crawler + .get_dynamic_field_objects(&tool_registry.tools) + .await + { Ok(tools) => tools, Err(e) => { tools_handle.error(); @@ -49,22 +50,65 @@ pub(crate) async fn list_tools() -> AnyResult<(), NexusCliError> { let mut tools_json = Vec::new(); for (fqn, tool) in tools { - let tool = tool.into_inner(); + let tool = tool.data; + + let (reference, description, registered_at_ms, input_schema, output_schema) = match &tool { + ToolVariant::OffChain(t) => ( + ToolRef::from(t.url.clone()), + t.description.clone(), + t.registered_at_ms, + t.input_schema.clone(), + t.output_schema.clone(), + ), + ToolVariant::OnChain(t) => ( + ToolRef::new_sui(&t.package_address, &t.module_name, &t.witness_id).map_err( + |_| { + NexusCliError::Any(anyhow!( + "Invalid package address, module name, or witness ID in onchain tool" + )) + }, + )?, + t.description.clone(), + t.registered_at_ms, + t.input_schema.clone(), + t.output_schema.clone(), + ), + }; - tools_json.push(json!( - { + let tool_type = if reference.is_onchain() { + "OnChain" + } else { + "OffChain" + }; + + // Build JSON output with common fields plus type-specific ones. + let mut tool_json = json!({ "fqn": fqn, - "url": tool.url, - "registered_at_ms": tool.registered_at_ms, - "description": tool.description - })); + "reference": reference.to_string(), + "type": tool_type, + "registered_at_ms": registered_at_ms, + "description": description, + }); + + // A bit redundant, but for sake of clarity. + if reference.is_onchain() { + tool_json["package_address"] = json!(reference.package_address().unwrap().to_string()); + tool_json["module_name"] = json!(reference.module_name().unwrap().to_string()); + tool_json["witness_id"] = json!(reference.witness_id().unwrap().to_string()); + } + + tool_json["input_schema"] = json!(input_schema); + tool_json["output_schema"] = json!(output_schema); + + tools_json.push(tool_json); item!( - "Tool '{fqn}' at '{url}' registered '{registered_at}' - {description}", + "{tool_type} Tool '{fqn}' at '{reference}' registered '{registered_at}' - {description}", + tool_type = tool_type.truecolor(100, 100, 100), fqn = fqn.to_string().truecolor(100, 100, 100), - url = tool.url.as_str().truecolor(100, 100, 100), - registered_at = tool.registered_at_ms.to_string().truecolor(100, 100, 100), - description = tool.description.truecolor(100, 100, 100), + reference = reference.to_string().truecolor(100, 100, 100), + registered_at = registered_at_ms.to_string().truecolor(100, 100, 100), + description = description.truecolor(100, 100, 100), ); } @@ -75,15 +119,41 @@ pub(crate) async fn list_tools() -> AnyResult<(), NexusCliError> { #[derive(Debug, Clone, Deserialize)] struct ToolRegistry { - tools: ObjectBag>, + tools: DynamicObjectMap, +} + +#[derive(Debug, Clone, Deserialize)] +#[serde(untagged)] +enum ToolVariant { + OffChain(OffChainTool), + OnChain(OnChainTool), } #[derive(Debug, Clone, Deserialize)] -struct Tool { +struct OffChainTool { #[serde(deserialize_with = "deserialize_bytes_to_url")] url: reqwest::Url, - #[serde(deserialize_with = "deserialize_bytes_to_lossy_utf8")] + #[serde(deserialize_with = "deserialize_bytes_to_string")] + description: String, + #[serde(deserialize_with = "deserialize_bytes_to_string")] + input_schema: String, + #[serde(deserialize_with = "deserialize_bytes_to_string")] + output_schema: String, + #[serde(deserialize_with = "deserialize_string_to_datetime")] + registered_at_ms: chrono::DateTime, +} + +#[derive(Debug, Clone, Deserialize)] +struct OnChainTool { + package_address: String, + module_name: String, + witness_id: String, + #[serde(deserialize_with = "deserialize_bytes_to_string")] description: String, + #[serde(deserialize_with = "deserialize_bytes_to_string")] + input_schema: String, + #[serde(deserialize_with = "deserialize_bytes_to_string")] + output_schema: String, #[serde(deserialize_with = "deserialize_string_to_datetime")] registered_at_ms: chrono::DateTime, } diff --git a/cli/src/tool/tool_new.rs b/cli/src/tool/tool_new.rs index 57d9c53c..b5264bdb 100644 --- a/cli/src/tool/tool_new.rs +++ b/cli/src/tool/tool_new.rs @@ -12,6 +12,7 @@ use { #[derive(Clone, Debug, ValueEnum)] pub(crate) enum ToolTemplate { Rust, + Move, } impl ToolTemplate { @@ -46,6 +47,49 @@ impl ToolTemplate { ), ]) } + ToolTemplate::Move => { + let name_snake_case = name.to_case(Case::Snake); + let name_pascal_case = name.to_case(Case::Pascal); + let name_uppercase = name.to_case(Case::UpperSnake); + + let mut env = Environment::new(); + + env.add_template("move_toml", include_str!("templates/move/Move.toml.jinja"))?; + env.add_template("tool_move", include_str!("templates/move/tool.move.jinja"))?; + env.add_template( + "tests_move", + include_str!("templates/move/tests.move.jinja"), + )?; + env.add_template("gitignore", include_str!("templates/move/.gitignore.jinja"))?; + + let move_toml_template = env.get_template("move_toml")?; + let tool_move_template = env.get_template("tool_move")?; + let tests_move_template = env.get_template("tests_move")?; + let gitignore_template = env.get_template("gitignore")?; + + Ok(vec![ + ("sources".to_string(), None), + ("tests".to_string(), None), + ( + "Move.toml".to_string(), + Some(move_toml_template.render(context! { name_snake_case })?), + ), + ( + format!("sources/{name_snake_case}.move"), + Some(tool_move_template.render( + context! { name_snake_case, name_pascal_case, name_uppercase }, + )?), + ), + ( + format!("tests/{name_snake_case}_tests.move"), + Some(tests_move_template.render(context! { name_snake_case })?), + ), + ( + ".gitignore".to_string(), + Some(gitignore_template.render(context! {})?), + ), + ]) + } } } } @@ -134,7 +178,7 @@ mod tests { #[tokio::test] async fn test_create_new_tool() { - let tempdir = tempfile::tempdir().unwrap().into_path(); + let tempdir = tempfile::tempdir().unwrap().keep(); let result = create_new_tool("test".to_string(), ToolTemplate::Rust, tempdir.clone()).await; @@ -158,4 +202,62 @@ mod tests { assert!(contents.contains(r#"name = "test""#)); assert!(contents.contains("[dependencies.nexus-toolkit]")); } + + #[tokio::test] + async fn test_create_new_move_tool() { + let tempdir = tempfile::tempdir().unwrap().keep(); + + let result = + create_new_tool("test_tool".to_string(), ToolTemplate::Move, tempdir.clone()).await; + + assert_matches!(result, Ok(())); + + // Check that directories were created. + let sources_dir = tempdir.join("test_tool/sources"); + assert!(sources_dir.exists()); + + let tests_dir = tempdir.join("test_tool/tests"); + assert!(tests_dir.exists()); + + // Check that Move.toml was written with correct contents. + let move_toml_path = tempdir.join("test_tool/Move.toml"); + let move_toml_contents = tokio::fs::read_to_string(move_toml_path).await.unwrap(); + + assert!(move_toml_contents.contains(r#"name = "test_tool""#)); + assert!(move_toml_contents.contains("edition = \"2024.beta\"")); + assert!(move_toml_contents.contains("nexus_primitives")); + assert!(move_toml_contents.contains("nexus_workflow")); + assert!(move_toml_contents.contains("nexus_interface")); + + // Check that the main Move file was written with correct contents. + let move_file_path = tempdir.join("test_tool/sources/test_tool.move"); + let move_contents = tokio::fs::read_to_string(move_file_path).await.unwrap(); + + assert!(move_contents.contains("module test_tool::test_tool;")); + assert!(move_contents.contains("public struct TEST_TOOL has drop {}")); + assert!(move_contents.contains("public struct TestToolWitness has key, store")); + assert!(move_contents.contains("public struct TestToolState has key")); + assert!(move_contents.contains("public enum Output")); + assert!(move_contents.contains("fun init(_otw: TEST_TOOL, ctx: &mut TxContext)")); + assert!(move_contents.contains("public fun execute(")); + assert!(move_contents.contains("worksheet: &mut ProofOfUID")); + assert!(move_contents.contains("): ToolOutput")); + assert!(move_contents.contains("public fun witness_id(self: &TestToolState): ID")); + assert!( + move_contents.contains("public fun init_for_test(otw: TEST_TOOL, ctx: &mut TxContext)") + ); + + // Check that the test file was written. + let test_file_path = tempdir.join("test_tool/tests/test_tool_tests.move"); + let test_contents = tokio::fs::read_to_string(test_file_path).await.unwrap(); + + assert!(test_contents.contains("module test_tool::test_tool_tests;")); + assert!(test_contents.contains("fun test_test_tool()")); + + // Check that .gitignore was written. + let gitignore_path = tempdir.join("test_tool/.gitignore"); + let gitignore_contents = tokio::fs::read_to_string(gitignore_path).await.unwrap(); + + assert!(gitignore_contents.contains("build/*")); + } } diff --git a/cli/src/tool/tool_register.rs b/cli/src/tool/tool_register.rs deleted file mode 100644 index 986f630c..00000000 --- a/cli/src/tool/tool_register.rs +++ /dev/null @@ -1,312 +0,0 @@ -use { - crate::{ - command_title, - display::json_output, - loading, - notify_error, - notify_success, - prelude::*, - sui::*, - tool::{tool_validate::*, ToolIdent}, - }, - nexus_sdk::{ - idents::{primitives, workflow}, - transactions::tool, - }, -}; - -/// Validate and then register a new Tool. -pub(crate) async fn register_tool( - ident: ToolIdent, - collateral_coin: Option, - invocation_cost: u64, - batch: bool, - no_save: bool, - sui_gas_coin: Option, - sui_gas_budget: u64, -) -> AnyResult<(), NexusCliError> { - let ident_check = ident.clone(); - - // Validate either a single tool or a batch of tools if the `batch` flag is - // provided. - let idents = if batch { - let Some(url) = &ident.off_chain else { - todo!("TODO: "); - }; - - // Fetch all tools on the webserver. - let response = reqwest::Client::new() - .get(url.join("/tools").expect("Joining URL must be valid")) - .send() - .await - .map_err(NexusCliError::Http)? - .json::>() - .await - .map_err(NexusCliError::Http)?; - - response - .iter() - .filter_map(|s| match url.join(s) { - Ok(url) => Some(ToolIdent { - off_chain: Some(url), - on_chain: None, - }), - Err(_) => None, - }) - .collect::>() - } else { - vec![ident] - }; - - let mut registration_results = Vec::with_capacity(idents.len()); - - for ident in idents { - let meta = validate_tool(ident).await?; - - command_title!( - "Registering Tool '{fqn}' at '{url}'", - fqn = meta.fqn, - url = meta.url - ); - - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); - - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas and collateral coin objects. - let (gas_coin, collateral_coin) = - fetch_gas_and_collateral_coins(&sui, address, sui_gas_coin, collateral_coin).await?; - - if gas_coin.coin_object_id == collateral_coin.coin_object_id { - return Err(NexusCliError::Any(anyhow!( - "Gas and collateral coins must be different." - ))); - } - - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; - - // Craft a TX to register the tool. - let tx_handle = loading!("Crafting transaction..."); - - // Explicitly check that we're registering an off-chain tool. This is mainly - // for when we implement logic for on-chain so that we don't forget to - // adjust the transaction. - if ident_check.on_chain.is_some() { - todo!("TODO: "); - } - - let mut tx = sui::ProgrammableTransactionBuilder::new(); - - if let Err(e) = tool::register_off_chain_for_self( - &mut tx, - objects, - &meta, - address.into(), - &collateral_coin, - invocation_cost, - ) { - tx_handle.error(); - - return Err(NexusCliError::Any(e)); - } - - tx_handle.success(); - - let tx_data = sui::TransactionData::new_programmable( - address, - vec![gas_coin.object_ref()], - tx.finish(), - sui_gas_budget, - reference_gas_price, - ); - - // Sign and submit the TX. - let response = match sign_and_execute_transaction(&sui, &wallet, tx_data).await { - Ok(response) => response, - // If the tool is already registered, we don't want to fail the - // command. - Err(NexusCliError::Any(e)) if e.to_string().contains("register_off_chain_tool_") => { - notify_error!( - "Tool '{fqn}' is already registered.", - fqn = meta.fqn.to_string().truecolor(100, 100, 100) - ); - - registration_results.push(json!({ - "tool_fqn": meta.fqn, - "already_registered": true, - })); - - continue; - } - // Any other error fails the tool registration but continues the - // loop. - Err(e) => { - notify_error!( - "Failed to register tool '{fqn}': {error}", - fqn = meta.fqn.to_string().truecolor(100, 100, 100), - error = e - ); - - registration_results.push(json!({ - "tool_fqn": meta.fqn, - "error": e.to_string(), - })); - - continue; - } - }; - - // Parse the owner cap object IDs from the response. - let owner_caps = response - .object_changes - .unwrap_or_default() - .into_iter() - .filter_map(|change| match change { - sui::ObjectChange::Created { - object_type, - object_id, - .. - } if object_type.address == *objects.primitives_pkg_id - && object_type.module - == primitives::OwnerCap::CLONEABLE_OWNER_CAP.module.into() - && object_type.name - == primitives::OwnerCap::CLONEABLE_OWNER_CAP.name.into() => - { - Some((object_id, object_type)) - } - _ => None, - }) - .collect::>(); - - // Find `CloneableOwnerCap` object ID. - let over_tool = owner_caps.iter().find_map(|(object_id, object_type)| { - match object_type.type_params.first() { - Some(sui::MoveTypeTag::Struct(what_for)) - if what_for.module == workflow::ToolRegistry::OVER_TOOL.module.into() - && what_for.name == workflow::ToolRegistry::OVER_TOOL.name.into() => - { - Some(object_id) - } - _ => None, - } - }); - - let Some(over_tool_id) = over_tool else { - return Err(NexusCliError::Any(anyhow!( - "Could not find the OwnerCap object ID in the transaction response." - ))); - }; - - // Find `CloneableOwnerCap` object ID. - let over_gas = owner_caps.iter().find_map(|(object_id, object_type)| { - match object_type.type_params.first() { - Some(sui::MoveTypeTag::Struct(what_for)) - if what_for.module == workflow::Gas::OVER_GAS.module.into() - && what_for.name == workflow::Gas::OVER_GAS.name.into() => - { - Some(object_id) - } - _ => None, - } - }); - - let Some(over_gas_id) = over_gas else { - return Err(NexusCliError::Any(anyhow!( - "Could not find the OwnerCap object ID in the transaction response." - ))); - }; - - notify_success!( - "OwnerCap object ID: {id}", - id = over_tool_id.to_string().truecolor(100, 100, 100) - ); - - notify_success!( - "OwnerCap object ID: {id}", - id = over_gas_id.to_string().truecolor(100, 100, 100) - ); - - // Save the owner caps to the CLI conf. - if !no_save { - let save_handle = loading!("Saving the owner caps to the CLI configuration..."); - - let mut conf = CliConf::load().await.unwrap_or_default(); - - conf.tools.insert( - meta.fqn.clone(), - ToolOwnerCaps { - over_tool: *over_tool_id, - over_gas: *over_gas_id, - }, - ); - - if let Err(e) = conf.save().await { - save_handle.error(); - - return Err(NexusCliError::Any(e)); - } - - save_handle.success(); - } - - registration_results.push(json!({ - "digest": response.digest, - "tool_fqn": meta.fqn, - "owner_cap_over_tool_id": over_tool_id, - "owner_cap_over_gas_id": over_gas_id, - "already_registered": false, - })) - } - - json_output(®istration_results)?; - - Ok(()) -} - -/// Fetch the gas and collateral coins from the Sui client. On Localnet, Devnet -/// and Testnet, we can use the faucet to get the coins. On Mainnet, this fails -/// if the coins are not present. -async fn fetch_gas_and_collateral_coins( - sui: &sui::Client, - addr: sui::Address, - sui_gas_coin: Option, - sui_collateral_coin: Option, -) -> AnyResult<(sui::Coin, sui::Coin), NexusCliError> { - let mut coins = fetch_all_coins_for_address(sui, addr).await?; - - if coins.len() < 2 { - return Err(NexusCliError::Any(anyhow!( - "The wallet does not have enough coins to register the tool" - ))); - } - - // If object IDs were specified, use them. If any of the specified coins is - // not found, return error. - let gas_coin = match sui_gas_coin { - Some(id) => coins - .iter() - .find(|coin| coin.coin_object_id == id) - .cloned() - .ok_or_else(|| NexusCliError::Any(anyhow!("Coin '{id}' not found in wallet")))?, - None => coins.remove(0), - }; - - let collateral_coin = match sui_collateral_coin { - Some(id) => coins - .iter() - .find(|coin| coin.coin_object_id == id) - .cloned() - .ok_or_else(|| NexusCliError::Any(anyhow!("Coin '{id}' not found in wallet")))?, - None => coins.remove(0), - }; - - Ok((gas_coin, collateral_coin)) -} diff --git a/cli/src/tool/tool_register_offchain.rs b/cli/src/tool/tool_register_offchain.rs new file mode 100644 index 00000000..a6421b3d --- /dev/null +++ b/cli/src/tool/tool_register_offchain.rs @@ -0,0 +1,262 @@ +use { + crate::{ + command_title, + display::json_output, + loading, + notify_error, + notify_success, + prelude::*, + sui::*, + tool::tool_validate::validate_off_chain_tool, + }, + nexus_sdk::{ + idents::{primitives, workflow}, + nexus::error::NexusError, + transactions::tool, + }, +}; + +/// Validate and then register a new offchain Tool. +pub(crate) async fn register_off_chain_tool( + url: reqwest::Url, + collateral_coin: Option, + invocation_cost: u64, + batch: bool, + no_save: bool, + sui_gas_coin: Option, + sui_gas_budget: u64, +) -> AnyResult<(), NexusCliError> { + // Validate either a single tool or a batch of tools if the `batch` flag is + // provided. + let urls = if batch { + // Fetch all tools on the webserver. + let response = reqwest::Client::new() + .get(url.join("/tools").expect("Joining URL must be valid")) + .send() + .await + .map_err(NexusCliError::Http)? + .json::>() + .await + .map_err(NexusCliError::Http)?; + + response + .iter() + .filter_map(|s| url.join(s).ok()) + .collect::>() + } else { + vec![url] + }; + + let mut registration_results = Vec::with_capacity(urls.len()); + + if collateral_coin.is_some() && collateral_coin == sui_gas_coin { + return Err(NexusCliError::Any(anyhow!( + "The coin used for collateral cannot be the same as the gas coin." + ))); + } + + let conf = CliConf::load().await.unwrap_or_default(); + let client = build_sui_grpc_client(&conf).await?; + let pk = get_signing_key(&conf).await?; + let owner = pk.public_key().derive_address(); + + for tool_url in urls { + let meta = validate_off_chain_tool(tool_url).await?; + + command_title!( + "Registering Tool '{fqn}' at '{url}'", + fqn = meta.fqn, + url = meta.url + ); + + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let collateral_coin = fetch_coin(client.clone(), owner, collateral_coin, 1).await?; + + // Craft a TX to register the tool. + let tx_handle = loading!("Crafting transaction..."); + + let mut tx = sui::tx::TransactionBuilder::new(); + + if let Err(e) = tool::register_off_chain_for_self( + &mut tx, + nexus_objects, + &meta, + address, + &collateral_coin, + invocation_cost, + ) { + tx_handle.error(); + + return Err(NexusCliError::Any(e)); + } + + tx_handle.success(); + + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); + + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); + + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + // Sign and submit the TX. + let response = match signer.execute_tx(tx, signature, &mut gas_coin).await { + Ok(response) => { + gas_config.release_gas_coin(gas_coin).await; + + response + } + // If the tool is already registered, we don't want to fail the + // command. + Err(NexusError::Wallet(e)) if e.to_string().contains("register_off_chain_tool_") => { + gas_config.release_gas_coin(gas_coin).await; + + notify_error!( + "Tool '{fqn}' is already registered.", + fqn = meta.fqn.to_string().truecolor(100, 100, 100) + ); + + registration_results.push(json!({ + "tool_fqn": meta.fqn, + "already_registered": true, + })); + + continue; + } + // Any other error fails the tool registration but continues the + // loop. + Err(e) => { + gas_config.release_gas_coin(gas_coin).await; + + notify_error!( + "Failed to register tool '{fqn}': {error}", + fqn = meta.fqn.to_string().truecolor(100, 100, 100), + error = e + ); + + registration_results.push(json!({ + "tool_fqn": meta.fqn, + "error": e.to_string(), + })); + + continue; + } + }; + + // Parse the owner cap object IDs from the response. + let owner_caps = response + .objects + .iter() + .filter_map(|obj| { + let sui::types::ObjectType::Struct(object_type) = obj.object_type() else { + return None; + }; + + if *object_type.address() == nexus_objects.primitives_pkg_id + && *object_type.module() == primitives::OwnerCap::CLONEABLE_OWNER_CAP.module + && *object_type.name() == primitives::OwnerCap::CLONEABLE_OWNER_CAP.name + { + Some((obj.object_id(), object_type)) + } else { + None + } + }) + .collect::>(); + + // Find `CloneableOwnerCap` object ID. + let over_tool = owner_caps.iter().find_map(|(object_id, object_type)| { + match object_type.type_params().first() { + Some(sui::types::TypeTag::Struct(what_for)) + if *what_for.module() == workflow::ToolRegistry::OVER_TOOL.module + && *what_for.name() == workflow::ToolRegistry::OVER_TOOL.name => + { + Some(object_id) + } + _ => None, + } + }); + + let Some(over_tool_id) = over_tool else { + return Err(NexusCliError::Any(anyhow!( + "Could not find the OwnerCap object ID in the transaction response." + ))); + }; + + // Find `CloneableOwnerCap` object ID. + let over_gas = owner_caps.iter().find_map(|(object_id, object_type)| { + match object_type.type_params().first() { + Some(sui::types::TypeTag::Struct(what_for)) + if *what_for.module() == workflow::Gas::OVER_GAS.module + && *what_for.name() == workflow::Gas::OVER_GAS.name => + { + Some(object_id) + } + _ => None, + } + }); + + let Some(over_gas_id) = over_gas else { + return Err(NexusCliError::Any(anyhow!( + "Could not find the OwnerCap object ID in the transaction response." + ))); + }; + + notify_success!( + "OwnerCap object ID: {id}", + id = over_tool_id.to_string().truecolor(100, 100, 100) + ); + + notify_success!( + "OwnerCap object ID: {id}", + id = over_gas_id.to_string().truecolor(100, 100, 100) + ); + + // Save the owner caps to the CLI conf. + if !no_save { + let save_handle = loading!("Saving the owner caps to the CLI configuration..."); + + let mut conf = CliConf::load().await.unwrap_or_default(); + + conf.tools.insert( + meta.fqn.clone(), + ToolOwnerCaps { + over_tool: *over_tool_id, + over_gas: Some(*over_gas_id), + }, + ); + + if let Err(e) = conf.save().await { + save_handle.error(); + + return Err(NexusCliError::Any(e)); + } + + save_handle.success(); + } + + registration_results.push(json!({ + "digest": response.digest, + "tool_fqn": meta.fqn, + "owner_cap_over_tool_id": over_tool_id, + "owner_cap_over_gas_id": over_gas_id, + "already_registered": false, + })) + } + + json_output(®istration_results)?; + + Ok(()) +} diff --git a/cli/src/tool/tool_register_onchain.rs b/cli/src/tool/tool_register_onchain.rs new file mode 100644 index 00000000..7cba5138 --- /dev/null +++ b/cli/src/tool/tool_register_onchain.rs @@ -0,0 +1,1285 @@ +use { + crate::{ + command_title, + display::json_output, + loading, + notify_error, + notify_success, + prelude::*, + sui::*, + }, + nexus_sdk::{idents::primitives, nexus::error::NexusError, sui, transactions::tool}, + serde::{Deserialize, Serialize}, + serde_json::{json, Map, Value}, + std::{ + collections::HashMap, + io::{self, Write}, + }, +}; + +/// Register a new onchain tool with automatic schema generation. +/// The input schema is automatically generated by introspecting the Move module's "execute" function. +/// todo: merge this function with the existing `tool_register.rs` function. +#[allow(clippy::too_many_arguments)] +pub(crate) async fn register_onchain_tool( + package: sui::types::Address, + module: sui::types::Identifier, + fqn: ToolFqn, + description: String, + witness_id: sui::types::Address, + collateral_coin: Option, + no_save: bool, + sui_gas_coin: Option, + sui_gas_budget: u64, +) -> AnyResult<(), NexusCliError> { + command_title!( + "Registering Onchain Tool '{fqn}' from package '{package}::{module}'", + fqn = fqn, + package = package, + module = module + ); + + if collateral_coin.is_some() && collateral_coin == sui_gas_coin { + return Err(NexusCliError::Any(anyhow!( + "The coin used for collateral cannot be the same as the gas coin." + ))); + } + + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let conf = CliConf::load().await.unwrap_or_default(); + let client = build_sui_grpc_client(&conf).await?; + + let collateral_coin = fetch_coin(client.clone(), address, collateral_coin, 1).await?; + + // Generate and customize schemas. + let (input_schema, output_schema) = + generate_and_customize_schemas(client, package, &module).await?; + + let tx_handle = loading!("Crafting transaction..."); + + let mut tx = sui::tx::TransactionBuilder::new(); + + if let Err(e) = tool::register_on_chain_for_self( + &mut tx, + nexus_objects, + package, + module.as_str(), + &input_schema, + &output_schema, + &fqn, + &description, + witness_id, + &collateral_coin, + address, + ) { + tx_handle.error(); + + return Err(NexusCliError::Any(e)); + } + + tx_handle.success(); + + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); + + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); + + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + // Sign and submit the TX. + let response = match signer.execute_tx(tx, signature, &mut gas_coin).await { + Ok(response) => { + gas_config.release_gas_coin(gas_coin).await; + + response + } + // If the tool is already registered, we don't want to fail the + // command. + Err(NexusError::Wallet(e)) if e.to_string().contains("register_on_chain_tool_") => { + gas_config.release_gas_coin(gas_coin).await; + + notify_error!( + "Tool '{fqn}' is already registered.", + fqn = fqn.to_string().truecolor(100, 100, 100) + ); + + json_output(&json!({ + "tool_fqn": fqn, + "already_registered": true, + }))?; + + return Err(NexusCliError::Any(e)); + } + // Any other error fails the tool registration but continues the + // loop. + Err(e) => { + gas_config.release_gas_coin(gas_coin).await; + + notify_error!( + "Failed to register tool '{fqn}': {error}", + fqn = fqn.to_string().truecolor(100, 100, 100), + error = e + ); + + return Err(NexusCliError::Nexus(e)); + } + }; + + // Extract the OwnerCap object ID. + let over_tool_id = extract_over_tool_owner_cap(&response.objects, nexus_objects)?; + + // Save the owner caps to the CLI conf. + if !no_save { + save_tool_owner_caps(fqn.clone(), over_tool_id).await?; + } + + json_output(&json!({ + "digest": response.digest, + "tool_fqn": fqn, + "package_address": package, + "module_name": module, + "witness_id": witness_id.to_string(), + "description": description, + "input_schema": input_schema, + "output_schema": output_schema, + "owner_cap_over_tool_id": over_tool_id, + "owner_cap_over_gas_id": null, + "already_registered": false, + }))?; + + Ok(()) +} + +/// Generate input and output schemas and allow user customization. +async fn generate_and_customize_schemas( + client: Arc>, + package_address: sui::types::Address, + module_name: &str, +) -> AnyResult<(String, String), NexusCliError> { + // Generate input schema by introspecting the Move module's "execute" function. + let input_handle = loading!("Auto-generating input schema from Move module..."); + let base_input_schema = match nexus_sdk::onchain_schema_gen::generate_input_schema( + client.clone(), + package_address, + module_name, + "execute", + ) + .await + { + Ok(schema) => { + input_handle.success(); + schema + } + Err(e) => { + input_handle.error(); + return Err(NexusCliError::Any(e)); + } + }; + + // Generate output schema by introspecting the Move module's "Output" enum. + let output_handle = loading!("Auto-generating output schema from Move module..."); + let base_output_schema = match nexus_sdk::onchain_schema_gen::generate_output_schema( + client, + package_address, + module_name, + "Output", + ) + .await + { + Ok(schema) => { + output_handle.success(); + schema + } + Err(e) => { + output_handle.error(); + return Err(NexusCliError::Any(e)); + } + }; + + // Allow user to customize parameter descriptions. + let input_schema = customize_parameter_descriptions(base_input_schema)?; + + // Allow user to customize output variant and field descriptions. + let output_schema = customize_output_variant_and_field_descriptions(base_output_schema)?; + + Ok((input_schema, output_schema)) +} + +/// Extract the OwnerCap object ID from the transaction response. +fn extract_over_tool_owner_cap( + objects: &[sui::types::Object], + nexus_objects: &NexusObjects, +) -> AnyResult { + // Find `CloneableOwnerCap` object ID + let over_tool = objects.iter().find_map(|obj| { + let sui::types::ObjectType::Struct(object_type) = obj.object_type() else { + return None; + }; + + if *object_type.address() == nexus_objects.primitives_pkg_id + && *object_type.module() == primitives::OwnerCap::CLONEABLE_OWNER_CAP.module + && *object_type.name() == primitives::OwnerCap::CLONEABLE_OWNER_CAP.name + { + Some(obj.object_id()) + } else { + None + } + }); + + let Some(over_tool_id) = over_tool else { + return Err(NexusCliError::Any(anyhow!( + "Could not find the OwnerCap object ID in the transaction response." + ))); + }; + + notify_success!( + "OwnerCap object ID: {id}", + id = over_tool_id.to_string().truecolor(100, 100, 100) + ); + + notify_success!("Onchain tools use a different gas model. No OverGas cap was created."); + + Ok(over_tool_id) +} + +/// Save the tool owner caps to the CLI configuration. +async fn save_tool_owner_caps( + fqn: ToolFqn, + over_tool_id: sui::types::Address, +) -> AnyResult<(), NexusCliError> { + let save_handle = loading!("Saving the owner cap to the CLI configuration..."); + + let mut conf = CliConf::load().await.unwrap_or_default(); + + // For onchain tools, we only have OverTool cap, no OverGas cap. + conf.tools.insert( + fqn, + ToolOwnerCaps { + over_tool: over_tool_id, + over_gas: None, + }, + ); + + if let Err(e) = conf.save().await { + save_handle.error(); + return Err(NexusCliError::Any(e)); + } + + save_handle.success(); + + Ok(()) +} + +/// Represents a single parameter in the input schema. +#[derive(Debug, Clone, Serialize, Deserialize)] +struct ParameterSchema { + #[serde(rename = "type")] + param_type: String, + description: String, + #[serde(skip_serializing_if = "Option::is_none")] + custom_name: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + mutable: Option, + #[serde(skip_serializing_if = "Option::is_none")] + parameter_index: Option, +} + +/// Represents an output variant with optional fields. +#[derive(Debug, Clone, Serialize, Deserialize)] +struct OutputVariantSchema { + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + variant_type: Option, + description: String, + #[serde(skip_serializing_if = "Option::is_none")] + fields: Option>, +} + +/// Allow the user to customize parameter descriptions interactively. +fn customize_parameter_descriptions_with_reader( + schema_json: String, + reader: &mut dyn std::io::BufRead, +) -> AnyResult { + // Skip interactive prompts in JSON mode. + if JSON_MODE.load(Ordering::Relaxed) { + return Ok(schema_json); + } + + // Deserialize into a typed map. + let mut schema: HashMap = serde_json::from_str(&schema_json) + .map_err(|e| NexusCliError::Any(anyhow::anyhow!("Failed to parse schema JSON: {e}")))?; + + if schema.is_empty() { + println!( + "\n{info} No parameters to customize.", + info = "▶".cyan().bold() + ); + return Ok(schema_json); + } + + println!( + "\n{title}", + title = "Input Schema Customization".bold().cyan() + ); + println!( + "Customize names and descriptions for each input parameter (press Enter to keep current)" + ); + + // Sort parameter keys numerically. + let mut param_keys: Vec = schema.keys().cloned().collect(); + param_keys.sort_by_key(|k| k.parse::().unwrap_or(i32::MAX)); + + // Customize each parameter. + for param_key in ¶m_keys { + if let Some(param) = schema.get_mut(param_key) { + customize_parameter(param_key, param, reader) + .map_err(|e| NexusCliError::Any(anyhow::anyhow!("I/O error: {e}")))?; + } + } + + println!(); + + // Convert schema to use custom names as keys. + let final_schema = build_final_schema(schema)?; + + // Serialize back to JSON. + serde_json::to_string(&final_schema) + .map_err(|e| NexusCliError::Any(anyhow::anyhow!("Failed to serialize schema: {e}"))) +} + +/// Build the final schema with custom names as keys. +fn build_final_schema( + schema: HashMap, +) -> AnyResult, NexusCliError> { + let mut final_schema = Map::new(); + + for (param_index, mut param) in schema { + // Use custom name if provided, otherwise use the integer index. + let key = param.custom_name.take().unwrap_or(param_index); + + // Clear metadata fields before serialization. + param.parameter_index = None; + + // Convert to JSON value (this automatically excludes None fields). + let param_value = serde_json::to_value(param).map_err(|e| { + NexusCliError::Any(anyhow::anyhow!("Failed to serialize parameter: {e}")) + })?; + + final_schema.insert(key, param_value); + } + + Ok(final_schema) +} + +/// Wrapper function that calls customize_parameter_descriptions_with_reader using stdin. +fn customize_parameter_descriptions(schema_json: String) -> AnyResult { + let stdin = io::stdin(); + let mut reader = stdin.lock(); + customize_parameter_descriptions_with_reader(schema_json, &mut reader) +} + +/// Allow the user to customize output variant and field descriptions through an interactive prompt. +fn customize_output_variant_and_field_descriptions_with_reader( + base_schema: String, + reader: &mut dyn std::io::BufRead, +) -> AnyResult { + // Skip interactive prompts in JSON mode. + if JSON_MODE.load(Ordering::Relaxed) { + return Ok(base_schema); + } + + // Deserialize into a typed map. + let mut schema: HashMap = serde_json::from_str(&base_schema) + .map_err(|e| NexusCliError::Any(anyhow::anyhow!("Failed to parse output schema: {e}")))?; + + // Display header. + println!( + "\n{title}", + title = "Output Schema Descriptions".bold().cyan() + ); + println!( + "Customize descriptions for output variants and their fields (press Enter to keep current)" + ); + + // Customize each variant. + for (variant_name, variant) in schema.iter_mut() { + customize_output_variant(variant_name, variant, reader) + .map_err(|e| NexusCliError::Any(anyhow::anyhow!("I/O error: {e}")))?; + } + + println!(); + + // Serialize back to JSON. + serde_json::to_string(&schema) + .map_err(|e| NexusCliError::Any(anyhow::anyhow!("Failed to serialize output schema: {e}"))) +} + +/// Wrapper function that calls customize_output_variant_and_field_descriptions_with_reader using stdin. +fn customize_output_variant_and_field_descriptions( + base_schema: String, +) -> AnyResult { + let stdin = io::stdin(); + let mut reader = stdin.lock(); + customize_output_variant_and_field_descriptions_with_reader(base_schema, &mut reader) +} + +/// Get a field's type and description from JSON value. +fn get_field_info(field_value: &Value) -> (String, String) { + let field_type = field_value + .get("type") + .and_then(|v| v.as_str()) + .unwrap_or("unknown") + .to_string(); + let description = field_value + .get("description") + .and_then(|v| v.as_str()) + .unwrap_or("No description") + .to_string(); + (field_type, description) +} + +/// Update a field's description in the JSON value. +fn update_field_description(field_value: &mut Value, new_description: String) { + if let Some(obj) = field_value.as_object_mut() { + obj.insert("description".to_string(), Value::String(new_description)); + } +} + +/// Prompt the user for optional input with a default fallback. +fn prompt_optional_input( + reader: &mut dyn std::io::BufRead, + prompt: &str, + current_value: &str, +) -> io::Result> { + println!("Current: {}", current_value.truecolor(150, 150, 150)); + print!("{prompt}: "); + io::stdout().flush()?; + + let mut input = String::new(); + reader.read_line(&mut input)?; + let trimmed = input.trim(); + + if trimmed.is_empty() { + println!("{} Kept current value", "→".truecolor(150, 150, 150)); + Ok(None) + } else { + println!("{} Updated", "✓".green().bold()); + Ok(Some(trimmed.to_string())) + } +} + +/// Customize a single parameter interactively. +fn customize_parameter( + param_key: &str, + param: &mut ParameterSchema, + reader: &mut dyn std::io::BufRead, +) -> io::Result<()> { + let is_mutable = param.mutable.unwrap_or(false); + let type_display = if is_mutable { + format!("mut {}", param.param_type) + } else { + param.param_type.clone() + }; + + println!(); + println!( + "{} Parameter {}: {} {}", + "▶".purple().bold(), + param_key.bold(), + type_display.blue().bold(), + if is_mutable { + "(mutable)".yellow() + } else { + "".normal() + } + ); + + // Customize parameter name. + let default_name = param.custom_name.as_deref().unwrap_or(param_key); + println!("Current name: {}", default_name.truecolor(150, 150, 150)); + if param.custom_name.is_some() { + println!("Current custom name: {}", default_name.green()); + } + + if let Some(new_name) = prompt_optional_input( + reader, + &format!("Custom name (Enter to keep '{default_name}')"), + default_name, + )? { + param.custom_name = Some(new_name); + } else if param.custom_name.is_some() { + // User pressed Enter but had a custom name. + param.custom_name = None; + } + + // Customize parameter description. + if let Some(new_desc) = prompt_optional_input( + reader, + "Custom description (Enter to keep)", + ¶m.description, + )? { + param.description = new_desc; + } + + Ok(()) +} + +/// Customize a single output field interactively. +fn customize_output_field( + field_name: &str, + field_value: &mut Value, + reader: &mut dyn std::io::BufRead, +) -> io::Result<()> { + let (field_type, description) = get_field_info(field_value); + + println!(); + println!( + "{} Field {}: {}", + "▶".purple().bold(), + field_name.bold(), + field_type.blue().bold() + ); + + if let Some(new_desc) = + prompt_optional_input(reader, "Custom description (Enter to keep)", &description)? + { + update_field_description(field_value, new_desc); + } + + Ok(()) +} + +/// Customize a single output variant interactively. +fn customize_output_variant( + variant_name: &str, + variant: &mut OutputVariantSchema, + reader: &mut dyn std::io::BufRead, +) -> io::Result<()> { + println!(); + println!( + "{} Variant {}: {}", + "▶".purple().bold(), + variant_name.bold(), + "variant".blue().bold() + ); + + // Customize variant description. + if let Some(new_desc) = prompt_optional_input( + reader, + "Custom description (Enter to keep)", + &variant.description, + )? { + variant.description = new_desc; + } + + // Customize field descriptions if present. + if let Some(fields) = &mut variant.fields { + for (field_name, field_value) in fields.iter_mut() { + customize_output_field(field_name, field_value, reader)?; + } + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use {super::*, nexus_sdk::test_utils::sui_mocks, std::sync::atomic::Ordering}; + + #[test] + fn test_build_final_schema_with_custom_names() { + // Create a schema with integer keys and custom names using typed structs. + let mut schema = HashMap::new(); + + // Parameter 0: u64 with custom name "increment_amount". + schema.insert( + "0".to_string(), + ParameterSchema { + param_type: "u64".to_string(), + description: "64-bit unsigned integer".to_string(), + custom_name: Some("increment_amount".to_string()), + mutable: None, + parameter_index: Some("0".to_string()), + }, + ); + + // Parameter 1: object with custom name "counter". + schema.insert( + "1".to_string(), + ParameterSchema { + param_type: "object".to_string(), + description: "Counter object reference".to_string(), + custom_name: Some("counter".to_string()), + mutable: Some(true), + parameter_index: Some("1".to_string()), + }, + ); + + // Convert schema. + let result = build_final_schema(schema).unwrap(); + + // Verify custom names are used as keys. + assert!(result.contains_key("increment_amount")); + assert!(result.contains_key("counter")); + assert!(!result.contains_key("0")); + assert!(!result.contains_key("1")); + + // Verify metadata fields are removed. + let increment_amount_param = result.get("increment_amount").unwrap().as_object().unwrap(); + assert!(!increment_amount_param.contains_key("custom_name")); + assert!(!increment_amount_param.contains_key("parameter_index")); + + // Verify type and description are preserved. + assert_eq!(increment_amount_param.get("type").unwrap(), "u64"); + assert_eq!( + increment_amount_param.get("description").unwrap(), + "64-bit unsigned integer" + ); + + // Verify mutable flag is preserved for counter. + let counter_param = result.get("counter").unwrap().as_object().unwrap(); + assert_eq!(counter_param.get("mutable").unwrap(), true); + assert!(!counter_param.contains_key("custom_name")); + assert!(!counter_param.contains_key("parameter_index")); + } + + #[test] + fn test_build_final_schema_without_custom_names() { + // Create a schema with integer keys but no custom names using typed structs. + let mut schema = HashMap::new(); + + // Parameter 0: u64 without custom name. + schema.insert( + "0".to_string(), + ParameterSchema { + param_type: "u64".to_string(), + description: "64-bit unsigned integer".to_string(), + custom_name: None, + mutable: None, + parameter_index: None, + }, + ); + + // Parameter 1: object without custom name. + schema.insert( + "1".to_string(), + ParameterSchema { + param_type: "object".to_string(), + description: "Object reference".to_string(), + custom_name: None, + mutable: Some(true), + parameter_index: None, + }, + ); + + // Convert schema. + let result = build_final_schema(schema).unwrap(); + + // Verify integer keys are preserved. + assert!(result.contains_key("0")); + assert!(result.contains_key("1")); + + // Verify all properties are preserved. + let param0_result = result.get("0").unwrap().as_object().unwrap(); + assert_eq!(param0_result.get("type").unwrap(), "u64"); + assert_eq!( + param0_result.get("description").unwrap(), + "64-bit unsigned integer" + ); + + let param1_result = result.get("1").unwrap().as_object().unwrap(); + assert_eq!(param1_result.get("type").unwrap(), "object"); + assert_eq!(param1_result.get("mutable").unwrap(), true); + } + + #[test] + fn test_build_final_schema_mixed() { + // Create a schema with some custom names and some without using typed structs. + let mut schema = HashMap::new(); + + // Parameter 0: with custom name. + schema.insert( + "0".to_string(), + ParameterSchema { + param_type: "u64".to_string(), + description: "Amount parameter".to_string(), + custom_name: Some("amount".to_string()), + mutable: None, + parameter_index: Some("0".to_string()), + }, + ); + + // Parameter 1: without custom name. + schema.insert( + "1".to_string(), + ParameterSchema { + param_type: "bool".to_string(), + description: "Flag parameter".to_string(), + custom_name: None, + mutable: None, + parameter_index: None, + }, + ); + + // Parameter 2: with custom name. + schema.insert( + "2".to_string(), + ParameterSchema { + param_type: "string".to_string(), + description: "Message parameter".to_string(), + custom_name: Some("message".to_string()), + mutable: None, + parameter_index: Some("2".to_string()), + }, + ); + + // Convert schema. + let result = build_final_schema(schema).unwrap(); + + // Verify mixed keys. + assert!(result.contains_key("amount")); // Custom name. + assert!(result.contains_key("1")); // Integer key preserved. + assert!(result.contains_key("message")); // Custom name. + assert!(!result.contains_key("0")); // Replaced by custom name. + assert!(!result.contains_key("2")); // Replaced by custom name. + + // Verify no metadata in results. + for (_, value) in result.iter() { + let obj = value.as_object().unwrap(); + assert!(!obj.contains_key("custom_name")); + assert!(!obj.contains_key("parameter_index")); + } + } + + #[test] + fn test_customize_parameter_descriptions_json_mode() { + // Set JSON mode to skip interactive prompts. + JSON_MODE.store(true, Ordering::Relaxed); + + // Input schema based on onchain tool example. + let input_schema = r#"{ + "0": { + "type": "object", + "description": "0x123::onchain_tool::RandomCounter", + "mutable": true + }, + "1": { + "type": "u64", + "description": "64-bit unsigned integer" + } + }"#; + + // Call customize function. + let result = customize_parameter_descriptions(input_schema.to_string()).unwrap(); + + // In JSON mode, schema should be returned unchanged. + let input_value: Value = serde_json::from_str(input_schema).unwrap(); + let result_value: Value = serde_json::from_str(&result).unwrap(); + + assert_eq!(input_value, result_value); + + // Reset JSON mode. + JSON_MODE.store(false, Ordering::Relaxed); + } + + #[test] + fn test_customize_parameter_descriptions_empty_schema() { + // Set JSON mode to skip interactive prompts. + JSON_MODE.store(true, Ordering::Relaxed); + + // Empty schema. + let input_schema = "{}"; + + // Call customize function. + let result = customize_parameter_descriptions(input_schema.to_string()).unwrap(); + + // Should return empty schema unchanged. + assert_eq!(result, "{}"); + + // Reset JSON mode. + JSON_MODE.store(false, Ordering::Relaxed); + } + + #[test] + fn test_customize_output_variant_and_field_descriptions_json_mode() { + // Set JSON mode to skip interactive prompts. + JSON_MODE.store(true, Ordering::Relaxed); + + // Output schema based on onchain tool example. + let output_schema = r#"{ + "ok": { + "type": "variant", + "description": "Ok variant", + "fields": { + "old_count": { + "type": "u64", + "description": "64-bit unsigned integer" + }, + "new_count": { + "type": "u64", + "description": "64-bit unsigned integer" + }, + "increment": { + "type": "u64", + "description": "64-bit unsigned integer" + } + } + }, + "err": { + "type": "variant", + "description": "Err variant", + "fields": { + "reason": { + "type": "string", + "description": "0x1::ascii::String" + } + } + }, + "largeincrement": { + "type": "variant", + "description": "LargeIncrement variant", + "fields": { + "old_count": { + "type": "u64", + "description": "64-bit unsigned integer" + }, + "new_count": { + "type": "u64", + "description": "64-bit unsigned integer" + }, + "increment": { + "type": "u64", + "description": "64-bit unsigned integer" + }, + "warning": { + "type": "string", + "description": "0x1::ascii::String" + } + } + } + }"#; + + // Call customize function. + let result = + customize_output_variant_and_field_descriptions(output_schema.to_string()).unwrap(); + + // In JSON mode, schema should be returned unchanged. + let input_value: Value = serde_json::from_str(output_schema).unwrap(); + let result_value: Value = serde_json::from_str(&result).unwrap(); + + assert_eq!(input_value, result_value); + + // Reset JSON mode. + JSON_MODE.store(false, Ordering::Relaxed); + } + + #[test] + fn test_build_final_schema_preserves_all_fields() { + // Test that all field types are properly preserved during conversion using typed structs. + let mut schema = HashMap::new(); + + // Parameter with various field types. + schema.insert( + "0".to_string(), + ParameterSchema { + param_type: "object".to_string(), + description: "Test object".to_string(), + custom_name: Some("my_param".to_string()), + mutable: Some(true), + parameter_index: Some("0".to_string()), + }, + ); + + // Convert. + let result = build_final_schema(schema).unwrap(); + + // Verify custom name is used and metadata removed. + assert!(result.contains_key("my_param")); + let my_param = result.get("my_param").unwrap().as_object().unwrap(); + + // Verify all non-metadata fields are preserved. + assert_eq!(my_param.get("type").unwrap(), "object"); + assert_eq!(my_param.get("description").unwrap(), "Test object"); + assert_eq!(my_param.get("mutable").unwrap(), true); + + // Verify metadata fields are removed. + assert!(!my_param.contains_key("custom_name")); + assert!(!my_param.contains_key("parameter_index")); + } + + #[test] + fn test_build_final_schema_with_none_custom_name() { + // Test that None custom_name is treated as no custom name using typed structs. + let mut schema = HashMap::new(); + + schema.insert( + "0".to_string(), + ParameterSchema { + param_type: "u64".to_string(), + description: "Test param".to_string(), + custom_name: None, + mutable: None, + parameter_index: None, + }, + ); + + // Convert. + let result = build_final_schema(schema).unwrap(); + + // Verify integer key is preserved when custom_name is None. + assert!(result.contains_key("0")); + assert_eq!(result.get("0").unwrap()["type"], "u64"); + } + + #[test] + fn test_customize_parameter_descriptions_with_mock_input() { + // Ensure JSON_MODE is off for this test. + JSON_MODE.store(false, Ordering::Relaxed); + + // Create input schema. + let input_schema = r#"{ + "0": { + "type": "u64", + "description": "64-bit unsigned integer" + } + }"#; + + // Mock user input: custom name "amount" + enter, then custom description "The amount to use" + enter. + let mock_input = "amount\nThe amount to use\n"; + let mut reader = std::io::Cursor::new(mock_input.as_bytes()); + + // Call the function with mock input. + let result = + customize_parameter_descriptions_with_reader(input_schema.to_string(), &mut reader) + .unwrap(); + + // Parse the result. + let result_value: Value = serde_json::from_str(&result).unwrap(); + + // Verify the custom name was applied. + assert!(result_value.get("amount").is_some()); + assert!(result_value.get("0").is_none()); + + // Verify the custom description was applied. + let amount_param = result_value.get("amount").unwrap(); + assert_eq!( + amount_param.get("description").unwrap().as_str().unwrap(), + "The amount to use" + ); + assert_eq!(amount_param.get("type").unwrap().as_str().unwrap(), "u64"); + + // Reset JSON_MODE. + JSON_MODE.store(false, Ordering::Relaxed); + } + + #[test] + fn test_customize_parameter_descriptions_keep_defaults() { + // Ensure JSON_MODE is off. + JSON_MODE.store(false, Ordering::Relaxed); + + // Create input schema. + let input_schema = r#"{ + "0": { + "type": "bool", + "description": "Boolean flag" + } + }"#; + + // Mock user input: empty (press enter) for both name and description to keep defaults. + let mock_input = "\n\n"; + let mut reader = std::io::Cursor::new(mock_input.as_bytes()); + + // Call the function. + let result = + customize_parameter_descriptions_with_reader(input_schema.to_string(), &mut reader) + .unwrap(); + + // Parse the result. + let result_value: Value = serde_json::from_str(&result).unwrap(); + + // Verify integer key is preserved (no custom name). + assert!(result_value.get("0").is_some()); + + // Verify description is unchanged. + let param = result_value.get("0").unwrap(); + assert_eq!( + param.get("description").unwrap().as_str().unwrap(), + "Boolean flag" + ); + + // Reset JSON_MODE. + JSON_MODE.store(false, Ordering::Relaxed); + } + + #[test] + fn test_customize_output_with_mock_input() { + // Ensure JSON_MODE is off. + JSON_MODE.store(false, Ordering::Relaxed); + + // Create output schema with one variant. + let output_schema = r#"{ + "ok": { + "type": "variant", + "description": "Ok variant", + "fields": { + "count": { + "type": "u64", + "description": "64-bit unsigned integer" + } + } + } + }"#; + + // Mock user input: + // 1. "Success case\n" for variant description + // 2. "The final count\n" for field description. + let mock_input = "Success case\nThe final count\n"; + let mut reader = std::io::Cursor::new(mock_input.as_bytes()); + + // Call the function. + let result = customize_output_variant_and_field_descriptions_with_reader( + output_schema.to_string(), + &mut reader, + ) + .unwrap(); + + // Parse the result. + let result_value: Value = serde_json::from_str(&result).unwrap(); + + // Verify variant description was updated. + let ok_variant = result_value.get("ok").unwrap(); + assert_eq!( + ok_variant.get("description").unwrap().as_str().unwrap(), + "Success case" + ); + + // Verify field description was updated. + let fields = ok_variant.get("fields").unwrap(); + let count_field = fields.get("count").unwrap(); + assert_eq!( + count_field.get("description").unwrap().as_str().unwrap(), + "The final count" + ); + + // Reset JSON_MODE. + JSON_MODE.store(false, Ordering::Relaxed); + } + + #[test] + fn test_customize_output_keep_defaults() { + // Ensure JSON_MODE is off. + JSON_MODE.store(false, Ordering::Relaxed); + + // Create output schema. + let output_schema = r#"{ + "err": { + "type": "variant", + "description": "Error variant", + "fields": { + "message": { + "type": "string", + "description": "Error message" + } + } + } + }"#; + + // Mock user input: empty (press enter) for both variant and field to keep defaults. + let mock_input = "\n\n"; + let mut reader = std::io::Cursor::new(mock_input.as_bytes()); + + // Call the function. + let result = customize_output_variant_and_field_descriptions_with_reader( + output_schema.to_string(), + &mut reader, + ) + .unwrap(); + + // Parse the result. + let result_value: Value = serde_json::from_str(&result).unwrap(); + + // Verify descriptions are unchanged. + let err_variant = result_value.get("err").unwrap(); + assert_eq!( + err_variant.get("description").unwrap().as_str().unwrap(), + "Error variant" + ); + + let fields = err_variant.get("fields").unwrap(); + let message_field = fields.get("message").unwrap(); + assert_eq!( + message_field.get("description").unwrap().as_str().unwrap(), + "Error message" + ); + + // Reset JSON_MODE. + JSON_MODE.store(false, Ordering::Relaxed); + } + + #[test] + fn test_extract_over_tool_owner_cap_success() { + let mut rng = rand::thread_rng(); + let nexus_objects = sui_mocks::mock_nexus_objects(); + + // Create a mock object vector with an OwnerCap. + let owner_cap_id = sui::types::Address::generate(&mut rng); + let owner_address = sui::types::Address::generate(&mut rng); + + let objects = vec![sui::types::Object::new( + sui::types::ObjectData::Struct( + sui::types::MoveStruct::new( + sui::types::StructTag::new( + nexus_objects.primitives_pkg_id, + sui::types::Identifier::from_static("owner_cap"), + sui::types::Identifier::from_static("CloneableOwnerCap"), + vec![], + ), + true, + 0, + owner_cap_id.to_bcs().unwrap(), + ) + .unwrap(), + ), + sui::types::Owner::Address(owner_address), + sui::types::Digest::generate(&mut rng), + 1000, + )]; + + // Extract the owner cap. + let result = extract_over_tool_owner_cap(&objects, &nexus_objects); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), owner_cap_id); + } + + #[test] + fn test_extract_over_tool_owner_cap_not_found() { + let nexus_objects = sui_mocks::mock_nexus_objects(); + + // Should fail because no owner cap is found. + let result = extract_over_tool_owner_cap(&[], &nexus_objects); + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains("Could not find the OwnerCap object ID")); + } + + #[tokio::test] + async fn test_generate_and_customize_schemas_integration() { + use crate::test_utils; + + // Spin up the Sui instance. + let test_utils::containers::SuiInstance { + rpc_port, + faucet_port, + pg: _pg, + container: _container, + .. + } = test_utils::containers::setup_sui_instance().await; + + let rpc_url = format!("http://127.0.0.1:{rpc_port}"); + let faucet_url = format!("http://127.0.0.1:{faucet_port}/gas"); + + let mut rng = rand::thread_rng(); + + // Create a wallet and request some gas tokens. + let pk = sui::crypto::Ed25519PrivateKey::generate(&mut rng); + let addr = pk.public_key().derive_address(); + + test_utils::faucet::request_tokens(&faucet_url, addr) + .await + .expect("Failed to request tokens from faucet."); + + let (gas_coin, _) = test_utils::gas::fetch_gas_coins(&rpc_url, addr) + .await + .expect("Failed to fetch gas coin.") + .into_iter() + .next() + .unwrap(); + + // Publish test onchain_tool package. + let response = test_utils::contracts::publish_move_package( + &pk, + &rpc_url, + "../sdk/tests/move/onchain_tool_test", + gas_coin, + ) + .await; + + let pkg_id = response + .objects + .iter() + .find_map(|c| match c.data() { + sui::types::ObjectData::Package(m) => Some(m.id), + _ => None, + }) + .expect("Move package must be published"); + + // Enable JSON mode to skip interactive prompts. + JSON_MODE.store(true, Ordering::Relaxed); + + let client = Arc::new(Mutex::new( + sui::grpc::Client::new(format!("http://127.0.0.1:{rpc_port}")) + .expect("Failed to create Sui gRPC client"), + )); + + // Generate and customize schemas. + let result = generate_and_customize_schemas( + client, + pkg_id.to_string().parse().unwrap(), + "onchain_tool", + ) + .await; + + // Reset JSON mode. + JSON_MODE.store(false, Ordering::Relaxed); + + // Should succeed. + assert!(result.is_ok()); + let (input_schema, output_schema) = result.unwrap(); + + // Verify input schema is valid JSON. + let input_json: serde_json::Value = + serde_json::from_str(&input_schema).expect("Input schema should be valid JSON"); + assert!(input_json.is_object()); + + // Verify output schema is valid JSON. + let output_json: serde_json::Value = + serde_json::from_str(&output_schema).expect("Output schema should be valid JSON"); + assert!(output_json.is_object()); + + // Verify input schema has expected parameters (counter and increase_with). + // After skipping ProofOfUID and TxContext, we should have 2 parameters. + assert_eq!(input_json.as_object().unwrap().len(), 2); + + // Verify output schema has expected variants. + let output_obj = output_json.as_object().unwrap(); + assert!(output_obj.contains_key("ok") || output_obj.contains_key("err")); + } + + #[tokio::test] + async fn test_save_tool_owner_caps_success() { + let mut rng = rand::thread_rng(); + // Create a test FQN and object ID. + let fqn = "com.example.testtool@1".parse::().unwrap(); + let over_tool_id = sui::types::Address::generate(&mut rng); + + // Call save_tool_owner_caps. + let result = save_tool_owner_caps(fqn.clone(), over_tool_id).await; + + // Should succeed. + assert!(result.is_ok()); + } +} diff --git a/cli/src/tool/tool_set_invocation_cost.rs b/cli/src/tool/tool_set_invocation_cost.rs index fa271be0..cd7ce692 100644 --- a/cli/src/tool/tool_set_invocation_cost.rs +++ b/cli/src/tool/tool_set_invocation_cost.rs @@ -1,52 +1,56 @@ use { - crate::{command_title, display::json_output, loading, prelude::*, sui::*}, + crate::{command_title, display::json_output, loading, notify_success, prelude::*, sui::*}, nexus_sdk::transactions::tool, }; /// Set the invocation cost in MIST for a tool based on its FQN. pub(crate) async fn set_tool_invocation_cost( tool_fqn: ToolFqn, - owner_cap: Option, + owner_cap: Option, invocation_cost: u64, - sui_gas_coin: Option, + sui_gas_coin: Option, sui_gas_budget: u64, ) -> AnyResult<(), NexusCliError> { command_title!("Setting '{invocation_cost}' invocation cost for tool '{tool_fqn}'"); - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let crawler = nexus_client.crawler(); - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui, address, sui_gas_coin).await?; - - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; + let conf = CliConf::load().await.unwrap_or_default(); // Use the provided or saved `owner_cap` object ID and fetch the object. - let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).map(|t| t.over_gas)) else { + let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).and_then(|t| t.over_gas)) else { return Err(NexusCliError::Any(anyhow!( "No OwnerCap object ID found for tool '{tool_fqn}'." ))); }; - let owner_cap = fetch_object_by_id(&sui, owner_cap).await?; + let owner_cap = crawler + .get_object_metadata(owner_cap) + .await + .map(|resp| resp.object_ref()) + .map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to fetch OwnerCap object metadata for '{owner_cap}': {e}" + )) + })?; // Craft the transaction. let tx_handle = loading!("Crafting transaction..."); - let mut tx = sui::ProgrammableTransactionBuilder::new(); + let mut tx = sui::tx::TransactionBuilder::new(); - if let Err(e) = - tool::set_invocation_cost(&mut tx, objects, &tool_fqn, &owner_cap, invocation_cost) - { + if let Err(e) = tool::set_invocation_cost( + &mut tx, + nexus_objects, + &tool_fqn, + &owner_cap, + invocation_cost, + ) { tx_handle.error(); return Err(NexusCliError::Any(e)); @@ -54,16 +58,33 @@ pub(crate) async fn set_tool_invocation_cost( tx_handle.success(); - let tx_data = sui::TransactionData::new_programmable( - address, - vec![gas_coin.object_ref()], - tx.finish(), - sui_gas_budget, - reference_gas_price, - ); + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); + + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); - // Sign and submit the TX. - let response = sign_and_execute_transaction(&sui, &wallet, tx_data).await?; + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + let response = signer + .execute_tx(tx, signature, &mut gas_coin) + .await + .map_err(NexusCliError::Nexus)?; + + gas_config.release_gas_coin(gas_coin).await; + + notify_success!( + "Transaction digest: {digest}", + digest = response.digest.to_string().truecolor(100, 100, 100) + ); json_output(&json!({ "digest": response.digest }))?; diff --git a/cli/src/tool/tool_unregister.rs b/cli/src/tool/tool_unregister.rs index 0281d8e5..e6a2bccc 100644 --- a/cli/src/tool/tool_unregister.rs +++ b/cli/src/tool/tool_unregister.rs @@ -1,13 +1,21 @@ use { - crate::{command_title, confirm, display::json_output, loading, prelude::*, sui::*}, + crate::{ + command_title, + confirm, + display::json_output, + loading, + notify_success, + prelude::*, + sui::*, + }, nexus_sdk::transactions::tool, }; /// Unregister a Tool based on the provided FQN. pub(crate) async fn unregister_tool( tool_fqn: ToolFqn, - owner_cap: Option, - sui_gas_coin: Option, + owner_cap: Option, + sui_gas_coin: Option, sui_gas_budget: u64, skip_confirmation: bool, ) -> AnyResult<(), NexusCliError> { @@ -19,38 +27,37 @@ pub(crate) async fn unregister_tool( ); } - // Load CLI configuration. - let mut conf = CliConf::load().await.unwrap_or_default(); + let nexus_client = get_nexus_client(sui_gas_coin, sui_gas_budget).await?; + let signer = nexus_client.signer(); + let gas_config = nexus_client.gas_config(); + let address = signer.get_active_address(); + let nexus_objects = &*nexus_client.get_nexus_objects(); + let crawler = nexus_client.crawler(); - // Nexus objects must be present in the configuration. - let objects = &get_nexus_objects(&mut conf).await?; - - // Create wallet context, Sui client and find the active address. - let mut wallet = create_wallet_context(&conf.sui.wallet_path, conf.sui.net).await?; - let sui = build_sui_client(&conf.sui).await?; - let address = wallet.active_address().map_err(NexusCliError::Any)?; - - // Fetch gas coin object. - let gas_coin = fetch_gas_coin(&sui, address, sui_gas_coin).await?; - - // Fetch reference gas price. - let reference_gas_price = fetch_reference_gas_price(&sui).await?; + let conf = CliConf::load().await.unwrap_or_default(); // Use the provided or saved `owner_cap` object ID and fetch the object. - let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).map(|t| t.over_tool)) else { + let Some(owner_cap) = owner_cap.or(conf.tools.get(&tool_fqn).and_then(|t| t.over_gas)) else { return Err(NexusCliError::Any(anyhow!( "No OwnerCap object ID found for tool '{tool_fqn}'." ))); }; - let owner_cap = fetch_object_by_id(&sui, owner_cap).await?; - + let owner_cap = crawler + .get_object_metadata(owner_cap) + .await + .map(|resp| resp.object_ref()) + .map_err(|e| { + NexusCliError::Any(anyhow!( + "Failed to fetch OwnerCap object metadata for '{owner_cap}': {e}" + )) + })?; // Craft a TX to unregister the tool. let tx_handle = loading!("Crafting transaction..."); - let mut tx = sui::ProgrammableTransactionBuilder::new(); + let mut tx = sui::tx::TransactionBuilder::new(); - if let Err(e) = tool::unregister(&mut tx, objects, &tool_fqn, &owner_cap) { + if let Err(e) = tool::unregister(&mut tx, nexus_objects, &tool_fqn, &owner_cap) { tx_handle.error(); return Err(NexusCliError::Any(e)); @@ -58,16 +65,33 @@ pub(crate) async fn unregister_tool( tx_handle.success(); - let tx_data = sui::TransactionData::new_programmable( - address, - vec![gas_coin.object_ref()], - tx.finish(), - sui_gas_budget, - reference_gas_price, - ); + let mut gas_coin = gas_config.acquire_gas_coin().await; + + tx.set_sender(address); + tx.set_gas_budget(gas_config.get_budget()); + tx.set_gas_price(nexus_client.get_reference_gas_price()); - // Sign and submit the TX. - let response = sign_and_execute_transaction(&sui, &wallet, tx_data).await?; + tx.add_gas_objects(vec![sui::tx::Input::owned( + *gas_coin.object_id(), + gas_coin.version(), + *gas_coin.digest(), + )]); + + let tx = tx.finish().map_err(|e| NexusCliError::Any(e.into()))?; + + let signature = signer.sign_tx(&tx).await.map_err(NexusCliError::Nexus)?; + + let response = signer + .execute_tx(tx, signature, &mut gas_coin) + .await + .map_err(NexusCliError::Nexus)?; + + gas_config.release_gas_coin(gas_coin).await; + + notify_success!( + "Transaction digest: {digest}", + digest = response.digest.to_string().truecolor(100, 100, 100) + ); json_output(&json!({ "digest": response.digest }))?; diff --git a/cli/src/tool/tool_validate.rs b/cli/src/tool/tool_validate.rs index b9d799e2..d4871e10 100644 --- a/cli/src/tool/tool_validate.rs +++ b/cli/src/tool/tool_validate.rs @@ -1,20 +1,13 @@ use { - crate::{command_title, loading, prelude::*, tool::ToolIdent}, + crate::{command_title, loading, prelude::*}, nexus_sdk::types::ToolMeta, reqwest::StatusCode, }; -/// Validate either an off-chain or an on-chain tool. -pub(crate) async fn validate_tool(ident: ToolIdent) -> AnyResult { - match (ident.off_chain, ident.on_chain) { - (Some(url), None) => validate_off_chain_tool(url).await, - (None, Some(ident)) => validate_on_chain_tool(ident).await, - _ => unreachable!("Checked by clap"), - } -} - /// Validate an off-chain tool based on the provided URL. -async fn validate_off_chain_tool(url: reqwest::Url) -> AnyResult { +pub(crate) async fn validate_off_chain_tool( + url: reqwest::Url, +) -> AnyResult { command_title!("Validating off-chain Tool at '{url}'"); // Strip the trailing slash from the URL path. @@ -94,7 +87,7 @@ async fn validate_off_chain_tool(url: reqwest::Url) -> AnyResult AnyResult { +pub(crate) async fn validate_on_chain_tool(_ident: String) -> AnyResult { todo!("TODO: ") } @@ -178,41 +171,33 @@ mod tests { tokio::time::sleep(std::time::Duration::from_millis(100)).await; // No path with slash - let meta = validate_tool(ToolIdent { - off_chain: Some(reqwest::Url::parse("http://localhost:8042/").unwrap()), - on_chain: None, - }) - .await; + let meta = + validate_off_chain_tool(reqwest::Url::parse("http://localhost:8042/").unwrap()).await; println!("{:?}", meta); assert!(meta.is_ok()); let meta = meta.unwrap(); assert_eq!(meta.fqn, fqn!("xyz.dummy.tool@1")); // No path no slash - let meta = validate_tool(ToolIdent { - off_chain: Some(reqwest::Url::parse("http://localhost:8042").unwrap()), - on_chain: None, - }) - .await; + let meta = + validate_off_chain_tool(reqwest::Url::parse("http://localhost:8042").unwrap()).await; assert!(meta.is_ok()); let meta = meta.unwrap(); assert_eq!(meta.fqn, fqn!("xyz.dummy.tool@1")); // Path with slash - let meta = validate_tool(ToolIdent { - off_chain: Some(reqwest::Url::parse("http://localhost:8042/dummy/tool/").unwrap()), - on_chain: None, - }) + let meta = validate_off_chain_tool( + reqwest::Url::parse("http://localhost:8042/dummy/tool/").unwrap(), + ) .await; assert!(meta.is_ok()); let meta = meta.unwrap(); assert_eq!(meta.fqn, fqn!("xyz.dummy.tool@1")); // Path no slash - let meta = validate_tool(ToolIdent { - off_chain: Some(reqwest::Url::parse("http://localhost:8042/dummy/tool").unwrap()), - on_chain: None, - }) + let meta = validate_off_chain_tool( + reqwest::Url::parse("http://localhost:8042/dummy/tool").unwrap(), + ) .await; assert!(meta.is_ok()); let meta = meta.unwrap(); diff --git a/cli/src/utils/secrets/aes_gcm_encryption.rs b/cli/src/utils/secrets/aes_gcm_encryption.rs index 280b65ba..caae641e 100644 --- a/cli/src/utils/secrets/aes_gcm_encryption.rs +++ b/cli/src/utils/secrets/aes_gcm_encryption.rs @@ -1,7 +1,7 @@ use { super::master_key_provider::MasterKeyProvider, aes_gcm::{ - aead::{Aead, Key, KeyInit}, + aead::{Aead, KeyInit}, Aes256Gcm, }, nexus_sdk::secret_core::{ @@ -21,7 +21,8 @@ impl EncryptionAlgo for AesGcmEncryption { fn encrypt(nonce: &[u8], plaintext: &[u8]) -> Result, SecretStoreError> { let key: Zeroizing<[u8; KEY_LEN]> = MasterKeyProvider.key()?; - let cipher = Aes256Gcm::new(Key::::from_slice(&*key)); + let cipher = + Aes256Gcm::new_from_slice(&*key).map_err(|e| SecretStoreError::Crypto(Box::new(e)))?; let nonce_array: [u8; 12] = nonce .try_into() .map_err(|_| SecretStoreError::Crypto("Invalid nonce length".into()))?; @@ -32,7 +33,8 @@ impl EncryptionAlgo for AesGcmEncryption { fn decrypt(nonce: &[u8], ciphertext: &[u8]) -> Result, SecretStoreError> { let key: Zeroizing<[u8; KEY_LEN]> = MasterKeyProvider.key()?; - let cipher = Aes256Gcm::new(Key::::from_slice(&*key)); + let cipher = + Aes256Gcm::new_from_slice(&*key).map_err(|e| SecretStoreError::Crypto(Box::new(e)))?; let nonce_array: [u8; 12] = nonce .try_into() .map_err(|_| SecretStoreError::Crypto("Invalid nonce length".into()))?; diff --git a/cli/src/utils/secrets/master_key.rs b/cli/src/utils/secrets/master_key.rs index d2d1066b..74be1666 100644 --- a/cli/src/utils/secrets/master_key.rs +++ b/cli/src/utils/secrets/master_key.rs @@ -26,9 +26,18 @@ pub const KEY_LEN: usize = 32; pub const SALT_LEN: usize = 16; /// Argon2id default parameters (64 MiB, 4 passes, single thread). +#[cfg(not(test))] const ARGON2_MEMORY_KIB: u32 = 64 * 1024; +#[cfg(not(test))] const ARGON2_ITERATIONS: u32 = 4; +/// Lighter Argon2 parameters used when building/running tests to keep the +/// suite fast. +#[cfg(test)] +const ARGON2_MEMORY_KIB: u32 = 1024; // 1 MiB +#[cfg(test)] +const ARGON2_ITERATIONS: u32 = 1; + // === error type === #[derive(Debug, Error)] diff --git a/cli/src/workflow/mod.rs b/cli/src/workflow/mod.rs new file mode 100644 index 00000000..b8490689 --- /dev/null +++ b/cli/src/workflow/mod.rs @@ -0,0 +1,401 @@ +use { + crate::{error::NexusCliError, prelude::AnyResult}, + anyhow::anyhow, + nexus_sdk::{ + nexus::crawler::{Crawler, Map, Set}, + sui, + types::{ + hint_remote_fields, + json_to_nexus_data_map, + EncryptionMode, + PortsData, + StorageKind, + TypeName, + }, + }, + serde::Deserialize, + serde_json::Value, + std::collections::{HashMap, HashSet}, +}; + +pub(crate) async fn fetch_encrypted_entry_ports( + crawler: &Crawler, + entry_group: String, + dag_id: &sui::types::Address, +) -> AnyResult>, NexusCliError> { + #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)] + struct EntryPort { + name: String, + encrypted: bool, + } + + type EntryGroups = Map>>; + + #[derive(Clone, Debug, Deserialize)] + struct Dag { + entry_groups: EntryGroups, + } + + let result = crawler + .get_object::(*dag_id) + .await + .map_err(|e| NexusCliError::Any(anyhow!(e)))?; + + let key = TypeName { + name: entry_group.clone(), + }; + + let entry_group = result + .data + .entry_groups + .into_inner() + .remove(&key) + .ok_or_else(|| { + NexusCliError::Any(anyhow!("Entry group '{entry_group}' not found in DAG")) + })?; + + Ok(entry_group + .into_inner() + .into_iter() + .filter_map(|(vertex, ports)| { + let encrypted_ports: Vec = ports + .into_inner() + .into_iter() + .filter_map(|port| port.encrypted.then_some(port.name)) + .collect(); + + (!encrypted_ports.is_empty()).then_some((vertex.name, encrypted_ports)) + }) + .collect()) +} + +/// Process entry ports: encrypt and/or store remotely as needed. `input` must +/// be at least a 2-level JSON object (vertex -> port -> value). +pub(crate) async fn process_entry_ports( + input: &Value, + preferred_remote_storage: Option, + encrypt: &HashMap>, + remote: &[String], + encryption_mode: EncryptionMode, +) -> Result, NexusCliError> { + let Some(vertices) = input.as_object() else { + return Err(NexusCliError::Any(anyhow!( + "Input JSON must be an object with vertex names as keys." + ))); + }; + + let remote_handles: HashSet = remote.iter().cloned().collect(); + let mut result = HashMap::new(); + + for (vertex, data) in vertices { + let Some(ports) = data.as_object() else { + return Err(NexusCliError::Any(anyhow!( + "Input JSON for vertex '{vertex}' must be an object with port names as keys." + ))); + }; + + // Figure out which ports need to be encrypted and stored remotely for + // this vertex. + let encrypt_fields = encrypt.get(vertex); + let remote_fields = ports + .iter() + .filter_map(|(port, _)| { + let handle = format!("{vertex}.{port}"); + remote_handles.contains(&handle).then_some(port.clone()) + }) + .collect::>(); + + // Convert this json into a map of port -> NexusData. + let nexus_data_map = json_to_nexus_data_map( + data, + encrypt_fields.unwrap_or(&vec![]), + &remote_fields, + preferred_remote_storage, + encryption_mode, + ) + .map_err(NexusCliError::Any)?; + + result.insert(vertex.clone(), PortsData::from_map(nexus_data_map)); + } + + // Hint the user if they should use remote storage and for what fields. + let mut flattened: serde_json::Map = serde_json::Map::new(); + for (vertex, ports) in vertices { + let ports = ports + .as_object() + .expect("Input JSON for vertex should already be validated as an object"); + for (port, data) in ports { + let handle = format!("{vertex}.{port}"); + if remote_handles.contains(&handle) { + continue; + } + flattened.insert(handle, data.clone()); + } + } + + let flattened_json = Value::Object(flattened); + let remote_hints = hint_remote_fields(&flattened_json).unwrap_or_default(); + + if !remote_hints.is_empty() { + return Err(NexusCliError::Any(anyhow!( + "Some input fields must be stored remotely to fit within transaction size limits. Please add the following argument to your command:\n\n{command} {fields}", + command = "--remote", + fields = remote_hints.join(",") + ))); + } + + Ok(result) +} +#[cfg(test)] +mod tests { + use { + super::*, + assert_matches::assert_matches, + mockito::{Server, ServerGuard}, + nexus_sdk::{ + test_utils::nexus_mocks, + types::{DataStorage, Storable, StorageConf}, + walrus::{BlobObject, BlobStorage, NewlyCreated, StorageInfo}, + }, + serde_json::json, + std::{collections::HashMap, sync::Arc}, + }; + + async fn setup_mock_server_and_conf() -> anyhow::Result<(ServerGuard, StorageConf)> { + let server = Server::new_async().await; + let server_url = server.url(); + + let storage_conf = StorageConf { + walrus_publisher_url: Some(server_url.clone()), + walrus_aggregator_url: Some(server_url), + walrus_save_for_epochs: Some(2), + }; + + Ok((server, storage_conf)) + } + + #[tokio::test] + async fn process_entry_ports_success_no_encrypt_no_remote() { + let input = json!({ + "vertex1": { + "port1": "value1", + "port2": "value2" + } + }); + let (_, storage_conf) = setup_mock_server_and_conf() + .await + .expect("Server must start"); + let (session, _) = nexus_mocks::mock_session(); + let encrypt = HashMap::new(); + let remote = vec![]; + + let result = process_entry_ports(&input, None, &encrypt, &remote, EncryptionMode::Standard) + .await + .expect("Should succeed"); + + let vertex = result + .get("vertex1") + .expect("vertex1 missing") + .clone() + .commit_all(&storage_conf, Arc::clone(&session)) + .await + .expect("commit_all failed"); + let port1 = vertex.get("port1").expect("port1 missing"); + let port2 = vertex.get("port2").expect("port2 missing"); + + assert_matches!(port1, DataStorage::Inline(_)); + assert_eq!(port1.as_json(), &json!("value1")); + assert!(!port1.is_encrypted()); + + assert_matches!(port2, DataStorage::Inline(_)); + assert_eq!(port2.as_json(), &json!("value2")); + assert!(!port2.is_encrypted()); + } + + #[tokio::test] + async fn process_entry_ports_encrypt_only() { + let input = json!({ + "vertex1": { + "port1": "value1", + "port2": "value2" + } + }); + let (_, storage_conf) = setup_mock_server_and_conf() + .await + .expect("Server must start"); + let (session, _) = nexus_mocks::mock_session(); + let mut encrypt = HashMap::new(); + encrypt.insert("vertex1".to_string(), vec!["port1".to_string()]); + let remote = vec![]; + + let result = process_entry_ports(&input, None, &encrypt, &remote, EncryptionMode::Standard) + .await + .expect("Should succeed"); + + let vertex = result + .get("vertex1") + .expect("vertex1 missing") + .clone() + .commit_all(&storage_conf, Arc::clone(&session)) + .await + .expect("commit_all failed"); + let port1 = vertex.get("port1").expect("port1 missing"); + let port2 = vertex.get("port2").expect("port2 missing"); + + assert!(port1.is_encrypted(), "port1 should be encrypted"); + assert!(!port2.is_encrypted(), "port2 should not be encrypted"); + } + + #[tokio::test] + async fn process_entry_ports_remote_only() { + let input = json!({ + "vertex1": { + "port1": "value1", + "port2": "value2" + } + }); + let (mut server, storage_conf) = setup_mock_server_and_conf() + .await + .expect("Server must start"); + let (session, _) = nexus_mocks::mock_session(); + let encrypt = HashMap::new(); + let remote = vec!["vertex1.port1".to_string()]; + + let mock_put_response = StorageInfo { + newly_created: Some(NewlyCreated { + blob_object: BlobObject { + blob_id: "json_blob_id".to_string(), + id: "json_object_id".to_string(), + storage: BlobStorage { end_epoch: 200 }, + }, + }), + already_certified: None, + }; + + let mock_put = server + .mock("PUT", "/v1/blobs?epochs=2") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(serde_json::to_string(&mock_put_response).expect("serialize")) + .create_async() + .await; + + let result = process_entry_ports(&input, None, &encrypt, &remote, EncryptionMode::Standard) + .await + .expect("Should succeed"); + + let vertex = result + .get("vertex1") + .expect("vertex1 missing") + .clone() + .commit_all(&storage_conf, Arc::clone(&session)) + .await + .expect("commit_all failed"); + let port1 = vertex.get("port1").expect("port1 missing"); + let port2 = vertex.get("port2").expect("port2 missing"); + + assert_matches!(port1, DataStorage::Walrus(_)); + assert_matches!(port2, DataStorage::Inline(_)); + + mock_put.assert_async().await; + } + + #[tokio::test] + async fn process_entry_ports_encrypt_and_remote() { + let input = json!({ + "vertex1": { + "port1": "value1", + "port2": "value2" + } + }); + let (mut server, storage_conf) = setup_mock_server_and_conf() + .await + .expect("Server must start"); + let (session, _) = nexus_mocks::mock_session(); + let mut encrypt = HashMap::new(); + encrypt.insert("vertex1".to_string(), vec!["port1".to_string()]); + let remote = vec!["vertex1.port1".to_string()]; + + let mock_put_response = StorageInfo { + newly_created: Some(NewlyCreated { + blob_object: BlobObject { + blob_id: "json_blob_id".to_string(), + id: "json_object_id".to_string(), + storage: BlobStorage { end_epoch: 200 }, + }, + }), + already_certified: None, + }; + + let mock_put = server + .mock("PUT", "/v1/blobs?epochs=2") + .with_status(200) + .with_header("content-type", "application/json") + .with_body(serde_json::to_string(&mock_put_response).expect("serialize")) + .create_async() + .await; + + let result = process_entry_ports(&input, None, &encrypt, &remote, EncryptionMode::Standard) + .await + .expect("Should succeed"); + + let vertex = result + .get("vertex1") + .expect("vertex1 missing") + .clone() + .commit_all(&storage_conf, Arc::clone(&session)) + .await + .expect("commit_all failed"); + let port1 = vertex.get("port1").expect("port1 missing"); + let port2 = vertex.get("port2").expect("port2 missing"); + + assert!(port1.is_encrypted(), "port1 should be encrypted"); + assert!(matches!(port1, DataStorage::Walrus(_)), "port1 remote"); + assert!(!port2.is_encrypted(), "port2 should not be encrypted"); + assert_matches!(port2, DataStorage::Inline(_)); + + mock_put.assert_async().await; + } + + #[tokio::test] + async fn process_entry_ports_missing_remote_hint() { + let input = json!({ + "vertex1": { + "port1": "a".repeat(20_000), + } + }); + let encrypt = HashMap::new(); + let remote = vec![]; + + let result = + process_entry_ports(&input, None, &encrypt, &remote, EncryptionMode::Standard).await; + + assert_matches!(result, Err(NexusCliError::Any(_))); + } + + #[tokio::test] + async fn process_entry_ports_invalid_input_not_object() { + let input = json!("not an object"); + let encrypt = HashMap::new(); + let remote = vec![]; + + let result = + process_entry_ports(&input, None, &encrypt, &remote, EncryptionMode::Standard).await; + + assert_matches!(result, Err(NexusCliError::Any(_))); + } + + #[tokio::test] + async fn process_entry_ports_invalid_vertex_not_object() { + let input = json!({ + "vertex1": "not an object" + }); + let encrypt = HashMap::new(); + let remote = vec![]; + + let result = + process_entry_ports(&input, None, &encrypt, &remote, EncryptionMode::Standard).await; + + assert_matches!(result, Err(NexusCliError::Any(_))); + } +} diff --git a/codecov.yml b/codecov.yml index ff261639..d0f0b570 100644 --- a/codecov.yml +++ b/codecov.yml @@ -35,6 +35,7 @@ coverage: informational: false paths: - "!cli/" + - "!sdk/src/test_utils/" component_management: individual_components: diff --git a/docs/cli.md b/docs/cli.md index 7ffe30d5..426ef5ab 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -20,13 +20,13 @@ Set of commands for managing Tools. --- -**`nexus tool new --template