diff --git a/.github/scripts/toolkit-config-v2.jq b/.github/scripts/toolkit-config-v2.jq new file mode 100644 index 0000000..89f7f2b --- /dev/null +++ b/.github/scripts/toolkit-config-v2.jq @@ -0,0 +1,18 @@ +(type == "object") +and (.version == 2) +and ((keys - ["invoke_max_body_bytes", "signed_http", "version"]) | length == 0) +and (.signed_http | type == "object") +and (.signed_http.mode == "required") +and ((.signed_http | keys - ["allowed_leaders", "allowed_leaders_path", "mode", "tools"]) | length == 0) +and ((.signed_http.allowed_leaders_path | type) == "string") +and ((.signed_http.allowed_leaders_path | length) > 0) +and (.signed_http.tools | type == "object") +and (($expected_fqns | length) > 0) +and ((.signed_http.tools | keys | sort) == $expected_fqns) +and ([ + .signed_http.tools[] | + (type == "object") + and ((keys - ["replay_cache_ttl_ms", "tool_signing_key"]) | length == 0) + and ((.tool_signing_key | type) == "string") + and ((.tool_signing_key | length) > 0) +] | all) diff --git a/.github/workflows/offchain-tools.prepare.yml b/.github/workflows/offchain-tools.prepare.yml index 7bc63e2..5c80056 100644 --- a/.github/workflows/offchain-tools.prepare.yml +++ b/.github/workflows/offchain-tools.prepare.yml @@ -143,7 +143,7 @@ jobs: - name: Generate signed HTTP keys (idempotent) run: | - set -euo pipefail + set -Eeuo pipefail VERSIONED="${{ steps.names.outputs.versioned }}" PROJECT="${{ vars.GCP_PROJECT_ID }}" META="${RUNNER_TEMP}/prepare/meta/${{ matrix.tool }}.json" @@ -151,19 +151,28 @@ jobs: FQNS=$(jq -r '.[].fqn' "$META" | tr '\n' ' ' | xargs) echo "FQNs for $VERSIONED: $FQNS" - FORCE_FLAG="" + FORCE_ARGS=() EXISTING=$(gcloud secrets versions access latest \ --secret="nexus-tools-${VERSIONED}-signed-http-keys" \ --project="$PROJECT" 2>/dev/null || echo "") - if [ -n "$EXISTING" ]; then - EXISTING_FQNS=$(echo "$EXISTING" | jq -r '[.keys_registry[].fqn] | sort | join(" ")') - WANTED_FQNS=$(echo "$FQNS" | tr ' ' '\n' | sort | tr '\n' ' ' | xargs) - if [ "$EXISTING_FQNS" != "$WANTED_FQNS" ]; then - echo "FQNs changed: '$EXISTING_FQNS' -> '$WANTED_FQNS' — forcing new keys" - FORCE_FLAG="--force" - else - echo "FQNs unchanged — skipping keygen" + if [[ -n "$EXISTING" ]]; then + EXPECTED_FQNS=$(jq -c '[.[].fqn] | sort' "$META") + if ! EXISTING_FQNS=$(jq -ce '[.keys_registry[].fqn] | sort' <<<"$EXISTING") \ + || ! EXISTING_TOOLKIT_CONFIG=$(jq -ce '.toolkit_config' <<<"$EXISTING"); then + echo "Malformed signing-key secret found — forcing Toolkit-v2 keys" + FORCE_ARGS=(--force) + elif [[ "$EXISTING_FQNS" != "$EXPECTED_FQNS" ]]; then + echo "FQNs changed: '$EXISTING_FQNS' -> '$EXPECTED_FQNS' — forcing new keys" + FORCE_ARGS=(--force) + elif jq -e \ + --argjson expected_fqns "$EXPECTED_FQNS" \ + -f .github/scripts/toolkit-config-v2.jq \ + <<<"$EXISTING_TOOLKIT_CONFIG" >/dev/null; then + echo "FQNs and Toolkit-v2 config unchanged — skipping keygen" exit 0 + else + echo "Legacy or invalid Toolkit config found — forcing Toolkit-v2 keys" + FORCE_ARGS=(--force) fi fi @@ -173,7 +182,7 @@ jobs: gcr.io/${{ vars.GCP_INFRA_PROJECT_ID }}/nexus-next/generate-signed-http-keys:latest \ python /app/bin/generate_signed_http_keys.py "nexus-tools" "$VERSIONED" \ --fqns $FQNS \ - --project "$PROJECT" $FORCE_FLAG + --project "$PROJECT" "${FORCE_ARGS[@]}" - name: Upload tool config to GCS run: | diff --git a/.github/workflows/offchain-tools.register.yml b/.github/workflows/offchain-tools.register.yml index c893b8c..eac3f38 100644 --- a/.github/workflows/offchain-tools.register.yml +++ b/.github/workflows/offchain-tools.register.yml @@ -372,15 +372,13 @@ jobs: done done - - name: Reconcile toolkit-config secret + - name: Reconcile Toolkit-v2 config secret shell: bash run: | - set -euo pipefail + set -Eeuo pipefail MANIFEST="${{ steps.artifacts.outputs.manifest }}" META_DIR="${{ steps.artifacts.outputs.meta_dir }}" PROJECT="${{ vars.GCP_PROJECT_ID }}" - BUCKET="${{ vars.GCP_PROJECT_ID }}-nexus-tools" - NETWORK="${{ vars.TOOLS_NAMESPACE || vars.SUI_NETWORK }}" for TOOL in $(jq -r 'keys[]' "$MANIFEST"); do VERSION=$(jq -r --arg t "$TOOL" '.[$t].version' "$MANIFEST") @@ -393,22 +391,23 @@ jobs: SKELETON=$(gcloud secrets versions access latest \ --secret="nexus-tools-${VERSIONED}-signed-http-keys" \ --project="$PROJECT" \ - | jq -c '.toolkit_config') + | jq -cS '.toolkit_config') + EXPECTED_FQNS=$(jq -c '[.[].fqn] | sort' "${META_DIR}/${TOOL}.json") + + # Runtime config v2 embeds only each Tool's signing key. `tool_kid` + # remains on-chain registration metadata and is forbidden here. + if ! jq -e \ + --argjson expected_fqns "$EXPECTED_FQNS" \ + -f .github/scripts/toolkit-config-v2.jq \ + <<<"$SKELETON" >/dev/null; then + echo "::error::Generated Toolkit config for $VERSIONED is not strict schema version 2" + exit 1 + fi DESIRED="$SKELETON" - for FQN in $(jq -r '.[].fqn' "${META_DIR}/${TOOL}.json"); do - REG=$(gsutil cat "gs://${BUCKET}/${NETWORK}/offchain/registration/${TOOL}/${FQN}.json") - KID=$(echo "$REG" | jq -r '.tool_kid // "MISSING"') - if [ "$KID" = "MISSING" ] || [ "$KID" = "null" ]; then - echo "::error::No tool_kid for $FQN — refusing to write toolkit-config" - exit 1 - fi - DESIRED=$(echo "$DESIRED" | jq -c --arg fqn "$FQN" --argjson kid "$KID" \ - '.signed_http.tools[$fqn].tool_kid = $kid') - done CURRENT=$(gcloud secrets versions access latest \ - --secret="$SECRET" --project="$PROJECT" 2>/dev/null | jq -c . 2>/dev/null || echo "") + --secret="$SECRET" --project="$PROJECT" 2>/dev/null | jq -cS . 2>/dev/null || echo "") if [ "$DESIRED" = "$CURRENT" ]; then echo " ✓ toolkit-config for $VERSIONED unchanged" else diff --git a/README.md b/README.md index 9fbdf10..c0ad623 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ conventions the CI pipeline relies on: } ~~~ + The `signed_http.enabled` value belongs to the infrastructure deployment descriptor; it is not the Toolkit runtime JSON. CI generates and validates a strict Toolkit runtime config with `"version": 2` before publishing the mounted `toolkit-config` secret. + 1. **`build.rs`** — must compile-time validate that the crate name matches the binary and emit `TOOL_FQN_VERSION` from the Docker build-arg (defaults to `"1"` for local builds). Copy from an @@ -99,14 +101,14 @@ Two roles for branches, one for tags: deployment is attributable to an exact ref — no merge-strategy gotchas, no long-lived deploy branches to keep clean. -| Trigger | Matrix | Build | Push images | Chain ops | TF apply | -| --- | --- | --- | --- | --- | --- | -| PR (any base) | changed | ✓ | dry-run | — | — | -| Push to `main` | changed | ✓ | ✓ | — | — | -| Push to `iterate/testnet/**` / `iterate/mainnet/**` | all | ✓ | ✓ | ✓ (env) | ✓ | -| Push tag `testnet-*` / `mainnet-*` | all | ✓ | ✓ | ✓ (tag prefix) | ✓ | -| `workflow_dispatch` `target-env=testnet\|mainnet` | all | ✓ | ✓ | ✓ (chosen) | ✓ | -| `workflow_dispatch` `pr-number=` | all | ✓ | ✓ | ✓ (PR's base) | ✓ | +| Trigger | Matrix | Build | Push images | Chain ops | TF apply | +| --------------------------------------------------- | ------- | ----- | ----------- | -------------- | -------- | +| PR (any base) | changed | ✓ | dry-run | — | — | +| Push to `main` | changed | ✓ | ✓ | — | — | +| Push to `iterate/testnet/**` / `iterate/mainnet/**` | all | ✓ | ✓ | ✓ (env) | ✓ | +| Push tag `testnet-*` / `mainnet-*` | all | ✓ | ✓ | ✓ (tag prefix) | ✓ | +| `workflow_dispatch` `target-env=testnet\|mainnet` | all | ✓ | ✓ | ✓ (chosen) | ✓ | +| `workflow_dispatch` `pr-number=` | all | ✓ | ✓ | ✓ (PR's base) | ✓ | ## Lifecycle: PR → main → deploy @@ -153,28 +155,16 @@ FQN URLs on chain point at the right endpoints. ## What "full pipeline" does -1. **discover** — globs `offchain/tools/*/tools.json` and computes a - per-tool content version from `git rev-parse HEAD:offchain/tools//` - (first 8 hex of sha256 → u32). -1. **deploy** — builds per-tool Docker images and pushes to - `gcr.io//nexus-tools/:sha-<7>`. Build and auth - are split so token expiry can't kill a slow build mid-push. -1. **prepare** — runs each container's `--meta` to extract the - ToolMeta JSON, renders a Cloud Run config blob to - `gs:////offchain/tools/-v.json`, - and generates a signed-HTTP keys secret in Secret Manager. The - `nexus_contracts_tag` is baked from `vars.NEXUS_TAG` into each blob - so it stays pinned to whichever Nexus version was current at - prepare time. +1. **discover** — globs `offchain/tools/*/tools.json` and computes a per-tool content version from `git rev-parse HEAD:offchain/tools//` (first 8 hex of sha256 → u32). +1. **deploy** — builds per-tool Docker images and pushes to `gcr.io//nexus-tools/:sha-<7>`. Build and auth are split so token expiry can't kill a slow build mid-push. +1. **prepare** — runs each container's `--meta` to extract the ToolMeta JSON, renders a Cloud Run config blob to `gs:////offchain/tools/-v.json`, and generates a signed-HTTP keys secret containing strict Toolkit runtime config version 2 in Secret Manager. The `nexus_contracts_tag` is baked from `vars.NEXUS_TAG` into each blob so it stays pinned to whichever Nexus version was current at prepare time. 1. **register** — for each FQN: - skips the CLI call if already on chain (snapshot from `nexus tool list --json`), - otherwise pipes the ToolMeta to `nexus tool register offchain --from-meta -`, - - persists `owner_cap_over_tool` + `owner_cap_over_gas` to - `gs:////offchain/registration//.json`, + - persists `owner_cap_over_tool` + `owner_cap_over_gas` to `gs:////offchain/registration//.json`, - registers signing keys (`nexus tool auth register-key`), - - reconciles the per-tool `toolkit-config` Secret Manager secret. - Pre-step: consolidates the deployer wallet's coins so a single coin - can cover the 1 SUI per-tx budget that heavy schemas require. + - validates strict Toolkit runtime config version 2 and reconciles the per-tool `toolkit-config` Secret Manager secret. + Pre-step: consolidates the deployer wallet's coins so a single coin can cover the 1 SUI per-tx budget that heavy schemas require. 1. **trigger-tf-apply** — dispatches `Talus-Network/tf-nexus-tools`'s `terraform.yml` (via `the-actions-org/workflow-dispatch`, which works with fine-grained PATs). Terraform materializes the Cloud diff --git a/offchain/Cargo.lock b/offchain/Cargo.lock index 6c61c6f..c4b9c61 100644 --- a/offchain/Cargo.lock +++ b/offchain/Cargo.lock @@ -46,6 +46,27 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocative" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fac2ce611db8b8cee9b2aa886ca03c924e9da5e5295d0dbd0526e5d0b0710f7" +dependencies = [ + "allocative_derive", + "ctor", +] + +[[package]] +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.118", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -172,7 +193,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -651,6 +672,16 @@ dependencies = [ "hybrid-array", ] +[[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 = "curve25519-dalek" version = "4.1.3" @@ -675,7 +706,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -699,7 +730,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.118", ] [[package]] @@ -710,7 +741,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -738,7 +769,7 @@ dependencies = [ "defmt-parser", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -785,7 +816,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -795,7 +826,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn", + "syn 2.0.118", ] [[package]] @@ -829,7 +860,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -941,7 +972,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -983,6 +1014,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "ethnum" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" + [[package]] name = "eventsource-stream" version = "0.2.3" @@ -1222,7 +1259,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1863,7 +1900,7 @@ checksum = "d0879bd39df99c4c5e2c6615ccc026391a423dde10532c573e6086eb94a802cc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -1893,7 +1930,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn", + "syn 2.0.118", ] [[package]] @@ -1912,7 +1949,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2051,7 +2088,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn", + "syn 2.0.118", ] [[package]] @@ -2304,8 +2341,9 @@ dependencies = [ [[package]] name = "nexus-sdk" version = "2.0.0-rc.4" -source = "git+https://github.com/Talus-Network/nexus-sdk?tag=v2.0.0-rc.4#3ec58133085610b59b3fb803297fac5d73bd0626" +source = "git+https://github.com/Talus-Network/nexus-sdk?rev=a6438bcf8ef5080c7c5002dcdf5ae8943803df8e#a6438bcf8ef5080c7c5002dcdf5ae8943803df8e" dependencies = [ + "allocative", "anyhow", "base64 0.21.7", "bcs", @@ -2340,17 +2378,17 @@ dependencies = [ "tokio-util", "toml", "tonic", - "tracing", "zeroize", ] [[package]] name = "nexus-toolkit" version = "2.0.0-rc.4" -source = "git+https://github.com/Talus-Network/nexus-sdk?tag=v2.0.0-rc.4#3ec58133085610b59b3fb803297fac5d73bd0626" +source = "git+https://github.com/Talus-Network/nexus-sdk?rev=a6438bcf8ef5080c7c5002dcdf5ae8943803df8e#a6438bcf8ef5080c7c5002dcdf5ae8943803df8e" dependencies = [ "anyhow", "base64 0.21.7", + "bcs", "bytes", "ed25519-dalek", "env_logger", @@ -2363,7 +2401,10 @@ dependencies = [ "serde", "serde_json", "serde_path_to_error", + "tls-listener", "tokio", + "tokio-rustls", + "tokio-stream", "tracing", "warp", ] @@ -2523,7 +2564,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2560,7 +2601,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2664,7 +2705,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -2744,7 +2785,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.118", ] [[package]] @@ -2832,7 +2873,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3052,7 +3093,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3284,7 +3325,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn", + "syn 2.0.118", "unicode-ident", ] @@ -3445,7 +3486,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 2.0.118", ] [[package]] @@ -3540,7 +3581,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3551,7 +3592,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3636,7 +3677,7 @@ checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3820,7 +3861,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -3831,10 +3872,11 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sui-crypto" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74ac8658aa496ce509efc8f1206ef45d1c31871381d56c6c2bc754287a2e856a" +checksum = "b7b8f54c4405002c9ece67cb848479f80837185a55a59d8c0a105240f737d0da" dependencies = [ + "base64ct", "ed25519-dalek", "k256", "p256", @@ -3848,9 +3890,10 @@ dependencies = [ [[package]] name = "sui-move" version = "0.1.0" -source = "git+https://github.com/Talus-Network/move-binding?branch=feat%2Fruntime_package#5612483c355384c08b3cb0919d244c15cc09a40f" +source = "git+https://github.com/Talus-Network/move-binding?branch=chore%2Fupdate_sdk_version#9a91dd8d6cb7d44e50bd3cc5908ed608dd2a4a87" dependencies = [ "bcs", + "ethnum", "serde", "sui-move-derive", "sui-sdk-types", @@ -3860,7 +3903,7 @@ dependencies = [ [[package]] name = "sui-move-call" version = "0.1.0" -source = "git+https://github.com/Talus-Network/move-binding?branch=feat%2Fruntime_package#5612483c355384c08b3cb0919d244c15cc09a40f" +source = "git+https://github.com/Talus-Network/move-binding?branch=chore%2Fupdate_sdk_version#9a91dd8d6cb7d44e50bd3cc5908ed608dd2a4a87" dependencies = [ "bcs", "serde", @@ -3872,7 +3915,7 @@ dependencies = [ [[package]] name = "sui-move-codegen" version = "0.1.0" -source = "git+https://github.com/Talus-Network/move-binding?branch=feat%2Fruntime_package#5612483c355384c08b3cb0919d244c15cc09a40f" +source = "git+https://github.com/Talus-Network/move-binding?branch=chore%2Fupdate_sdk_version#9a91dd8d6cb7d44e50bd3cc5908ed608dd2a4a87" dependencies = [ "prettyplease", "proc-macro2", @@ -3881,24 +3924,24 @@ dependencies = [ "serde_json", "sui-rpc", "sui-sdk-types", - "syn", + "syn 2.0.118", "thiserror 1.0.69", ] [[package]] name = "sui-move-derive" version = "0.1.0" -source = "git+https://github.com/Talus-Network/move-binding?branch=feat%2Fruntime_package#5612483c355384c08b3cb0919d244c15cc09a40f" +source = "git+https://github.com/Talus-Network/move-binding?branch=chore%2Fupdate_sdk_version#9a91dd8d6cb7d44e50bd3cc5908ed608dd2a4a87" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] name = "sui-move-ptb" version = "0.1.0" -source = "git+https://github.com/Talus-Network/move-binding?branch=feat%2Fruntime_package#5612483c355384c08b3cb0919d244c15cc09a40f" +source = "git+https://github.com/Talus-Network/move-binding?branch=chore%2Fupdate_sdk_version#9a91dd8d6cb7d44e50bd3cc5908ed608dd2a4a87" dependencies = [ "sui-move", "sui-move-call", @@ -3908,9 +3951,9 @@ dependencies = [ [[package]] name = "sui-rpc" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00a313a63f07ada0be04c03e9d6e8c1d176e5a469ee6e3b6f973dee525667311" +checksum = "617504d5c6b748af0494b73db80fb893840522329788c48c826d8298e7edb0aa" dependencies = [ "base64 0.22.1", "bcs", @@ -3918,6 +3961,7 @@ dependencies = [ "futures", "http 1.4.2", "http-body 1.0.1", + "hyper 1.10.1", "prost", "prost-types", "serde", @@ -3932,9 +3976,9 @@ dependencies = [ [[package]] name = "sui-sdk-types" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23f800c4c3539246ba94a825f6afe7faab0bc8fc4dda56c6cfa493ea2a32ecbd" +checksum = "a22266119e43d3da1a8bc9945c8b569a513e7682f561265d70ef8b6e198dd664" dependencies = [ "base64ct", "bcs", @@ -3955,9 +3999,9 @@ dependencies = [ [[package]] name = "sui-transaction-builder" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58530d23db8328c97b4cd2dec73af501f76fc7b3d538629ca32ec011efdc3cf8" +checksum = "39697547ef9a6c03725dca449c33c3b47619b0a76ca5df827e4501944ebdaebb" dependencies = [ "async-trait", "bcs", @@ -3968,6 +4012,17 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.118" @@ -3996,7 +4051,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4079,7 +4134,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4090,7 +4145,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4137,6 +4192,19 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls-listener" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1461056cc1ef47003f7ee16e4cef3741068d4c7f6b627bfce49b7c00c120a530" +dependencies = [ + "futures-util", + "pin-project-lite", + "thiserror 2.0.18", + "tokio", + "tokio-rustls", +] + [[package]] name = "tokio" version = "1.52.3" @@ -4162,7 +4230,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4409,7 +4477,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4669,7 +4737,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.118", "wasm-bindgen-shared", ] @@ -4763,7 +4831,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -4774,7 +4842,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -5017,7 +5085,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", "synstructure", ] @@ -5038,7 +5106,7 @@ checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -5058,7 +5126,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", "synstructure", ] @@ -5079,7 +5147,7 @@ checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] @@ -5112,7 +5180,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.118", ] [[package]] diff --git a/offchain/Cargo.toml b/offchain/Cargo.toml index 3f4f3c0..8be05d2 100644 --- a/offchain/Cargo.toml +++ b/offchain/Cargo.toml @@ -49,13 +49,13 @@ features = ["full"] # For local dev convenience # path = "../../nexus-sdk/sdk" git = "https://github.com/Talus-Network/nexus-sdk" -tag = "v2.0.0-rc.4" +rev = "a6438bcf8ef5080c7c5002dcdf5ae8943803df8e" [workspace.dependencies.nexus-toolkit] # For local dev convenience # path = "../../nexus-sdk/toolkit-rust" git = "https://github.com/Talus-Network/nexus-sdk" -tag = "v2.0.0-rc.4" +rev = "a6438bcf8ef5080c7c5002dcdf5ae8943803df8e" [profile.release] diff --git a/offchain/tools/math/tests/toolkit_config_v2.rs b/offchain/tools/math/tests/toolkit_config_v2.rs new file mode 100644 index 0000000..7c51865 --- /dev/null +++ b/offchain/tools/math/tests/toolkit_config_v2.rs @@ -0,0 +1,139 @@ +use std::{ + fs, + net::{TcpListener, TcpStream}, + path::{Path, PathBuf}, + process::{Child, Command, Output, Stdio}, + thread, + time::{Duration, Instant, SystemTime, UNIX_EPOCH}, +}; + +struct ChildGuard(Option); + +impl ChildGuard { + fn spawn(config_path: &Path, bind_addr: &str) -> Self { + let child = Command::new(env!("CARGO_BIN_EXE_math")) + .env("NEXUS_TOOLKIT_CONFIG_PATH", config_path) + .env("BIND_ADDR", bind_addr) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("math binary should start"); + + Self(Some(child)) + } + + fn child_mut(&mut self) -> &mut Child { + self.0.as_mut().expect("child should be present") + } + + fn terminate_and_collect(mut self) -> Output { + let mut child = self.0.take().expect("child should be present"); + if child + .try_wait() + .expect("child status should be readable") + .is_none() + { + child.kill().expect("math binary should terminate"); + } + child + .wait_with_output() + .expect("math binary output should be readable") + } +} + +impl Drop for ChildGuard { + fn drop(&mut self) { + if let Some(child) = self.0.as_mut() { + let _ = child.kill(); + let _ = child.wait(); + } + } +} + +struct TempDir(PathBuf); + +impl TempDir { + fn create() -> Self { + let unique = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system clock should be after Unix epoch") + .as_nanos(); + let path = std::env::temp_dir().join(format!( + "nexus-tools-math-toolkit-v2-{}-{unique}", + std::process::id() + )); + fs::create_dir(&path).expect("temporary config directory should be created"); + Self(path) + } +} + +impl Drop for TempDir { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.0); + } +} + +fn process_failure(message: &str, output: Output) -> String { + format!( + "{message}\nstatus: {}\nstdout:\n{}\nstderr:\n{}", + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ) +} + +#[test] +fn math_binary_accepts_workbench_toolkit_config_v2() { + let temp_dir = TempDir::create(); + let config_path = temp_dir.0.join("toolkit-config.json"); + fs::write( + &config_path, + r#"{"version":2,"signed_http":{"mode":"disabled"}}"#, + ) + .expect("Workbench-compatible Toolkit config should be written"); + + let listener = + TcpListener::bind(("127.0.0.1", 0)).expect("an available loopback port should exist"); + let bind_addr = listener + .local_addr() + .expect("reserved loopback address should be readable"); + drop(listener); + + let mut child = ChildGuard::spawn(&config_path, &bind_addr.to_string()); + let deadline = Instant::now() + Duration::from_secs(10); + + loop { + if TcpStream::connect_timeout(&bind_addr, Duration::from_millis(100)).is_ok() { + break; + } + + if child + .child_mut() + .try_wait() + .expect("math binary status should be readable") + .is_some() + { + let output = child.terminate_and_collect(); + panic!( + "{}", + process_failure( + "math binary exited before accepting the Workbench Toolkit-v2 config", + output, + ) + ); + } + + if Instant::now() >= deadline { + let output = child.terminate_and_collect(); + panic!( + "{}", + process_failure( + "math binary did not listen after accepting the Workbench Toolkit-v2 config", + output, + ) + ); + } + + thread::sleep(Duration::from_millis(25)); + } +}