diff --git a/scripts/vk/README.md b/scripts/vk/README.md new file mode 100644 index 00000000..0871ec86 --- /dev/null +++ b/scripts/vk/README.md @@ -0,0 +1,68 @@ +# VK Scripts Architecture + +This folder centralizes all Verification Key (VK) operations for development and local workflows. + +## Structure + +- `lib/registry.sh` + - Atomic on-chain operations by circuit/version: + - `register` + - `set-active` + - `remove` +- `workflows/setup-dev.sh` + - DEV bootstrap for fixed circuits `1,2,4,5`. +- `workflows/rotate-dev.sh` + - Version rotation `old -> new` with RPC validation and optional `--remove-old`. +- `policy/verify-window-dev.sh` + - Version-window policy validation (`active` + `supported_versions`). + +## Conventions + +- Do not use legacy scripts outside this namespace. +- `lib/` does not depend on `workflows/`. +- `workflows/` uses `policy/` to gate destructive actions. +- Artifact source: `node/artifacts/verification_key_*.json`. + +## Core commands + +- Setup: + - `bash scripts/vk/workflows/setup-dev.sh ws://127.0.0.1:9944 "//Alice" 1` +- Rotate: + - `bash scripts/vk/workflows/rotate-dev.sh 2 ws://127.0.0.1:9944 "//Alice" 1` +- Rotate + remove old: + - `bash scripts/vk/workflows/rotate-dev.sh 2 ws://127.0.0.1:9944 "//Alice" 1 --remove-old` +- Verify window: + - `bash scripts/vk/policy/verify-window-dev.sh 2 http://127.0.0.1:9944 "1,2" true` + +## Security + +### Who can modify VKs? + +Only the account holding the **sudo key** of the chain. All management extrinsics +(`register_verification_key`, `set_active_version`, `remove_verification_key`, +`batch_register_verification_keys`) enforce `ensure_root(origin)` at the runtime +level — any other origin is rejected with `BadOrigin` before the call is executed. +Regular nodes, validators, and signed accounts have no access. + +`verify_proof` is the only extrinsic that accepts a signed (non-root) origin, and +it is read-only from a state perspective. + +### Passing the sudo seed safely in production + +Never pass the mnemonic inline on the CLI — it ends up in shell history: + +```bash +# BAD — mnemonic visible in `history` +bash scripts/vk/workflows/setup-dev.sh ws://... "word1 word2 ..." 1 + +# GOOD — load from environment variable +export SUDO_SEED="word1 word2 ..." +bash scripts/vk/workflows/setup-dev.sh ws://... "$SUDO_SEED" 1 +unset SUDO_SEED +``` + +After the session, clear the history entry: + +```bash +history -d $(history 1 | awk '{print $1}') +``` diff --git a/scripts/vk/lib/registry.sh b/scripts/vk/lib/registry.sh new file mode 100755 index 00000000..0b6e5860 --- /dev/null +++ b/scripts/vk/lib/registry.sh @@ -0,0 +1,230 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================= +# scripts/vk/lib/registry.sh +# Atomic on-chain verification key (VK) management in pallet-zk-verifier via sudo +# +# USAGE: +# # Register/manage individual VKs: +# bash scripts/vk/lib/registry.sh register +# bash scripts/vk/lib/registry.sh set-active +# bash scripts/vk/lib/registry.sh remove +# +# # Register and activate all 4 circuits in ONE atomic transaction: +# bash scripts/vk/lib/registry.sh batch-register \ +# \ +# +# +# EXAMPLES: +# bash scripts/vk/lib/registry.sh register 1 2 ./artifacts/verification_key_transfer.json ws://127.0.0.1:9944 "//Alice" +# bash scripts/vk/lib/registry.sh set-active 1 2 ws://127.0.0.1:9944 "//Alice" +# bash scripts/vk/lib/registry.sh remove 1 1 ws://127.0.0.1:9944 "//Alice" +# bash scripts/vk/lib/registry.sh batch-register 1 1 \ +# ./artifacts/verification_key_transfer.json \ +# ./artifacts/verification_key_unshield.json \ +# ./artifacts/verification_key_value_proof.json \ +# ./artifacts/verification_key_private_link.json \ +# ws://127.0.0.1:9944 "//Alice" +# ============================================================================= + +ACTION="${1:-}" +CIRCUIT_ID="${2:-}" +VERSION="${3:-}" + +err() { + echo "[ERROR] $*" >&2 + exit 1 +} + +log() { + echo "[$(date '+%H:%M:%S')] $*" +} + +[[ -n "$ACTION" ]] || err "Missing action: register | set-active | remove | batch-register" +[[ -n "$CIRCUIT_ID" ]] || err "Missing circuit_id (or for batch-register)" +[[ -n "$VERSION" ]] || err "Missing version" + +# Initialize all optional variables to avoid unbound variable errors with set -u +VK_FILE="" +BATCH_VERSION="" +BATCH_SET_ACTIVE="" +BATCH_VK_TRANSFER="" +BATCH_VK_UNSHIELD="" +BATCH_VK_VALUE_PROOF="" +BATCH_VK_PRIVATE_LINK="" +RPC_WS="" +SUDO_SEED="" + +case "$ACTION" in + register) + VK_FILE="${4:-}" + RPC_WS="${5:-}" + SUDO_SEED="${6:-}" + [[ -f "$VK_FILE" ]] || err "VK file not found: $VK_FILE" + ;; + set-active|remove) + RPC_WS="${4:-}" + SUDO_SEED="${5:-}" + VK_FILE="" + ;; + batch-register) + # For batch-register the positional args shift: $2=version $3=set_active $4..7=vk_files $8=rpc $9=seed + # Re-read with cleaner names to avoid confusion with CIRCUIT_ID/VERSION used by other actions + BATCH_VERSION="${2:-}" + BATCH_SET_ACTIVE="${3:-1}" + BATCH_VK_TRANSFER="${4:-}" + BATCH_VK_UNSHIELD="${5:-}" + BATCH_VK_VALUE_PROOF="${6:-}" + BATCH_VK_PRIVATE_LINK="${7:-}" + RPC_WS="${8:-}" + SUDO_SEED="${9:-}" + [[ -n "$BATCH_VERSION" ]] || err "batch-register: missing " + [[ "$BATCH_VERSION" =~ ^[0-9]+$ ]] || err "batch-register: version must be an integer >= 0" + [[ "$BATCH_SET_ACTIVE" =~ ^[01]$ ]] || err "batch-register: set_active must be 0 or 1" + for _f in "$BATCH_VK_TRANSFER" "$BATCH_VK_UNSHIELD" "$BATCH_VK_VALUE_PROOF" "$BATCH_VK_PRIVATE_LINK"; do + [[ -f "$_f" ]] || err "VK file not found: $_f" + done + ;; + *) + err "Invalid action: $ACTION (use register | set-active | remove | batch-register)" + ;; +esac + +[[ -n "$RPC_WS" ]] || err "Missing RPC WS URL" +[[ -n "$SUDO_SEED" ]] || err "Missing sudo seed/mnemonic" + +command -v node >/dev/null 2>&1 || err "Node.js not found" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VK_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +if [[ ! -d "$VK_DIR/node_modules/@polkadot/api" ]]; then + log "Installing JS dependencies..." + cd "$VK_DIR" + npm install --silent @polkadot/api @polkadot/keyring + cd - >/dev/null +fi + +export NODE_PATH="$VK_DIR/node_modules${NODE_PATH:+:$NODE_PATH}" + +log "Action: $ACTION | circuit: $CIRCUIT_ID | version: $VERSION" +log "RPC: $RPC_WS" + +node - "$ACTION" "$CIRCUIT_ID" "$VERSION" "$VK_FILE" "$RPC_WS" "$SUDO_SEED" \ + "$BATCH_VERSION" "$BATCH_SET_ACTIVE" \ + "$BATCH_VK_TRANSFER" "$BATCH_VK_UNSHIELD" "$BATCH_VK_VALUE_PROOF" "$BATCH_VK_PRIVATE_LINK" << 'JS' +const fs = require('fs'); +const path = require('path'); +const { ApiPromise, WsProvider } = require('@polkadot/api'); +const { Keyring } = require('@polkadot/keyring'); + +const [,, action, circuitIdRaw, versionRaw, vkFile, rpcWs, sudoSeed, + batchVersion, batchSetActive, + batchVkTransfer, batchVkUnshield, batchVkValueProof, batchVkPrivateLink] = process.argv; + +function assertNumber(name, value) { + if (!Number.isInteger(value) || value < 0) { + throw new Error(`${name} must be an integer >= 0`); + } +} + +(async () => { + // For non-batch actions parse circuit_id/version as before + let circuitId, version; + if (action !== 'batch-register') { + circuitId = Number(circuitIdRaw); + version = Number(versionRaw); + assertNumber('circuit_id', circuitId); + assertNumber('version', version); + } + + const provider = new WsProvider(rpcWs); + const api = await ApiPromise.create({ provider }); + + const keyring = new Keyring({ type: 'sr25519' }); + const sudoPair = keyring.addFromUri(sudoSeed); + + let innerCall; + + if (action === 'register') { + const vkData = fs.readFileSync(path.resolve(vkFile)); + if (!vkData.length) throw new Error('VK file is empty'); + + innerCall = api.tx.zkVerifier.registerVerificationKey( + circuitId, + version, + Array.from(vkData) + ); + } else if (action === 'set-active') { + innerCall = api.tx.zkVerifier.setActiveVersion(circuitId, version); + } else if (action === 'remove') { + innerCall = api.tx.zkVerifier.removeVerificationKey(circuitId, version); + } else if (action === 'batch-register') { + // circuit IDs: transfer=1, unshield=2, value_proof=6, private_link=5 + const ver = Number(batchVersion); + const setActive = batchSetActive === '1'; + assertNumber('batch_version', ver); + + const vkFiles = [ + { circuitId: 1, file: batchVkTransfer }, + { circuitId: 2, file: batchVkUnshield }, + { circuitId: 6, file: batchVkValueProof }, + { circuitId: 5, file: batchVkPrivateLink }, + ]; + + const entries = vkFiles.map(({ circuitId: cid, file }) => { + const data = fs.readFileSync(path.resolve(file)); + if (!data.length) throw new Error(`VK file is empty: ${file}`); + return { + circuit_id: cid, + version: ver, + verification_key: Array.from(data), + set_active: setActive, + }; + }); + + innerCall = api.tx.zkVerifier.batchRegisterVerificationKeys(entries); + } else { + throw new Error(`Unsupported action: ${action}`); + } + + const sudoCall = api.tx.sudo.sudo(innerCall); + + await new Promise((resolve, reject) => { + sudoCall.signAndSend(sudoPair, ({ status, events, dispatchError }) => { + if (dispatchError) { + if (dispatchError.isModule) { + const decoded = api.registry.findMetaError(dispatchError.asModule); + reject(new Error(`DispatchError: ${decoded.section}.${decoded.name}: ${decoded.docs}`)); + } else { + reject(new Error(`DispatchError: ${dispatchError.toString()}`)); + } + return; + } + + for (const { event } of events) { + if (api.events.sudo.Sudid.is(event)) { + const [result] = event.data; + if (result.isErr) { + reject(new Error(`sudo.Sudid failed: ${result.asErr}`)); + return; + } + } + } + + if (status.isFinalized) { + console.log(`[success] Action ${action} finalized in block ${status.asFinalized}`); + resolve(); + } + }).catch(reject); + }); + + await api.disconnect(); +})().catch((error) => { + console.error('[fatal]', error.message); + process.exit(1); +}); +JS + +log "VK operation completed" diff --git a/scripts/vk/package-lock.json b/scripts/vk/package-lock.json new file mode 100644 index 00000000..0bb41d8e --- /dev/null +++ b/scripts/vk/package-lock.json @@ -0,0 +1,926 @@ +{ + "name": "orbinum-vk-scripts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "orbinum-vk-scripts", + "version": "1.0.0", + "dependencies": { + "@polkadot/api": "^14.3.1", + "@polkadot/keyring": "^13.5.9" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz", + "integrity": "sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz", + "integrity": "sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot-api/metadata-builders": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz", + "integrity": "sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@polkadot-api/observable-client": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz", + "integrity": "sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/metadata-builders": "0.3.2", + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + }, + "peerDependencies": { + "@polkadot-api/substrate-client": "0.1.4", + "rxjs": ">=7.8.0" + } + }, + "node_modules/@polkadot-api/substrate-bindings": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz", + "integrity": "sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@noble/hashes": "^1.3.1", + "@polkadot-api/utils": "0.1.0", + "@scure/base": "^1.1.1", + "scale-ts": "^1.6.0" + } + }, + "node_modules/@polkadot-api/substrate-client": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz", + "integrity": "sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.1", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@polkadot-api/utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz", + "integrity": "sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot/api": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-14.3.1.tgz", + "integrity": "sha512-ZBKSXEVJa1S1bnmpnA7KT/fX3sJDIJOdVD9Hp3X+G73yvXzuK5k1Mn5z9bD/AcMs/HAGcbuYU+b9+b9IByH9YQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-augment": "14.3.1", + "@polkadot/api-base": "14.3.1", + "@polkadot/api-derive": "14.3.1", + "@polkadot/keyring": "^13.2.3", + "@polkadot/rpc-augment": "14.3.1", + "@polkadot/rpc-core": "14.3.1", + "@polkadot/rpc-provider": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/types-augment": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/types-create": "14.3.1", + "@polkadot/types-known": "14.3.1", + "@polkadot/util": "^13.2.3", + "@polkadot/util-crypto": "^13.2.3", + "eventemitter3": "^5.0.1", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-augment": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-14.3.1.tgz", + "integrity": "sha512-PE6DW+8kRhbnGKn7qCF7yM6eEt/kqrY8bh1i0RZcPY9QgwXW4bZZrtMK4WssX6Z70NTEoOW6xHYIjc7gFZuz8g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-base": "14.3.1", + "@polkadot/rpc-augment": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/types-augment": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-base": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-14.3.1.tgz", + "integrity": "sha512-GZT6rTpT3HYZ/C3rLPjoX3rX3DOxNG/zgts+jKjNrCumAeZkVq5JErKIX8/3f2TVaE2Kbqniy3d1TH/AL4HBPA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/util": "^13.2.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-derive": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-14.3.1.tgz", + "integrity": "sha512-PhqUEJCY54vXtIaoYqGUtJY06wHd/K0cBmBz9yCLxp8UZkLoGWhfJRTruI25Jnucf9awS5cZKYqbsoDrL09Oqg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api": "14.3.1", + "@polkadot/api-augment": "14.3.1", + "@polkadot/api-base": "14.3.1", + "@polkadot/rpc-core": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/util": "^13.2.3", + "@polkadot/util-crypto": "^13.2.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/keyring": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.5.9.tgz", + "integrity": "sha512-bMCpHDN7U8ytxawjBZ89/he5s3AmEZuOdkM/ABcorh/flXNPfyghjFK27Gy4OKoFxX52yJ2sTHR4NxM87GuFXQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.5.9", + "@polkadot/util-crypto": "13.5.9", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9", + "@polkadot/util-crypto": "13.5.9" + } + }, + "node_modules/@polkadot/networks": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.5.9.tgz", + "integrity": "sha512-nmKUKJjiLgcih0MkdlJNMnhEYdwEml2rv/h59ll2+rAvpsVWMTLCb6Cq6q7UC44+8kiWK2UUJMkFU+3PFFxndA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.5.9", + "@substrate/ss58-registry": "^1.51.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-augment": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-14.3.1.tgz", + "integrity": "sha512-Z8Hp8fFHwFCiTX0bBCDqCZ4U26wLIJl1NRSjJTsAr+SS68pYZBDGCwhKztpKGqndk1W1akRUaxrkGqYdIFmspQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-core": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-14.3.1.tgz", + "integrity": "sha512-FV2NPhFwFxmX8LqibDcGc6IKTBqmvwr7xwF2OA60Br4cX+AQzMSVpFlfQcETll+0M+LnRhqGKGkP0EQWXaSowA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-augment": "14.3.1", + "@polkadot/rpc-provider": "14.3.1", + "@polkadot/types": "14.3.1", + "@polkadot/util": "^13.2.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-provider": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-14.3.1.tgz", + "integrity": "sha512-NF/Z/7lzT+jp5LZzC49g+YIjRzXVI0hFag3+B+4zh6E/kKADdF59EHj2Im4LDhRGOnEO9AE4H6/UjNEbZ94JtA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^13.2.3", + "@polkadot/types": "14.3.1", + "@polkadot/types-support": "14.3.1", + "@polkadot/util": "^13.2.3", + "@polkadot/util-crypto": "^13.2.3", + "@polkadot/x-fetch": "^13.2.3", + "@polkadot/x-global": "^13.2.3", + "@polkadot/x-ws": "^13.2.3", + "eventemitter3": "^5.0.1", + "mock-socket": "^9.3.1", + "nock": "^13.5.5", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@substrate/connect": "0.8.11" + } + }, + "node_modules/@polkadot/types": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-14.3.1.tgz", + "integrity": "sha512-O748XgCLDQYxS5nQ6TJSqW88oC4QNIoNVlWZC2Qq4SmEXuSzaNHQwSVtdyPRJCCc4Oi1DCQvGui4O+EukUl7HA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^13.2.3", + "@polkadot/types-augment": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/types-create": "14.3.1", + "@polkadot/util": "^13.2.3", + "@polkadot/util-crypto": "^13.2.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-14.3.1.tgz", + "integrity": "sha512-SC4M6TBlgCglNz+gRbvfoVRDz0Vyeev6v0HeAdw0H6ayEW4BXUdo5bFr0092bdS5uTrEPgiSyUry5TJs2KoXig==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-codec": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-14.3.1.tgz", + "integrity": "sha512-3y3RBGd+8ebscGbNUOjqUjnRE7hgicgid5LtofHK3O1EDcJQJnYBDkJ7fOAi96CDgHsg+f2FWWkBWEPgpOQoMQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^13.2.3", + "@polkadot/x-bigint": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-create": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-14.3.1.tgz", + "integrity": "sha512-F4EBvF3Zvym0xrkAA5Yz01IAVMepMV3w2Dwd0C9IygEAQ5sYLLPHmf72/aXn+Ag+bSyT2wlJHpDc+nEBXNQ3Gw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types-codec": "14.3.1", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-known": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-14.3.1.tgz", + "integrity": "sha512-58b3Yc7+sxwNjs8axmrA9OCgnxmEKIq7XCH2VxSgLqTeqbohVtxwUSCW/l8NPrq1nxzj4J2sopu0PPg8/++q4g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/networks": "^13.2.3", + "@polkadot/types": "14.3.1", + "@polkadot/types-codec": "14.3.1", + "@polkadot/types-create": "14.3.1", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-support": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-14.3.1.tgz", + "integrity": "sha512-MfVe4iIOJIfBr+gj8Lu8gwIvhnO6gDbG5LeaKAjY6vS6Oh0y5Ztr8NdMIl8ccSpoyt3LqIXjfApeGzHiLzr6bw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.5.9.tgz", + "integrity": "sha512-pIK3XYXo7DKeFRkEBNYhf3GbCHg6dKQisSvdzZwuyzA6m7YxQq4DFw4IE464ve4Z7WsJFt3a6C9uII36hl9EWw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-global": "13.5.9", + "@polkadot/x-textdecoder": "13.5.9", + "@polkadot/x-textencoder": "13.5.9", + "@types/bn.js": "^5.1.6", + "bn.js": "^5.2.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", + "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.5.9", + "@polkadot/util": "13.5.9", + "@polkadot/wasm-crypto": "^7.5.3", + "@polkadot/wasm-util": "^7.5.3", + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-randomvalues": "13.5.9", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9" + } + }, + "node_modules/@polkadot/wasm-bridge": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.5.4.tgz", + "integrity": "sha512-6xaJVvoZbnbgpQYXNw9OHVNWjXmtcoPcWh7hlwx3NpfiLkkjljj99YS+XGZQlq7ks2fVCg7FbfknkNb8PldDaA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.5.4", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.5.4.tgz", + "integrity": "sha512-1seyClxa7Jd7kQjfnCzTTTfYhTa/KUTDUaD3DMHBk5Q4ZUN1D1unJgX+v1aUeXSPxmzocdZETPJJRZjhVOqg9g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.5.4", + "@polkadot/wasm-crypto-asmjs": "7.5.4", + "@polkadot/wasm-crypto-init": "7.5.4", + "@polkadot/wasm-crypto-wasm": "7.5.4", + "@polkadot/wasm-util": "7.5.4", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.5.4.tgz", + "integrity": "sha512-ZYwxQHAJ8pPt6kYk9XFmyuFuSS+yirJLonvP+DYbxOrARRUHfN4nzp4zcZNXUuaFhpbDobDSFn6gYzye6BUotA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-init": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.5.4.tgz", + "integrity": "sha512-U6s4Eo2rHs2n1iR01vTz/sOQ7eOnRPjaCsGWhPV+ZC/20hkVzwPAhiizu/IqMEol4tO2yiSheD4D6bn0KxUJhg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.5.4", + "@polkadot/wasm-crypto-asmjs": "7.5.4", + "@polkadot/wasm-crypto-wasm": "7.5.4", + "@polkadot/wasm-util": "7.5.4", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.5.4.tgz", + "integrity": "sha512-PsHgLsVTu43eprwSvUGnxybtOEuHPES6AbApcs7y5ZbM2PiDMzYbAjNul098xJK/CPtrxZ0ePDFnaQBmIJyTFw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.5.4", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-util": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.5.4.tgz", + "integrity": "sha512-hqPpfhCpRAqCIn/CYbBluhh0TXmwkJnDRjxrU9Bnqtw9nMNa97D8JuOjdd2pi0rxm+eeLQ/f1rQMp71RMM9t4w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/x-bigint": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.5.9.tgz", + "integrity": "sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-fetch": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-13.5.9.tgz", + "integrity": "sha512-urwXQZtT4yYROiRdJS6zHu18J/jCoAGpbgPIAjwdqjT11t9XIq4SjuPMxD19xBRhbYe9ocWV8i1KHuoMbZgKbA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "node-fetch": "^3.3.2", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-global": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.5.9.tgz", + "integrity": "sha512-zSRWvELHd3Q+bFkkI1h2cWIqLo1ETm+MxkNXLec3lB56iyq/MjWBxfXnAFFYFayvlEVneo7CLHcp+YTFd9aVSA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.5.9.tgz", + "integrity": "sha512-Uuuz3oubf1JCCK97fsnVUnHvk4BGp/W91mQWJlgl5TIOUSSTIRr+lb5GurCfl4kgnQq53Zi5fJV+qR9YumbnZw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.5.9.tgz", + "integrity": "sha512-W2HhVNUbC/tuFdzNMbnXAWsIHSg9SC9QWDNmFD3nXdSzlXNgL8NmuiwN2fkYvCQBtp/XSoy0gDLx0C+Fo19cfw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-textencoder": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.5.9.tgz", + "integrity": "sha512-SG0MHnLUgn1ZxFdm0KzMdTHJ47SfqFhdIPMcGA0Mg/jt2rwrfrP3jtEIJMsHfQpHvfsNPfv55XOMmoPWuQnP/Q==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-ws": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.5.9.tgz", + "integrity": "sha512-NKVgvACTIvKT8CjaQu9d0dERkZsWIZngX/4NVSjc01WHmln4F4y/zyBdYn/Z2V0Zw28cISx+lB4qxRmqTe7gbg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "13.5.9", + "tslib": "^2.8.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@substrate/connect": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz", + "integrity": "sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==", + "deprecated": "versions below 1.x are no longer maintained", + "license": "GPL-3.0-only", + "optional": true, + "dependencies": { + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "@substrate/light-client-extension-helpers": "^1.0.0", + "smoldot": "2.0.26" + } + }, + "node_modules/@substrate/connect-extension-protocol": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.2.tgz", + "integrity": "sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/connect-known-chains": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.10.3.tgz", + "integrity": "sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/light-client-extension-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz", + "integrity": "sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "^0.0.1", + "@polkadot-api/json-rpc-provider-proxy": "^0.1.0", + "@polkadot-api/observable-client": "^0.3.0", + "@polkadot-api/substrate-client": "^0.1.2", + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "rxjs": "^7.8.1" + }, + "peerDependencies": { + "smoldot": "2.x" + } + }, + "node_modules/@substrate/ss58-registry": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz", + "integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==", + "license": "Apache-2.0" + }, + "node_modules/@types/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "25.4.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.4.0.tgz", + "integrity": "sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/bn.js": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz", + "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", + "license": "MIT" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/mock-socket": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nock": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/scale-ts": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz", + "integrity": "sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==", + "license": "MIT", + "optional": true + }, + "node_modules/smoldot": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz", + "integrity": "sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "optional": true, + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/scripts/vk/package.json b/scripts/vk/package.json new file mode 100644 index 00000000..c7b90444 --- /dev/null +++ b/scripts/vk/package.json @@ -0,0 +1,10 @@ +{ + "name": "orbinum-vk-scripts", + "version": "1.0.0", + "private": true, + "description": "VK operations scripts dependencies", + "dependencies": { + "@polkadot/api": "^14.3.1", + "@polkadot/keyring": "^13.5.9" + } +} diff --git a/scripts/vk/policy/verify-window-dev.sh b/scripts/vk/policy/verify-window-dev.sh new file mode 100755 index 00000000..f35871c8 --- /dev/null +++ b/scripts/vk/policy/verify-window-dev.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================ +# scripts/vk/policy/verify-window-dev.sh +# Verifies VK version-window policy for fixed DEV circuits. +# +# Verified circuits: 1 (transfer), 2 (unshield), 6 (value_proof), 5 (private_link) +# +# USAGE: +# bash scripts/vk/policy/verify-window-dev.sh [rpc_http] [required_versions_csv] [strict] +# +# PARAMETERS: +# expected_active version that must be active on all circuits +# rpc_http default: http://127.0.0.1:9944 +# required_versions_csv default: expected_active (e.g., "1,2") +# strict default: false +# - true: supported_versions must be EXACTLY required_versions +# - false: supported_versions must contain required_versions +# +# EXAMPLES: +# bash scripts/vk/policy/verify-window-dev.sh 2 +# bash scripts/vk/policy/verify-window-dev.sh 2 http://127.0.0.1:9944 "1,2" true +# ============================================================================ + +EXPECTED_ACTIVE="${1:-}" +RPC_HTTP="${2:-http://127.0.0.1:9944}" +REQUIRED_CSV="${3:-${EXPECTED_ACTIVE}}" +STRICT="${4:-false}" + +err() { + echo "[ERROR] $*" >&2 + exit 1 +} + +log() { + echo "[$(date '+%H:%M:%S')] $*" +} + +[[ -n "$EXPECTED_ACTIVE" ]] || err "Missing " +[[ "$EXPECTED_ACTIVE" =~ ^[0-9]+$ ]] || err "expected_active must be an integer >= 0" +[[ "$STRICT" == "true" || "$STRICT" == "false" ]] || err "strict must be true|false" + +CIRCUITS=(1 2 6 5) + +log "Verifying VK window" +log "RPC: $RPC_HTTP" +log "expected_active: $EXPECTED_ACTIVE" +log "required_versions: $REQUIRED_CSV" +log "strict: $STRICT" + +for cid in "${CIRCUITS[@]}"; do + response=$(curl -s -H "Content-Type: application/json" \ + -d "{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"zkVerifier_getCircuitVersionInfo\",\"params\":[${cid}]}" \ + "$RPC_HTTP") + + python3 - "$cid" "$EXPECTED_ACTIVE" "$REQUIRED_CSV" "$STRICT" "$response" <<'PY' +import json +import sys + +cid = int(sys.argv[1]) +expected_active = int(sys.argv[2]) +required = [int(x.strip()) for x in sys.argv[3].split(',') if x.strip()] +strict = sys.argv[4].lower() == 'true' +raw = sys.argv[5] + +try: + payload = json.loads(raw) +except Exception as e: + print(f"[ERROR] circuit {cid}: invalid JSON response: {e}") + sys.exit(1) + +result = payload.get('result') +if not result: + print(f"[ERROR] circuit {cid}: missing result in RPC response") + sys.exit(1) + +active = result.get('active_version') +supported = result.get('supported_versions') or [] + +if active != expected_active: + print(f"[ERROR] circuit {cid}: active_version={active} expected={expected_active}") + sys.exit(1) + +missing = [v for v in required if v not in supported] +if missing: + print(f"[ERROR] circuit {cid}: missing required versions {missing}; supported={supported}") + sys.exit(1) + +if strict: + if sorted(supported) != sorted(required): + print(f"[ERROR] circuit {cid}: strict=true and supported={supported} != required={required}") + sys.exit(1) + +print(f"[OK] circuit {cid}: active={active} supported={supported}") +PY + +done + +log "✅ VK window is valid for all 4 circuits" diff --git a/scripts/vk/workflows/rotate-dev.sh b/scripts/vk/workflows/rotate-dev.sh new file mode 100755 index 00000000..e5c93c76 --- /dev/null +++ b/scripts/vk/workflows/rotate-dev.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================ +# scripts/vk/workflows/rotate-dev.sh +# DEV on-chain VK rotation for the 4 supported circuits. +# +# Flow: +# 1) register NEW_VERSION on 1,2,4,5 +# 2) set-active NEW_VERSION on 1,2,4,5 +# 3) RPC validation per circuit +# 4) optional: remove OLD_VERSION on 1,2,4,5 (if --remove-old) +# +# USAGE: +# bash scripts/vk/workflows/rotate-dev.sh [rpc_ws] [sudo_seed] [old_version] +# +# FLAGS: +# --remove-old removes old_version at the end (default: do not remove) +# +# EXAMPLES: +# bash scripts/vk/workflows/rotate-dev.sh 2 +# bash scripts/vk/workflows/rotate-dev.sh 2 ws://127.0.0.1:9944 "//Alice" 1 +# bash scripts/vk/workflows/rotate-dev.sh 2 ws://127.0.0.1:9944 "//Alice" 1 --remove-old +# ============================================================================ + +NEW_VERSION="${1:-}" +RPC_WS="${2:-ws://127.0.0.1:9944}" +SUDO_SEED="${3:-//Alice}" +OLD_VERSION="${4:-1}" +REMOVE_OLD=false + +for arg in "$@"; do + if [[ "$arg" == "--remove-old" ]]; then + REMOVE_OLD=true + fi +done + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +NODE_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)" +VK_REGISTRY_SCRIPT="$SCRIPT_DIR/../lib/registry.sh" +VERIFY_WINDOW_SCRIPT="$SCRIPT_DIR/../policy/verify-window-dev.sh" +ARTIFACTS_DIR="$NODE_DIR/artifacts" +CONVERT_VK_BIN="$NODE_DIR/../groth16-proofs/target/release/convert-vk" + +RPC_HTTP="${RPC_WS/ws:\/\//http://}" +RPC_HTTP="${RPC_HTTP/wss:\/\//https://}" + +err() { + echo "[ERROR] $*" >&2 + exit 1 +} + +log() { + echo "[$(date '+%H:%M:%S')] $*" +} + +[[ -n "$NEW_VERSION" ]] || err "Missing . Usage: bash scripts/vk/workflows/rotate-dev.sh [rpc_ws] [sudo_seed] [old_version] [--remove-old]" +[[ "$NEW_VERSION" =~ ^[0-9]+$ ]] || err "new_version must be an integer >= 0" +[[ "$OLD_VERSION" =~ ^[0-9]+$ ]] || err "old_version must be an integer >= 0" +[[ "$NEW_VERSION" != "$OLD_VERSION" ]] || err "new_version and old_version cannot be equal" +[[ -f "$VK_REGISTRY_SCRIPT" ]] || err "Not found: $VK_REGISTRY_SCRIPT" +[[ -f "$VERIFY_WINDOW_SCRIPT" ]] || err "Not found: $VERIFY_WINDOW_SCRIPT" + +# Ensure the convert-vk binary is available +if [[ ! -x "$CONVERT_VK_BIN" ]]; then + log "Building convert-vk tool..." + (cd "$NODE_DIR/../groth16-proofs" && cargo build --bin convert-vk --release --quiet) \ + || err "Failed to build convert-vk. Run: cd groth16-proofs && cargo build --bin convert-vk --release" +fi + +# Convert each JSON VK to arkworks compressed binary and return the .bin path +convert_vk() { + local json_path="$1" + local bin_path="${json_path%.json}.bin" + "$CONVERT_VK_BIN" "$json_path" "$bin_path" \ + || err "Failed to convert $(basename "$json_path") to binary" + echo "$bin_path" +} + +VK_TRANSFER_BIN=$(convert_vk "$ARTIFACTS_DIR/verification_key_transfer.json") +VK_UNSHIELD_BIN=$(convert_vk "$ARTIFACTS_DIR/verification_key_unshield.json") +VK_VALUE_PROOF_BIN=$(convert_vk "$ARTIFACTS_DIR/verification_key_value_proof.json") +VK_PRIVATE_LINK_BIN=$(convert_vk "$ARTIFACTS_DIR/verification_key_private_link.json") + +validate_rpc() { + local circuit_id="$1" + local label="$2" + + local response + response=$(curl -s -H "Content-Type: application/json" \ + -d "{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"zkVerifier_getCircuitVersionInfo\",\"params\":[${circuit_id}]}" \ + "$RPC_HTTP") + + local active + active=$(printf '%s' "$response" | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get("result",{}).get("active_version",""))' 2>/dev/null || true) + + [[ "$active" == "$NEW_VERSION" ]] || err "[$label] active_version expected=$NEW_VERSION current=$active" + log "[$label] RPC ok (active_version=$active)" +} + +remove_old() { + local circuit_id="$1" + local label="$2" + + log "[$label] remove old v$OLD_VERSION" + bash "$VK_REGISTRY_SCRIPT" remove "$circuit_id" "$OLD_VERSION" "$RPC_WS" "$SUDO_SEED" +} + +log "Starting DEV VK rotation" +log "RPC: $RPC_WS" +log "old_version: $OLD_VERSION" +log "new_version: $NEW_VERSION" +log "remove_old: $REMOVE_OLD" + +# Register and activate all 4 circuits in ONE atomic transaction +log "Batch registering + activating all 4 circuits (v$NEW_VERSION) in a single tx..." +bash "$VK_REGISTRY_SCRIPT" batch-register \ + "$NEW_VERSION" 1 \ + "$VK_TRANSFER_BIN" "$VK_UNSHIELD_BIN" "$VK_VALUE_PROOF_BIN" "$VK_PRIVATE_LINK_BIN" \ + "$RPC_WS" "$SUDO_SEED" + +validate_rpc 1 "transfer" +validate_rpc 2 "unshield" +validate_rpc 6 "value_proof" +validate_rpc 5 "private_link" + +if [[ "$REMOVE_OLD" == true ]]; then + log "Checking pre-remove window (required=[${OLD_VERSION},${NEW_VERSION}])" + bash "$VERIFY_WINDOW_SCRIPT" "$NEW_VERSION" "$RPC_HTTP" "${OLD_VERSION},${NEW_VERSION}" true + + remove_old 1 "transfer" + remove_old 2 "unshield" + remove_old 6 "value_proof" + remove_old 5 "private_link" + + log "Checking post-remove state (required=[${NEW_VERSION}])" + bash "$VERIFY_WINDOW_SCRIPT" "$NEW_VERSION" "$RPC_HTTP" "${NEW_VERSION}" false + + log "old_version v$OLD_VERSION removed from all 4 circuits" +fi + +# Remove generated .bin files — they are build artefacts excluded from git +rm -f "$VK_TRANSFER_BIN" "$VK_UNSHIELD_BIN" "$VK_VALUE_PROOF_BIN" "$VK_PRIVATE_LINK_BIN" + +log "✅ VK rotation completed" diff --git a/scripts/vk/workflows/setup-dev.sh b/scripts/vk/workflows/setup-dev.sh new file mode 100755 index 00000000..c2ddcd82 --- /dev/null +++ b/scripts/vk/workflows/setup-dev.sh @@ -0,0 +1,151 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================ +# scripts/vk/workflows/setup-dev.sh +# DEV setup for on-chain VKs (main entrypoint for developers) +# +# Registers + activates ONLY these 4 circuits: +# 1 transfer +# 2 unshield +# 6 value_proof +# 5 private_link +# +# VK artifacts are resolved from the @orbinum/circuits npm package via unpkg: +# 1. Fetches manifest.json to determine the latest package_version +# 2. Pins all artifact URLs to that exact version +# 3. Downloads the JSON VK if not already present in ./artifacts/ +# 4. Converts each JSON VK to arkworks compressed binary format before +# registering it on-chain (the Rust verifier expects binary, not JSON). +# +# USAGE: +# bash scripts/vk/workflows/setup-dev.sh [rpc_ws] [sudo_seed] [version] [npm_package] +# +# EXAMPLES: +# bash scripts/vk/workflows/setup-dev.sh +# bash scripts/vk/workflows/setup-dev.sh ws://127.0.0.1:9944 "//Alice" 1 +# bash scripts/vk/workflows/setup-dev.sh ws://10.0.0.5:9944 "" 2 "@orbinum/circuits@0.4.4" +# ============================================================================ + +RPC_WS="${1:-ws://127.0.0.1:9944}" +SUDO_SEED="${2:-//Alice}" +VERSION="${3:-1}" +NPM_PACKAGE="${4:-@orbinum/circuits}" + +UNPKG_BASE="https://unpkg.com" +MANIFEST_URL="$UNPKG_BASE/$NPM_PACKAGE/manifest.json" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +NODE_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)" +VK_REGISTRY_SCRIPT="$SCRIPT_DIR/../lib/registry.sh" +ARTIFACTS_DIR="$NODE_DIR/artifacts" +CONVERT_VK_BIN="$NODE_DIR/../groth16-proofs/target/release/convert-vk" + +err() { + echo "[ERROR] $*" >&2 + exit 1 +} + +log() { + echo "[$(date '+%H:%M:%S')] $*" >&2 +} + +command -v curl >/dev/null 2>&1 || err "curl is required" +command -v jq >/dev/null 2>&1 || err "jq is required" + +[[ -f "$VK_REGISTRY_SCRIPT" ]] || err "Not found: $VK_REGISTRY_SCRIPT" +[[ "$VERSION" =~ ^[0-9]+$ ]] || err "version must be an integer >= 0" + +# Ensure the convert-vk binary is available +if [[ ! -x "$CONVERT_VK_BIN" ]]; then + log "Building convert-vk tool..." + (cd "$NODE_DIR/../groth16-proofs" && cargo build --bin convert-vk --release --quiet) \ + || err "Failed to build convert-vk. Run: cd groth16-proofs && cargo build --bin convert-vk --release" +fi + +# ─── Fetch manifest ─────────────────────────────────────────────────────────── + +log "Fetching manifest from $MANIFEST_URL..." +MANIFEST_JSON=$(curl -sfL "$MANIFEST_URL") || err "Failed to fetch manifest from $MANIFEST_URL" + +PKG_VERSION=$(echo "$MANIFEST_JSON" | jq -r '.package_version') +CDN_BASE="$UNPKG_BASE/@orbinum/circuits@$PKG_VERSION" + +log "Resolved @orbinum/circuits version: $PKG_VERSION" +log "Artifact base URL: $CDN_BASE" + +# ─── Helpers ───────────────────────────────────────────────────────────────── + +# Returns the active vk_json filename for a circuit from the manifest +get_vk_filename() { + local circuit="$1" + local active_version + active_version=$(echo "$MANIFEST_JSON" | jq -r ".circuits[\"$circuit\"].active_version") + echo "$MANIFEST_JSON" | jq -r ".circuits[\"$circuit\"].versions[\"$active_version\"].artifacts.vk_json.file" +} + +TEMP_DIR="" + +# Resolves a VK JSON path (local artifact or CDN download), then converts it +# to arkworks compressed binary format (.bin) required by the on-chain verifier. +resolve_vk() { + local circuit="$1" + local filename + filename=$(get_vk_filename "$circuit") + + [[ "$filename" != "null" && -n "$filename" ]] \ + || err "Cannot resolve vk_json filename for circuit '$circuit' in manifest" + + # Always download from CDN — do not use local artifacts. + # setup-dev.sh is the CDN workflow; use setup-dev-local.sh for local artifacts. + if [[ -z "$TEMP_DIR" ]]; then + TEMP_DIR=$(mktemp -d) + log "Created temp directory: $TEMP_DIR" + fi + + local url="$CDN_BASE/$filename" + local json_path="$TEMP_DIR/$filename" + log "Downloading $filename from $url..." + curl -sfL "$url" -o "$json_path" || err "Failed to download $filename from $url" + + # Convert JSON → arkworks compressed binary + local bin_path="${json_path%.json}.bin" + "$CONVERT_VK_BIN" "$json_path" "$bin_path" \ + || err "Failed to convert $filename to binary" + echo "$bin_path" +} + +# ─── Resolve VKs ───────────────────────────────────────────────────────────── + +log "Setting up DEV VKs" +log "RPC: $RPC_WS | Circuit version: $VERSION" + +VK_TRANSFER=$(resolve_vk "transfer") +VK_UNSHIELD=$(resolve_vk "unshield") +VK_VALUE_PROOF=$(resolve_vk "value_proof") +VK_PRIVATE_LINK=$(resolve_vk "private_link") + +log "Circuits: transfer(1), unshield(2), value_proof(6), private_link(5)" + +# ─── Register + activate ───────────────────────────────────────────────────── + +log "Batch registering + activating all 4 circuits (v$VERSION) in a single tx..." +bash "$VK_REGISTRY_SCRIPT" batch-register \ + "$VERSION" 1 \ + "$VK_TRANSFER" "$VK_UNSHIELD" "$VK_VALUE_PROOF" "$VK_PRIVATE_LINK" \ + "$RPC_WS" "$SUDO_SEED" + +# ─── Cleanup ───────────────────────────────────────────────────────────────── + +if [[ -n "$TEMP_DIR" ]]; then + log "Cleaning up temp directory..." + rm -rf "$TEMP_DIR" +fi + +# Remove any .bin files produced from local artifacts (they are generated +# artefacts and are excluded from version control via .gitignore) +for circuit in transfer unshield value_proof private_link; do + rm -f "$ARTIFACTS_DIR/verification_key_${circuit}.bin" +done + +log "DEV VKs configured successfully (circuits@$PKG_VERSION)"