diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e7c975f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +name: Build and Test +on: + push: + branches: + - "**" + tags: + - "v*" + pull_request: + branches: + - "**" +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'npm' + - run: npm ci + - run: npm run test:all + release: + needs: test + if: startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + registry-url: 'https://registry.npmjs.org/' + cache: 'npm' + - run: npm ci + - run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 4927829..31a3c59 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ /node_modules/ /contract/stores/ /contract/stores_msb/ -/package-lock.json +.vscode/ \ No newline at end of file diff --git a/DOCS.md b/DOCS.md new file mode 100644 index 0000000..4506c07 --- /dev/null +++ b/DOCS.md @@ -0,0 +1,494 @@ +# trac-peer — Setup & App Guide + +This document explains how to run `trac-peer`, connect it to an existing MSB network, create/join a subnet, and test the built‑in demo app (“contract”) (`ping` + `set`), plus chat and the HTTP RPC API. + +It’s written to be usable even if you’re not deeply familiar with P2P/blockchain systems. + +If something doesn’t work, jump to **Troubleshooting** at the end. + +--- + +## What you are running (plain English) + +- **MSB (Main Settlement Bus)** is the settlement layer. A transaction becomes “real” when MSB accepts it (validators confirm it). +- **trac-peer** is a subnet runner. A **subnet** is a smaller P2P network with: + - a shared ordered log (who wrote what, in what order) + - a deterministic app/contract state derived from that log +- Subnet nodes only **execute** a transaction locally once they can prove it exists in MSB (confirmed). + +In practice: +1) You run an MSB network (you already have this). +2) You run one or more `trac-peer` nodes that join that MSB network (each trac-peer starts a small “MSB client node” in-process so it can talk to MSB). +3) Those peers form (or join) a subnet and replicate state P2P. +4) When you call `/tx`, trac-peer broadcasts the tx to MSB. +5) When MSB confirms it, the subnet appends an op referencing that MSB tx and all peers execute the same contract logic and derive the same state. + +--- + +## Requirements + +- Node.js + npm (recommended: a modern Node LTS). +- `pear` is optional (you can run in pure Node). If you use Pear, add it to PATH as Pear suggests. +- You need your **MSB bootstrap** (32‑byte hex / 64 hex characters) and **MSB channel** (string). + +--- + +## Install + +From the `trac-peer` repo folder: + +```sh +npm install +``` + +--- + +## Key concepts you will see in logs + +- **Store**: a local folder where a node saves its data (keys, logs, derived state). In this repo it defaults to `stores//...`. +- **MSB bootstrap**: the unique ID of the MSB network you are joining (32 bytes, hex string length 64). +- **MSB channel**: a discovery topic used to find the MSB network peers (must match your MSB network). +- **Subnet bootstrap**: the unique ID of a subnet (32 bytes, hex string length 64). Everyone in the same subnet must use the same subnet bootstrap. +- **Subnet channel**: a discovery topic used to find other subnet peers. Everyone in the same subnet must use the same subnet channel. +- **Peer pubkey (hex)**: your trac-peer identity key (public). Used for subnet permissions (admin). +- **Peer writer key (hex)**: the key that identifies a subnet writer (used for add/remove writer/indexer). +- **Peer MSB address**: the MSB “account/address” derived from your peer keypair. MSB must fund it so MSB fee checks pass. + +Important safety note: +- Do **not** share your `Secret Key` printed by `/get_keys`. + +--- + +## TL;DR (first time, one peer) + +1) Start peer: + +```sh +npm run peer:run -- --msb-bootstrap= --msb-channel= +``` + +2) Copy the printed `Peer MSB address: trac1...` and fund it on your MSB admin node. +3) In the peer console: + +```txt +/deploy_subnet +/add_admin --address +/tx --command "ping hello" +/get --key app/ping/ +``` + +--- + +## Start a peer (Node runner) + +The Node runner starts: +- a local MSB client node (in-process) that joins your MSB network (new store), and +- the `trac-peer` subnet node on top of it. + +Basic: + +```sh +npm run peer:run -- \ + --msb-bootstrap=<32-byte-hex> \ + --msb-channel= +``` + +If you want separate store names (recommended when running multiple peers on one machine): + +```sh +npm run peer:run -- \ + --msb-bootstrap=<32-byte-hex> \ + --msb-channel= \ + --msb-store-name=peer-msb-1 \ + --peer-store-name=peer1 +``` + +Notes: +- In `zsh`, always put all flags **on the same command** or use `\` line continuations. If you start a new line without `\`, your shell will treat it as a new command. + +--- + +## Start a peer (Pear runner) + +Pear runner is similar, but uses Pear runtime (like MSB does). + +Recommended: set store names explicitly (this avoids confusion when running multiple nodes on one machine). + +```sh +npm run peer:pear -- \ + --msb-bootstrap=<32-byte-hex> \ + --msb-channel= \ + --msb-store-name=peer-msb-1 \ + --peer-store-name=peer1 +``` + +Notes: +- If `--peer-store-name` is omitted, you *can* pass an optional first positional arg (Pear “store label”) and it will be used as the peer store name. +- To start a fresh subnet, delete `stores//subnet-bootstrap.hex` (and optionally the whole store folder). + +--- + +## First-time setup: fund your peer on MSB + +After a peer starts, it prints something like: + +- `Peer MSB address: trac1...` + +On your already-running MSB admin node, **fund that address**. + +Why? +- MSB validators reject txs from addresses that don’t exist in MSB state or that have insufficient balance to pay fees. + +Until the address is funded, you’ll see errors like: +- `Requester address not found in state` + +--- + +## Register the subnet in MSB (required before TX settlement) + +In the peer console: + +```txt +/deploy_subnet +``` + +This registers the subnet bootstrap + subnet channel with MSB so TX settlement is allowed. + +--- + +## Become subnet admin (recommended) + +Admin is stored in subnet state (`admin` key). Admin can manage writers/indexers and chat moderation. + +In the peer console: + +```txt +/add_admin --address +``` + +You can find your public key: +- printed on startup as `Peer pubkey (hex): ...`, or +- via `/get_keys` (public key only; never share secret key). + +Verify: + +```txt +/get --key admin +``` + +Notes: +- `/add_admin` is allowed only once, and only from the **bootstrap node**. + +--- + +## Test the demo app (“contract”) manually + +The default runner includes a demo protocol + demo contract: +- `ping` writes `app/ping/` +- `set` writes `app/kv/` + +### 1) Send a ping tx + +```txt +/tx --command "ping hello" +``` + +You should see `MSB TX broadcasted: ` and then after confirmation an `appended...` message. + +Read what the contract wrote: + +```txt +/get --key app/ping/ +``` + +### 2) Set and read a key/value + +```txt +/tx --command "set foo bar" +/get --key app/kv/foo +``` + +You should see `value: "bar"` plus metadata (tx hash, from). + +--- + +## Run a 3-node subnet (admin + writer + reader) + +You’ll run three terminals. The important rule is: **each node must use a different store**, but all nodes must point to the same **MSB bootstrap/channel** and the same **subnet bootstrap/channel**. + +### Terminal 1: start the bootstrap/admin node (Peer 1) + +```sh +npm run peer:run -- \ + --msb-bootstrap= \ + --msb-channel= \ + --msb-store-name=peer-msb-1 \ + --peer-store-name=peer1 +``` + +Then: + +1) Fund the printed `Peer MSB address: trac1...` on your MSB admin node. +2) In Peer 1 console: + +```txt +/deploy_subnet +/add_admin --address +/set_chat_status --enabled 1 +/set_nick --nick "admin" +``` + +### Terminal 2: start a second node that will become a writer (Peer 2) + +```sh +npm run peer:run -- \ + --msb-bootstrap= \ + --msb-channel= \ + --msb-store-name=peer-msb-2 \ + --peer-store-name=peer2 \ + --subnet-bootstrap= \ + --subnet-channel=trac-peer-subnet +``` + +Peer 2 will print: +- `Peer Writer: ` +- `Peer pubkey (hex): ` +- `Peer MSB address: trac1...` (fund it on MSB too, otherwise Peer 2 cannot settle `/tx`) + +On Peer 1 (admin), add Peer 2 as writer (and optionally indexer): + +```txt +/add_writer --key +/add_indexer --key +``` + +### Terminal 3: start a read-only node (Peer 3) + +Start Peer 3 exactly like Peer 2 but do not add its writer key as a writer: + +```sh +npm run peer:run -- \ + --msb-bootstrap= \ + --msb-channel= \ + --msb-store-name=peer-msb-3 \ + --peer-store-name=peer3 \ + --subnet-bootstrap= \ + --subnet-channel=trac-peer-subnet +``` + +Peer 3 should replicate state (you’ll see connections in `/stats`) but it won’t be able to append `/tx` unless an admin adds its writer key. + +### Test: chat + tx across peers + +1) On Peer 2: + +```txt +/set_nick --nick "writer2" +/post --message "hello from peer2" +``` + +2) On Peer 1 (admin), send a tx: + +```txt +/tx --command "set shared hello-world" +``` + +3) On Peer 3 (reader), read the state: + +```txt +/get --key app/kv/shared +``` + +--- + +## Add more peers (join an existing subnet) + +To simulate multiple nodes, run multiple peers with different stores. + +### Peer 1 (already running) +Peer 1 prints the subnet bootstrap (or stores it to `stores//subnet-bootstrap.hex`). + +### Peer 2 (join the same subnet) +Use the same subnet bootstrap and channel: + +```sh +npm run peer:run -- \ + --msb-bootstrap= \ + --msb-channel= \ + --msb-store-name=peer-msb-2 \ + --peer-store-name=peer2 \ + --subnet-bootstrap= \ + --subnet-channel=trac-peer-subnet +``` + +If you started Peer 1 with Pear, you can also start Peer 2 with Pear: + +```sh +npm run peer:pear -- \ + --msb-bootstrap= \ + --msb-channel= \ + --msb-store-name=peer-msb-2 \ + --peer-store-name=peer2 \ + --subnet-bootstrap= \ + --subnet-channel=trac-peer-subnet +``` + +### Writers vs readers +- A peer is **writer** when it has permission to append to the subnet log. +- A peer is **reader** when it can replicate but not append. + +Admin can add a writer by writer key: + +```txt +/add_writer --key +``` + +You can see a peer’s writer key in its startup banner: `Peer Writer: ...` + +--- + +## Chat (in-subnet) + +Chat is disabled by default. + +1) Enable chat (admin-only): + +```txt +/set_chat_status --enabled 1 +``` + +2) Set a nickname: + +```txt +/set_nick --nick "alice" +``` + +3) Post a message: + +```txt +/post --message "Hello from peer1" +``` + +Messages are replicated like any other subnet op. + +--- + +## HTTP RPC (operator API) + +RPC is an HTTP server that runs alongside your peer and lets you control it with requests. + +### Start with RPC enabled (Node) + +```sh +npm run peer:run-rpc -- \ + --msb-bootstrap= \ + --msb-channel= \ + --rpc-host=127.0.0.1 \ + --rpc-port=5001 +``` + +### Start with RPC enabled (Pear) + +```sh +npm run peer:pear-rpc -- \ + --msb-bootstrap= \ + --msb-channel= \ + --msb-store-name=peer-msb-rpc \ + --peer-store-name=peer-rpc \ + --rpc-host=127.0.0.1 \ + --rpc-port=5001 +``` + +### Important: the health endpoint is versioned + +- ✅ `GET http://127.0.0.1:5001/v1/health` +- ❌ `GET http://127.0.0.1:5001/health` (returns 404) + +### Common RPC calls + +- Status: + - `GET /v1/status` +- Read state: + - `GET /v1/state?key=app%2Fkv%2Ffoo&confirmed=true` +- Enable chat (admin-only): + - `POST /v1/chat/status` body: `{ "enabled": true }` +- Post message: + - `POST /v1/chat/post` body: `{ "message": "hello" }` +- Broadcast tx: + - `POST /v1/tx` body: `{ "command": "set foo bar", "sim": false }` +- Add writer (admin-only): + - `POST /v1/admin/add-writer` body: `{ "key": "" }` + +Notes: +- These RPC endpoints currently operate the **local node** (operator style). They do not accept an external user signature format yet. + +--- + +## Building your own app (Protocol + Contract) + +The runner uses a demo `DevProtocol` and `DevContract` (see `peer-main.mjs` / `scripts/run-peer.mjs`) so you can test quickly. + +For a real app, you typically: + +1) Create a custom Protocol that maps user commands to typed operations: + - implement `mapTxCommand(command) -> { type, value }` + +2) Create a custom Contract that deterministically handles those ops and writes to state: + - `addFunction("yourOp")` or schemas + - implement `async yourOp() { await this.put("app/...", ...) }` + +All nodes in the subnet must run the same Protocol/Contract logic for deterministic results. + +--- + +## How `/tx` works (the lifecycle) + +When you run `/tx --command "..."` (or call `POST /v1/tx`) the flow is: + +1) The command string is mapped into an operation object: `{ type, value }`. +2) trac-peer hashes and signs the operation and broadcasts a settlement tx to MSB. +3) trac-peer waits until its local MSB view confirms that tx. +4) trac-peer appends a subnet op that references the confirmed MSB tx (so every subnet node can verify it). +5) Every subnet node applies the subnet op and runs contract logic locally, deriving the same results from the same ordered log. + +Where does step (1) happen? +- In the demo runner (`scripts/run-peer.mjs` / `peer-main.mjs`) it’s in `DevProtocol.mapTxCommand(...)`. +- The base protocol method is `Protocol.mapTxCommand(...)` in `src/protocol.js`. For your own app you override that function. + +--- + +## Reset / clean start + +If you want to “start over”, stop the peer and delete its store folder(s): + +```sh +rm -rf stores/peer1 stores/peer2 stores/peer3 +``` + +If you only want to change subnet bootstrap generation for one peer, delete just: +- `stores//subnet-bootstrap.hex` + +--- + +## Troubleshooting + +### “Requester address not found in state” +MSB doesn’t know your Peer MSB address yet. Fund it on the MSB admin node. + +### “ID must be 32-bytes long” +One of your bootstraps is the wrong length. It must be exactly 64 hex characters (32 bytes). Common causes: +- copy/paste includes whitespace +- you accidentally repeated the bootstrap twice in the command +- you passed a non-hex string + +### “Subnet deployment broadcast failed” +Usually: +- peer MSB address not funded, or +- MSB validator connectivity issue. + +### “TextEncoder is not a constructor” (Pear/Bare) +This happens when a new store needs to generate a new keypair and Pear’s runtime provides a broken `TextEncoder`. +`trac-peer` includes a runtime workaround so you should just re-run after updating; if it persists, confirm you are on the latest workspace state. + +### RPC returns 404 on `/health` +Use versioned routes: `/v1/health`. diff --git a/README.md b/README.md index ad6d506..f16643e 100644 --- a/README.md +++ b/README.md @@ -15,4 +15,108 @@ git clone -b trac-peer-r1 --single-branch git@github.com:Trac-Systems/trac-peer. ## Usage Trac Peer must be used in combination the MSB and a Protocol/Contract pair in a unified setup. -Please check our repos for sample setups. \ No newline at end of file +Please check our repos for sample setups. + +### Local runner (interactive) + +If you already have an MSB network running (same `bootstrap` + `channel`), you can start a local MSB node (new store) that joins that network, and run `trac-peer` on top of it (this runner uses the `trac-msb` package, so it does not require a local `main_settlement_bus` repo checkout). + +This is currently required because `trac-peer` needs an in-process MSB instance to: +- broadcast `/deploy_subnet` + `/tx` payloads, and +- observe MSB confirmed state to decide when to execute subnet ops locally. + +```sh +npm run peer:run -- --msb-bootstrap=<32-byte-hex> --msb-channel= +``` + +### Pear runner (interactive) + +Runs `trac-peer` using the Pear runtime (similar to `trac-msb`). You can control stores via flags (recommended); an optional first positional arg can be used as a “store label” fallback if `--peer-store-name` is omitted. + +```sh +npm run peer:pear -- \ + --msb-bootstrap=<32-byte-hex> \ + --msb-channel= \ + --msb-store-name=peer-msb-1 \ + --peer-store-name=peer1 +``` + +Example (second node joining an existing subnet): + +```sh +npm run peer:pear -- \ + --msb-bootstrap=<32-byte-hex> \ + --msb-channel= \ + --msb-store-name=peer-msb-2 \ + --peer-store-name=peer2 \ + --subnet-bootstrap= +``` + +If you prefer multi-line in `zsh`, use `\` line continuations: + +```sh +npm run peer:run -- \ + --msb-bootstrap <32-byte-hex> \ + --msb-channel \ + --msb-stores-directory stores \ + --msb-store-name peer-msb-client \ + --peer-stores-directory stores \ + --peer-store-name subnet-peer \ + --subnet-channel subnet-1 +``` + +The runner prints the Peer MSB address. Fund that address on MSB (so the node entry exists and fee checks pass), then in the peer console run: +- `/deploy_subnet` +- `/tx --command "ping hello"` (dev protocol) +- If you want to use admin-only commands (writer/indexer management, chat moderation), run `/add_admin --address ""` and verify with `/get --key admin`. + +Notes: +- The subnet bootstrap key is auto-generated the first time and persisted to `stores//subnet-bootstrap.hex`. +- To start a fresh subnet, delete that file (and optionally the corresponding `stores//` directory). +- To join an existing subnet explicitly, pass `--subnet-bootstrap `. + +### Start a second peer (separate store) + +To run another peer on the same machine without clobbering the first one, use a different `--peer-store-name` (and a different `--msb-store-name` for the embedded MSB client node): + +```sh +# peer 1 prints (or stores) its subnet bootstrap in: stores/peer/subnet-bootstrap.hex +npm run peer:run -- \ + --msb-bootstrap <32-byte-hex> \ + --msb-channel \ + --msb-store-name peer-msb-2 \ + --peer-store-name peer2 \ + --subnet-bootstrap +``` + +## RPC API (HTTP) + +You can start an HTTP API alongside the interactive peer: + +```sh +npm run peer:run -- \ + --msb-bootstrap <32-byte-hex> \ + --msb-channel \ + --rpc \ + --rpc-host 127.0.0.1 \ + --rpc-port 5001 +``` + +Endpoints (all JSON): +- `GET /v1/health` +- `GET /v1/status` +- `GET /v1/state?key=&confirmed=true|false` +- `POST /v1/tx` body: `{ "command": "ping hello", "sim": false }` +- `POST /v1/deploy-subnet` +- `POST /v1/chat/status` body: `{ "enabled": true }` +- `POST /v1/chat/post` body: `{ "message": "hello", "reply_to": 1 }` +- `POST /v1/chat/nick` body: `{ "nick": "alice" }` +- `POST /v1/admin/add-admin` body: `{ "address": "" }` +- `POST /v1/admin/add-writer` body: `{ "key": "" }` +- `POST /v1/admin/add-indexer` body: `{ "key": "" }` +- `POST /v1/admin/remove-writer` body: `{ "key": "" }` +- `POST /v1/msb/join-validator` body: `{ "address": "" }` + +Notes: +- Write endpoints require the node to be subnet-writable (`/v1/status` shows `peer.baseWritable`). +- RPC request bodies are limited to `1_000_000` bytes by default (override with `--rpc-max-body-bytes`). diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5107322 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4684 @@ +{ + "name": "trac-peer", + "version": "0.1.68", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "trac-peer", + "version": "0.1.68", + "dependencies": { + "@tracsystems/blake3": "^0.0.15", + "assert": "npm:bare-node-assert", + "autobase": "7.6.3", + "b4a": "1.6.7", + "bare-assert": "1.0.2", + "bare-buffer": "3.1.2", + "bare-console": "6.0.1", + "bare-crypto": "1.4.3", + "bare-events": "2.5.4", + "bare-fetch": "2.2.2", + "bare-fs": "4.1.2", + "bare-http1": "4.0.2", + "bare-https": "2.0.0", + "bare-inspector": "4.0.1", + "bare-module": "4.8.4", + "bare-os": "3.6.1", + "bare-path": "3.0.0", + "bare-process": "4.2.1", + "bare-readline": "1.0.7", + "bare-repl": "4.0.0", + "bare-stream": "2.6.5", + "bare-subprocess": "5.0.3", + "bare-timers": "3.0.1", + "bare-tls": "2.0.4", + "bare-tty": "5.0.2", + "bare-url": "2.1.5", + "bare-utils": "1.2.0", + "bare-worker": "3.0.0", + "bare-zlib": "1.2.5", + "bip39": "3.1.0", + "brittle": "3.0.0", + "buffer": "npm:bare-node-buffer", + "child_process": "npm:bare-node-child-process", + "compact-encoding": "2.16.1", + "console": "npm:bare-node-console", + "corestore": "7.4.1", + "crypto": "npm:bare-node-crypto", + "debounceify": "1.1.0", + "events": "npm:bare-node-events", + "fastest-validator": "1.19.0", + "fetch": "npm:bare-node-fetch", + "fs": "npm:bare-node-fs", + "http": "npm:bare-node-http", + "https": "npm:bare-node-https", + "hyperbee": "2.24.2", + "hypercore": "11.6.2", + "hypercore-crypto": "3.4.0", + "hyperdht": "6.20.5", + "hyperswarm": "4.11.5", + "inspector": "npm:bare-node-inspector", + "is-options": "1.0.2", + "module": "npm:bare-node-module", + "multicoin-address-validator": "0.5.25", + "os": "npm:bare-node-os", + "path": "npm:bare-node-path", + "pear-interface": "1.0.0", + "process": "npm:bare-node-process", + "protomux": "3.10.1", + "protomux-wakeup": "2.4.0", + "readline": "npm:bare-node-readline", + "ready-resource": "1.1.2", + "repl": "npm:bare-node-repl", + "safety-catch": "1.0.2", + "sodium-native": "5.0.1", + "stream": "npm:bare-node-stream", + "timers": "npm:bare-node-timers", + "tls": "npm:bare-node-tls", + "trac-msb": "^0.2.7", + "trac-wallet": "0.0.43-msb-r2.9", + "tty": "npm:bare-node-tty", + "url": "npm:bare-node-url", + "util": "npm:bare-node-util", + "worker_threads": "npm:bare-node-worker-threads", + "xache": "1.2.0", + "zlib": "npm:bare-node-zlib" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "license": "MIT" + }, + "node_modules/@ethereumjs/common": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-3.2.0.tgz", + "integrity": "sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==", + "license": "MIT", + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "crc-32": "^1.2.0" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-4.2.0.tgz", + "integrity": "sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==", + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/common": "^3.2.0", + "@ethereumjs/rlp": "^4.0.1", + "@ethereumjs/util": "^8.1.0", + "ethereum-cryptography": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@hyperswarm/secret-stream": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@hyperswarm/secret-stream/-/secret-stream-6.9.1.tgz", + "integrity": "sha512-xb0S5y3YJwBakD77JOGBHlBxdp63mHClZoXBYoLv+9wH8e054ESKlmQptWqjJK5dv5VMUIVYOJB4MaOpB0JdGw==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.1.0", + "hypercore-crypto": "^3.3.1", + "noise-curve-ed": "^2.0.1", + "noise-handshake": "^4.0.0", + "sodium-secretstream": "^1.1.0", + "sodium-universal": "^5.0.0", + "streamx": "^2.14.0", + "timeout-refresh": "^2.0.0", + "unslab": "^1.3.0" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@metamask/key-tree": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@metamask/key-tree/-/key-tree-10.1.1.tgz", + "integrity": "sha512-k9/MljlUqXC86hAOp6QGUwNm9ODWuA/YkMxiEwXcChNJgQSYfPzDh+Hp6Agf3g2mLKagMbl2nkH0+4vas+Pnyw==", + "license": "MIT", + "dependencies": { + "@metamask/scure-bip39": "^2.1.1", + "@metamask/utils": "^11.0.1", + "@noble/curves": "^1.8.1", + "@noble/hashes": "^1.3.2", + "@scure/base": "^1.0.0" + }, + "engines": { + "node": "^18.20 || ^20.17 || >=22" + } + }, + "node_modules/@metamask/scure-bip39": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@metamask/scure-bip39/-/scure-bip39-2.1.1.tgz", + "integrity": "sha512-1K8aBsAqr6+8jWhguVl06n8e+zjV9sUnys+5PLyVU4mb8LbulQ60F6cq7iQys3xX/yCwKt1+7c7j2nuTEpW+ZQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.3.2", + "@scure/base": "~1.1.3" + }, + "engines": { + "node": "^16.20 || ^18.16 || >=20" + } + }, + "node_modules/@metamask/scure-bip39/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/scure-bip39/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/superstruct": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@metamask/superstruct/-/superstruct-3.2.1.tgz", + "integrity": "sha512-fLgJnDOXFmuVlB38rUN5SmU7hAFQcCjrg3Vrxz67KTY7YHFnSNEKvX4avmEBdOI0yTCxZjwMCFEqsC8k2+Wd3g==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/utils": { + "version": "11.9.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-11.9.0.tgz", + "integrity": "sha512-wRnoSDD9jTWOge/+reFviJQANhS+uy8Y+OEwRanp5mQeGTjBFmK1r2cTOnei2UCZRV1crXHzeJVSFEoDDcgRbA==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.1.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "@types/lodash": "^4.17.20", + "debug": "^4.3.4", + "lodash": "^4.17.21", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": "^18.18 || ^20.14 || >=22" + } + }, + "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/@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/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@tracsystems/blake3": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@tracsystems/blake3/-/blake3-0.0.15.tgz", + "integrity": "sha512-Qaw9WKoz1ZB7zeuhShCt9qohCoaHlQEd6Zvz9qC1ysrN65eCLfG/ODZE3T8x2Z0FRfqW69UfosCl0GFUGU83Qg==" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==", + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assert": { + "name": "bare-node-assert", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-assert/-/bare-node-assert-1.0.0.tgz", + "integrity": "sha512-twItXSJk9/Q3AE6ZKwNl0gIo/FiciPp35VRJ/rPh64GzUiW5uDcSBOeoVW1JCikYh+APPvh+Ix2vuH2+WqwKTQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-assert": "*" + } + }, + "node_modules/autobase": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/autobase/-/autobase-7.6.3.tgz", + "integrity": "sha512-jwNL3bpin9Rdb0Lw2ffHk4KCXwovvFm3RBcH1CJdERG4fXYYJCzdZWfG5DrMB/qA5j01SLc6X1L/QKKI531KZQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.1", + "bare-events": "^2.2.0", + "compact-encoding": "^2.16.0", + "core-coupler": "^2.0.0", + "debounceify": "^1.0.0", + "hyperbee": "^2.22.0", + "hypercore": "^11.4.0", + "hypercore-crypto": "^3.4.0", + "hypercore-id-encoding": "^1.2.0", + "index-encoder": "^3.3.2", + "mutexify": "^1.4.0", + "nanoassert": "^2.0.0", + "protomux-wakeup": "^2.0.0", + "ready-resource": "^1.0.0", + "resolve-reject-promise": "^1.1.0", + "safety-catch": "^1.0.2", + "signal-promise": "^1.0.3", + "sub-encoder": "^2.1.1", + "tiny-buffer-map": "^1.1.1" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "license": "Apache-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-addon-resolve": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.9.6.tgz", + "integrity": "sha512-hvOQY1zDK6u0rSr27T6QlULoVLwi8J2k8HHHJlxSfT7XQdQ/7bsS+AnjYkHtu/TkL+gm3aMXAKucJkJAbrDG/g==", + "license": "Apache-2.0", + "dependencies": { + "bare-module-resolve": "^1.10.0", + "bare-semver": "^1.0.0" + }, + "peerDependencies": { + "bare-url": "*" + }, + "peerDependenciesMeta": { + "bare-url": { + "optional": true + } + } + }, + "node_modules/bare-ansi-escapes": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/bare-ansi-escapes/-/bare-ansi-escapes-2.2.3.tgz", + "integrity": "sha512-02ES4/E2RbrtZSnHJ9LntBhYkLA6lPpSEeP8iqS3MccBIVhVBlEmruF1I7HZqx5Q8aiTeYfQVeqmrU9YO2yYoQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-stream": "^2.6.5" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bare-assert/-/bare-assert-1.0.2.tgz", + "integrity": "sha512-7AGTrUCz7OOWnMOp4hWnksAkFeZlvW7WMwvKQBANVJIOtjWa6RLSPyUN+zs3QBufRZwIYhYB3UpkAlDbBPp2/Q==", + "license": "Apache-2.0" + }, + "node_modules/bare-buffer": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bare-buffer/-/bare-buffer-3.1.2.tgz", + "integrity": "sha512-FEKFcT3CIVRHGVAURr2QOppK7xXARQZc3U97Ce+79z/aHZKudK+ggSZgIxB/6SA4FMS44QuOt3irWW4bWa3/pA==", + "license": "Apache-2.0", + "engines": { + "bare": ">=1.13.0" + } + }, + "node_modules/bare-bundle": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/bare-bundle/-/bare-bundle-1.10.0.tgz", + "integrity": "sha512-4LVlnJAHr00Hh6Vu6ZUJS38rcEtJT3b3vChXSsBsJ2mk1TN0lQ+gzd+Dw5L0aV7uqDZv84smuwW+O02X7PfDlw==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-buffer": "*", + "bare-url": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-url": { + "optional": true + } + } + }, + "node_modules/bare-channel": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bare-channel/-/bare-channel-5.2.2.tgz", + "integrity": "sha512-lM5nPZKV4mWB0djziPr+FaLeoaMmNeUjlbtRZ84ad0FbZEWjlrEIj3ShufHx5xXMXgot0GZk+iLtubCYZVoSNQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.0.0", + "bare-stream": "^2.7.0", + "bare-structured-clone": "^1.4.0" + }, + "engines": { + "bare": ">=1.7.0" + } + }, + "node_modules/bare-channel/node_modules/bare-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", + "license": "Apache-2.0", + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-console": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/bare-console/-/bare-console-6.0.1.tgz", + "integrity": "sha512-uSE6oqMZQ5jNrJFzynNDwvgmEHW3wWwktjQ+iGWEb/XTvhNVQDfVugwF/YQ2ZO/RIfMl0P89RoCSkX5bOPjzig==", + "license": "Apache-2.0", + "dependencies": { + "bare-hrtime": "^2.0.0", + "bare-logger": "^1.0.0" + } + }, + "node_modules/bare-crypto": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/bare-crypto/-/bare-crypto-1.4.3.tgz", + "integrity": "sha512-XlIhSB+2ht2+Yli2Ic+HhOvdP0vd+TWWISxzYp50EwfsOwCI4TaeONj5hIr6aDWKhYn0FMsFec7U30QOIXMD4Q==", + "license": "Apache-2.0", + "dependencies": { + "bare-stream": "^2.6.3" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-debug-log": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-debug-log/-/bare-debug-log-1.0.0.tgz", + "integrity": "sha512-tN02RFk6yiS2s7DerJVLvE4W/xrRkNMiCilvS/3fQbu72tj1rteXcQMiztICX5Z3yYsTNJrBokUnAUPjfDU45A==", + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^2.3.0" + } + }, + "node_modules/bare-debug-log/node_modules/bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "license": "Apache-2.0" + }, + "node_modules/bare-dns": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/bare-dns/-/bare-dns-2.1.4.tgz", + "integrity": "sha512-abwjHmpWqSRNB7V5615QxPH92L71AVzFm/kKTs8VYiNTAi2xVdonpv0BjJ0hwXLwomoW+xsSOPjW6PZPO14asg==", + "license": "Apache-2.0", + "engines": { + "bare": ">=1.7.0" + } + }, + "node_modules/bare-encoding": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-encoding/-/bare-encoding-1.0.0.tgz", + "integrity": "sha512-9T5CSCaytaIWZpFWx9LQLJ6/z/m2Slnan9tQBKmOvoq/UtPBbOKT/B2fo29Xhi4X1FFtNx8DFdtrFgqm2yse/Q==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-env": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-env/-/bare-env-3.0.0.tgz", + "integrity": "sha512-0u964P5ZLAxTi+lW4Kjp7YRJQ5gZr9ycYOtjLxsSrupgMz3sn5Z9n4SH/JIifHwvadsf1brA2JAjP+9IOWwTiw==", + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-events": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", + "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", + "license": "Apache-2.0" + }, + "node_modules/bare-fetch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/bare-fetch/-/bare-fetch-2.2.2.tgz", + "integrity": "sha512-YfdLJDGi0SzDFcPnA2sOZLIVx+l965+CF+P+ZUa/mtk1Ul9OepLQLIRFR050GBqnr1fHrF8ewV7BvEz0pSSlRQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-http1": "^4.0.2", + "bare-https": "^2.0.0", + "bare-stream": "^2.6.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-format/-/bare-format-1.0.1.tgz", + "integrity": "sha512-1oS+LZrWK6tnYnvNSHDGljc2MPunRxwhpFriuCgzNF+oklrnwmBKD91tS0yt+jpl2j3UgcSDzBIMiVTvLs9A8w==", + "license": "Apache-2.0", + "dependencies": { + "bare-inspect": "^3.0.0" + } + }, + "node_modules/bare-fs": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.2.tgz", + "integrity": "sha512-8wSeOia5B7LwD4+h465y73KOdj5QHsbbuoUfPBi+pXgFJIPuG7SsiOdJuijWMyfid49eD+WivpfY7KT8gbAzBA==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-hrtime": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bare-hrtime/-/bare-hrtime-2.1.1.tgz", + "integrity": "sha512-VMb3tHo05gsnbu3OXTmkDiwTjMlOsbQmKoysKqKEyR09m77TuDrYFbj3Q5GGk10dAKsUHrnXmwCaeJqzVpB5ZA==", + "license": "Apache-2.0" + }, + "node_modules/bare-http-parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-http-parser/-/bare-http-parser-1.0.1.tgz", + "integrity": "sha512-A3LTDTcELcmNJ3g5liIaS038v/BQxOhA9cjhBESn7eoV7QCuMoIRBKLDadDe08flxyLbxI2f+1l2MZ/5+HnKPA==", + "license": "Apache-2.0" + }, + "node_modules/bare-http1": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/bare-http1/-/bare-http1-4.0.2.tgz", + "integrity": "sha512-6Jns5oBG9LFz/U2PuFk2LHQbQ1ZuUgPyuahfbNikPL/HcjpSGdSl0xMiN3Qt852Xm1sLBsTGxl0OoM2cVCYzNQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.0.0", + "bare-stream": "^2.3.0", + "bare-tcp": "^2.0.0" + } + }, + "node_modules/bare-https": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bare-https/-/bare-https-2.0.0.tgz", + "integrity": "sha512-qmjNZmYQ4nn+k3CLlxVyOqWYamdBPqE7psR5/lFWG39fskAR4C2h29d1Ka5BeWOGDAWhXImFIwZUxwCE/7xeLA==", + "license": "Apache-2.0", + "dependencies": { + "bare-http1": "^4.0.0", + "bare-tcp": "^2.0.0", + "bare-tls": "^2.0.0" + } + }, + "node_modules/bare-inspect": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/bare-inspect/-/bare-inspect-3.1.4.tgz", + "integrity": "sha512-jfW5KRA84o3REpI6Vr4nbvMn+hqVAw8GU1mMdRwUsY5yJovQamxYeKGVKGqdzs+8ZbG4jRzGUXP/3Ji/DnqfPg==", + "license": "Apache-2.0", + "dependencies": { + "bare-ansi-escapes": "^2.1.0", + "bare-type": "^1.0.0" + }, + "engines": { + "bare": ">=1.18.0" + } + }, + "node_modules/bare-inspector": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bare-inspector/-/bare-inspector-4.0.1.tgz", + "integrity": "sha512-MZGIci2OFzwDNWlY/JUmEVtK/0xa+mbdggOB0jSI0WYkQpyxaiD4q/8xZ42k4kOoZNLKpYMI31wmllm6jS5w9g==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.1.0", + "bare-http1": "^4.0.0", + "bare-stream": "^2.0.0", + "bare-url": "^2.0.0", + "bare-ws": "^2.0.0" + }, + "engines": { + "bare": ">=1.2.0" + } + }, + "node_modules/bare-logger": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bare-logger/-/bare-logger-1.2.1.tgz", + "integrity": "sha512-QeGZ+hWPzU5uJObORFuXYM8s3pqCUZLQlQdmDnQz+vkBhGpAoyr+r+FY9FovSbeGDBu/NRgezjL3L6SmkgBCew==", + "license": "Apache-2.0", + "dependencies": { + "bare-format": "^1.0.0" + } + }, + "node_modules/bare-module": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/bare-module/-/bare-module-4.8.4.tgz", + "integrity": "sha512-U/IIju5zeW9IsGvaeIiEXyFjPPndyrLuA8YKx8LRrKTN8Ttd7p4T7Lz1/axMoDTBSSrApYXI4tDKlxJfitmd4g==", + "license": "Apache-2.0", + "dependencies": { + "bare-bundle": "^1.3.0", + "bare-module-lexer": "^1.0.0", + "bare-module-resolve": "^1.8.0", + "bare-path": "^3.0.0", + "bare-url": "^2.0.1" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-module-lexer": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/bare-module-lexer/-/bare-module-lexer-1.4.7.tgz", + "integrity": "sha512-0klU4eMsjh/wcxi8FdHmNom2j2F4kmkXOhyJFL9qTaSFp2lE3m6BtbKgMHY8R5miqC9r8/IfA8wzXnC5Os14WA==", + "license": "Apache-2.0", + "dependencies": { + "require-addon": "^1.0.2" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-module-resolve": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.1.tgz", + "integrity": "sha512-hbmAPyFpEq8FoZMd5sFO3u6MC5feluWoGE8YKlA8fCrl6mNtx68Wjg4DTiDJcqRJaovTvOYKfYngoBUnbaT7eg==", + "license": "Apache-2.0", + "dependencies": { + "bare-semver": "^1.0.0" + }, + "peerDependencies": { + "bare-url": "*" + }, + "peerDependenciesMeta": { + "bare-url": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz", + "integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==", + "license": "Apache-2.0", + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-pipe": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/bare-pipe/-/bare-pipe-4.1.2.tgz", + "integrity": "sha512-btXtZLlABEDRp50cfLj9iweISqAJSNMCjeq5v0v9tBY2a7zSSqmfa2ZoE1ki2qxAvubagLUqw6VDifpsuI/qmg==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.0.0", + "bare-stream": "^2.0.0" + }, + "engines": { + "bare": ">=1.16.0" + } + }, + "node_modules/bare-process": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/bare-process/-/bare-process-4.2.1.tgz", + "integrity": "sha512-wcmyQWTHxd2xRgeKUSY46ofmuEAJ9CLo/6swJTHOZFPYpBShMWNPVI2Ba8o0n/X/YE4as99M28x37saWZ1L1vQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-env": "^3.0.0", + "bare-events": "^2.3.1", + "bare-hrtime": "^2.0.0", + "bare-os": "^3.5.0", + "bare-pipe": "^4.0.0", + "bare-signals": "^4.0.0", + "bare-tty": "^5.0.0" + } + }, + "node_modules/bare-readline": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/bare-readline/-/bare-readline-1.0.7.tgz", + "integrity": "sha512-9r+pgm/POnrcUcN1HuFwZ/FODNdkpxVooXwLMQekFaiaajG27gZVdt4nmJwdCsStRPr+I/hBffLcuxJuhN24cg==", + "license": "Apache-2.0", + "dependencies": { + "bare-ansi-escapes": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-repl": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/bare-repl/-/bare-repl-4.0.0.tgz", + "integrity": "sha512-bfTf47f4a+//MHjMYmOzbxLwmaVDZWgKIESushdWT93Lq4+fr/8ePVqI2ShRWag+UUy3yGJ+Gxa2LtBMyEFQDA==", + "license": "Apache-2.0", + "dependencies": { + "bare-inspect": "^3.0.0", + "bare-module": "^4.0.0", + "bare-os": "^3.0.1", + "bare-path": "^3.0.0", + "bare-pipe": "^4.0.0", + "bare-readline": "^1.0.0", + "bare-stream": "^2.0.0", + "bare-tty": "^5.0.0" + } + }, + "node_modules/bare-semver": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.0.2.tgz", + "integrity": "sha512-ESVaN2nzWhcI5tf3Zzcq9aqCZ676VWzqw07eEZ0qxAcEOAFYBa0pWq8sK34OQeHLY3JsfKXZS9mDyzyxGjeLzA==", + "license": "Apache-2.0" + }, + "node_modules/bare-signals": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/bare-signals/-/bare-signals-4.2.0.tgz", + "integrity": "sha512-fNHMOdQIlYuTvMB3Oh9Apk99hLKn351+Ir8vz+khiPTcOqIyGG4uWWjdLTzxWdYGsA0eT+We3y0K74hjj2nq7A==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.3", + "bare-os": "^3.3.1" + }, + "engines": { + "bare": ">=1.7.0" + } + }, + "node_modules/bare-stream": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", + "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", + "license": "Apache-2.0", + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-structured-clone": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/bare-structured-clone/-/bare-structured-clone-1.5.2.tgz", + "integrity": "sha512-V5yevE00wUcmPGgk/O+bsEmuMRGRkwMW1Fncbl8cjH/Eu0+7g0oZSbN7oVGi19c8df9dn25NYymC0v03VVN70w==", + "license": "Apache-2.0", + "dependencies": { + "bare-type": "^1.1.0", + "compact-encoding": "^2.15.0" + }, + "engines": { + "bare": ">=1.2.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-url": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-url": { + "optional": true + } + } + }, + "node_modules/bare-subprocess": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/bare-subprocess/-/bare-subprocess-5.0.3.tgz", + "integrity": "sha512-iCx8kfvqClPAQGsbL2RfMubB6EYoZ67ZhaEIpn6wIqIa60p4zLAlGJyEQQtXPo/5dclbpgzWre5hvJ7HzXC/aA==", + "license": "Apache-2.0", + "dependencies": { + "bare-env": "^3.0.0", + "bare-events": "^2.5.4", + "bare-os": "^3.0.1", + "bare-pipe": "^4.0.0" + }, + "engines": { + "bare": ">=1.7.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-tcp": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/bare-tcp/-/bare-tcp-2.2.2.tgz", + "integrity": "sha512-bYnw1AhzGlfLOD4nTceUXkhhgznZKvDuwjX1Au0VWaVitwqG40oaTvvhEQVCcK3FEwjRTiukUzHnAFsYXUI+3Q==", + "license": "Apache-2.0", + "dependencies": { + "bare-dns": "^2.0.4", + "bare-events": "^2.5.4", + "bare-stream": "^2.6.4" + }, + "engines": { + "bare": ">=1.16.0" + } + }, + "node_modules/bare-timers": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bare-timers/-/bare-timers-3.0.1.tgz", + "integrity": "sha512-5AOY4/V3VROnkLSxfNvKJtRr9PHYRN2jR76PCjBGKUikvKfeCrCfh34oESZf15hli9CImZmiJaDkAEtVjDQNLA==", + "license": "Apache-2.0", + "dependencies": { + "tiny-binary-heap": "^1.1.0" + }, + "engines": { + "bare": ">=1.7.0" + } + }, + "node_modules/bare-tls": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/bare-tls/-/bare-tls-2.0.4.tgz", + "integrity": "sha512-mJK7CHoXhBEd+z7KIFf0e5GIJgkIp9b9kt7axTmFyUEyQlyzlAzTrQHCkw++PEwF0soNXgOYQtmb+KUqhpgK+g==", + "license": "Apache-2.0", + "dependencies": { + "bare-stream": "^2.6.4" + }, + "engines": { + "bare": ">=1.7.0" + } + }, + "node_modules/bare-tty": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/bare-tty/-/bare-tty-5.0.2.tgz", + "integrity": "sha512-xOHwI7zZl2Opm7Rul5O+okE32j7O14feJhgovJX2EghtQ2IWVfiC1oH0DmFruMvKthvhZY/Lpg8n5SVBaZhV1A==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.2.0", + "bare-signals": "^4.0.0", + "bare-stream": "^2.0.0" + }, + "engines": { + "bare": ">=1.16.0" + } + }, + "node_modules/bare-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bare-type/-/bare-type-1.1.0.tgz", + "integrity": "sha512-LdtnnEEYldOc87Dr4GpsKnStStZk3zfgoEMXy8yvEZkXrcCv9RtYDrUYWFsBQHtaB0s1EUWmcvS6XmEZYIj3Bw==", + "license": "Apache-2.0", + "engines": { + "bare": ">=1.2.0" + } + }, + "node_modules/bare-url": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.1.5.tgz", + "integrity": "sha512-lNImB5KLN+ggw+SYDYvqf/yCizXIyq8U/nWBlx7m4pc4TKS24SB/1WWskzGacon5cVVAC6qUzCYzI/aMYCf4Ng==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/bare-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bare-utils/-/bare-utils-1.2.0.tgz", + "integrity": "sha512-Y9G5DbMUgcx078Etc7h9CD31aI9vYFZ/xl6JLnyvBX9+4lrlXw+5/6toNJGaNSylo4jJf8Cu3yBIDxMLviRFFw==", + "license": "Apache-2.0", + "dependencies": { + "bare-debug-log": "^1.0.0", + "bare-format": "^1.0.0", + "bare-inspect": "^3.0.0" + } + }, + "node_modules/bare-worker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-worker/-/bare-worker-3.0.0.tgz", + "integrity": "sha512-MapYKzGlH8byGwbFOhDd2N/0y2gA9xoFFknlAy7DtSlt1Xs2O4dww4UNZCNBuQsB5pYsZ/q5GjlhZdRZZMjA/Q==", + "license": "Apache-2.0", + "dependencies": { + "bare-channel": "^5.0.1", + "bare-events": "^2.2.1", + "bare-module": "^4.0.0", + "bare-os": "^3.0.1", + "bare-url": "^2.0.1" + } + }, + "node_modules/bare-ws": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/bare-ws/-/bare-ws-2.0.4.tgz", + "integrity": "sha512-SQMXzBYna9dRj57Dz1/ag+VWHCRXbfCjMHgyfM2F2lhkVLzMjnVSZP72aVeFWPFqe494Rd70Kzhe2JElGwFlJQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-crypto": "^1.2.0", + "bare-events": "^2.3.1", + "bare-http1": "^4.0.0", + "bare-https": "^2.0.0", + "bare-stream": "^2.1.2" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-url": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-url": { + "optional": true + } + } + }, + "node_modules/bare-zlib": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/bare-zlib/-/bare-zlib-1.2.5.tgz", + "integrity": "sha512-6oUWbXDHL8UUdWru/EAteNvXd71LaFr22xrJQh90vnmhMA5+xeU5GM3xox5HVgL5uZ0UFVJ6CVuEuiUcbCYp/w==", + "license": "Apache-2.0", + "dependencies": { + "bare-stream": "^2.0.0" + } + }, + "node_modules/base-x": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.1.tgz", + "integrity": "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==", + "license": "MIT" + }, + "node_modules/big-sparse-array": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/big-sparse-array/-/big-sparse-array-1.0.3.tgz", + "integrity": "sha512-6RjV/3mSZORlMdpUaQ6rUSpG637cZm0//E54YYGtQg1c1O+AbZP8UTdJ/TchsDZcTVLmyWZcseBfp2HBeXUXOQ==", + "license": "MIT" + }, + "node_modules/bip39": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", + "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", + "license": "ISC", + "dependencies": { + "@noble/hashes": "^1.2.0" + } + }, + "node_modules/bip39-mnemonic": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bip39-mnemonic/-/bip39-mnemonic-2.5.0.tgz", + "integrity": "sha512-hgCxFHN129ta0bHl9VPykoqCPagBUr2oaXjfSbfeXAPe+y/byxwjPFFEFTMNO/1T/GSoMP1cOXHa2ZaprHG1kg==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.6", + "nanoassert": "^2.0.0", + "sodium-universal": "^5.0.1" + } + }, + "node_modules/bits-to-bytes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bits-to-bytes/-/bits-to-bytes-1.3.0.tgz", + "integrity": "sha512-OJoHTpFXS9bXHBCekGTByf3MqM8CGblBDIduKQeeVVeiU9dDWywSSirXIBYGgg3d1zbVuvnMa1vD4r6PA0kOKg==", + "license": "ISC", + "dependencies": { + "b4a": "^1.5.0" + } + }, + "node_modules/blake2b": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/blake2b/-/blake2b-2.1.4.tgz", + "integrity": "sha512-AyBuuJNI64gIvwx13qiICz6H6hpmjvYS5DGkG6jbXMOT8Z3WUJ3V1X0FlhIoT1b/5JtHE3ki+xjtMvu1nn+t9A==", + "license": "ISC", + "dependencies": { + "blake2b-wasm": "^2.4.0", + "nanoassert": "^2.0.0" + } + }, + "node_modules/blake2b-wasm": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz", + "integrity": "sha512-S1kwmW2ZhZFFFOghcx73+ZajEfKBqhP82JMssxtLVMxlaPea1p9uoLiUZ5WYyHn0KddwbLc+0vh4wR0KBNoT5w==", + "license": "MIT", + "dependencies": { + "b4a": "^1.0.1", + "nanoassert": "^2.0.0" + } + }, + "node_modules/blind-relay": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/blind-relay/-/blind-relay-1.4.0.tgz", + "integrity": "sha512-6xt7fDfCs6eGmNNym6I9N42jmjcMQn2qwwOVnkP9ZnrkXFk6c4/tdO1xqRmDEzKzV8gigd+DVdCUG/RUYnen7Q==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4", + "bare-events": "^2.2.0", + "bits-to-bytes": "^1.3.0", + "compact-encoding": "^2.12.0", + "compact-encoding-bitfield": "^1.0.0", + "protomux": "^3.5.1", + "sodium-universal": "^5.0.0", + "streamx": "^2.15.1" + } + }, + "node_modules/bogon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bogon/-/bogon-1.1.0.tgz", + "integrity": "sha512-a6SnToksXHuUlgeMvI/txWmTcKz7c7iBa8f0HbXL4toN1Uza/CTQ4F7n9jSDX49TCpxv3KUP100q4sZfwLyLiw==", + "license": "MIT", + "dependencies": { + "compact-encoding": "^2.11.0", + "compact-encoding-net": "^1.2.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/brittle": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/brittle/-/brittle-3.0.0.tgz", + "integrity": "sha512-B8FXAhVDPJ0jyto4MbMDkLHEF3SAHfcj4Ih7rGDbesKhAlz0EHscHxyOLf6kqL1qTv1grot/YbiFg9Q5takgtQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.0", + "c8": "^7.11.3", + "deep-equal": "^2.0.5", + "error-stack-parser": "^2.1.4", + "tmatch": "^5.0.0" + }, + "bin": { + "brittle": "cmd.js" + } + }, + "node_modules/browserify-bignum": { + "version": "1.3.0-2", + "resolved": "https://registry.npmjs.org/browserify-bignum/-/browserify-bignum-1.3.0-2.tgz", + "integrity": "sha512-PwVvKC3WIV7ENfsG6VAIDq4R5st6kQt+Fod3WL5l7+MRONClo3J6xGQvRJHHM/ScwcNCH3GfYX5UOCuoNN/rLw==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer": { + "name": "bare-node-buffer", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-buffer/-/bare-node-buffer-1.0.0.tgz", + "integrity": "sha512-sgKJRlxGOjdvT/th+7OY/ONvq4DoVXfiAlnQDa0gNLbftS0wHrsoYBkDUoHMKmdY8IMlPXn+KBidnl///sFBxA==", + "license": "Apache-2.0", + "dependencies": { + "bare-buffer": "*" + } + }, + "node_modules/bundle": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bundle/-/bundle-2.1.0.tgz", + "integrity": "sha512-d7TeT8m2HuymDjSEmMppWe/h5SSPPUZkaWKrAofx6gNXDdZ3FL/81oOTGPG+LIaZbNr9m4rtUi98Yw0Q1vHIIw==", + "license": "MIT" + }, + "node_modules/c8": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.14.0.tgz", + "integrity": "sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw==", + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^2.0.0", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-reports": "^3.1.4", + "rimraf": "^3.0.2", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cbor-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cbor-js/-/cbor-js-0.1.0.tgz", + "integrity": "sha512-7sQ/TvDZPl7csT1Sif9G0+MA0I0JOVah8+wWlJVQdVEgIbCzlN/ab3x+uvMNsc34TUvO6osQTAmB2ls80JX6tw==", + "license": "MIT" + }, + "node_modules/chacha20-universal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chacha20-universal/-/chacha20-universal-1.0.4.tgz", + "integrity": "sha512-/IOxdWWNa7nRabfe7+oF+jVkGjlr2xUL4J8l/OvzZhj+c9RpMqoo3Dq+5nU1j/BflRV4BKnaQ4+4oH1yBpQG1Q==", + "license": "ISC", + "dependencies": { + "nanoassert": "^2.0.0" + } + }, + "node_modules/child_process": { + "name": "bare-node-child-process", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-child-process/-/bare-node-child-process-1.0.1.tgz", + "integrity": "sha512-zfLNSl0fmARbseK5InrczAgs2j1jgEMV7CU9N2JO5c200g6Cmzrjuxua4AHL+PjDl+W64VnmtNSWKzQe3mkU3w==", + "license": "Apache-2.0", + "dependencies": { + "bare-subprocess": "*" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/codecs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/codecs/-/codecs-3.1.0.tgz", + "integrity": "sha512-Dqx8NwvBvnMeuPQdVKy/XEF71igjR5apxBvCGeV0pP1tXadOiaLvDTXt7xh+/5wI1ASB195mXQGJbw3Ml4YDWQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.3" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/compact-encoding": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/compact-encoding/-/compact-encoding-2.16.1.tgz", + "integrity": "sha512-vP39X4nwtesmZucaAxDg4wnudOoaJTSR+fikzi8VLVxbwLmcWXf3t0LxY0n2H1AMpdoQZ08lmUf4GY3XiDPnMQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.3.0" + } + }, + "node_modules/compact-encoding-bitfield": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/compact-encoding-bitfield/-/compact-encoding-bitfield-1.0.0.tgz", + "integrity": "sha512-3nMVKUg+PF72UHfainmCL8uKvyWfxsjqOtUY+HiMPGLPCTjnwzoKfFAMo1Ad7nwTPdjBqtGK5b3BOFTFW4EBTg==", + "license": "ISC", + "dependencies": { + "compact-encoding": "^2.4.1" + } + }, + "node_modules/compact-encoding-net": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/compact-encoding-net/-/compact-encoding-net-1.2.0.tgz", + "integrity": "sha512-LVXpNpF7PGQeHRVVLGgYWzuVoYAaDZvKUsUxRioGfkotzvOh4AzoQF1HBH3zMNaSnx7gJXuUr3hkjnijaH/Eng==", + "license": "ISC", + "dependencies": { + "compact-encoding": "^2.4.1" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/console": { + "name": "bare-node-console", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-console/-/bare-node-console-1.0.1.tgz", + "integrity": "sha512-UgdbfQ3wfNSjURCohf2YAmgw8RPCm/e6s2ZQRXtlUutFSOx6S/ntjCPO6HYSvtCgn2CNpTU6nYcVjTle9lWdfg==", + "license": "Apache-2.0", + "dependencies": { + "bare-console": "*" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/core-coupler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/core-coupler/-/core-coupler-2.0.0.tgz", + "integrity": "sha512-FJuEvsdCMwx0Wu+gFQ49rGCi8LCXh8kizHsCQwkdgPZFEFiF0z2HDvyIs+fPt5wMIfU2UVFDuN+dtpfbIxJE6g==", + "license": "Apache-2.0", + "dependencies": { + "safety-catch": "^1.0.2" + } + }, + "node_modules/corestore": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/corestore/-/corestore-7.4.1.tgz", + "integrity": "sha512-P4pe0IxSYj6i8oUdU4rDRNNqV5qNER1+4CmqFBCRKF6JbfEZ+hwEZ1uZ5qSxu6DrN/R5V52+GM8mq0I+ELS9xw==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.7", + "hypercore": "^11.0.0", + "hypercore-crypto": "^3.4.2", + "hypercore-errors": "^1.4.0", + "hypercore-id-encoding": "^1.3.0", + "ready-resource": "^1.1.1", + "sodium-universal": "^5.0.1" + } + }, + "node_modules/corestore/node_modules/hypercore-crypto": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-3.6.1.tgz", + "integrity": "sha512-ltIz2uDwy9pO/ZGTvqcjzyBkvt6O4cVm4r/nNxh0GFs/RbQtqP/i4wCvLEdmU7ptgtnw7fI67WYD1aHPuv4OVA==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.6", + "compact-encoding": "^2.15.0", + "sodium-universal": "^5.0.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto": { + "name": "bare-node-crypto", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-crypto/-/bare-node-crypto-1.0.0.tgz", + "integrity": "sha512-Obdo8pNHotL2McWqYMuIIW/9MV4Crn1GFDcB/A+PnCcIbh+snTYSpynCDJkU7HDMhv3WLRKzf4ulHOxHR1eJJw==", + "license": "Apache-2.0", + "dependencies": { + "bare-crypto": "*" + } + }, + "node_modules/debounceify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debounceify/-/debounceify-1.1.0.tgz", + "integrity": "sha512-eKuHDVfJVg+u/0nPy8P+fhnLgbyuTgVxuCRrS/R7EpDSMMkBDgSes41MJtSAY1F1hcqfHz3Zy/qpqHHIp/EhdA==", + "license": "MIT" + }, + "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/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/device-file": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/device-file/-/device-file-2.1.3.tgz", + "integrity": "sha512-EKXOEa63atDkCLbUVZOTASG+6hXsg4ay9lcLZ53C1hkPLYjS2gIVIi0jtxz7ZXwCitcCRz5FI3NBZfQQifhf1g==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.7", + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0", + "fd-lock": "^2.1.0", + "fs-native-extensions": "^1.4.0", + "ready-resource": "^1.2.0" + } + }, + "node_modules/device-file/node_modules/ready-resource": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ready-resource/-/ready-resource-1.2.0.tgz", + "integrity": "sha512-nfcco/8iAFV0M+2PYnmIc+/xY0iRb35d42HFHQ7AfjulbGEAFa+XWpByfwSyeVeiBoMLLFVMv1HixxNCqzSQ1g==", + "license": "MIT", + "dependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/dht-rpc": { + "version": "6.23.2", + "resolved": "https://registry.npmjs.org/dht-rpc/-/dht-rpc-6.23.2.tgz", + "integrity": "sha512-3o5sOae76W3WHVE5hK2L78HGv276ZdbMAkBFNh8ICS4Rlb6JQ3MVW9RStOxeD39Td+SF8VD3410JEHh/WCdZ+Q==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.1", + "bare-events": "^2.2.0", + "compact-encoding": "^2.11.0", + "compact-encoding-net": "^1.2.0", + "fast-fifo": "^1.1.0", + "kademlia-routing-table": "^1.0.1", + "nat-sampler": "^1.0.1", + "sodium-universal": "^5.0.0", + "streamx": "^2.13.2", + "time-ordered-set": "^2.0.0", + "udx-native": "^1.5.3" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "license": "MIT", + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/events": { + "name": "bare-node-events", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-events/-/bare-node-events-1.0.1.tgz", + "integrity": "sha512-3RZAWtrpWmpI0BwvJpH3VxyN0FCvsIGfhf2tvwbM795qoqqltTaC2IjrUBm39OVmJNVUZxBIf+noo945hsvtUw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "*" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/events-universal/node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fastest-validator": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastest-validator/-/fastest-validator-1.19.0.tgz", + "integrity": "sha512-wUfJBrXmccVz4kARAiWTkuqsC6EFeqbNxwfysCDk+maExBPP8KxyBwaWtayrWjKIaBIbaz+rqI2kel6ecayxcg==", + "license": "MIT" + }, + "node_modules/fd-lock": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fd-lock/-/fd-lock-2.1.1.tgz", + "integrity": "sha512-H3VkcWFl39Rk0xBokDcUyBcVs6VrYTUo/DhDWMzJOF99wXWDAvmvq/GlLTS2NTehsznZhp3fXVyrtB2ldDXhwg==", + "license": "Apache-2.0", + "dependencies": { + "bare-fs": "^4.5.0", + "fs-native-extensions": "^1.4.4", + "ready-resource": "^1.2.0", + "resource-on-exit": "^1.0.0" + } + }, + "node_modules/fd-lock/node_modules/bare-fs": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.2.tgz", + "integrity": "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/fd-lock/node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/fd-lock/node_modules/ready-resource": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ready-resource/-/ready-resource-1.2.0.tgz", + "integrity": "sha512-nfcco/8iAFV0M+2PYnmIc+/xY0iRb35d42HFHQ7AfjulbGEAFa+XWpByfwSyeVeiBoMLLFVMv1HixxNCqzSQ1g==", + "license": "MIT", + "dependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/fetch": { + "name": "bare-node-fetch", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-fetch/-/bare-node-fetch-1.0.0.tgz", + "integrity": "sha512-lKK9WGKvLGJuuVHGT/CBOmVrdTO3DRnopJQP6TfwPILmZNhSZgJPqjOsgd2hW8GOAV69uJMpXS9VdJBNikccWA==", + "license": "Apache-2.0", + "dependencies": { + "bare-fetch": "^2.2.1" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-tree": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/flat-tree/-/flat-tree-1.13.0.tgz", + "integrity": "sha512-fT3HIuCPwHhFgJ20QYzDHgUG0zMmFg5cHvFiFo5h+QMSJ28TihsEVY0f8HGliuO+pOzmvjMx1odToeaEWkTnyQ==", + "license": "MIT" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fs": { + "name": "bare-node-fs", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bare-node-fs/-/bare-node-fs-1.0.2.tgz", + "integrity": "sha512-Tas23gfqHmkNQe1VUID6ifMi3oAYHqTuYr8sCq/xcskpTaDChGxZKJnFJzi1/pSnHzgQCLlkX+FpPnqNJTyQgg==", + "license": "Apache-2.0", + "dependencies": { + "bare-fs": "*" + } + }, + "node_modules/fs-native-extensions": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/fs-native-extensions/-/fs-native-extensions-1.4.5.tgz", + "integrity": "sha512-ekV0T//iDm4AvhOcuPaHpxub4DI7HvY5ucLJVDvi7T2J+NZkQ9S6MuvgP0yeQvoqNUaAGyLjVYb1905BF9bpmg==", + "license": "Apache-2.0", + "dependencies": { + "require-addon": "^1.1.0", + "which-runtime": "^1.2.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generate-object-property": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-2.0.0.tgz", + "integrity": "sha512-KwuURPyqn2Mz8DdV29pJwQu0Y7tcsbkULr82eeOcY/ZllFK6I9Wm8dsRByIu7CKWlFi9BdW1b3mcXMp/kQBQsw==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.0" + } + }, + "node_modules/generate-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/generate-string/-/generate-string-1.0.1.tgz", + "integrity": "sha512-IfTY0dKZM43ACyGvXkbG7De7WY7MxTS5VO6Juhe8oJKpCmrYYXoqp/cJMskkpi0k9H8wuXq0H+eI898/BCqvXg==", + "license": "MIT" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "license": "MIT" + }, + "node_modules/http": { + "name": "bare-node-http", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-http/-/bare-node-http-1.0.1.tgz", + "integrity": "sha512-74KBjcJPXpl6ySRM9YHy40m9eFckPNvS0jLtwBdWhPfxJWTlW7oJQL30LIaKkX7Y6ZMnZ2XV6ek3PGdcljS6aQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-http1": "*" + } + }, + "node_modules/https": { + "name": "bare-node-https", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-https/-/bare-node-https-1.0.0.tgz", + "integrity": "sha512-HhIWViqqewd32YHJZQ6c1nJ9s0ukWwTPmobiaClx7Ihzrj0x5oEosjXIIw2p++7zV/38LdH2I8zLYSgOnHOp3g==", + "license": "Apache-2.0", + "dependencies": { + "bare-https": "*" + } + }, + "node_modules/hyperbee": { + "version": "2.24.2", + "resolved": "https://registry.npmjs.org/hyperbee/-/hyperbee-2.24.2.tgz", + "integrity": "sha512-RAzptsdDN4oDCQ/MjWavjt720D+jRbzHvVl+YW6OwdcaLJslGpbKjbdWV1yuDiGwBs7iRwTUaFA78GtcRHZFwA==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.0", + "codecs": "^3.0.0", + "debounceify": "^1.0.0", + "hypercore-errors": "^1.0.0", + "mutexify": "^1.4.0", + "protocol-buffers-encodings": "^1.2.0", + "rache": "^1.0.0", + "ready-resource": "^1.0.0", + "safety-catch": "^1.0.2", + "streamx": "^2.12.4", + "unslab": "^1.2.0" + } + }, + "node_modules/hypercore": { + "version": "11.6.2", + "resolved": "https://registry.npmjs.org/hypercore/-/hypercore-11.6.2.tgz", + "integrity": "sha512-CHKW2jg+YyyJ4fp93MJ1YJbgm2zadvgc+1/cXPQLPeW8SYIr4wnnkUAU27fRUZFCg5xJQycSImGTpCdPUxj8xA==", + "license": "MIT", + "dependencies": { + "@hyperswarm/secret-stream": "^6.0.0", + "b4a": "^1.1.0", + "bare-events": "^2.2.0", + "big-sparse-array": "^1.0.3", + "compact-encoding": "^2.11.0", + "fast-fifo": "^1.3.0", + "flat-tree": "^1.9.0", + "hypercore-crypto": "^3.2.1", + "hypercore-errors": "^1.2.0", + "hypercore-id-encoding": "^1.2.0", + "hypercore-storage": "^1.0.0", + "is-options": "^1.0.1", + "nanoassert": "^2.0.0", + "protomux": "^3.5.0", + "quickbit-universal": "^2.2.0", + "random-array-iterator": "^1.0.0", + "safety-catch": "^1.0.1", + "sodium-universal": "^5.0.1", + "streamx": "^2.12.4", + "unslab": "^1.3.0", + "z32": "^1.0.0" + } + }, + "node_modules/hypercore-crypto": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-3.4.0.tgz", + "integrity": "sha512-0cZA1B58p1J84TDbTh8DMMIj7Qr7Rzz8NyGIo+ykUhdwD21gtjiiLWoME92QvN+lgbfu0Zfr9vwxT8sRmyg+AA==", + "license": "MIT", + "dependencies": { + "b4a": "^1.1.0", + "compact-encoding": "^2.5.1", + "sodium-universal": "^4.0.0" + } + }, + "node_modules/hypercore-crypto/node_modules/sodium-native": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.3.3.tgz", + "integrity": "sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw==", + "license": "MIT", + "dependencies": { + "require-addon": "^1.1.0" + } + }, + "node_modules/hypercore-crypto/node_modules/sodium-universal": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-4.0.1.tgz", + "integrity": "sha512-sNp13PrxYLaUFHTGoDKkSDFvoEu51bfzE12RwGlqU1fcrkpAOK0NvizaJzOWV0Omtk9me2+Pnbjcf/l0efxuGQ==", + "license": "MIT", + "dependencies": { + "sodium-native": "^4.0.0" + }, + "peerDependencies": { + "sodium-javascript": "~0.8.0" + }, + "peerDependenciesMeta": { + "sodium-javascript": { + "optional": true + } + } + }, + "node_modules/hypercore-errors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/hypercore-errors/-/hypercore-errors-1.5.0.tgz", + "integrity": "sha512-5KQ/SuDxsvet+7qWA35Ay6zdD9WyAHQoyWHGcPUTbmJBd300gvNIJoi3oma7kp4TTCSzii6qYumNZe/s0j/saQ==", + "license": "Apache-2.0", + "dependencies": { + "hypercore-id-encoding": "^1.3.0" + } + }, + "node_modules/hypercore-id-encoding": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/hypercore-id-encoding/-/hypercore-id-encoding-1.3.0.tgz", + "integrity": "sha512-W6sHdGo5h7LXEsoWfKf/KfuROZmZRQDlGqJF2EPHW+noCK66Vvr0+zE6cL0vqQi18s0kQPeN7Sq3QyR0Ytc2VQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.5.3", + "z32": "^1.0.0" + } + }, + "node_modules/hypercore-storage": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/hypercore-storage/-/hypercore-storage-1.18.0.tgz", + "integrity": "sha512-AXWqX4M/Jxx93zv5hviq/5P3E3kvOjh1rSXtHHBK/mxqzpkmS2olM2utRjrBtyX2hT0rCLP5P1tQEszUpYZRqQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.7", + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0", + "compact-encoding": "^2.16.0", + "device-file": "^2.0.0", + "flat-tree": "^1.12.1", + "hypercore-crypto": "^3.4.2", + "hyperschema": "^1.7.0", + "index-encoder": "^3.3.2", + "resolve-reject-promise": "^1.0.0", + "rocksdb-native": "^3.1.1", + "scope-lock": "^1.2.4", + "streamx": "^2.21.1" + } + }, + "node_modules/hypercore-storage/node_modules/hypercore-crypto": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-3.6.1.tgz", + "integrity": "sha512-ltIz2uDwy9pO/ZGTvqcjzyBkvt6O4cVm4r/nNxh0GFs/RbQtqP/i4wCvLEdmU7ptgtnw7fI67WYD1aHPuv4OVA==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.6", + "compact-encoding": "^2.15.0", + "sodium-universal": "^5.0.0" + } + }, + "node_modules/hyperdht": { + "version": "6.20.5", + "resolved": "https://registry.npmjs.org/hyperdht/-/hyperdht-6.20.5.tgz", + "integrity": "sha512-eDAwTmAtE9rjMivgqYtqHalTdBVhhCMBVHlCWRVhEcWtchpDonsd2dmX26lJ0raoF+l9djkXvPcN1/kb9/kykw==", + "license": "MIT", + "dependencies": { + "@hyperswarm/secret-stream": "^6.6.2", + "b4a": "^1.3.1", + "bare-events": "^2.2.0", + "blind-relay": "^1.3.0", + "bogon": "^1.0.0", + "compact-encoding": "^2.4.1", + "compact-encoding-net": "^1.0.1", + "dht-rpc": "^6.15.1", + "hypercore-crypto": "^3.3.0", + "hypercore-id-encoding": "^1.2.0", + "noise-curve-ed": "^2.0.0", + "noise-handshake": "^4.0.0", + "record-cache": "^1.1.1", + "safety-catch": "^1.0.1", + "signal-promise": "^1.0.3", + "sodium-universal": "^5.0.1", + "streamx": "^2.16.1", + "unslab": "^1.3.0", + "xache": "^1.1.0" + }, + "bin": { + "hyperdht": "bin.js" + } + }, + "node_modules/hyperschema": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/hyperschema/-/hyperschema-1.19.0.tgz", + "integrity": "sha512-gHbLxLygsDUmX9MVs8G1W4xC9NglSyrw+t28sfFFzdU40gCUUmIo3n2MkIRHpUOT7Jj+8iuvq2wSkpaG+3k/Xg==", + "license": "Apache-2.0", + "dependencies": { + "bare-fs": "^4.0.1", + "compact-encoding": "^2.15.0", + "generate-object-property": "^2.0.0", + "generate-string": "^1.0.1" + } + }, + "node_modules/hyperswarm": { + "version": "4.11.5", + "resolved": "https://registry.npmjs.org/hyperswarm/-/hyperswarm-4.11.5.tgz", + "integrity": "sha512-KJ5qQrfFLYZvvgwxf43dcgp9R7CX3quwUEeycK9BWYHcZAqv7NggIQf6q3YqfHHH7ecks6WdRjD6s2dMsLdmzw==", + "license": "MIT", + "dependencies": { + "b4a": "^1.3.1", + "bare-events": "^2.2.0", + "hyperdht": "^6.11.0", + "safety-catch": "^1.0.2", + "shuffled-priority-queue": "^2.1.0", + "unslab": "^1.3.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/index-encoder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/index-encoder/-/index-encoder-3.4.0.tgz", + "integrity": "sha512-k3+ENtseFYI9ZPOIZzVH8LlONUvXAcd4jvCPo+Nob/T/2t5R5Rfh8XiFXBG++gHHuVby7HBDp/3YbyEmE481cg==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inspector": { + "name": "bare-node-inspector", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-inspector/-/bare-node-inspector-1.0.1.tgz", + "integrity": "sha512-m3GFxUsW7MJN5Lw+2eS9dtj0zwenoUJ5hdLt0IWGIY2RUnSFy/56bCK3MIvPvTCva1+ujQpe6tcLCSERIovBpA==", + "license": "Apache-2.0", + "dependencies": { + "bare-inspector": "*" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-options": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-options/-/is-options-1.0.2.tgz", + "integrity": "sha512-u+Ai74c8Q74aS8BuHwPdI1jptGOT1FQXgCq8/zv0xRuE+wRgSMEJLj8lVO8Zp9BeGb29BXY6AsNPinfqjkr7Fg==", + "license": "MIT", + "dependencies": { + "b4a": "^1.1.1" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-sha512": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.9.0.tgz", + "integrity": "sha512-mirki9WS/SUahm+1TbAPkqvbCiCfOAAsyXeHxK1UkullnJVVqoJG2pL9ObvT05CN+tM7fxhfYm0NbXn+1hWoZg==", + "license": "MIT" + }, + "node_modules/jssha": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz", + "integrity": "sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/kademlia-routing-table": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/kademlia-routing-table/-/kademlia-routing-table-1.0.6.tgz", + "integrity": "sha512-Ve6jwIlUCYvUzBnXnzVRHDZCFgXURW9gmF3r7n05kZs/2rNbLHXwGdcq0qIaSwdmJCvtosgR4JensnVU65hzNQ==", + "license": "MIT", + "dependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/module": { + "name": "bare-node-module", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-module/-/bare-node-module-1.0.0.tgz", + "integrity": "sha512-7ZTCfExhk24aNqEGKgLOb3HsApNFdlcxTxeR8POsHyoQQeOy1pA3i8JxS97djNxunCnsfHAMJ5HeKo+55YsY5g==", + "license": "Apache-2.0", + "dependencies": { + "bare-module": "*" + } + }, + "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/multicoin-address-validator": { + "version": "0.5.25", + "resolved": "https://registry.npmjs.org/multicoin-address-validator/-/multicoin-address-validator-0.5.25.tgz", + "integrity": "sha512-CYV5fK3AuMJF6YEDIBQkdklcxjNJxms3rZ0gJ4CIOvZrwqdwPZCEd+N4PVz3Kh4E6nUBP0KpU5GKkrbHPVo63A==", + "license": "MIT", + "dependencies": { + "base-x": "^4.0.0", + "browserify-bignum": "^1.3.0-2", + "buffer": "^6.0.3", + "bundle": "^2.1.0", + "cbor-js": "^0.1.0", + "crc": "^4.3.2", + "js-sha512": "^0.9.0", + "jssha": "^3.3.1", + "lodash.isequal": "^4.5.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/multicoin-address-validator/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/multicoin-address-validator/node_modules/crc": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/crc/-/crc-4.3.2.tgz", + "integrity": "sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "buffer": ">=6.0.3" + }, + "peerDependenciesMeta": { + "buffer": { + "optional": true + } + } + }, + "node_modules/mutexify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/mutexify/-/mutexify-1.4.0.tgz", + "integrity": "sha512-pbYSsOrSB/AKN5h/WzzLRMFgZhClWccf2XIB4RSMC8JbquiB0e0/SH5AIfdQMdyHmYtv4seU7yV/TvAwPLJ1Yg==", + "license": "MIT", + "dependencies": { + "queue-tick": "^1.0.0" + } + }, + "node_modules/nanoassert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", + "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==", + "license": "ISC" + }, + "node_modules/nat-sampler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nat-sampler/-/nat-sampler-1.0.1.tgz", + "integrity": "sha512-yQvyNN7xbqR8crTKk3U8gRgpcV1Az+vfCEijiHu9oHHsnIl8n3x+yXNHl42M6L3czGynAVoOT9TqBfS87gDdcw==", + "license": "MIT" + }, + "node_modules/noise-curve-ed": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/noise-curve-ed/-/noise-curve-ed-2.1.0.tgz", + "integrity": "sha512-zAzJx+VwZM3w6EA1hTmDhJfvAnCeBQn/1FAeZ0LtGxCcCtlAK/uJXQVF/eDVUOaAZ286lHlx77WJ+qj9SmsRRg==", + "license": "ISC", + "dependencies": { + "b4a": "^1.1.0", + "nanoassert": "^2.0.0", + "sodium-universal": "^5.0.0" + } + }, + "node_modules/noise-handshake": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/noise-handshake/-/noise-handshake-4.2.0.tgz", + "integrity": "sha512-9O/VTNX/E2/AToyMTTDU0J/4WhaXMTdqc2DHs9vf+snoZ0cenSBq0dNYTVV1snYYEkmo6QeRrYMxtqtoYnY+LA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.1.0", + "nanoassert": "^2.0.0", + "sodium-universal": "^5.0.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/os": { + "name": "bare-node-os", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-os/-/bare-node-os-1.0.1.tgz", + "integrity": "sha512-do2XWmW6QiSxVYLjmycUydQ3+/8JaDKHRv0P3NRFOlbdbVFVU2+0B6u2t5pm+K68grLbkV8lmtD2ZLUVPSeMEg==", + "license": "Apache-2.0", + "dependencies": { + "bare-os": "*" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path": { + "name": "bare-node-path", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-path/-/bare-node-path-1.0.1.tgz", + "integrity": "sha512-44YiNN/ofG1cEOWwdRVbG9ZFyskZKXPJnSUOyUpdeRm+MeSXiY+pSjSa7SZ56C/XpPVxE0OEl/AV8u+3FKsclw==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "*" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pear-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pear-interface/-/pear-interface-1.0.0.tgz", + "integrity": "sha512-eM2yObk+vU8Cemg3L1zTktskrkW7zHIqMUkztKWK6lGD+FwUIYUEMXFPWXS6tGDjAxL8ir+c5qFRN1BpRCCixQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-fs": "^2.3.1", + "bare-pipe": "^3.3.2", + "streamx": "^2.18.0" + } + }, + "node_modules/pear-interface/node_modules/bare-fs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/pear-interface/node_modules/bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "license": "Apache-2.0" + }, + "node_modules/pear-interface/node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/pear-interface/node_modules/bare-pipe": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/bare-pipe/-/bare-pipe-3.3.8.tgz", + "integrity": "sha512-X8Ulz/os6LR/cF5HBljq/tJ0NP7eNGjiripZF53EMtgkhJ9FVl1/WLZxwvUvcqbR82Ywaq6KYM+A6zS7DGPPUw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/pony-cause": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", + "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==", + "license": "0BSD", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/process": { + "name": "bare-node-process", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-process/-/bare-node-process-1.0.1.tgz", + "integrity": "sha512-7PVYyfcXV/YU5UozeyjBKxlma7IwZ2uaFomZuLg2R18RUCR9TkOnEcqh9wirvEh5z8ooSD0n9rV/rVol8VpLhQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-process": "*" + } + }, + "node_modules/protocol-buffers-encodings": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-encodings/-/protocol-buffers-encodings-1.2.0.tgz", + "integrity": "sha512-daeNPuKh1NlLD1uDfbLpD+xyUTc07nEtfHwmBZmt/vH0B7VOM+JOCOpDcx9ZRpqHjAiIkGqyTDi+wfGSl17R9w==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.0", + "signed-varint": "^2.0.1", + "varint": "5.0.0" + } + }, + "node_modules/protomux": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/protomux/-/protomux-3.10.1.tgz", + "integrity": "sha512-jgBqx8ZyaBWea/DFG4eOu1scOaeBwcnagiRC1XFVrjeGt7oAb0Pk5udPpBUpJ4DJBRjra50jD6YcZiQQTRqaaA==", + "license": "MIT", + "dependencies": { + "b4a": "^1.3.1", + "compact-encoding": "^2.5.1", + "queue-tick": "^1.0.0", + "safety-catch": "^1.0.1", + "unslab": "^1.3.0" + } + }, + "node_modules/protomux-wakeup": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/protomux-wakeup/-/protomux-wakeup-2.4.0.tgz", + "integrity": "sha512-8R7PpvzEV8yHS6cYxLG4zMIV16HZ1StIyEVuYWY8lKjU9hxcl7NKUoDtGgGrrxsC3g28u/77ZO4FpNZZoQv1KQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.7", + "hypercore-crypto": "^3.5.0", + "hyperschema": "^1.10.4", + "protomux": "^3.10.1" + } + }, + "node_modules/protomux-wakeup/node_modules/hypercore-crypto": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-3.6.1.tgz", + "integrity": "sha512-ltIz2uDwy9pO/ZGTvqcjzyBkvt6O4cVm4r/nNxh0GFs/RbQtqP/i4wCvLEdmU7ptgtnw7fI67WYD1aHPuv4OVA==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.6", + "compact-encoding": "^2.15.0", + "sodium-universal": "^5.0.0" + } + }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "license": "MIT" + }, + "node_modules/quickbit-native": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/quickbit-native/-/quickbit-native-2.4.8.tgz", + "integrity": "sha512-FcCcqI+nIAWGknqhtrYT5TSD7t/N+Xd8ctM+2PrIIBuwOi5hx0SxAvuPtzLIEMfT/2h9+fhBakUe2uALOHX6yw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "require-addon": "^1.1.0" + } + }, + "node_modules/quickbit-universal": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/quickbit-universal/-/quickbit-universal-2.2.0.tgz", + "integrity": "sha512-w02i1R8n7+6pEKTud8DfF8zbFY9o7RtPlUc3jWbtCkDKvhbx/AvV7oNnz4/TcmsPGpSJS+fq5Ud6RH6+YPvSGg==", + "license": "ISC", + "dependencies": { + "b4a": "^1.6.0", + "simdle-universal": "^1.1.0" + }, + "optionalDependencies": { + "quickbit-native": "^2.2.0" + } + }, + "node_modules/rache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rache/-/rache-1.0.0.tgz", + "integrity": "sha512-e0k0g0w/8jOCB+7YqCIlOa+OJ38k0wrYS4x18pMSmqOvLKoyhmMhmQyCcvfY6VaP8D75cqkEnlakXs+RYYLqNg==", + "license": "Apache-2.0" + }, + "node_modules/random-array-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-array-iterator/-/random-array-iterator-1.0.0.tgz", + "integrity": "sha512-u7xCM93XqKEvPTP6xZp2ehttcAemKnh73oKNf1FvzuVCfpt6dILDt1Kxl1LeBjm2iNIeR49VGFhy4Iz3yOun+Q==", + "license": "MIT" + }, + "node_modules/readline": { + "name": "bare-node-readline", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-readline/-/bare-node-readline-1.0.1.tgz", + "integrity": "sha512-A+6ewT/b4wkHe5clWe+H589fQDKrFMYsMsSCk1KHFzmVI60wXL6eT5iRB4n3wlCH476Zauig2XLMj2RznCBs4g==", + "license": "Apache-2.0", + "dependencies": { + "bare-readline": "*" + } + }, + "node_modules/ready-resource": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ready-resource/-/ready-resource-1.1.2.tgz", + "integrity": "sha512-BN2Yfg/avHpozP+XSo+gsjHQ0AejnfbCJeJT4eamAHSf7dgYmNNWsZqTt5IEc06mjlLao+c2jlTbZvpZyRtRNQ==", + "license": "MIT", + "dependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/record-cache": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/record-cache/-/record-cache-1.2.0.tgz", + "integrity": "sha512-kyy3HWCez2WrotaL3O4fTn0rsIdfRKOdQQcEJ9KpvmKmbffKVvwsloX063EgRUlpJIXHiDQFhJcTbZequ2uTZw==", + "license": "MIT", + "dependencies": { + "b4a": "^1.3.1" + } + }, + "node_modules/refcounter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/refcounter/-/refcounter-1.0.0.tgz", + "integrity": "sha512-1WosVzUy0kPUaPMEtlNDwm99UsteALIhXXR8rerELoa63WkYIXAl0hxgwPFrIYBRWZPGUyekQ04FRtPJ7dHk9w==", + "license": "Apache-2.0" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/repl": { + "name": "bare-node-repl", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-repl/-/bare-node-repl-1.0.1.tgz", + "integrity": "sha512-CwBYC7HmsT1pEdS+zgLbGZB073c/6AxJWScuYFnxWAFmaFckEla2C13g0RKQ9m2GAShYdoM5QtWsQWT2ihMo6w==", + "license": "Apache-2.0", + "dependencies": { + "bare-repl": "*" + } + }, + "node_modules/require-addon": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", + "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==", + "license": "Apache-2.0", + "dependencies": { + "bare-addon-resolve": "^1.3.0" + }, + "engines": { + "bare": ">=1.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-reject-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-reject-promise/-/resolve-reject-promise-1.1.0.tgz", + "integrity": "sha512-LWsTOA91AqzBTjSGgX79Tc130pwcBK6xjpJEO+qRT5IKZ6bGnHKcc8QL3upUBcWuU8OTIDzKK2VNSwmmlqvAVg==", + "license": "MIT" + }, + "node_modules/resource-on-exit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resource-on-exit/-/resource-on-exit-1.0.0.tgz", + "integrity": "sha512-ViJwJAknCkLRJRPR+9SISQQ7R5eRgtdIHLJsM2hHx1MweAJbJxJ5XnMjjq0Lc7ZGv44ufzAqds1nKxiVkdy4ag==", + "license": "Apache-2.0" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rocksdb-native": { + "version": "3.11.4", + "resolved": "https://registry.npmjs.org/rocksdb-native/-/rocksdb-native-3.11.4.tgz", + "integrity": "sha512-vG6NIkmipcAYV9QHIN1tALBHfIAcAfRlV5YWSwfn7yMmsXW0AJel0oZyeW8lRpTD7HTWq88GwzIMI+MV4iPSOg==", + "license": "Apache-2.0", + "dependencies": { + "compact-encoding": "^2.15.0", + "ready-resource": "^1.0.0", + "refcounter": "^1.0.0", + "require-addon": "^1.0.2", + "resolve-reject-promise": "^1.1.0", + "signal-promise": "^1.0.3", + "streamx": "^2.16.1" + }, + "engines": { + "bare": ">=1.16.0" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safety-catch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/safety-catch/-/safety-catch-1.0.2.tgz", + "integrity": "sha512-C1UYVZ4dtbBxEtvOcpjBaaD27nP8MlvyAQEp2fOTOEe6pfUpk1cDUxij6BR1jZup6rSyUTaBBplK7LanskrULA==", + "license": "MIT" + }, + "node_modules/scope-lock": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/scope-lock/-/scope-lock-1.2.4.tgz", + "integrity": "sha512-BpSd8VCuCxW9ZitcdIC/vjs3gMaP9bRBL5nkHcyfX2VrS52n13/rHuBA2xJ/S/4DPuRdAO/Bk8pWd8eD/gHCIA==", + "license": "Apache-2.0" + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sha256-universal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sha256-universal/-/sha256-universal-1.2.1.tgz", + "integrity": "sha512-ghn3muhdn1ailCQqqceNxRgkOeZSVfSE13RQWEg6njB+itsFzGVSJv+O//2hvNXZuxVIRyNzrgsZ37SPDdGJJw==", + "license": "ISC", + "dependencies": { + "b4a": "^1.0.1", + "sha256-wasm": "^2.2.1" + } + }, + "node_modules/sha256-wasm": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/sha256-wasm/-/sha256-wasm-2.2.2.tgz", + "integrity": "sha512-qKSGARvao+JQlFiA+sjJZhJ/61gmW/3aNLblB2rsgIxDlDxsJPHo8a1seXj12oKtuHVgJSJJ7QEGBUYQN741lQ==", + "license": "ISC", + "dependencies": { + "b4a": "^1.0.1", + "nanoassert": "^2.0.0" + } + }, + "node_modules/sha512-universal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sha512-universal/-/sha512-universal-1.2.1.tgz", + "integrity": "sha512-kehYuigMoRkIngCv7rhgruLJNNHDnitGTBdkcYbCbooL8Cidj/bS78MDxByIjcc69M915WxcQTgZetZ1JbeQTQ==", + "license": "ISC", + "dependencies": { + "b4a": "^1.0.1", + "sha512-wasm": "^2.3.1" + } + }, + "node_modules/sha512-wasm": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/sha512-wasm/-/sha512-wasm-2.3.4.tgz", + "integrity": "sha512-akWoxJPGCB3aZCrZ+fm6VIFhJ/p8idBv7AWGFng/CZIrQo51oQNsvDbTSRXWAzIiZJvpy16oIDiCCPqTe21sKg==", + "license": "ISC", + "dependencies": { + "b4a": "^1.0.1", + "nanoassert": "^2.0.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shuffled-priority-queue": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/shuffled-priority-queue/-/shuffled-priority-queue-2.1.0.tgz", + "integrity": "sha512-xhdh7fHyMsr0m/w2kDfRJuBFRS96b9l8ZPNWGaQ+PMvnUnZ/Eh+gJJ9NsHBd7P9k0399WYlCLzsy18EaMfyadA==", + "license": "MIT", + "dependencies": { + "unordered-set": "^2.0.1" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/signal-promise": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/signal-promise/-/signal-promise-1.0.3.tgz", + "integrity": "sha512-WBgv0UnIq2C+Aeh0/n+IRpP6967eIx9WpynTUoiW3isPpfe1zu2LJzyfXdo9Tgef8yR/sGjcMvoUXD7EYdiz+g==", + "license": "MIT" + }, + "node_modules/signed-varint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/signed-varint/-/signed-varint-2.0.1.tgz", + "integrity": "sha512-abgDPg1106vuZZOvw7cFwdCABddfJRz5akcCcchzTbhyhYnsG31y4AlZEgp315T7W3nQq5P4xeOm186ZiPVFzw==", + "license": "MIT", + "dependencies": { + "varint": "~5.0.0" + } + }, + "node_modules/simdle-native": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/simdle-native/-/simdle-native-1.3.9.tgz", + "integrity": "sha512-Isc8sP4OiiIU0mpslD4GHEnR0VQWvR/54WN7YtwEDkNdTJVWtpmvsSvsgRlw5BNGxdYXlVRegdnrSu10H/PhvA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "b4a": "^1.6.0", + "require-addon": "^1.1.0" + } + }, + "node_modules/simdle-universal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/simdle-universal/-/simdle-universal-1.1.2.tgz", + "integrity": "sha512-3n3w1bs+uwgHKQjt6arez83EywNlhZzYvNOhvAASTl/8KqNIcqr6aHyGt3JRlfuUC7iB0tomJRPlJ2cRGIpBzA==", + "license": "ISC", + "dependencies": { + "b4a": "^1.6.0" + }, + "optionalDependencies": { + "simdle-native": "^1.1.1" + } + }, + "node_modules/siphash24": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/siphash24/-/siphash24-1.3.1.tgz", + "integrity": "sha512-moemC3ZKiTzH29nbFo3Iw8fbemWWod4vNs/WgKbQ54oEs6mE6XVlguxvinYjB+UmaE0PThgyED9fUkWvirT8hA==", + "license": "MIT", + "dependencies": { + "nanoassert": "^2.0.0" + } + }, + "node_modules/sodium-javascript": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/sodium-javascript/-/sodium-javascript-0.8.0.tgz", + "integrity": "sha512-rEBzR5mPxPES+UjyMDvKPIXy9ImF17KOJ32nJNi9uIquWpS/nfj+h6m05J5yLJaGXjgM72LmQoUbWZVxh/rmGg==", + "license": "MIT", + "dependencies": { + "blake2b": "^2.1.1", + "chacha20-universal": "^1.0.4", + "nanoassert": "^2.0.0", + "sha256-universal": "^1.1.0", + "sha512-universal": "^1.1.0", + "siphash24": "^1.0.1", + "xsalsa20": "^1.0.0" + } + }, + "node_modules/sodium-native": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.0.1.tgz", + "integrity": "sha512-Q305aUXc0OzK7VVRvWkeEQJQIHs6slhFwWpyqLB5iJqhpyt2lYIVu96Y6PQ7TABIlWXVF3YiWDU3xS2Snkus+g==", + "license": "MIT", + "dependencies": { + "require-addon": "^1.1.0", + "which-runtime": "^1.2.1" + }, + "engines": { + "bare": ">=1.16.0" + } + }, + "node_modules/sodium-secretstream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/sodium-secretstream/-/sodium-secretstream-1.2.0.tgz", + "integrity": "sha512-q/DbraNFXm1KfCiiZvapmz5UC3OlpirYFIvBK2MhGaOFSb3gRyk8OXTi17UI9SGfshQNCpsVvlopogbzZNyW6Q==", + "license": "MIT", + "dependencies": { + "b4a": "^1.1.1", + "sodium-universal": "^5.0.0" + } + }, + "node_modules/sodium-universal": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", + "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", + "license": "MIT", + "dependencies": { + "sodium-native": "^5.0.1" + }, + "peerDependencies": { + "sodium-javascript": "~0.8.0" + }, + "peerDependenciesMeta": { + "sodium-javascript": { + "optional": true + } + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stream": { + "name": "bare-node-stream", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-stream/-/bare-node-stream-1.0.0.tgz", + "integrity": "sha512-fE3xRlcMyxqMi96k4FxvZdGJMndt4fsf9U1T7mU7ZV8h+r6DxNkm2qCdX1+Ie+5mCx6Hoh/jv+Lw/OKpta9H7w==", + "license": "Apache-2.0", + "dependencies": { + "bare-stream": "*" + } + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sub-encoder": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/sub-encoder/-/sub-encoder-2.1.3.tgz", + "integrity": "sha512-Xxx04ygZo/1J3yHvaSA6VhDmiSaBQkw/PmO3YnnYFXle+tfOGToC6FcDpIfMztWZXJzuKG14b/57HMkiL58C6A==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.0", + "codecs": "^3.1.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/time-ordered-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/time-ordered-set/-/time-ordered-set-2.0.1.tgz", + "integrity": "sha512-VJEKmgSN2UiOLB8BpN8Sh2b9LGMHTP5OPrQRpnKjvOheOyzk0mufbjzjKTIG2gO4A+Y+vDJ+0TcLbpUmMLsg8A==", + "license": "MIT" + }, + "node_modules/timeout-refresh": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/timeout-refresh/-/timeout-refresh-2.0.1.tgz", + "integrity": "sha512-SVqEcMZBsZF9mA78rjzCrYrUs37LMJk3ShZ851ygZYW1cMeIjs9mL57KO6Iv5mmjSQnOe/29/VAfGXo+oRCiVw==", + "license": "MIT" + }, + "node_modules/timers": { + "name": "bare-node-timers", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-timers/-/bare-node-timers-1.0.0.tgz", + "integrity": "sha512-M9kaXT+/NyRExANbdkN+Xl5uN2dsKFewQcX4droPfdrZT7PmucCvCjR2YAwBKV3KGzGp0C+y8TzDbt85JcZgYA==", + "license": "Apache-2.0", + "dependencies": { + "bare-timers": "*" + } + }, + "node_modules/tiny-binary-heap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-binary-heap/-/tiny-binary-heap-1.1.0.tgz", + "integrity": "sha512-IaM+/bh71URhhzlH1qNKNylamK4nxaby2q0qxolWL4Typ3+sIcZHxLe/Mj35b7GOHat/RKpp2vbRepjV6sD+Rw==", + "license": "MIT" + }, + "node_modules/tiny-buffer-map": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tiny-buffer-map/-/tiny-buffer-map-1.1.1.tgz", + "integrity": "sha512-C1eDw6ks9CmkDbWVCPHobuixPTkxGa7IDERlaVk98dv4tOUdz42o3haHBr0uhNxbj0gczBTVIyS2uQsu+1vc2Q==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.0" + } + }, + "node_modules/tls": { + "name": "bare-node-tls", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-tls/-/bare-node-tls-1.0.0.tgz", + "integrity": "sha512-BHRiftAc5F4PvRDcVYhuCYRv06NqbToYLQZQJQlifmkIJ74Rujy0fVn8UZ9Q6xrq3C7OenSeUV0rcgroppjiBw==", + "license": "Apache-2.0", + "dependencies": { + "bare-tls": "*" + } + }, + "node_modules/tmatch": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tmatch/-/tmatch-5.0.0.tgz", + "integrity": "sha512-Ib9OtBkpHn07tXP04SlN1SYRxFgTk6wSM2EBmjjxug4u5RXPRVLkdFJSS1PmrQidaSB8Lru9nRtViQBsbxzE5Q==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/trac-crypto-api": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/trac-crypto-api/-/trac-crypto-api-0.1.2.tgz", + "integrity": "sha512-hzgEuEzTbJfzYHbiJhqBpAQtNlLghnl5c1mQqzzxz48jAQzqyYqi79RoFC5mhDyV1VCQe5FQjtIxu6lf4UkfYA==", + "license": "ISC", + "dependencies": { + "@metamask/key-tree": "10.1.1", + "@tracsystems/blake3": "0.0.13", + "b4a": "1.6.7", + "bare-fs": "4.5.0", + "bech32": "2.0.0", + "bip39-mnemonic": "2.5.0", + "sodium-javascript": "0.8.0", + "sodium-universal": "5.0.1" + } + }, + "node_modules/trac-crypto-api/node_modules/@tracsystems/blake3": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@tracsystems/blake3/-/blake3-0.0.13.tgz", + "integrity": "sha512-soitNKETtj30MfYGmUt9806P5e0LuGs3I4bKd64aYxZzDZnc/z5W2kc2Z/M1NYR3FKLqmHP0BcS4fzV6MRvtLg==" + }, + "node_modules/trac-crypto-api/node_modules/bare-fs": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.0.tgz", + "integrity": "sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/trac-crypto-api/node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/trac-msb": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/trac-msb/-/trac-msb-0.2.7.tgz", + "integrity": "sha512-Aa4Lqf7q1MXVEXedMnl2Mda7YTbEletnciqY2Tnzpr8bu+DQZzc4XnbhmBXzJNz09QCCLLWBXFsyB5fq/EflVA==", + "dependencies": { + "@tracsystems/blake3": "0.0.13", + "autobase": "7.20.1", + "b4a": "1.6.7", + "bare-crypto": "1.12.0", + "bare-fs": "4.5.0", + "bare-http1": "4.1.5", + "bare-readline": "1.0.7", + "bare-tty": "5.0.2", + "bare-utils": "1.5.1", + "bech32": "2.0.0", + "compact-encoding": "2.18.0", + "corestore": "7.5.0", + "crypto": "npm:bare-node-crypto", + "fastest-validator": "1.19.0", + "http": "npm:bare-node-http", + "hyperbee": "2.26.5", + "hypercore": "11.18.3", + "hypercore-crypto": "3.6.1", + "hyperdht": "6.27.0", + "hyperswarm": "4.14.2", + "protocol-buffers-encodings": "1.2.0", + "protomux": "3.10.1", + "protomux-wakeup": "2.4.0", + "readline": "npm:bare-node-readline", + "ready-resource": "1.1.2", + "trac-wallet": "0.0.43-msb-r2.8", + "tty": "npm:bare-node-tty" + } + }, + "node_modules/trac-msb/node_modules/@tracsystems/blake3": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@tracsystems/blake3/-/blake3-0.0.13.tgz", + "integrity": "sha512-soitNKETtj30MfYGmUt9806P5e0LuGs3I4bKd64aYxZzDZnc/z5W2kc2Z/M1NYR3FKLqmHP0BcS4fzV6MRvtLg==" + }, + "node_modules/trac-msb/node_modules/autobase": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/autobase/-/autobase-7.20.1.tgz", + "integrity": "sha512-aiguRQZTfxfKzOP2B7iBwVJEQKDzcSlwocA/sOAGOvcHph3aq7aJNWJp18Z1aKtycVR/v8doBjF4XaS00logpA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.1", + "bare-events": "^2.2.0", + "compact-encoding": "^2.16.0", + "core-coupler": "^2.0.0", + "debounceify": "^1.0.0", + "hyperbee": "^2.22.0", + "hypercore": "^11.4.0", + "hypercore-crypto": "^3.4.0", + "hypercore-id-encoding": "^1.2.0", + "hyperschema": "^1.12.1", + "index-encoder": "^3.3.2", + "nanoassert": "^2.0.0", + "protomux-wakeup": "^2.0.0", + "ready-resource": "^1.0.0", + "resolve-reject-promise": "^1.1.0", + "safety-catch": "^1.0.2", + "scope-lock": "^1.2.4", + "signal-promise": "^1.0.3", + "sodium-universal": "^5.0.1", + "sub-encoder": "^2.1.1", + "tiny-buffer-map": "^1.1.1" + } + }, + "node_modules/trac-msb/node_modules/bare-crypto": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/bare-crypto/-/bare-crypto-1.12.0.tgz", + "integrity": "sha512-GLeZOS2RQkEawVNPvBKmVZQWwSs4jLhculTvuOfLdP+RTMN1Gv3pz+oI9fcQj7/LE2QJM4cPOHfx4u0izBaTHA==", + "license": "Apache-2.0", + "dependencies": { + "bare-stream": "^2.6.3" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/trac-msb/node_modules/bare-debug-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bare-debug-log/-/bare-debug-log-2.0.0.tgz", + "integrity": "sha512-Vi42PkMQsNV9PUpx2Gl1hikshx5O9FzMJ6o9Nnopseg7qLBBK7Nl31d0RHcfwLEAfmcPApytpc0ZFfq68u22FQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/trac-msb/node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/trac-msb/node_modules/bare-fs": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.0.tgz", + "integrity": "sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/trac-msb/node_modules/bare-http1": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/bare-http1/-/bare-http1-4.1.5.tgz", + "integrity": "sha512-YT6tf5R7eIt1VBBc/mpNDXqbxl21WoYGdBXT5anQz2Xt4G2w2gKjmlkDPFwDARZd5wmgH5D2iIiRWAKwbKgVTQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.6.0", + "bare-http-parser": "^1.0.0", + "bare-stream": "^2.3.0", + "bare-tcp": "^2.0.3" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-url": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-url": { + "optional": true + } + } + }, + "node_modules/trac-msb/node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/trac-msb/node_modules/bare-utils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/bare-utils/-/bare-utils-1.5.1.tgz", + "integrity": "sha512-mxCkFvmDU3mlD/mb+pT64kKXOsx2KMsWLQbngN1LB+NOXfhfnRnyvpy3VZc6m7gzQxe57Bsi+aTCBqA4/S3elQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-debug-log": "^2.0.0", + "bare-encoding": "^1.0.0", + "bare-format": "^1.0.0", + "bare-inspect": "^3.0.0", + "bare-type": "^1.0.6" + } + }, + "node_modules/trac-msb/node_modules/compact-encoding": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/compact-encoding/-/compact-encoding-2.18.0.tgz", + "integrity": "sha512-goACAOlhMI2xo5jGOMUDfOLnGdRE1jGfyZ+zie8N5114nHrbPIqf6GLUtzbLof6DSyrERlYRm3EcBplte5LcQw==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.3.0" + } + }, + "node_modules/trac-msb/node_modules/corestore": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/corestore/-/corestore-7.5.0.tgz", + "integrity": "sha512-vANDd5bsg67jR16arnqtqLoEOt25clDZPFVCmip7aKMksLg0kQgTde+WccmPx7fIhYLpEloThtzNcQg0dIlBDg==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.7", + "hypercore": "^11.0.0", + "hypercore-crypto": "^3.4.2", + "hypercore-errors": "^1.4.0", + "hypercore-id-encoding": "^1.3.0", + "ready-resource": "^1.1.1", + "sodium-universal": "^5.0.1", + "which-runtime": "^1.2.1" + } + }, + "node_modules/trac-msb/node_modules/hyperbee": { + "version": "2.26.5", + "resolved": "https://registry.npmjs.org/hyperbee/-/hyperbee-2.26.5.tgz", + "integrity": "sha512-6XIwQIF9wuFVCSS6ciXX+TNv3aUGzEujvI8rUYMjwDCBU+aYVbJuq2k2CEhjeLQSZHnt8j95h2lUtjkV1pBqKg==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.0", + "codecs": "^3.0.0", + "debounceify": "^1.0.0", + "hypercore-errors": "^1.0.0", + "mutexify": "^1.4.0", + "protocol-buffers-encodings": "^1.2.0", + "rache": "^1.0.0", + "ready-resource": "^1.0.0", + "resolve-reject-promise": "^1.1.0", + "safety-catch": "^1.0.2", + "streamx": "^2.12.4", + "unslab": "^1.2.0" + } + }, + "node_modules/trac-msb/node_modules/hypercore": { + "version": "11.18.3", + "resolved": "https://registry.npmjs.org/hypercore/-/hypercore-11.18.3.tgz", + "integrity": "sha512-2HuECoz432aR556mHtzkJcOiO6wrcmCCTlR8RovXVTnQGPkP2BmyIQF7qd65pHxKSn/sBt/Zn4PI/pj4+6NKjw==", + "license": "MIT", + "dependencies": { + "@hyperswarm/secret-stream": "^6.0.0", + "b4a": "^1.1.0", + "bare-events": "^2.2.0", + "big-sparse-array": "^1.0.3", + "compact-encoding": "^2.11.0", + "fast-fifo": "^1.3.0", + "flat-tree": "^1.9.0", + "hypercore-crypto": "^3.2.1", + "hypercore-errors": "^1.5.0", + "hypercore-id-encoding": "^1.2.0", + "hypercore-storage": "^1.0.0", + "is-options": "^1.0.1", + "nanoassert": "^2.0.0", + "protomux": "^3.5.0", + "quickbit-universal": "^2.2.0", + "random-array-iterator": "^1.0.0", + "safety-catch": "^1.0.1", + "sodium-universal": "^5.0.1", + "streamx": "^2.12.4", + "unslab": "^1.3.0", + "z32": "^1.0.0" + } + }, + "node_modules/trac-msb/node_modules/hypercore-crypto": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-3.6.1.tgz", + "integrity": "sha512-ltIz2uDwy9pO/ZGTvqcjzyBkvt6O4cVm4r/nNxh0GFs/RbQtqP/i4wCvLEdmU7ptgtnw7fI67WYD1aHPuv4OVA==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.6", + "compact-encoding": "^2.15.0", + "sodium-universal": "^5.0.0" + } + }, + "node_modules/trac-msb/node_modules/hyperdht": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/hyperdht/-/hyperdht-6.27.0.tgz", + "integrity": "sha512-ZOXPSpsphn83hBxfFcZKNfXcgLLZH3HBWM2G0TAT0vl0hrBY287oEJ/2Nj0NimgxizpzA4J4t8wFsp/LSLs6Sw==", + "license": "MIT", + "dependencies": { + "@hyperswarm/secret-stream": "^6.6.2", + "b4a": "^1.3.1", + "bare-events": "^2.2.0", + "blind-relay": "^1.3.0", + "bogon": "^1.0.0", + "compact-encoding": "^2.4.1", + "compact-encoding-net": "^1.0.1", + "dht-rpc": "^6.15.1", + "hypercore-crypto": "^3.3.0", + "hypercore-id-encoding": "^1.2.0", + "noise-curve-ed": "^2.0.0", + "noise-handshake": "^4.0.0", + "record-cache": "^1.1.1", + "safety-catch": "^1.0.1", + "signal-promise": "^1.0.3", + "sodium-universal": "^5.0.1", + "streamx": "^2.16.1", + "unslab": "^1.3.0", + "xache": "^1.1.0" + }, + "bin": { + "hyperdht": "bin.js" + } + }, + "node_modules/trac-msb/node_modules/hyperswarm": { + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/hyperswarm/-/hyperswarm-4.14.2.tgz", + "integrity": "sha512-MAHSqHt14v13ZeDFyV5HC2HXDTHh4ikfJU6odls3LOXBkZKCCkmKrhFzjxbxMYNYY8qVupzpNWL6JKaBKMs+Ag==", + "license": "MIT", + "dependencies": { + "b4a": "^1.3.1", + "bare-events": "^2.2.0", + "hyperdht": "^6.21.0", + "safety-catch": "^1.0.2", + "shuffled-priority-queue": "^2.1.0", + "streamx": "^2.22.1", + "unslab": "^1.3.0" + } + }, + "node_modules/trac-msb/node_modules/trac-wallet": { + "version": "0.0.43-msb-r2.8", + "resolved": "https://registry.npmjs.org/trac-wallet/-/trac-wallet-0.0.43-msb-r2.8.tgz", + "integrity": "sha512-f/8owYK3GzH7ee2ifNN2PLJiuKfls5wJvrG2vuOuia3nw7no0+383aRyZLB/vIVtyR3S8+wspVextKqXxeSdkg==", + "license": "ISC", + "dependencies": { + "b4a": "1.6.7", + "bare-fs": "^4.0.2", + "bare-path": "^3.0.0", + "bare-readline": "1.0.7", + "bare-tty": "^5.0.2", + "fs": "npm:bare-node-fs", + "path": "npm:bare-node-path", + "readline": "npm:bare-node-readline", + "trac-crypto-api": "0.1.2", + "tty": "npm:bare-node-tty" + } + }, + "node_modules/trac-wallet": { + "version": "0.0.43-msb-r2.9", + "resolved": "https://registry.npmjs.org/trac-wallet/-/trac-wallet-0.0.43-msb-r2.9.tgz", + "integrity": "sha512-t73B/GcXxn0orEUyaG7Jx6Zz2MiPlhiTVIPgGnULhlHqtN+3VegoDBeF7/E+QR4/o0AZNFBRWZY/nQcn99qnew==", + "license": "ISC", + "dependencies": { + "b4a": "1.6.7", + "bare-fs": "^4.0.2", + "bare-path": "^3.0.0", + "bare-readline": "1.0.7", + "bare-tty": "^5.0.2", + "fs": "npm:bare-node-fs", + "path": "npm:bare-node-path", + "readline": "npm:bare-node-readline", + "trac-crypto-api": "0.1.3", + "tty": "npm:bare-node-tty" + } + }, + "node_modules/trac-wallet/node_modules/bare-fs": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.0.tgz", + "integrity": "sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/trac-wallet/node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/trac-wallet/node_modules/trac-crypto-api": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/trac-crypto-api/-/trac-crypto-api-0.1.3.tgz", + "integrity": "sha512-wiPW8IhQjahGwNG/rr+ynZo8E7ugKUq2xILicJ3LH86spax9fA6PFaupIaqafnKILPATd/f4Xwtl2llbBEJOsg==", + "license": "ISC", + "dependencies": { + "@metamask/key-tree": "10.1.1", + "@tracsystems/blake3": "0.0.15", + "b4a": "1.6.7", + "bare-fs": "4.5.0", + "bech32": "2.0.0", + "bip39-mnemonic": "2.5.0", + "sodium-javascript": "0.8.0", + "sodium-universal": "5.0.1" + } + }, + "node_modules/tty": { + "name": "bare-node-tty", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-tty/-/bare-node-tty-1.0.1.tgz", + "integrity": "sha512-ZHPC3Cag1Td46uPOt2fq/rYVRmI/kOYyV8307lgS5w62REGPC8+uMapqmPGM6eJMo9WLacf/fgSR9VlelkPCFw==", + "license": "Apache-2.0", + "dependencies": { + "bare-tty": "*" + } + }, + "node_modules/udx-native": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/udx-native/-/udx-native-1.19.2.tgz", + "integrity": "sha512-RNYh+UhfryCsF5hE2ZOuIqcZ+qdipXK3UsarwxWJwsUQZFE3ybwz0mPjwb5ev1PMBcjFahWiepS/q0wwL51c2g==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.5.0", + "bare-events": "^2.2.0", + "require-addon": "^1.1.0", + "streamx": "^2.22.0" + }, + "engines": { + "bare": ">=1.17.4" + } + }, + "node_modules/unordered-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unordered-set/-/unordered-set-2.0.1.tgz", + "integrity": "sha512-eUmNTPzdx+q/WvOHW0bgGYLWvWHNT3PTKEQLg0MAQhc0AHASHVHoP/9YytYd4RBVariqno/mEUhVZN98CmD7bg==", + "license": "MIT" + }, + "node_modules/unslab": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/unslab/-/unslab-1.3.0.tgz", + "integrity": "sha512-YATkfKAFj47kTzmiQrWXMyRvaVrHsW6MEALa4bm+FhiA2YG4oira+Z3DXN6LrYOYn2Y8eO94Lwl9DOHjs1FpoQ==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.6" + } + }, + "node_modules/url": { + "name": "bare-node-url", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bare-node-url/-/bare-node-url-1.0.1.tgz", + "integrity": "sha512-YZTxBngwq+7KGILI/xzFVi7jcnkkuYgfbuI1rIQ19KzoClJsPkbW4pV4UR9Km9xTLKbOHelW0yfwHofGASTsXA==", + "license": "Apache-2.0", + "dependencies": { + "bare-url": "*" + } + }, + "node_modules/util": { + "name": "bare-node-util", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-util/-/bare-node-util-1.0.0.tgz", + "integrity": "sha512-as4V8zYrS0dMw5RI77qqRtEmngb68drxK2BJszxRFqmKWMc5x7izlHy9cG+Z8CmVefPSb191Fy3gpm8VHFtmpw==", + "license": "Apache-2.0", + "dependencies": { + "bare-utils": "*" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/varint": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.0.tgz", + "integrity": "sha512-gC13b/bWrqQoKY2EmROCZ+AR0jitc6DnDGaQ6Ls9QpKmuSgJB1eQ7H3KETtQm7qSdMWMKCmsshyCmUwMLh3OAA==", + "license": "MIT" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-runtime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/which-runtime/-/which-runtime-1.3.2.tgz", + "integrity": "sha512-5kwCfWml7+b2NO7KrLMhYihjRx0teKkd3yGp1Xk5Vaf2JGdSh+rgVhEALAD9c/59dP+YwJHXoEO7e8QPy7gOkw==", + "license": "Apache-2.0" + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/worker_threads": { + "name": "bare-node-worker-threads", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-worker-threads/-/bare-node-worker-threads-1.0.0.tgz", + "integrity": "sha512-N3cLs8edH4x6UkZDq8NmZqX/WbNsWMgDuPLFmW8v711ail0+JwmX8D+zGUraVZgWnfBTTlnPYjD5sm+y4h9vtQ==", + "license": "Apache-2.0", + "dependencies": { + "bare-worker": "*" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xache": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/xache/-/xache-1.2.0.tgz", + "integrity": "sha512-AiWCGTrnsh//rrbJt8DLbDkDW8eLp1Ktkq0nTWzpE+FKCY35oeqLjtz+LNb4abMnjfTgL0ZBaSwzhgzan1ocEw==", + "license": "MIT" + }, + "node_modules/xsalsa20": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/xsalsa20/-/xsalsa20-1.2.0.tgz", + "integrity": "sha512-FIr/DEeoHfj7ftfylnoFt3rAIRoWXpx2AoDfrT2qD2wtp7Dp+COajvs/Icb7uHqRW9m60f5iXZwdsJJO3kvb7w==", + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/z32": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/z32/-/z32-1.1.0.tgz", + "integrity": "sha512-1WUHy+VS6d0HPNspDxvLssBbeQjXMjSnpv0vH82vRAUfg847NmX3OXozp/hRP5jPhxBbrVzrgvAt+UsGNzRFQQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.5.3" + } + }, + "node_modules/zlib": { + "name": "bare-node-zlib", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bare-node-zlib/-/bare-node-zlib-1.0.0.tgz", + "integrity": "sha512-1PTJaUKTBEZ2HdDnGYKYos6t5HEvHQzrJjSVvVSBaEOVMN+cpl2LuwV02rK9lpOTKPv+Lbs0A/a2MdETKPCIWg==", + "license": "Apache-2.0", + "dependencies": { + "bare-zlib": "*" + } + } + } +} diff --git a/package.json b/package.json index 05b7722..4a80bca 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,33 @@ { "name": "trac-peer", "main": "src/index.js", - "version": "0.1.68", + "version": "0.2.11", "type": "module", + "pear": { + "name": "trac-peer", + "type": "terminal" + }, + "exports": { + ".": { + "bare": "./peer.mjs", + "node": "./src/index.js", + "default": "./src/index.js" + }, + "./peer": "./peer.mjs", + "./src/*": "./src/*" + }, + "scripts": { + "peer:run": "node scripts/run-peer.mjs", + "peer:run-rpc": "node scripts/run-peer.mjs --rpc", + "peer:smoke": "node scripts/smoke-msb-sync.mjs", + "peer:pear": "pear run -d .", + "peer:pear-rpc": "pear run -d . --rpc", + "test:unit:node": "node tests/unit/unit.test.js", + "test:acceptance:node": "node tests/acceptance/acceptance.test.js", + "test:all": "npm run test:unit:node && npm run test:acceptance:node" + }, "dependencies": { + "@tracsystems/blake3": "^0.0.15", "assert": "npm:bare-node-assert", "autobase": "7.6.3", "b4a": "1.6.7", @@ -13,7 +37,7 @@ "bare-crypto": "1.4.3", "bare-events": "2.5.4", "bare-fetch": "2.2.2", - "bare-fs": "4.1.2", + "bare-fs": "^4.1.5", "bare-http1": "4.0.2", "bare-https": "2.0.0", "bare-inspector": "4.0.1", @@ -24,7 +48,7 @@ "bare-readline": "1.0.7", "bare-repl": "4.0.0", "bare-stream": "2.6.5", - "bare-subprocess": "5.0.3", + "bare-subprocess": "5.0.4", "bare-timers": "3.0.1", "bare-tls": "2.0.4", "bare-tty": "5.0.2", @@ -33,12 +57,11 @@ "bare-worker": "3.0.0", "bare-zlib": "1.2.5", "bip39": "3.1.0", - "brittle": "3.0.0", "buffer": "npm:bare-node-buffer", "child_process": "npm:bare-node-child-process", "compact-encoding": "2.16.1", "console": "npm:bare-node-console", - "corestore": "7.4.1", + "corestore": "7.4.4", "crypto": "npm:bare-node-crypto", "debounceify": "1.1.0", "events": "npm:bare-node-events", @@ -47,11 +70,11 @@ "fs": "npm:bare-node-fs", "http": "npm:bare-node-http", "https": "npm:bare-node-https", - "hyperbee": "2.24.2", - "hypercore": "11.6.2", - "hypercore-crypto": "3.4.0", - "hyperdht": "6.20.5", - "hyperswarm": "4.11.5", + "hyperbee": "^2.24.2", + "hypercore": "11.8.3", + "hypercore-crypto": "^3.4.0", + "hyperdht": "^6.20.5", + "hyperswarm": "^4.11.5", "inspector": "npm:bare-node-inspector", "is-options": "1.0.2", "module": "npm:bare-node-module", @@ -60,8 +83,8 @@ "path": "npm:bare-node-path", "pear-interface": "1.0.0", "process": "npm:bare-node-process", - "protomux": "3.10.1", - "protomux-wakeup": "2.4.0", + "protomux": "^3.10.1", + "protomux-wakeup": "^2.4.0", "readline": "npm:bare-node-readline", "ready-resource": "1.1.2", "repl": "npm:bare-node-repl", @@ -70,7 +93,8 @@ "stream": "npm:bare-node-stream", "timers": "npm:bare-node-timers", "tls": "npm:bare-node-tls", - "trac-wallet": "0.0.43", + "trac-msb": "^0.2.7", + "trac-wallet": "^0.0.43-msb-r2.9", "tty": "npm:bare-node-tty", "url": "npm:bare-node-url", "util": "npm:bare-node-util", @@ -80,6 +104,7 @@ }, "publishConfig": { "registry": "https://registry.npmjs.org", - "access": "public" + "access": "public", + "brittle": "3.0.0" } } diff --git a/peer-main.mjs b/peer-main.mjs new file mode 100644 index 0000000..ab9071f --- /dev/null +++ b/peer-main.mjs @@ -0,0 +1,131 @@ +import b4a from "b4a"; +import path from "path"; +import fs from "fs"; +import { ensureTextCodecs } from "./src/textCodec.js"; + +import { Peer, Wallet } from "./src/index.js"; +import { MainSettlementBus } from "trac-msb/src/index.js"; +import { getPearRuntime } from "./src/runnerArgs.js"; +import { resolvePeerRunnerConfig } from "./src/peerRunnerConfig.js"; +import { startRpcServer } from "./rpc/rpc_server.js"; +import { startInteractiveCli } from "./src/cli.js"; +import PokemonProtocol from "./src/dev/pokemonProtocol.js"; +import PokemonContract from "./src/dev/pokemonContract.js"; + +console.log("Starting trac-peer (Pear runner)..."); + +const ensureKeypairFile = async (keyPairPath) => { + if (fs.existsSync(keyPairPath)) return; + fs.mkdirSync(path.dirname(keyPairPath), { recursive: true }); + await ensureTextCodecs(); + const { default: PeerWallet } = await import("trac-wallet"); + const wallet = new PeerWallet(); + await wallet.ready; + if (!wallet.secretKey) { + await wallet.generateKeyPair(); + await wallet.ready; + } + wallet.exportToFile(keyPairPath, b4a.alloc(0)); +}; + +const { env, storeLabel, flags } = getPearRuntime(); + +const die = (message) => { + console.error(message); + if (typeof process !== "undefined" && process.exit) process.exit(1); + if (typeof Pear !== "undefined" && Pear.exit) Pear.exit(1); + throw new Error(message); +}; + +let cfg; +try { + cfg = resolvePeerRunnerConfig({ env, storeLabel, flags }); +} catch (e) { + die(e?.message || String(e)); +} + +await ensureKeypairFile(cfg.msbKeyPairPath); +await ensureKeypairFile(cfg.peerKeyPairPath); + +const msb = new MainSettlementBus({ + stores_directory: cfg.msbStoresDirectory, + store_name: `/${cfg.msbStoreName}`, + bootstrap: cfg.msbBootstrapHex, + channel: cfg.msbChannel, + enable_interactive_mode: false, + enable_wallet: true, + enable_validator_observer: true, + enable_role_requester: false, + enable_tx_apply_logs: false, + enable_error_apply_logs: false, +}); +await msb.ready(); + +// DevProtocol and DevContract moved to separate files for reuse. + +const peer = new Peer({ + stores_directory: cfg.peerStoresDirectory, + store_name: cfg.peerStoreName, + msb, + wallet: new Wallet(), + protocol: PokemonProtocol, + contract: PokemonContract, + bootstrap: cfg.subnetBootstrap ? b4a.from(cfg.subnetBootstrap, "hex") : null, + channel: cfg.subnetChannel, + enable_interactive_mode: true, +}); +await peer.ready(); + +let rpcServer = null; +if (cfg.rpcEnabled) { + rpcServer = startRpcServer(peer, cfg.rpcHost, cfg.rpcPort, { + allowOrigin: cfg.rpcAllowOrigin, + maxBodyBytes: cfg.rpcMaxBodyBytes, + }); +} + +const peerMsbAddress = peer.msbClient.pubKeyHexToAddress(peer.wallet.publicKey); +const effectiveSubnetBootstrapHex = peer.base?.key + ? peer.base.key.toString("hex") + : b4a.isBuffer(peer.bootstrap) + ? peer.bootstrap.toString("hex") + : String(peer.bootstrap); + +if (!cfg.subnetBootstrap) { + fs.mkdirSync(path.dirname(cfg.subnetBootstrapFile), { recursive: true }); + fs.writeFileSync(cfg.subnetBootstrapFile, `${effectiveSubnetBootstrapHex}\\n`); +} + +console.log(""); +console.log("==================== TRAC-PEER (PEAR) ===================="); +console.log("Peer store:", path.join(cfg.peerStoresDirectory, cfg.peerStoreNameRaw)); +console.log("Subnet bootstrap:", effectiveSubnetBootstrapHex); +console.log("Subnet channel:", cfg.subnetChannel); +console.log("Peer pubkey (hex):", peer.wallet.publicKey); +console.log("Peer MSB address:", peerMsbAddress); +console.log("----------------------------------------------------------"); +console.log("MSB network bootstrap:", msb.bootstrap?.toString("hex") ?? null); +console.log("MSB channel:", b4a.toString(msb.channel, "utf8")); +console.log("MSB wallet address:", msb.wallet?.address ?? null); +console.log("----------------------------------------------------------"); +console.log("Next steps:"); +console.log("- Fund this Peer MSB address on your MSB admin node:"); +console.log(" ", peerMsbAddress); +console.log("- In this peer console:"); +console.log(" - /deploy_subnet"); +console.log(' - /tx --command "ping hello"'); +console.log("=========================================================="); +console.log(""); + +await startInteractiveCli(peer); + +const shutdown = async (code) => { + if (rpcServer) await new Promise((resolve) => rpcServer.close(resolve)); + await Promise.allSettled([peer.close(), msb.close()]); + if (typeof process !== "undefined") process.exit(code); + if (typeof Pear !== "undefined" && Pear.exit) Pear.exit(code); +}; + +if (typeof process !== "undefined" && typeof process.on === "function") { + process.on("SIGINT", () => shutdown(130)); +} diff --git a/peer.mjs b/peer.mjs new file mode 100644 index 0000000..f393c02 --- /dev/null +++ b/peer.mjs @@ -0,0 +1,5 @@ +import { ensureTextCodecs } from "./src/textCodec.js"; + +await ensureTextCodecs(); + +await import("./peer-main.mjs"); diff --git a/rpc/constants.js b/rpc/constants.js new file mode 100644 index 0000000..16a3627 --- /dev/null +++ b/rpc/constants.js @@ -0,0 +1,4 @@ +export const DEFAULT_RPC_HOST = "127.0.0.1"; +export const DEFAULT_RPC_PORT = 5001; +export const DEFAULT_MAX_BODY_BYTES = 1_000_000; + diff --git a/rpc/cors.js b/rpc/cors.js new file mode 100644 index 0000000..437936e --- /dev/null +++ b/rpc/cors.js @@ -0,0 +1,13 @@ +export function applyCors(req, res, { allowOrigin = "*" } = {}) { + res.setHeader("Access-Control-Allow-Origin", allowOrigin); + res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); + res.setHeader("Access-Control-Allow-Headers", "Content-Type"); + + if (req.method === "OPTIONS") { + res.writeHead(204); + res.end(); + return true; + } + return false; +} + diff --git a/rpc/create_server.js b/rpc/create_server.js new file mode 100644 index 0000000..9671be1 --- /dev/null +++ b/rpc/create_server.js @@ -0,0 +1,49 @@ +import http from "http"; +import { applyCors } from "./cors.js"; +import { routes } from "./routes/index.js"; +import { DEFAULT_MAX_BODY_BYTES } from "./constants.js"; + +export const createServer = (peer, { maxBodyBytes = DEFAULT_MAX_BODY_BYTES, allowOrigin = "*" } = {}) => { + const server = http.createServer({}, async (req, res) => { + const respond = (code, payload) => { + if (res.headersSent) return; + res.writeHead(code, { "Content-Type": "application/json" }); + res.end(JSON.stringify(payload ?? {})); + }; + + req.on("error", (err) => { + console.error("RPC request stream error:", err); + respond(500, { error: "Request stream error." }); + }); + + if (applyCors(req, res, { allowOrigin })) return; + + const requestPath = (req.url || "/").split("?")[0]; + const sortedRoutes = [...routes].sort((a, b) => b.path.length - a.path.length); + + for (const route of sortedRoutes) { + if (req.method !== route.method) continue; + if (requestPath !== route.path) continue; + try { + await route.handler({ req, res, respond, peer, maxBodyBytes }); + } catch (error) { + const code = + error?.code === "BODY_TOO_LARGE" + ? 413 + : error?.code === "BAD_JSON" + ? 400 + : typeof error?.message === "string" && + /^(Missing|Invalid|Empty|Peer subnet is not writable)/.test(error.message) + ? 400 + : 500; + if (code === 500) console.error("RPC handler error:", error); + respond(code, { error: code === 500 ? "An internal error occurred." : error?.message || "Bad Request" }); + } + return; + } + + respond(404, { error: "Not Found" }); + }); + + return server; +}; diff --git a/rpc/handlers.js b/rpc/handlers.js new file mode 100644 index 0000000..a4f7e06 --- /dev/null +++ b/rpc/handlers.js @@ -0,0 +1,111 @@ +import { buildRequestUrl } from "./utils/url.js"; +import { readJsonBody } from "./utils/body.js"; +import { + getStatus, + getState, + broadcastTx, + deploySubnet, + setChatStatus, + postChatMessage, + setNick, + addAdmin, + addWriter, + addIndexer, + removeWriter, + removeIndexer, + joinValidator, +} from "./services.js"; + +export async function handleHealth({ respond }) { + respond(200, { ok: true }); +} + +export async function handleStatus({ respond, peer }) { + respond(200, await getStatus(peer)); +} + +export async function handleGetState({ req, respond, peer }) { + const url = buildRequestUrl(req); + const key = url.searchParams.get("key"); + const confirmed = url.searchParams.get("confirmed"); + const confirmedBool = confirmed == null ? true : confirmed === "true"; + const value = await getState(peer, key, { confirmed: confirmedBool }); + respond(200, { key, confirmed: confirmedBool, value }); +} + +export async function handleBroadcastTx({ req, respond, peer, maxBodyBytes }) { + const body = await readJsonBody(req, { maxBytes: maxBodyBytes }); + if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." }); + const payload = await broadcastTx(peer, { command: body.command, sim: body.sim }); + respond(200, { payload }); +} + +export async function handleDeploySubnet({ req, respond, peer, maxBodyBytes }) { + // Optional body for future compatibility. + await readJsonBody(req, { maxBytes: maxBodyBytes }).catch(() => null); + const payload = await deploySubnet(peer); + respond(200, { payload }); +} + +export async function handleSetChatStatus({ req, respond, peer, maxBodyBytes }) { + const body = await readJsonBody(req, { maxBytes: maxBodyBytes }); + if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." }); + await setChatStatus(peer, body.enabled); + respond(200, { ok: true }); +} + +export async function handlePostChatMessage({ req, respond, peer, maxBodyBytes }) { + const body = await readJsonBody(req, { maxBytes: maxBodyBytes }); + if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." }); + await postChatMessage(peer, { message: body.message, reply_to: body.reply_to }); + respond(200, { ok: true }); +} + +export async function handleSetNick({ req, respond, peer, maxBodyBytes }) { + const body = await readJsonBody(req, { maxBytes: maxBodyBytes }); + if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." }); + await setNick(peer, { nick: body.nick, user: body.user }); + respond(200, { ok: true }); +} + +export async function handleAddAdmin({ req, respond, peer, maxBodyBytes }) { + const body = await readJsonBody(req, { maxBytes: maxBodyBytes }); + if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." }); + await addAdmin(peer, { address: body.address }); + respond(200, { ok: true }); +} + +export async function handleAddWriter({ req, respond, peer, maxBodyBytes }) { + const body = await readJsonBody(req, { maxBytes: maxBodyBytes }); + if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." }); + await addWriter(peer, { key: body.key }); + respond(200, { ok: true }); +} + +export async function handleAddIndexer({ req, respond, peer, maxBodyBytes }) { + const body = await readJsonBody(req, { maxBytes: maxBodyBytes }); + if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." }); + await addIndexer(peer, { key: body.key }); + respond(200, { ok: true }); +} + +export async function handleRemoveWriter({ req, respond, peer, maxBodyBytes }) { + const body = await readJsonBody(req, { maxBytes: maxBodyBytes }); + if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." }); + await removeWriter(peer, { key: body.key }); + respond(200, { ok: true }); +} + +export async function handleRemoveIndexer({ req, respond, peer, maxBodyBytes }) { + const body = await readJsonBody(req, { maxBytes: maxBodyBytes }); + if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." }); + await removeIndexer(peer, { key: body.key }); + respond(200, { ok: true }); +} + +export async function handleJoinValidator({ req, respond, peer, maxBodyBytes }) { + const body = await readJsonBody(req, { maxBytes: maxBodyBytes }); + if (!body || typeof body !== "object") return respond(400, { error: "Missing JSON body." }); + await joinValidator(peer, { address: body.address }); + respond(200, { ok: true }); +} diff --git a/rpc/routes/index.js b/rpc/routes/index.js new file mode 100644 index 0000000..ad79f40 --- /dev/null +++ b/rpc/routes/index.js @@ -0,0 +1,9 @@ +import { v1Routes } from "./v1.js"; + +export const routes = [ + ...v1Routes.map((r) => ({ + ...r, + path: `/v1${r.path}`, + })), +]; + diff --git a/rpc/routes/v1.js b/rpc/routes/v1.js new file mode 100644 index 0000000..5cfe693 --- /dev/null +++ b/rpc/routes/v1.js @@ -0,0 +1,37 @@ +import { + handleHealth, + handleStatus, + handleGetState, + handleBroadcastTx, + handleDeploySubnet, + handleSetChatStatus, + handlePostChatMessage, + handleSetNick, + handleAddAdmin, + handleAddWriter, + handleAddIndexer, + handleRemoveWriter, + handleRemoveIndexer, + handleJoinValidator, +} from "../handlers.js"; + +export const v1Routes = [ + { method: "GET", path: "/health", handler: handleHealth }, + { method: "GET", path: "/status", handler: handleStatus }, + { method: "GET", path: "/state", handler: handleGetState }, + + { method: "POST", path: "/tx", handler: handleBroadcastTx }, + { method: "POST", path: "/deploy-subnet", handler: handleDeploySubnet }, + + { method: "POST", path: "/chat/status", handler: handleSetChatStatus }, + { method: "POST", path: "/chat/post", handler: handlePostChatMessage }, + { method: "POST", path: "/chat/nick", handler: handleSetNick }, + + { method: "POST", path: "/admin/add-admin", handler: handleAddAdmin }, + { method: "POST", path: "/admin/add-writer", handler: handleAddWriter }, + { method: "POST", path: "/admin/add-indexer", handler: handleAddIndexer }, + { method: "POST", path: "/admin/remove-writer", handler: handleRemoveWriter }, + { method: "POST", path: "/admin/remove-indexer", handler: handleRemoveIndexer }, + + { method: "POST", path: "/msb/join-validator", handler: handleJoinValidator }, +]; diff --git a/rpc/rpc_server.js b/rpc/rpc_server.js new file mode 100644 index 0000000..cb09ceb --- /dev/null +++ b/rpc/rpc_server.js @@ -0,0 +1,9 @@ +import { createServer } from "./create_server.js"; + +export function startRpcServer(peer, host, port, options = {}) { + const server = createServer(peer, options); + return server.listen(port, host, () => { + console.log(`trac-peer RPC listening at http://${host}:${port}`); + }); +} + diff --git a/rpc/services.js b/rpc/services.js new file mode 100644 index 0000000..3ba43b8 --- /dev/null +++ b/rpc/services.js @@ -0,0 +1,150 @@ +import b4a from "b4a"; +import { deploySubnet as deploySubnetFn, setChatStatus as setChatStatusFn, setNick as setNickFn } from "../src/functions.js"; +import { addAdminKey, addWriterKey, addIndexerKey, removeWriterKey, removeIndexerKey, joinValidator as joinValidatorFn } from "../src/functions.js"; + +const asHex32 = (value, field) => { + const hex = String(value ?? "").trim().toLowerCase(); + if (!/^[0-9a-f]{64}$/.test(hex)) throw new Error(`Invalid ${field}. Expected 32-byte hex (64 chars).`); + return hex; +}; + +export async function getStatus(peer) { + const subnetBootstrapHex = b4a.isBuffer(peer.bootstrap) + ? peer.bootstrap.toString("hex") + : peer.bootstrap != null + ? String(peer.bootstrap) + : null; + + const peerMsbAddress = peer.msbClient?.isReady() ? peer.msbClient.pubKeyHexToAddress(peer.wallet.publicKey) : null; + + const admin = peer.base?.view ? await peer.base.view.get("admin") : null; + const chatStatus = peer.base?.view ? await peer.base.view.get("chat_status") : null; + + return { + peer: { + pubKeyHex: peer.wallet?.publicKey ?? null, + writerKeyHex: peer.writerLocalKey ?? null, + msbAddress: peerMsbAddress, + baseWritable: !!peer.base?.writable, + isIndexer: !!peer.base?.isIndexer, + isWriter: !!peer.base?.writable, + subnetBootstrapHex, + subnetChannelUtf8: peer.channel ? b4a.toString(peer.channel, "utf8") : null, + subnetSignedLength: peer.base?.view?.core?.signedLength ?? null, + subnetUnsignedLength: peer.base?.view?.core?.length ?? null, + admin: admin?.value ?? null, + chatStatus: chatStatus?.value ?? null, + }, + msb: { + ready: peer.msbClient?.isReady?.() ?? false, + bootstrapHex: peer.msbClient?.bootstrapHex ?? null, + networkId: peer.msbClient?.networkId ?? null, + signedLength: peer.msbClient?.isReady?.() ? peer.msbClient.getSignedLength() : null, + }, + }; +} + +export async function getState(peer, key, { confirmed = true } = {}) { + const k = String(key ?? ""); + if (!k) throw new Error("Missing key."); + if (!peer.base?.view) throw new Error("Peer view not ready."); + if (confirmed) { + const viewSession = peer.base.view.checkout(peer.base.view.core.signedLength); + try { + const res = await viewSession.get(k); + return res?.value ?? null; + } finally { + await viewSession.close(); + } + } + const res = await peer.base.view.get(k); + return res?.value ?? null; +} + +export async function broadcastTx(peer, { command, sim = false } = {}) { + if (!peer.base?.writable) throw new Error("Peer subnet is not writable (writer required for /tx)."); + const cmd = command; + if (!cmd) throw new Error("Missing command."); + return await peer.protocol_instance.tx({ command: cmd }, !!sim); +} + +export async function deploySubnet(peer) { + if (!peer.base?.writable) throw new Error("Peer subnet is not writable (writer required for /deploy-subnet)."); + return await deploySubnetFn("/deploy_subnet", peer); +} + +export async function setChatStatus(peer, enabled) { + if (!peer.base?.writable) throw new Error("Peer subnet is not writable (writer required)."); + const on = enabled === true || enabled === 1 || enabled === "1"; + return await setChatStatusFn(`/set_chat_status --enabled ${on ? 1 : 0}`, peer); +} + +export async function postChatMessage(peer, { message, reply_to = null } = {}) { + if (!peer.base?.writable) throw new Error("Peer subnet is not writable (writer required)."); + const chatStatus = await peer.base.view.get('chat_status'); + if (chatStatus === null || chatStatus.value !== 'on') throw new Error('Chat is disabled.'); + const msg = String(message ?? ""); + if (!msg.trim()) throw new Error("Empty message not allowed."); + if (b4a.byteLength(msg) > peer.protocol_instance.msgMaxBytes()) throw new Error("Message too large."); + const nonce = peer.protocol_instance.generateNonce(); + const signature = { + dispatch: { + type: "msg", + msg, + address: peer.wallet.publicKey, + attachments: [], + deleted_by: null, + reply_to: reply_to != null ? parseInt(reply_to) : null, + pinned: false, + pin_id: null, + }, + }; + const hash = peer.wallet.sign(JSON.stringify(signature) + nonce); + await peer.base.append({ type: "msg", value: signature, hash, nonce }); + return { ok: true }; +} + +export async function setNick(peer, { nick, user = null } = {}) { + if (!peer.base?.writable) throw new Error("Peer subnet is not writable (writer required)."); + const n = String(nick ?? "").trim(); + if (!n) throw new Error("Missing nick."); + const u = user != null ? String(user).trim().toLowerCase() : null; + const input = u ? `/set_nick --nick "${n}" --user "${u}"` : `/set_nick --nick "${n}"`; + return await setNickFn(input, peer); +} + +export async function addAdmin(peer, { address }) { + if (!peer.base?.writable) throw new Error("Peer subnet is not writable (writer required)."); + const pk = asHex32(address, "address"); + return await addAdminKey(pk, peer); +} + +export async function addWriter(peer, { key }) { + if (!peer.base?.writable) throw new Error("Peer subnet is not writable (writer required)."); + const wk = asHex32(key, "key"); + return await addWriterKey(wk, peer); +} + +export async function addIndexer(peer, { key }) { + if (!peer.base?.writable) throw new Error("Peer subnet is not writable (writer required)."); + const wk = asHex32(key, "key"); + return await addIndexerKey(wk, peer); +} + +export async function removeWriter(peer, { key }) { + if (!peer.base?.writable) throw new Error("Peer subnet is not writable (writer required)."); + const wk = asHex32(key, "key"); + return await removeWriterKey(wk, peer); +} + +export async function removeIndexer(peer, { key }) { + if (!peer.base?.writable) throw new Error("Peer subnet is not writable (writer required)."); + const wk = asHex32(key, "key"); + return await removeIndexerKey(wk, peer); +} + +export async function joinValidator(peer, { address }) { + const addr = String(address ?? "").trim(); + if (!addr) throw new Error("Missing address."); + return await joinValidatorFn(`/join_validator --address ${addr}`, peer); +} diff --git a/rpc/utils/body.js b/rpc/utils/body.js new file mode 100644 index 0000000..2d314ce --- /dev/null +++ b/rpc/utils/body.js @@ -0,0 +1,49 @@ +export async function readJsonBody(req, { maxBytes }) { + let body = ""; + let bytesRead = 0; + let done = false; + + return await new Promise((resolve, reject) => { + req.on("data", (chunk) => { + if (done) return; + bytesRead += chunk.length; + if (bytesRead > maxBytes) { + done = true; + const err = new Error("Request body too large."); + err.code = "BODY_TOO_LARGE"; + // Keep the socket alive long enough to let the server respond with 413. + // Drain remaining incoming data without buffering it. + try { + req.removeAllListeners("data"); + req.removeAllListeners("end"); + req.on("data", () => {}); + req.on("end", () => {}); + req.resume?.(); + } catch (_e) {} + reject(err); + return; + } + body += chunk.toString(); + }); + + req.on("end", () => { + if (done) return; + if (!body) return resolve(null); + try { + resolve(JSON.parse(body)); + } catch (e) { + const err = new Error("Invalid JSON payload."); + err.code = "BAD_JSON"; + reject(err); + } + }); + + req.on("error", (err) => { + if (done) return; + const e = new Error("Request stream error."); + e.code = "STREAM_ERROR"; + e.cause = err; + reject(e); + }); + }); +} diff --git a/rpc/utils/url.js b/rpc/utils/url.js new file mode 100644 index 0000000..a0723f1 --- /dev/null +++ b/rpc/utils/url.js @@ -0,0 +1,5 @@ +export function buildRequestUrl(req) { + // We only need path + search params; base is a dummy placeholder. + return new URL(req.url || "/", "http://localhost"); +} + diff --git a/scripts/run-peer.mjs b/scripts/run-peer.mjs new file mode 100644 index 0000000..6e6a2b7 --- /dev/null +++ b/scripts/run-peer.mjs @@ -0,0 +1,258 @@ +import b4a from "b4a"; +import path from "path"; +import fs from "fs"; +import PeerWallet from "trac-wallet"; + +import { Peer, Wallet } from "../src/index.js"; +import { MainSettlementBus } from "trac-msb/src/index.js"; +import { startRpcServer } from "../rpc/rpc_server.js"; +import { DEFAULT_RPC_HOST, DEFAULT_RPC_PORT, DEFAULT_MAX_BODY_BYTES } from "../rpc/constants.js"; +import { startInteractiveCli } from "../src/cli.js"; +import { ensureTextCodecs } from "../src/textCodec.js"; +import PokemonProtocol from "../src/dev/pokemonProtocol.js"; +import PokemonContract from "../src/dev/pokemonContract.js"; + +const toArgMap = (argv) => { + const out = {}; + for (let i = 0; i < argv.length; i++) { + const raw = argv[i]; + if (!raw.startsWith("--")) continue; + const eq = raw.indexOf("="); + if (eq !== -1) { + const k = raw.slice(2, eq); + const v = raw.slice(eq + 1); + out[k] = v; + continue; + } + const k = raw.slice(2); + const next = argv[i + 1]; + if (next !== undefined && !String(next).startsWith("--")) { + out[k] = next; + i++; + } else { + out[k] = true; + } + } + return out; +}; + +const ensureTrailingSlash = (value) => (value.endsWith("/") ? value : `${value}/`); + +const ensureKeypairFile = async (keyPairPath) => { + if (fs.existsSync(keyPairPath)) return; + fs.mkdirSync(path.dirname(keyPairPath), { recursive: true }); + await ensureTextCodecs(); + const wallet = new PeerWallet(); + await wallet.ready; + if (!wallet.secretKey) { + await wallet.generateKeyPair(); + await wallet.ready; + } + wallet.exportToFile(keyPairPath, b4a.alloc(0)); +}; + +const readHexFile = (filePath, byteLength) => { + try { + if (fs.existsSync(filePath)) { + const hex = fs.readFileSync(filePath, "utf8").trim().toLowerCase(); + if (/^[0-9a-f]+$/.test(hex) && hex.length === byteLength * 2) return hex; + } + } catch (_e) {} + return null; +}; + +const args = toArgMap(process.argv.slice(2)); + +const rpcEnabled = + args["rpc"] === true || args["rpc"] === "true" || process.env.PEER_RPC === "true" || process.env.PEER_RPC === "1"; +const rpcHost = (args["rpc-host"] && String(args["rpc-host"])) || process.env.PEER_RPC_HOST || DEFAULT_RPC_HOST; +const rpcPortRaw = + (args["rpc-port"] && String(args["rpc-port"])) || process.env.PEER_RPC_PORT || String(DEFAULT_RPC_PORT); +const rpcPort = parseInt(rpcPortRaw); +if (rpcEnabled && (isNaN(rpcPort) || rpcPort < 1 || rpcPort > 65535)) { + console.error("Invalid --rpc-port. Expected integer 1-65535."); + process.exit(1); +} +const rpcAllowOrigin = + (args["rpc-allow-origin"] && String(args["rpc-allow-origin"])) || process.env.PEER_RPC_ALLOW_ORIGIN || "*"; +const rpcMaxBodyBytesRaw = + (args["rpc-max-body-bytes"] && String(args["rpc-max-body-bytes"])) || + process.env.PEER_RPC_MAX_BODY_BYTES || + String(DEFAULT_MAX_BODY_BYTES); +const rpcMaxBodyBytes = parseInt(rpcMaxBodyBytesRaw); +if (rpcEnabled && (isNaN(rpcMaxBodyBytes) || rpcMaxBodyBytes < 1)) { + console.error("Invalid --rpc-max-body-bytes. Expected a positive integer."); + process.exit(1); +} + +const msbStoresDirectory = + (args["msb-stores-directory"] && String(args["msb-stores-directory"])) || + process.env.MSB_STORES_DIRECTORY || + "stores/"; + +const msbStoreName = + (args["msb-store-name"] && String(args["msb-store-name"])) || + process.env.MSB_STORE_NAME || + "peer-msb"; + +const msbBootstrap = + (args["msb-bootstrap"] && String(args["msb-bootstrap"])) || + process.env.MSB_BOOTSTRAP || + null; + +const msbChannel = + (args["msb-channel"] && String(args["msb-channel"])) || + process.env.MSB_CHANNEL || + null; + +const peerStoresDirectory = + (args["peer-stores-directory"] && String(args["peer-stores-directory"])) || + process.env.PEER_STORES_DIRECTORY || + "stores/"; + +const peerStoreNameRaw = + (args["peer-store-name"] && String(args["peer-store-name"])) || + process.env.PEER_STORE_NAME || + "peer"; +const peerStoreName = ensureTrailingSlash(peerStoreNameRaw); + +const subnetBootstrapHex = + (args["subnet-bootstrap"] && String(args["subnet-bootstrap"])) || + process.env.SUBNET_BOOTSTRAP || + null; + +const subnetChannel = + (args["subnet-channel"] && String(args["subnet-channel"])) || + process.env.SUBNET_CHANNEL || + "trac-peer-subnet"; + +const msbBootstrapHex = msbBootstrap ? String(msbBootstrap).trim().toLowerCase() : null; +if (msbBootstrapHex && !/^[0-9a-f]{64}$/.test(msbBootstrapHex)) { + console.error( + `Invalid --msb-bootstrap. Expected 32-byte hex (64 chars), got length ${msbBootstrapHex.length}.` + ); + process.exit(1); +} + +if (!msbBootstrapHex || !msbChannel) { + console.error( + "Missing MSB network params. Provide --msb-bootstrap= and --msb-channel= (or env MSB_BOOTSTRAP/MSB_CHANNEL)." + ); + process.exit(1); +} + +// Important: this starts a *local MSB node* (its own store) that joins your already-running MSB network. +// trac-peer currently requires an MSB instance to broadcast and to observe confirmed state. + +const msbStoresFullPath = `${ensureTrailingSlash(msbStoresDirectory)}/${msbStoreName}`; +const msbKeyPairPath = `${msbStoresFullPath}/db/keypair.json`; +await ensureKeypairFile(msbKeyPairPath); + +const peerKeyPairPath = path.join( + peerStoresDirectory, + peerStoreName, + "db", + "keypair.json" +); + +await ensureKeypairFile(peerKeyPairPath); + +const subnetBootstrapFile = path.join( + peerStoresDirectory, + peerStoreNameRaw, + "subnet-bootstrap.hex" +); +let subnetBootstrap = subnetBootstrapHex ? subnetBootstrapHex.trim().toLowerCase() : null; +if (subnetBootstrap) { + if (!/^[0-9a-f]{64}$/.test(subnetBootstrap)) { + console.error("Invalid --subnet-bootstrap. Provide 32-byte hex (64 chars)."); + process.exit(1); + } + if (subnetBootstrap === msbBootstrap.trim().toLowerCase()) { + console.error("Subnet bootstrap cannot equal MSB bootstrap."); + process.exit(1); + } +} else { + subnetBootstrap = readHexFile(subnetBootstrapFile, 32); + if (subnetBootstrap && subnetBootstrap === msbBootstrap.trim().toLowerCase()) { + console.error("Stored subnet bootstrap equals MSB bootstrap. Delete the file and rerun."); + process.exit(1); + } +} + +const msb = new MainSettlementBus({ + stores_directory: ensureTrailingSlash(msbStoresDirectory), + store_name: `/${msbStoreName}`, + bootstrap: msbBootstrapHex, + channel: msbChannel, + enable_interactive_mode: false, + enable_wallet: true, + enable_validator_observer: true, + enable_role_requester: false, + enable_tx_apply_logs: false, + enable_error_apply_logs: false, +}); +await msb.ready(); + +// DevProtocol and DevContract moved to shared src files + +const peer = new Peer({ + stores_directory: ensureTrailingSlash(peerStoresDirectory), + store_name: peerStoreName, + msb, + wallet: new Wallet(), + protocol: PokemonProtocol, + contract: PokemonContract, + bootstrap: subnetBootstrap ? b4a.from(subnetBootstrap, "hex") : null, + channel: subnetChannel, + enable_interactive_mode: true, +}); +await peer.ready(); + +let rpcServer = null; +if (rpcEnabled) { + rpcServer = startRpcServer(peer, rpcHost, rpcPort, { allowOrigin: rpcAllowOrigin, maxBodyBytes: rpcMaxBodyBytes }); +} + +const peerMsbAddress = peer.msbClient.pubKeyHexToAddress(peer.wallet.publicKey); +const effectiveSubnetBootstrapHex = peer.base?.key ? peer.base.key.toString("hex") : (b4a.isBuffer(peer.bootstrap) ? peer.bootstrap.toString("hex") : String(peer.bootstrap)); +if (!subnetBootstrap) { + fs.mkdirSync(path.dirname(subnetBootstrapFile), { recursive: true }); + fs.writeFileSync(subnetBootstrapFile, `${effectiveSubnetBootstrapHex}\n`); +} + +console.log(""); +console.log("==================== TRAC-PEER RUNNER ===================="); +console.log("MSB network bootstrap:", msb.bootstrap?.toString("hex") ?? null); +console.log("MSB channel:", b4a.toString(msb.channel, "utf8")); +console.log("MSB wallet address:", msb.wallet?.address ?? null); +console.log("----------------------------------------------------------"); +console.log("Peer store:", path.join(peerStoresDirectory, peerStoreNameRaw)); +console.log("Peer subnet bootstrap:", effectiveSubnetBootstrapHex); +console.log("Peer subnet channel:", subnetChannel); +console.log("Peer pubkey (hex):", peer.wallet.publicKey); +console.log("Peer MSB address:", peerMsbAddress); +console.log("Subnet writable:", !!peer.base?.writable); +if (peer.base && peer.base.writable === false) { + console.log("WARNING: Subnet is read-only for this keypair. If you're trying to create a new subnet, delete:"); + console.log(" ", subnetBootstrapFile); +} +console.log("----------------------------------------------------------"); +console.log("Next steps:"); +console.log("- On your already-running MSB admin node:"); +console.log(" - Fund this Peer MSB address (this creates the MSB node entry + covers fee checks):"); +console.log(" ", peerMsbAddress); +console.log("- In this peer console:"); +console.log(" - /deploy_subnet"); +console.log(' - /tx --command "ping hello"'); +console.log(' - /get --key "txl"'); +console.log("=========================================================="); +console.log(""); + +await startInteractiveCli(peer); + +process.on("SIGINT", async () => { + if (rpcServer) await new Promise((resolve) => rpcServer.close(resolve)); + await Promise.allSettled([peer.close(), msb.close()]); + process.exit(130); +}); diff --git a/scripts/smoke-msb-sync.mjs b/scripts/smoke-msb-sync.mjs new file mode 100644 index 0000000..6ba3853 --- /dev/null +++ b/scripts/smoke-msb-sync.mjs @@ -0,0 +1,113 @@ +import b4a from "b4a"; +import PeerWallet from "trac-wallet"; + +import { Peer, Wallet, Protocol, Contract } from "../src/index.js"; +import { deploySubnet } from "../src/functions.js"; + +import { + initTemporaryDirectory, + removeTemporaryDirectory, + setupMsbAdmin, + setupMsbWriter, + setupMsbPeer, + tryToSyncWriters, + randomBytes, + initDirectoryStructure, + fundPeer, +} from "trac-msb/tests/helpers/setupApplyTests.js"; +import { + testKeyPair1, + testKeyPair2, + testKeyPair3, + testKeyPair4, +} from "trac-msb/tests/fixtures/apply.fixtures.js"; +import { getDeploymentCommand } from "trac-msb/src/utils/cliCommands.js"; +import { $TNK } from "trac-msb/src/core/state/utils/balance.js"; + +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); + +let tmp = null; +let admin = null; +let writer = null; +let reader = null; +let peer = null; + +async function cleanupAndExit(code) { + await Promise.allSettled([ + peer?.close?.(), + reader?.msb?.close?.(), + writer?.msb?.close?.(), + admin?.msb?.close?.(), + ]); + if (tmp) await removeTemporaryDirectory(tmp).catch(() => {}); + process.exit(code); +} + +process.on("SIGINT", () => cleanupAndExit(130)); + +try { + tmp = await initTemporaryDirectory(); + + const opts = { + bootstrap: randomBytes(32).toString("hex"), + channel: randomBytes(32).toString("hex"), + enable_role_requester: false, + enable_wallet: true, + enable_validator_observer: true, + enable_interactive_mode: false, + disable_rate_limit: true, + enable_tx_apply_logs: false, + enable_error_apply_logs: false, + stores_directory: `${tmp}/stores/`, + }; + + admin = await setupMsbAdmin(testKeyPair1, tmp, opts); + writer = await setupMsbWriter(admin, "writer", testKeyPair2, tmp, admin.options); + reader = await setupMsbPeer("reader", testKeyPair3, tmp, admin.options); + await tryToSyncWriters(admin, writer, reader); + + // Fund a non-validator account that will be used as the subnet deployer. + const deployerWallet = new PeerWallet(testKeyPair4); + await deployerWallet.ready; + await fundPeer(admin, { wallet: deployerWallet }, $TNK(10n)); + await sleep(250); + await tryToSyncWriters(admin, writer, reader); + + // Create a Peer keypair file for the same deployer account. + const peerDir = await initDirectoryStructure("subnetpeer", testKeyPair4, tmp); + + peer = new Peer({ + stores_directory: peerDir.storesDirectory, + store_name: peerDir.storeName, + msb: reader.msb, + wallet: new Wallet(), + protocol: Protocol, + contract: Contract, + bootstrap: randomBytes(32), + channel: "subnet-peer-test", + enable_interactive_mode: false, + replicate: false, + }); + + await peer.ready(); + + const subnetBootstrapHex = b4a.isBuffer(peer.bootstrap) + ? peer.bootstrap.toString("hex") + : String(peer.bootstrap); + + console.log("Peer ready."); + console.log("Subnet bootstrap:", subnetBootstrapHex); + + const payload = await deploySubnet("/deploy_subnet", peer); + console.log("deploySubnet tx:", payload?.bdo?.tx ?? null); + + await sleep(2000); + await reader.msb.state.base.view.update(); + + await getDeploymentCommand(reader.msb.state, subnetBootstrapHex); + + await cleanupAndExit(0); +} catch (e) { + console.error(e); + await cleanupAndExit(1); +} diff --git a/src/api.js b/src/api.js index e05b49f..224fce1 100644 --- a/src/api.js +++ b/src/api.js @@ -22,7 +22,7 @@ export class ProtocolApi{ * @returns {null|string} */ getPeerValidatorAddress(){ - return this.peer.msb.getNetwork().validator; + return null; } /** @@ -38,7 +38,7 @@ export class ProtocolApi{ * @returns {null|string} */ getPeerMsbBootstrap(){ - return this.peer.msb.bootstrap; + return this.peer.msbClient.bootstrapHex; } /** @@ -153,10 +153,17 @@ export class ProtocolApi{ * @returns {Promise<*>} */ async generateTx(address, command_hash, nonce) { - if(this.getPeerValidatorAddress() === null) throw new Error('Peer not connected to a validator.'); - return await this.peer.protocol_instance.generateTx(this.getPeerBootstrap(), - this.getPeerMsbBootstrap(), this.getPeerValidatorAddress(), this.getPeerWriterKey(), - address, command_hash, nonce); + const txvHex = await this.peer.msbClient.getTxvHex(); + const subnetBootstrapHex = (b4a.isBuffer(this.getPeerBootstrap()) ? this.getPeerBootstrap().toString('hex') : (''+this.getPeerBootstrap())).toLowerCase(); + return await this.peer.protocol_instance.generateTx( + this.peer.msbClient.networkId, + txvHex, + this.getPeerWriterKey(), + command_hash, + subnetBootstrapHex, + this.getPeerMsbBootstrap(), + nonce + ); } /** @@ -183,7 +190,7 @@ export class ProtocolApi{ * * let address = your_ed25519_wallet.getAccountAddress() * let nonce = api.generateNonce() - * let tx_hash = api.generateTx(address, a_sha256_function(JSON.stringify(api.prepareTxCommand(command))), nonce) + * let tx_hash = api.generateTx(address, a_blake3_function(JSON.stringify(api.prepareTxCommand(command))), nonce) * let signature = your_ed25519_wallet.sign(tx + nonce) * // simulating * let sim_result = api.tx(tx_hash, api.prepareTxCommand(command), address, signature, nonce, true) @@ -202,7 +209,6 @@ export class ProtocolApi{ async tx(tx, prepared_command, address, signature, nonce, sim = false ){ if(true !== this.api_tx_exposed) throw new Error('Transactions not exposed in API.'); if(this.peer.base.writable === false) throw new Error('Peer is not writable.'); - if(this.getPeerValidatorAddress() === null) throw new Error('Peer not connected to a validator.'); if(typeof prepared_command !== 'object') throw new Error('prepared_command must be an object.'); if(typeof prepared_command.type !== 'string') throw new Error('prepared_command.type must exist and be a string.'); if(prepared_command.value === undefined) throw new Error('prepared_command.value is missing.'); @@ -213,16 +219,18 @@ export class ProtocolApi{ if(b4a.toString(b4a.from(address, 'hex'), 'hex') !== address) throw new Error('Invalid address.'); if(b4a.toString(b4a.from(signature, 'hex'), 'hex') !== signature) throw new Error('Invalid signature.'); if(b4a.toString(b4a.from(nonce, 'hex'), 'hex') !== nonce) throw new Error('Invalid nonce.'); - const verified = this.peer.wallet.verify(signature, tx + nonce, address); + const verified = this.peer.wallet.verify(signature, b4a.from(tx, 'hex'), address); if(false === verified) throw new Error('Invalid signature.'); - const content_hash = await this.peer.createHash('sha256', this.peer.protocol_instance.safeJsonStringify(prepared_command)); + const content_hash = await this.peer.createHash('blake3', this.peer.protocol_instance.safeJsonStringify(prepared_command)); let _tx = await this.generateTx(address, content_hash, nonce); if(tx !== _tx) throw new Error('Invalid TX.'); const surrogate = { tx : _tx, nonce : ''+nonce, signature : ''+signature, address : ''+address }; let res = false; try{ - const subject = { command : prepared_command.value, validator : this.getPeerValidatorAddress() }; - res = await this.peer.protocol_instance.tx(subject, sim === true, surrogate); + res = await this.peer.protocol_instance.broadcastTransaction({ + type: prepared_command.type, + value: prepared_command.value + }, sim === true, surrogate); } catch(e){ console.log(e) } if(res !== false) { const err = this.peer.protocol_instance.getError(res); @@ -546,4 +554,4 @@ export class ProtocolApi{ } } -export default ProtocolApi; \ No newline at end of file +export default ProtocolApi; diff --git a/src/check.js b/src/check.js index 05d2d58..868dfe4 100644 --- a/src/check.js +++ b/src/check.js @@ -15,10 +15,34 @@ class Check { customFunctions : { bitcoinAddress : (value, errors)=>{ let result = false + let result2 = false + let result3 = false + let result4 = false + let result5 = false try{ result = WAValidator.validate(value, 'Bitcoin', { networkType : 'both' }); + if(false === result){ + result2 = WAValidator.validate(value, 'Solana', { networkType : 'both' }); + if(false === result2){ + result3 = WAValidator.validate(value, 'DogeCoin', { networkType : 'both' }); + if(false === result3){ + result4 = WAValidator.validate(value, 'BinanceSmartChain', { networkType : 'both' }); + if(false === result4){ + // check for Trac public key hex + let buf = null + try{ + buf = b4a.from(value, 'hex') + result5 = value === b4a.toString(buf, 'hex') + if(result5){ + result5 = value.length === 64 + } + } catch (e) {} + } + } + } + } } catch (e) {} - return result; + return result || result2 || result3 || result4 || result5; }, hexCheck : (value, errors) => { let buf = null @@ -85,6 +109,29 @@ class Check { this._mod = this.compileMod(); this._whitelist_status = this.compileWhitelistStatus(); this._enable_whitelist = this.compileEnableWhitelist(); + this._enable_transactions = this.compileEnableTransactions(); + } + + compileEnableTransactions (){ + const schema = { + nonce: { type : "string", min : 1, max : 256 }, + hash: { type : "is_hex" }, + value : { + $$type: "object", + dispatch : { + $$type : "object", + enabled : { type : "boolean" }, + type : { type : "string", min : 1, max : 256 }, + address : { type : "is_hex" } + } + } + }; + return this.validator.compile(schema); + } + + enableTransactions(op){ + const res = this._enable_transactions(op); + return res === true; } compileEnableWhitelist (){ @@ -515,4 +562,4 @@ class Check { } } -export default Check; \ No newline at end of file +export default Check; diff --git a/src/cli.js b/src/cli.js new file mode 100644 index 0000000..989150f --- /dev/null +++ b/src/cli.js @@ -0,0 +1,274 @@ +/** @typedef {import('pear-interface')} */ /* global Pear */ +import readline from 'readline'; +import tty from 'tty'; + +import { + addWriter, + addIndexer, + addAdmin, + setAutoAddWriters, + setChatStatus, + setMod, + deleteMessage, + enableWhitelist, + postMessage, + setNick, + muteStatus, + setWhitelistStatus, + updateAdmin, + tx, + pinMessage, + joinValidator, + removeWriter, + removeIndexer, + unpinMessage, + deploySubnet, + enableTransactions +} from "./functions.js"; + + + +// printHelp(){ +// console.log('Node started. Available commands:'); +// console.log(' '); +// console.log('- Setup Commands:'); +// console.log('- /add_admin | Works only once and only on bootstrap node! Enter a wallet address to assign admin rights: \'/add_admin --address "
"\'.'); +// console.log('- /update_admin | Existing admins may transfer admin ownership. Enter "null" as address to waive admin rights for this peer entirely: \'/update_admin --address "
"\'.'); +// console.log('- /add_indexer | Only admin. Enter a peer writer key to get included as indexer for this network: \'/add_indexer --key ""\'.'); +// console.log('- /add_writer | Only admin. Enter a peer writer key to get included as writer for this network: \'/add_writer --key ""\'.'); +// console.log('- /remove_writer | Only admin. Enter a peer writer key to get removed as writer or indexer for this network: \'/remove_writer --key ""\'.'); +// console.log('- /set_auto_add_writers | Only admin. Allow any peer to join as writer automatically: \'/set_auto_add_writers --enabled 1\''); +// console.log('- /enable_transactions | Only admin. Disable/enable transactions. Enabled by default: \'/enable_transactions --enabled 0\''); +// console.log(' '); +// console.log('- Chat Commands:'); +// console.log('- /set_chat_status | Only admin. Enable/disable the built-in chat system: \'/set_chat_status --enabled 1\'. The chat system is disabled by default.'); +// console.log('- /post | Post a message: \'/post --message "Hello"\'. Chat must be enabled. Optionally use \'--reply_to \' to respond to a desired message.'); +// console.log('- /set_nick | Change your nickname like this \'/set_nick --nick "Peter"\'. Chat must be enabled. Can be edited by admin and mods using the optional --user
flag.'); +// console.log('- /mute_status | Only admin and mods. Mute or unmute a user by their address: \'/mute_status --user "
" --muted 1\'.'); +// console.log('- /set_mod | Only admin. Set a user as mod: \'/set_mod --user "
" --mod 1\'.'); +// console.log('- /delete_message | Delete a message: \'/delete_message --id 1\'. Chat must be enabled.'); +// console.log('- /pin_message | Set the pin status of a message: \'/pin_message --id 1 --pin 1\'. Chat must be enabled.'); +// console.log('- /unpin_message | Unpin a message by its pin id: \'/unpin_message --pin_id 1\'. Chat must be enabled.'); +// console.log('- /enable_whitelist | Only admin. Enable/disable chat whitelists: \'/enable_whitelist --enabled 1\'.'); +// console.log('- /set_whitelist_status | Only admin. Add/remove users to/from the chat whitelist: \'/set_whitelist_status --user "
" --status 1\'.'); +// console.log(' '); +// console.log('- System Commands:'); +// console.log('- /tx | Perform a contract transaction. The command flag contains contract commands (format is protocol dependent): \'/tx --command ""\'. To simulate a tx, additionally use \'--sim 1\'.'); +// console.log('- /join_validator | Try to connect to a specific validator with its MSB address: \'/join_validator --address "
"\'.'); +// console.log('- /stats | check system properties such as writer key, DAG, etc.'); +// console.log('- /get_keys | prints your public and private keys. Be careful and never share your private key!'); +// console.log('- /exit | Exit the program'); +// console.log('- /help | This help text'); + +// this.protocol_instance.printOptions(); +// } + +// async interactiveMode() { +// if(this.readline_instance === null || (global.Pear !== undefined && global.Pear.config.options.type === 'desktop')) return; + +// const rl = this.readline_instance; + +// this.printHelp(); + +// rl.on('line', async (input) => { +// switch (input) { +// case '/stats': +// await this.verifyDag(); +// break; +// case '/help': +// await this.printHelp(); +// break; +// case '/exit': +// console.log('Exiting...'); +// rl.close(); +// await this.close(); +// typeof process !== "undefined" ? process.exit(0) : Pear.exit(0); +// case '/get_keys': +// console.log("Public Key: ", this.wallet.publicKey); +// console.log("Secret Key: ", this.wallet.secretKey); +// break; +// default: +// try { +// if (input.startsWith('/tx')) { +// await tx(input, this); +// } else if (input.startsWith('/add_indexer') || input.startsWith('/add_writer')) { +// await addWriter(input, this); +// } else if (input.startsWith('/remove_writer')) { +// await removeWriter(input, this); +// } else if (input.startsWith('/add_admin')) { +// await addAdmin(input, this); +// } else if (input.startsWith('/update_admin')) { +// await updateAdmin(input, this); +// } else if (input.startsWith('/set_auto_add_writers')) { +// await setAutoAddWriters(input, this); +// } else if (input.startsWith('/enable_transactions')) { +// await enableTransactions(input, this); +// } else if (input.startsWith('/set_chat_status')) { +// await setChatStatus(input, this); +// } else if (input.startsWith('/post')) { +// await postMessage(input, this); +// } else if (input.startsWith('/set_nick')) { +// await setNick(input, this); +// } else if (input.startsWith('/mute_status')) { +// await muteStatus(input, this); +// } else if (input.startsWith('/pin_message')) { +// await pinMessage(input, this); +// } else if (input.startsWith('/unpin_message')) { +// await unpinMessage(input, this); +// } else if (input.startsWith('/set_mod')) { +// await setMod(input, this); +// } else if (input.startsWith('/delete_message')) { +// await deleteMessage(input, this); +// } else if (input.startsWith('/enable_whitelist')) { +// await enableWhitelist(input, this); +// } else if (input.startsWith('/set_whitelist_status')) { +// await setWhitelistStatus(input, this); +// } else if (input.startsWith('/join_validator')) { +// await joinValidator(input, this); +// } else { +// await this.protocol_instance.customCommand(input); +// } +// } catch(e) { +// console.log('Command failed:', e.message); +// } +// } +// rl.prompt(); +// }); + +// rl.prompt(); +// } + + +export function printHelp(peer) { + console.log('Node started. Available commands:'); + console.log(' '); + console.log('- Setup Commands:'); + console.log('- /add_admin | Works only once and only on the bootstrap node. Enter a peer public key (hex) to assign admin rights: \'/add_admin --address ""\'.'); + console.log('- /update_admin | Existing admins may transfer admin ownership. Enter "null" as address to waive admin rights for this peer entirely: \'/update_admin --address "
"\'.'); + console.log('- /add_indexer | Only admin. Enter a peer writer key to get included as indexer for this network: \'/add_indexer --key ""\'.'); + console.log('- /add_writer | Only admin. Enter a peer writer key to get included as writer for this network: \'/add_writer --key ""\'.'); + console.log('- /remove_writer | Only admin. Enter a peer writer key to get removed as writer or indexer for this network: \'/remove_writer --key ""\'.'); + console.log('- /remove_indexer | Only admin. Alias of /remove_writer (removes indexer as well): \'/remove_indexer --key ""\'.'); + console.log('- /set_auto_add_writers | Only admin. Allow any peer to join as writer automatically: \'/set_auto_add_writers --enabled 1\''); + console.log('- /enable_transactions | Enable transactions.'); + console.log(' '); + console.log('- Chat Commands:'); + console.log('- /set_chat_status | Only admin. Enable/disable the built-in chat system: \'/set_chat_status --enabled 1\'. The chat system is disabled by default.'); + console.log('- /post | Post a message: \'/post --message "Hello"\'. Chat must be enabled. Optionally use \'--reply_to \' to respond to a desired message.'); + console.log('- /set_nick | Change your nickname like this \'/set_nick --nick "Peter"\'. Chat must be enabled. Can be edited by admin and mods using the optional --user
flag.'); + console.log('- /mute_status | Only admin and mods. Mute or unmute a user by their address: \'/mute_status --user "
" --muted 1\'.'); + console.log('- /set_mod | Only admin. Set a user as mod: \'/set_mod --user "
" --mod 1\'.'); + console.log('- /delete_message | Delete a message: \'/delete_message --id 1\'. Chat must be enabled.'); + console.log('- /pin_message | Set the pin status of a message: \'/pin_message --id 1 --pin 1\'. Chat must be enabled.'); + console.log('- /unpin_message | Unpin a message by its pin id: \'/unpin_message --pin_id 1\'. Chat must be enabled.'); + console.log('- /enable_whitelist | Only admin. Enable/disable chat whitelists: \'/enable_whitelist --enabled 1\'.'); + console.log('- /set_whitelist_status | Only admin. Add/remove users to/from the chat whitelist: \'/set_whitelist_status --user "
" --status 1\'.'); + console.log(' '); + console.log('- System Commands:'); + console.log('- /tx | Perform a contract transaction. The command flag contains contract commands (format is protocol dependent): \'/tx --command ""\'. To simulate a tx, additionally use \'--sim 1\'.'); + console.log('- /join_validator | Try to connect to a specific validator with its MSB address: \'/join_validator --address "
"\'.'); + console.log('- /deploy_subnet | Register this subnet in the MSB (required before TX settlement): \'/deploy_subnet\'.'); + console.log('- /stats | check system properties such as writer key, DAG, etc.'); + console.log('- /get_keys | prints your public and private keys. Be careful and never share your private key!'); + console.log('- /exit | Exit the program'); + console.log('- /help | This help text'); + + peer.protocol_instance.printOptions(); +} + +export async function startInteractiveCli(peer, { readlineInstance = null } = {}) { + if (!peer) return; + if (global.Pear !== undefined && global.Pear.config?.options?.type === 'desktop') return; + + let rl = readlineInstance; + if (!rl) { + try { + rl = readline.createInterface({ + input: new tty.ReadStream(0), + output: new tty.WriteStream(1), + }); + } catch (_e) { + try { + rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + } catch (_e2) { + return; + } + } + } + + printHelp(peer); + + rl.on('line', async (input) => { + switch (input) { + case '/stats': + await peer.verifyDag(); + break; + case '/help': + await printHelp(peer); + break; + case '/exit': + console.log('Exiting...'); + rl.close(); + await peer.close(); + typeof process !== "undefined" ? process.exit(0) : Pear.exit(0); + case '/get_keys': + console.log("Public Key: ", peer.wallet.publicKey); + console.log("Secret Key: ", peer.wallet.secretKey); + break; + default: + try { + if (input.startsWith('/tx')) { + await tx(input, peer); + } else if (input.startsWith('/add_indexer')) { + await addIndexer(input, peer); + } else if (input.startsWith('/add_writer')) { + await addWriter(input, peer); + } else if (input.startsWith('/remove_writer')) { + await removeWriter(input, peer); + } else if (input.startsWith('/remove_indexer')) { + await removeIndexer(input, peer); + } else if (input.startsWith('/add_admin')) { + await addAdmin(input, peer); + } else if (input.startsWith('/update_admin')) { + await updateAdmin(input, peer); + } else if (input.startsWith('/enable_transactions')) { + await enableTransactions(input, peer); + } else if (input.startsWith('/set_auto_add_writers')) { + await setAutoAddWriters(input, peer); + } else if (input.startsWith('/set_chat_status')) { + await setChatStatus(input, peer); + } else if (input.startsWith('/post')) { + await postMessage(input, peer); + } else if (input.startsWith('/set_nick')) { + await setNick(input, peer); + } else if (input.startsWith('/mute_status')) { + await muteStatus(input, peer); + } else if (input.startsWith('/pin_message')) { + await pinMessage(input, peer); + } else if (input.startsWith('/unpin_message')) { + await unpinMessage(input, peer); + } else if (input.startsWith('/set_mod')) { + await setMod(input, peer); + } else if (input.startsWith('/delete_message')) { + await deleteMessage(input, peer); + } else if (input.startsWith('/enable_whitelist')) { + await enableWhitelist(input, peer); + } else if (input.startsWith('/set_whitelist_status')) { + await setWhitelistStatus(input, peer); + } else if (input.startsWith('/join_validator')) { + await joinValidator(input, peer); + } else if (input.startsWith('/deploy_subnet')) { + await deploySubnet(input, peer); + } else { + await peer.protocol_instance.customCommand(input); + } + } catch (e) { + console.log('Command failed:', e.message); + } + } + rl.prompt(); + }); + + rl.prompt(); + return rl; +} diff --git a/src/contract.js b/src/contract.js index 2453c5e..6e163c2 100644 --- a/src/contract.js +++ b/src/contract.js @@ -38,11 +38,8 @@ class Contract { type : { type : "string", min : 1, max : 256 }, value : { type : "any", nullable : true } }, - value : { - $$type : "object", - ipk : { type : "is_hex" }, - wp : { type : "is_hex" } - } + ipk : { type : "is_hex" }, + wp : { type : "is_hex" } } }); @@ -76,8 +73,8 @@ class Contract { if(op.type !== 'feature' && op.type !== 'msg'){ if(false === this.tx_schema(op)) return false; - this.address = op.value.value.ipk; - this.validator_address = op.value.value.wp; + this.address = op.value.ipk; + this.validator_address = op.value.wp; } else { if(true !== this.address_schema(op)) return false; if(op.type === 'feature' && true !== this.textkey_schema(op)) return false; @@ -99,7 +96,7 @@ class Contract { await this.features[this.op.type](); } } else if(this.isMessage()) { - if(this.message_handler !== undefined){ + if(typeof this.message_handler === 'function'){ try { _return = await this.message_handler(); } catch(e) { @@ -185,7 +182,7 @@ class Contract { key.startsWith('umsg/') || key.startsWith('umsg/') || key.startsWith('msgl/') || key === 'admin' || key === 'auto_add_writers' || key.startsWith('nick/') || key.startsWith('mod/') || key === 'chat_status' || key.startsWith('mtd/') || key === 'delml' || key === 'wlst' || key === 'txl' || key.startsWith('txi/') || key.startsWith('wl/') || key === 'pnl' || key.startsWith('pni/') || - key.startsWith('utxl/') || key.startsWith('utxi/') || key.startsWith('bnd/'); + key.startsWith('utxl/') || key.startsWith('utxi/') || key.startsWith('bnd/') || key === 'txen'; } async del(key){ @@ -233,4 +230,4 @@ class RethrownError extends Error { } } -export default Contract; \ No newline at end of file +export default Contract; diff --git a/src/dev/pokemonContract.js b/src/dev/pokemonContract.js new file mode 100644 index 0000000..9596493 --- /dev/null +++ b/src/dev/pokemonContract.js @@ -0,0 +1,193 @@ +import BaseContract from "../contract.js"; + +export const GEN1_POKEMON = [ + { id: 1, name: "Bulbasaur" }, + { id: 2, name: "Ivysaur" }, + { id: 3, name: "Venusaur" }, + { id: 4, name: "Charmander" }, + { id: 5, name: "Charmeleon" }, + { id: 6, name: "Charizard" }, + { id: 7, name: "Squirtle" }, + { id: 8, name: "Wartortle" }, + { id: 9, name: "Blastoise" }, + { id: 10, name: "Caterpie" }, + { id: 11, name: "Metapod" }, + { id: 12, name: "Butterfree" }, + { id: 13, name: "Weedle" }, + { id: 14, name: "Kakuna" }, + { id: 15, name: "Beedrill" }, + { id: 16, name: "Pidgey" }, + { id: 17, name: "Pidgeotto" }, + { id: 18, name: "Pidgeot" }, + { id: 19, name: "Rattata" }, + { id: 20, name: "Raticate" }, + { id: 21, name: "Spearow" }, + { id: 22, name: "Fearow" }, + { id: 23, name: "Ekans" }, + { id: 24, name: "Arbok" }, + { id: 25, name: "Pikachu" }, + { id: 26, name: "Raichu" }, + { id: 27, name: "Sandshrew" }, + { id: 28, name: "Sandslash" }, + { id: 29, name: "Nidoran♀" }, + { id: 30, name: "Nidorina" }, + { id: 31, name: "Nidoqueen" }, + { id: 32, name: "Nidoran♂" }, + { id: 33, name: "Nidorino" }, + { id: 34, name: "Nidoking" }, + { id: 35, name: "Clefairy" }, + { id: 36, name: "Clefable" }, + { id: 37, name: "Vulpix" }, + { id: 38, name: "Ninetales" }, + { id: 39, name: "Jigglypuff" }, + { id: 40, name: "Wigglytuff" }, + { id: 41, name: "Zubat" }, + { id: 42, name: "Golbat" }, + { id: 43, name: "Oddish" }, + { id: 44, name: "Gloom" }, + { id: 45, name: "Vileplume" }, + { id: 46, name: "Paras" }, + { id: 47, name: "Parasect" }, + { id: 48, name: "Venonat" }, + { id: 49, name: "Venomoth" }, + { id: 50, name: "Diglett" }, + { id: 51, name: "Dugtrio" }, + { id: 52, name: "Meowth" }, + { id: 53, name: "Persian" }, + { id: 54, name: "Psyduck" }, + { id: 55, name: "Golduck" }, + { id: 56, name: "Mankey" }, + { id: 57, name: "Primeape" }, + { id: 58, name: "Growlithe" }, + { id: 59, name: "Arcanine" }, + { id: 60, name: "Poliwag" }, + { id: 61, name: "Poliwhirl" }, + { id: 62, name: "Poliwrath" }, + { id: 63, name: "Abra" }, + { id: 64, name: "Kadabra" }, + { id: 65, name: "Alakazam" }, + { id: 66, name: "Machop" }, + { id: 67, name: "Machoke" }, + { id: 68, name: "Machamp" }, + { id: 69, name: "Bellsprout" }, + { id: 70, name: "Weepinbell" }, + { id: 71, name: "Victreebel" }, + { id: 72, name: "Tentacool" }, + { id: 73, name: "Tentacruel" }, + { id: 74, name: "Geodude" }, + { id: 75, name: "Graveler" }, + { id: 76, name: "Golem" }, + { id: 77, name: "Ponyta" }, + { id: 78, name: "Rapidash" }, + { id: 79, name: "Slowpoke" }, + { id: 80, name: "Slowbro" }, + { id: 81, name: "Magnemite" }, + { id: 82, name: "Magneton" }, + { id: 83, name: "Farfetch'd" }, + { id: 84, name: "Doduo" }, + { id: 85, name: "Dodrio" }, + { id: 86, name: "Seel" }, + { id: 87, name: "Dewgong" }, + { id: 88, name: "Grimer" }, + { id: 89, name: "Muk" }, + { id: 90, name: "Shellder" }, + { id: 91, name: "Cloyster" }, + { id: 92, name: "Gastly" }, + { id: 93, name: "Haunter" }, + { id: 94, name: "Gengar" }, + { id: 95, name: "Onix" }, + { id: 96, name: "Drowzee" }, + { id: 97, name: "Hypno" }, + { id: 98, name: "Krabby" }, + { id: 99, name: "Kingler" }, + { id: 100, name: "Voltorb" }, + { id: 101, name: "Electrode" }, + { id: 102, name: "Exeggcute" }, + { id: 103, name: "Exeggutor" }, + { id: 104, name: "Cubone" }, + { id: 105, name: "Marowak" }, + { id: 106, name: "Hitmonlee" }, + { id: 107, name: "Hitmonchan" }, + { id: 108, name: "Lickitung" }, + { id: 109, name: "Koffing" }, + { id: 110, name: "Weezing" }, + { id: 111, name: "Rhyhorn" }, + { id: 112, name: "Rhydon" }, + { id: 113, name: "Chansey" }, + { id: 114, name: "Tangela" }, + { id: 115, name: "Kangaskhan" }, + { id: 116, name: "Horsea" }, + { id: 117, name: "Seadra" }, + { id: 118, name: "Goldeen" }, + { id: 119, name: "Seaking" }, + { id: 120, name: "Staryu" }, + { id: 121, name: "Starmie" }, + { id: 122, name: "Mr. Mime" }, + { id: 123, name: "Scyther" }, + { id: 124, name: "Jynx" }, + { id: 125, name: "Electabuzz" }, + { id: 126, name: "Magmar" }, + { id: 127, name: "Pinsir" }, + { id: 128, name: "Tauros" }, + { id: 129, name: "Magikarp" }, + { id: 130, name: "Gyarados" }, + { id: 131, name: "Lapras" }, + { id: 132, name: "Ditto" }, + { id: 133, name: "Eevee" }, + { id: 134, name: "Vaporeon" }, + { id: 135, name: "Jolteon" }, + { id: 136, name: "Flareon" }, + { id: 137, name: "Porygon" }, + { id: 138, name: "Omanyte" }, + { id: 139, name: "Omastar" }, + { id: 140, name: "Kabuto" }, + { id: 141, name: "Kabutops" }, + { id: 142, name: "Aerodactyl" }, + { id: 143, name: "Snorlax" }, + { id: 144, name: "Articuno" }, + { id: 145, name: "Zapdos" }, + { id: 146, name: "Moltres" }, + { id: 147, name: "Dratini" }, + { id: 148, name: "Dragonair" }, + { id: 149, name: "Dragonite" }, + { id: 150, name: "Mewtwo" }, + { id: 151, name: "Mew" }, +]; + +class PokemonContract extends BaseContract { + constructor(protocol) { + super(protocol); + this.addFunction("catch"); + } + + async catch() { + const { pokemons } = await this.#ensurePokedex() + const parsed = JSON.parse(pokemons) + + const pick = GEN1_POKEMON[Math.floor(Math.random() * GEN1_POKEMON.length)]; + + const exists = parsed.some(p => p.id === pick.id); + if (!exists) parsed.push({ id: pick.id, name: pick.name, tx: this.tx }); + + await this.#setPokedex(parsed) + } + + async #ensurePokedex() { + const pokedex = await this.get(this.#pokedexKey()) + if (pokedex === null) { + await this.#setPokedex([]) + } + + return await this.get(this.#pokedexKey()) + } + + #pokedexKey() { + return `app/pokedex/${this.address}` + } + + async #setPokedex(pokemons) { + await this.put(this.#pokedexKey(), { pokemons: JSON.stringify(pokemons) }); + } +} + +export default PokemonContract; diff --git a/src/dev/pokemonProtocol.js b/src/dev/pokemonProtocol.js new file mode 100644 index 0000000..cff3dc4 --- /dev/null +++ b/src/dev/pokemonProtocol.js @@ -0,0 +1,81 @@ +import BaseProtocol from "../protocol.js"; +import { bufferToBigInt, bigIntToDecimalString } from "trac-msb/src/utils/amountSerialization.js"; + +class PokemonProtocol extends BaseProtocol { + mapTxCommand(command) { + if (typeof command !== "string" || command.trim() === "") return null; + const raw = command.trim(); + if (raw.startsWith("{")) { + try { + const parsed = JSON.parse(raw); + if (parsed && typeof parsed.type === "string" && parsed.value !== undefined) { + return { type: parsed.type, value: parsed.value }; + } + } catch (_e) {} + } + if (raw === "ping" || raw.startsWith("ping ")) { + const msg = raw === "ping" ? "pong" : raw.slice(5); + return { type: "ping", value: { msg } }; + } + if (raw.startsWith("set ")) { + const parts = raw.split(" ").filter(Boolean); + if (parts.length >= 3) { + const key = parts[1]; + const value = parts.slice(2).join(" "); + return { type: "set", value: { key, value } }; + } + } + return { type: "ping", value: { msg: raw } }; + } + + async customCommand(input) { + if (typeof input !== "string") return; + if (input.startsWith("/get")) { + const m = input.match(/(?:^|\s)--key(?:=|\s+)(.+)$/); + const raw = m ? m[1].trim() : null; + if (!raw) { + console.log('Usage: /get --key ""'); + return; + } + const key = raw.replace(/^\"(.*)\"$/, "$1").replace(/^'(.*)'$/, "$1"); + const v = await this.getSigned(key); + console.log(v); + return; + } + if (input.startsWith("/msb")) { + const txv = await this.peer.msbClient.getTxvHex(); + const peerMsbAddress = this.peer.msbClient.pubKeyHexToAddress(this.peer.wallet.publicKey); + const entry = peerMsbAddress ? await this.peer.msb.state.getNodeEntryUnsigned(peerMsbAddress) : null; + const balance = entry?.balance ? bigIntToDecimalString(bufferToBigInt(entry.balance)) : null; + const fee = bigIntToDecimalString(bufferToBigInt(this.peer.msb.state.getFee())); + const validators = this.peer.msb.network?.validatorConnectionManager?.connectionCount?.() ?? 0; + console.log({ + networkId: this.peer.msbClient.networkId, + msbBootstrap: this.peer.msbClient.bootstrapHex, + txv, + msbSignedLength: this.peer.msb.state.getSignedLength(), + msbUnsignedLength: this.peer.msb.state.getUnsignedLength(), + connectedValidators: validators, + peerMsbAddress, + peerMsbBalance: balance, + msbFee: fee, + }); + return; + } + } + + async printOptions() { + console.log(""); + console.log("- Dev commands:"); + console.log('- /msb | prints MSB txv + lengths (local MSB node view).'); + console.log('- /get --key "" | reads signed subnet state key.'); + console.log(""); + console.log("- Dev TX examples:"); + console.log('- /tx --command "ping hello"'); + console.log('- /tx --command "set foo bar"'); + console.log('- /tx --command "{\\"type\\":\\"ping\\",\\"value\\":{\\"msg\\":\\"hi\\"}}"'); + } +} + +export default PokemonProtocol; + diff --git a/src/feature.js b/src/feature.js index 4a28345..c54cba0 100644 --- a/src/feature.js +++ b/src/feature.js @@ -9,6 +9,7 @@ class Feature { async getSigned(key){ const view_session = this.peer.base.view.checkout(this.peer.base.view.core.signedLength); const result = await view_session.get(key); + await view_session.close(); if(result === null) return null; return result.value; } diff --git a/src/functions.js b/src/functions.js index 084a00d..de66a21 100644 --- a/src/functions.js +++ b/src/functions.js @@ -1,61 +1,38 @@ import b4a from "b4a"; +import { createMessage } from 'trac-msb/src/utils/buffer.js'; +import { blake3Hash } from 'trac-msb/src/utils/crypto.js'; +import { bigIntToDecimalString, decimalStringToBigInt } from 'trac-msb/src/utils/amountSerialization.js'; +import { MSB_OPERATION_TYPE } from './msbClient.js'; +import { requireAdmin, requireAdminOrMod, requireBootstrapNodeForAdminSet } from "./permissions.js"; export function resolveNumberString(number, decimals){ - number = number + ''; - decimals = isNaN(decimals) ? 18 : parseInt(decimals); - let splitted = number.split("."); - if(splitted.length == 1 && decimals > 0){ - splitted[1] = ''; + const raw = (number ?? '').toString().trim(); + const safeDecimals = isNaN(decimals) ? 18 : parseInt(decimals); + const match = raw.match(/^(\d*)(?:\.(\d*))?$/); + if (!match) return '0'; + const integerPart = match[1] && match[1].length ? match[1] : '0'; + let fractionalPart = match[2] ?? ''; + if (safeDecimals <= 0) { + return decimalStringToBigInt(integerPart, 0).toString(); } - if(splitted.length > 1) { - let size = decimals - splitted[1].length; - for (let i = 0; i < size; i++) { - splitted[1] += "0"; - } - let new_splitted = ''; - for(let i = 0; i < splitted[1].length; i++) - { - if(i >= decimals) - { - break; - } - new_splitted += splitted[1][i]; - } - number = "" + (splitted[0] == '0' ? '' : splitted[0]) + new_splitted; - if(BigInt(number) == 0n || number === ''){ - number = "0"; - } - } - - try { - - while (number.charAt(0) === '0') { - number = number.substring(1); - } - - }catch(e){ - - number = '0'; - } - - return number === '' ? '0' : number; + if (fractionalPart.length > safeDecimals) fractionalPart = fractionalPart.slice(0, safeDecimals); + const normalized = `${integerPart}${fractionalPart.length ? '.' + fractionalPart : ''}`; + return decimalStringToBigInt(normalized, safeDecimals).toString(); } export function formatNumberString(string, decimals) { - string = string + ''; - decimals = isNaN(decimals) ? 18 : parseInt(decimals); - let pos = string.length - decimals; - - if(decimals == 0) { - // nothing - }else - if(pos > 0){ - string = string.substring(0, pos) + "." + string.substring(pos, string.length); - }else{ - string = '0.' + ( "0".repeat( decimals - string.length ) ) + string; + const raw = (string ?? '').toString().trim(); + const safeDecimals = isNaN(decimals) ? 18 : parseInt(decimals); + if (!raw) return '0'; + try { + // Accept either an already-decimal string or an integer-encoded string. + const asBigInt = raw.includes('.') + ? decimalStringToBigInt(raw, safeDecimals) + : BigInt(raw); + return bigIntToDecimalString(asBigInt, safeDecimals); + } catch (_e) { + return '0'; } - - return removeTrailingZeros(string); } export function removeTrailingZeros(value) { @@ -122,6 +99,7 @@ export function safeClone(obj){ } export async function setWhitelistStatus(input, peer){ + await requireAdmin(peer); const splitted = peer.protocol_instance.parseArgs(input) const value = ''+splitted.user; const status = parseInt(splitted.status) === 1; @@ -136,9 +114,23 @@ export async function setWhitelistStatus(input, peer){ await peer.base.append({type: 'setWhitelistStatus', value: signature, hash : hash, nonce: nonce }); } +export async function enableTransactions(input, peer){ + const splitted = peer.protocol_instance.parseArgs(input) + const value = parseInt(splitted.enabled) === 1; + const nonce = peer.protocol_instance.generateNonce(); + const signature = { dispatch : { + type : 'enableTransactions', + enabled: value, + address : peer.wallet.publicKey + }}; + const hash = peer.wallet.sign(JSON.stringify(signature) + nonce); + await peer.base.append({type: 'enableTransactions', value: signature, hash : hash, nonce: nonce }); +} + export async function enableWhitelist(input, peer){ + await requireAdmin(peer); const splitted = peer.protocol_instance.parseArgs(input) - const value = splitted.enabled === 1; + const value = parseInt(splitted.enabled) === 1; const nonce = peer.protocol_instance.generateNonce(); const signature = { dispatch : { type : 'enableWhitelist', @@ -150,6 +142,7 @@ export async function enableWhitelist(input, peer){ } export async function unpinMessage(input, peer){ + await requireAdminOrMod(peer); let address = null; const splitted = peer.protocol_instance.parseArgs(input) const value = parseInt(splitted.pin_id); @@ -164,6 +157,7 @@ export async function unpinMessage(input, peer){ } export async function pinMessage(input, peer){ + await requireAdminOrMod(peer); let address = null; const splitted = peer.protocol_instance.parseArgs(input) const value = parseInt(splitted.id); @@ -180,6 +174,7 @@ export async function pinMessage(input, peer){ } export async function deleteMessage(input, peer){ + await requireAdminOrMod(peer); let address = null; const splitted = peer.protocol_instance.parseArgs(input) const value = parseInt(splitted.id); @@ -194,6 +189,7 @@ export async function deleteMessage(input, peer){ } export async function updateAdmin(input, peer){ + await requireAdmin(peer); const splitted = peer.protocol_instance.parseArgs(input) const value = ''+splitted.address === 'null' ? null : ''+splitted.address; const nonce = peer.protocol_instance.generateNonce(); @@ -207,6 +203,7 @@ export async function updateAdmin(input, peer){ } export async function setMod(input, peer){ + await requireAdmin(peer); const splitted = peer.protocol_instance.parseArgs(input) const value = ''+splitted.user; const mod = parseInt(splitted.mod) === 1; @@ -222,6 +219,7 @@ export async function setMod(input, peer){ } export async function muteStatus(input, peer){ + await requireAdminOrMod(peer); const splitted = peer.protocol_instance.parseArgs(input) const value = ''+splitted.user; const muted = parseInt(splitted.muted) === 1; @@ -243,6 +241,9 @@ export async function setNick(input, peer){ if(splitted.user !== undefined){ user = ''+splitted.user; } + if (user !== null && user !== peer.wallet.publicKey) { + await requireAdminOrMod(peer); + } const nonce = peer.protocol_instance.generateNonce(); const signature = { dispatch : { type : 'setNick', @@ -257,6 +258,8 @@ export async function setNick(input, peer){ export async function postMessage(input, peer){ const splitted = peer.protocol_instance.parseArgs(input) if(typeof splitted.message === "boolean" || splitted.message === undefined) throw new Error('Empty message not allowed'); + const chat_status = await peer.base.view.get('chat_status'); + if (chat_status === null || chat_status.value !== 'on') throw new Error('Chat is disabled.'); const reply_to = splitted.reply_to !== undefined ? parseInt(splitted.reply_to) : null; const value = '' + splitted.message; const nonce = peer.protocol_instance.generateNonce(); @@ -275,6 +278,7 @@ export async function postMessage(input, peer){ } export async function setChatStatus(input, peer){ + await requireAdmin(peer); const splitted = peer.protocol_instance.parseArgs(input) const value = parseInt(splitted.enabled) === 1 ? 'on' : 'off'; const nonce = peer.protocol_instance.generateNonce(); @@ -288,6 +292,7 @@ export async function setChatStatus(input, peer){ } export async function setAutoAddWriters(input, peer){ + await requireAdmin(peer); const splitted = peer.protocol_instance.parseArgs(input) const value = parseInt(splitted.enabled) === 1 ? 'on' : 'off'; const nonce = peer.protocol_instance.generateNonce(); @@ -301,80 +306,125 @@ export async function setAutoAddWriters(input, peer){ } export async function addAdmin(input, peer){ + await requireBootstrapNodeForAdminSet(peer); const splitted = peer.protocol_instance.parseArgs(input) - const publicKey = ''+splitted.address; + const publicKey = (splitted.address != null ? String(splitted.address) : '').trim().toLowerCase(); + if (!/^[0-9a-f]{64}$/.test(publicKey)) { + throw new Error( + 'Invalid --address. Expected a 32-byte hex public key (64 hex chars). Example: /add_admin --address ""' + ); + } + await addAdminKey(publicKey, peer); +} + +export async function addAdminKey(publicKeyHex, peer){ + await requireBootstrapNodeForAdminSet(peer); + const publicKey = (publicKeyHex != null ? String(publicKeyHex) : '').trim().toLowerCase(); + if (!/^[0-9a-f]{64}$/.test(publicKey)) { + throw new Error('Invalid public key. Expected 32-byte hex (64 chars).'); + } + if(peer.base.writable === false){ + throw new Error('Peer is not writable.'); + } await peer.base.append({ type: 'addAdmin', key: publicKey }); } -export async function addWriter(input, peer){ - const splitted = input.split(' '); - const parsed = peer.protocol_instance.parseArgs(input) +async function appendAddWriter(peer, keyHex, isIndexer){ + await requireAdmin(peer); + const wk = (keyHex != null ? String(keyHex) : '').trim().toLowerCase(); + if (!/^[0-9a-f]{64}$/.test(wk)) throw new Error('Invalid --key. Expected 32-byte hex (64 chars).'); const nonce = peer.protocol_instance.generateNonce(); - if(splitted[0] === '/add_indexer'){ - const msg = { type: 'addIndexer', key: ''+parsed.key } - const signature = { - msg: msg - }; - const hash = peer.wallet.sign(JSON.stringify(msg) + nonce); - if(peer.base.writable){ - await peer.base.append({ op : 'append_writer', type: 'addIndexer', key: parsed.key, value: signature, hash: hash, nonce: nonce }); - } - } else if(splitted[0] === '/add_writer') { - const msg = { type: 'addWriter', key: ''+parsed.key } - const signature = { - msg: msg - }; - const hash = peer.wallet.sign(JSON.stringify(msg) + nonce); - if(peer.base.writable){ - await peer.base.append({ op : 'append_writer', type: 'addWriter', key: ''+parsed.key, value: signature, hash: hash, nonce : nonce }); - } + const msg = { type: isIndexer ? 'addIndexer' : 'addWriter', key: wk }; + const signature = { msg: msg }; + const hash = peer.wallet.sign(JSON.stringify(msg) + nonce); + if(peer.base.writable === false){ + throw new Error('Peer is not writable.'); } + await peer.base.append({ + op : 'append_writer', + type: isIndexer ? 'addIndexer' : 'addWriter', + key: wk, + value: signature, + hash: hash, + nonce: nonce + }); } +export async function addWriterKey(keyHex, peer){ + return appendAddWriter(peer, keyHex, false); +} -export async function removeWriter(input, peer){ - const splitted = input.split(' '); - const parsed = peer.protocol_instance.parseArgs(input) +export async function addIndexerKey(keyHex, peer){ + return appendAddWriter(peer, keyHex, true); +} + +export async function addWriter(input, peer){ + await requireAdmin(peer); + const parsed = peer.protocol_instance.parseArgs(input); + return addWriterKey(parsed.key, peer); +} + +export async function addIndexer(input, peer){ + await requireAdmin(peer); + const parsed = peer.protocol_instance.parseArgs(input); + return addIndexerKey(parsed.key, peer); +} + +async function appendRemoveWriter(peer, keyHex){ + await requireAdmin(peer); + const wk = (keyHex != null ? String(keyHex) : '').trim().toLowerCase(); + if (!/^[0-9a-f]{64}$/.test(wk)) throw new Error('Invalid --key. Expected 32-byte hex (64 chars).'); const nonce = peer.protocol_instance.generateNonce(); - if(splitted[0] === '/remove_writer') { - const msg = { type: 'removeWriter', key: ''+parsed.key } - const signature = { - msg: msg - }; - const hash = peer.wallet.sign(JSON.stringify(msg) + nonce); - if(peer.base.writable){ - await peer.base.append({ op : 'remove_writer', type: 'removeWriter', key: ''+parsed.key, value: signature, hash: hash, nonce : nonce }); - } + const msg = { type: 'removeWriter', key: wk }; + const signature = { msg: msg }; + const hash = peer.wallet.sign(JSON.stringify(msg) + nonce); + if(peer.base.writable === false){ + throw new Error('Peer is not writable.'); } + await peer.base.append({ op : 'remove_writer', type: 'removeWriter', key: wk, value: signature, hash: hash, nonce : nonce }); +} + +export async function removeWriterKey(keyHex, peer){ + return appendRemoveWriter(peer, keyHex); +} + +export async function removeIndexerKey(keyHex, peer){ + // Current apply operation is `removeWriter` and it removes either writer or indexer. + // This keeps the exact on-chain / on-log operation type unchanged. + return appendRemoveWriter(peer, keyHex); +} + +export async function removeWriter(input, peer){ + await requireAdmin(peer); + const parsed = peer.protocol_instance.parseArgs(input); + return removeWriterKey(parsed.key, peer); +} + +export async function removeIndexer(input, peer){ + await requireAdmin(peer); + const parsed = peer.protocol_instance.parseArgs(input); + return removeIndexerKey(parsed.key, peer); } export async function joinValidator(input, peer){ console.log('Please wait...') const splitted = peer.protocol_instance.parseArgs(input) const address = ''+splitted.address; - const validator = await peer.msb.base.view.get(address); - if(validator === null || false === validator.value.isWriter || true === validator.value.isIndexer){ - throw new Error('Invalid validator address. The target does not seem to be a validator or does not exist.'); - } - let cnt = 0; - while(peer.msb.getNetwork().validator !== address){ - if(cnt >= 3) break; - await peer.msb.tryConnection(address); - await peer.sleep(10); - cnt += 1; - } - if(peer.msb.getNetwork().validator !== address){ - console.log('Could not connect. You will be connected with the next available one instead.'); + if(peer.msbClient?.isReady()){ + const pubKeyHex = peer.msbClient.addressToPubKeyHex(address); + if(pubKeyHex === null) throw new Error('Invalid validator address.'); + await peer.msb.network.tryConnect(pubKeyHex, 'validator'); + return; } + throw new Error('MSB is not ready.'); } export async function tx(input, peer){ + if(peer.base?.writable === false) throw new Error('Peer is not writable.'); const splitted = peer.protocol_instance.parseArgs(input); let res = false; if(splitted.command === undefined){ res = new Error('Missing option. Please use the --command flag.'); - } else if(splitted.sim === undefined && peer.msb.getNetwork().validator === null){ - res = new Error('No validator available: Please wait for your peer to find an available one or use joinValidator to connect to a specific one.'); } let sim = false; try{ @@ -387,7 +437,59 @@ export async function tx(input, peer){ const err = peer.protocol_instance.getError(res); if(null !== err){ console.log(err.message); + } else if(res && typeof res === 'object') { + if(res.txo && res.txo.tx) { + console.log('MSB TX broadcasted:', res.txo.tx); + } else if(res.bdo && res.bdo.tx) { + console.log('MSB BOOTSTRAP_DEPLOYMENT broadcasted:', res.bdo.tx); + } } } return res; -} \ No newline at end of file +} + +export async function deploySubnet(input, peer){ + if(!peer.msbClient?.isReady()) throw new Error('MSB is not ready.'); + if(peer.wallet.publicKey === null || peer.wallet.secretKey === null) throw new Error('Wallet is not initialized.'); + + const txvHex = await peer.msbClient.getTxvHex(); + const nonceHex = peer.protocol_instance.generateNonce(); + const subnetBootstrapHex = (b4a.isBuffer(peer.bootstrap) ? peer.bootstrap.toString('hex') : (''+peer.bootstrap)).toLowerCase(); + const channelHex = b4a.isBuffer(peer.channel) ? peer.channel.toString('hex') : null; + if(channelHex === null) throw new Error('Peer channel is not initialized.'); + + const address = peer.msbClient.pubKeyHexToAddress(peer.wallet.publicKey); + if(address === null) throw new Error('Failed to create MSB address from public key.'); + + const msg = createMessage( + peer.msbClient.networkId, + b4a.from(txvHex, 'hex'), + b4a.from(subnetBootstrapHex, 'hex'), + b4a.from(channelHex, 'hex'), + b4a.from(nonceHex, 'hex'), + MSB_OPERATION_TYPE.BOOTSTRAP_DEPLOYMENT + ); + const txBuf = await blake3Hash(msg); + const txHex = txBuf.toString('hex'); + const signatureHex = peer.wallet.sign(txBuf); + + const payload = { + type: MSB_OPERATION_TYPE.BOOTSTRAP_DEPLOYMENT, + address, + bdo: { + tx: txHex, + txv: txvHex, + bs: subnetBootstrapHex, + ic: channelHex, + in: nonceHex, + is: signatureHex + } + }; + + const ok = await peer.msbClient.broadcastBootstrapDeployment(payload); + if(ok !== true) throw new Error('Subnet deployment broadcast failed.'); + console.log('Subnet bootstrap:', subnetBootstrapHex); + console.log('Subnet channel (hex):', channelHex); + console.log('Subnet deployment tx:', payload.bdo.tx); + return payload; +} diff --git a/src/index.js b/src/index.js index 5d926ed..0a86b73 100644 --- a/src/index.js +++ b/src/index.js @@ -5,19 +5,15 @@ import ReadyResource from 'ready-resource'; import b4a from 'b4a'; import sodium from 'sodium-native' import Hyperbee from 'hyperbee'; -import readline from 'readline'; -import tty from 'tty' import Corestore from 'corestore'; import w from 'protomux-wakeup'; const wakeup = new w(); import Protomux from 'protomux' import c from 'compact-encoding' -import { - addWriter, addAdmin, setAutoAddWriters, setChatStatus, setMod, deleteMessage, - enableWhitelist, postMessage, jsonStringify, visibleLength, setNick, - muteStatus, setWhitelistStatus, updateAdmin, tx, safeClone, jsonParse, - pinMessage, joinValidator, removeWriter, unpinMessage -} from "./functions.js"; +import { MsbClient } from './msbClient.js'; +import { safeDecodeApplyOperation } from 'trac-msb/src/utils/protobuf/operationHelpers.js'; +import { blake3Hash } from 'trac-msb/src/utils/crypto.js'; +import { jsonStringify, visibleLength, safeClone, jsonParse } from "./functions.js"; import Check from "./check.js"; export {default as Protocol} from "./protocol.js"; export {default as Contract} from "./contract.js"; @@ -28,11 +24,14 @@ export class Peer extends ReadyResource { constructor(options = {}) { super(); + this.enable_background_tasks = options.enable_background_tasks !== false; + this.enable_updater = options.enable_updater !== false; this.STORES_DIRECTORY = options.stores_directory; this.KEY_PAIR_PATH = `${this.STORES_DIRECTORY}${options.store_name}/db/keypair.json`; this.keyPair = null; this.store = new Corestore(this.STORES_DIRECTORY + options.store_name); this.msb = options.msb || null; + this.msbClient = new MsbClient(this.msb); this.swarm = null; this.base = null; this.key = null; @@ -40,6 +39,10 @@ export class Peer extends ReadyResource { this.writerLocalKey = null; this.tx_pool_max_size = options.tx_pool_max_size || 1_000; this.max_tx_delay = options.max_tx_delay || 60; + this.max_msb_signed_length = Number.isSafeInteger(options.max_msb_signed_length) ? options.max_msb_signed_length : 1_000_000_000; + this.max_msb_apply_operation_bytes = Number.isSafeInteger(options.max_msb_apply_operation_bytes) + ? options.max_msb_apply_operation_bytes + : 1024 * 1024; this.bootstrap = options.bootstrap || null; this.protocol = options.protocol || null; this.contract = options.contract || null; @@ -57,20 +60,13 @@ export class Peer extends ReadyResource { this.options = options; this.check = new Check(); this.dhtBootstrap = ['116.202.214.149:10001', '157.180.12.214:10001', 'node1.hyperdht.org:49737', 'node2.hyperdht.org:49737', 'node3.hyperdht.org:49737']; - this.readline_instance = null; - this.enable_interactive_mode = options.enable_interactive_mode !== false; - if(this.enable_interactive_mode !== false){ - try{ - this.readline_instance = readline.createInterface({ - input: new tty.ReadStream(0), - output: new tty.WriteStream(1) - }); - }catch(e){ } - } + this.readline_instance = options.readline_instance || null; - this.tx_observer(); - this.validator_observer(); - this.nodeListener(); + if (this.enable_background_tasks) { + this.tx_observer(); + this.validator_observer(); + this.nodeListener(); + } this._boot(); this.ready().catch(noop); } @@ -92,66 +88,96 @@ export class Peer extends ReadyResource { if(this.contract_instance === null) await this.initContract(); const batch = view.batch(); for (const node of nodes) { + // Basic node shape validation (prevents apply crashes on malformed entries) if(false === this.check.node(node)) continue; const op = node.value; if (op.type === 'tx') { + // TX apply: only accept subnet TXs that are confirmed on MSB, then execute contract logic + // deterministically (same ordered log => same state everywhere). + + // Payload size guard (protect apply from huge JSON ops) if(b4a.byteLength(jsonStringify(op)) > _this.protocol_instance.txMaxBytes()) continue; + // Schema validation (required fields / types) if(false === this.check.tx(op)) continue; - while (_this.msb.base.view.core.signedLength < op.value.msbsl) { - await new Promise( (resolve, reject) => { - _this.msb.base.view.core.once('append', resolve); - }); + // Stall guard: don't allow a writer to pin apply waiting on an absurd MSB height + if (op.value.msbsl > _this.max_msb_signed_length) continue; + if (!_this.msbClient.isReady()) continue; + const msbCore = _this.msb.state.base.view.core; + // Wait for local MSB view to reach the referenced signed length + while (msbCore.signedLength < op.value.msbsl) { + await new Promise((resolve) => msbCore.once('append', resolve)); } - const msb_view_session = _this.msb.base.view.checkout(op.value.msbsl); - const post_tx = await msb_view_session.get(op.key); + // Fetch MSB apply-op at msbsl by tx key (op.key = tx hash) + const msb_view_session = _this.msb.state.base.view.checkout(op.value.msbsl); + const msb_tx_entry = await msb_view_session.get(op.key); await msb_view_session.close(); - if(false === this.check.postTx(post_tx)) continue; - const content_hash = await _this.createHash('sha256', jsonStringify(op.value.dispatch)) - if (op.key === post_tx.value.tx && - null === await batch.get('tx/'+post_tx.value.tx) && - post_tx.value.ch === content_hash && - _this.wallet.verify(post_tx.value.ws, post_tx.value.tx + post_tx.value.wn, op.value.wp) && - _this.wallet.verify(post_tx.value.is, post_tx.value.tx + post_tx.value.in, op.value.ipk) && - post_tx.value.tx === await _this.protocol_instance.generateTx( - _this.bootstrap, _this.msb.bootstrap, - post_tx.value.wp, post_tx.value.i, post_tx.value.ipk, - post_tx.value.ch, post_tx.value.in - )) { - const err = _this.protocol_instance.getError( - await _this.contract_instance.execute(op, batch) - ); - let _err = null; - if(null !== err) { - if(err.constructor.name === 'UnknownContractOperationType') continue; - const _err_msg = parseInt(err.message); - _err = isNaN(_err_msg) ? ''+err.message : _err_msg; - } - let len = await batch.get('txl'); - if(null === len) { - len = 0; - } else { - len = len.value; - } - const dta = {}; - dta['val'] = safeClone(op.value.dispatch); - dta['err'] = _err; - dta['tx'] = post_tx.value.tx; - dta['ipk'] = post_tx.value.ipk; - dta['wp'] = post_tx.value.wp; - await batch.put('txi/'+len, dta); - await batch.put('txl', len + 1); - await batch.put('tx/'+post_tx.value.tx, len); - let ulen = await batch.get('utxl/'+post_tx.value.ipk); - if(null === ulen) { - ulen = 0; - } else { - ulen = ulen.value; - } - await batch.put('utxi/'+post_tx.value.ipk+'/'+ulen, len); - await batch.put('utxl/'+post_tx.value.ipk, ulen + 1); - console.log(`${post_tx.value.tx} appended. Signed length:`, _this.base.view.core.signedLength, 'tx length', len + 1); + // MSB entry shape/size guards (protect protobuf decode + keep apply bounded) + if (null === msb_tx_entry || false === b4a.isBuffer(msb_tx_entry.value)) continue; + if (msb_tx_entry.value.byteLength > _this.max_msb_apply_operation_bytes) continue; + // Decode MSB operation and ensure it's a TX (type 12) with required fields + const decoded = safeDecodeApplyOperation(msb_tx_entry.value); + if (null === decoded || decoded.txo === undefined) continue; + if (decoded.type !== 12) continue; + // Cross-check: tx hash matches op.key + if (null === decoded.txo.tx || decoded.txo.tx.toString('hex') !== op.key) continue; + // Cross-check: MSB tx targets this subnet + this MSB network + const subnetBootstrapHex = (b4a.isBuffer(_this.bootstrap) ? _this.bootstrap.toString('hex') : ('' + _this.bootstrap)).toLowerCase(); + if (null === decoded.txo.bs || decoded.txo.bs.toString('hex') !== subnetBootstrapHex) continue; + if (null === decoded.txo.mbs || decoded.txo.mbs.toString('hex') !== _this.msbClient.bootstrapHex) continue; + // Cross-check: content hash matches the subnet dispatch payload (blake3) + const content_hash = await _this.createHash('blake3', jsonStringify(op.value.dispatch)); + if (null === decoded.txo.ch || decoded.txo.ch.toString('hex') !== content_hash) continue; + // Cross-check: requester identity matches ipk + const invokerAddress = decoded.address ? decoded.address.toString('ascii') : null; + const invokerPubKeyHex = invokerAddress ? _this.msbClient.addressToPubKeyHex(invokerAddress) : null; + if (null === invokerPubKeyHex || invokerPubKeyHex !== ('' + op.value.ipk).toLowerCase()) continue; + // Cross-check: validator identity matches wp + const validatorAddress = decoded.txo.va ? decoded.txo.va.toString('ascii') : null; + const validatorPubKeyHex = validatorAddress ? _this.msbClient.addressToPubKeyHex(validatorAddress) : null; + if (null === validatorPubKeyHex || validatorPubKeyHex !== ('' + op.value.wp).toLowerCase()) continue; + // Transactions enabled gate (default: enabled if missing) + const enabled = await batch.get('txen'); + if (!(enabled === null || enabled.value === true)) continue; + // Replay protection: ignore already-indexed TXs + if (null !== await batch.get('tx/' + op.key)) continue; + // Execute contract and index deterministic result into subnet state + const err = _this.protocol_instance.getError( + await _this.contract_instance.execute(op, batch) + ); + let _err = null; + if(null !== err) { + if(err.constructor.name === 'UnknownContractOperationType') continue; + const _err_msg = parseInt(err.message); + _err = isNaN(_err_msg) ? ''+err.message : _err_msg; + } + let len = await batch.get('txl'); + if(null === len) { + len = 0; + } else { + len = len.value; + } + const dta = {}; + dta['val'] = safeClone(op.value.dispatch); + dta['err'] = _err; + dta['tx'] = op.key; + dta['ipk'] = op.value.ipk; + dta['wp'] = op.value.wp; + await batch.put('txi/'+len, dta); + await batch.put('txl', len + 1); + await batch.put('tx/'+op.key, len); + let ulen = await batch.get('utxl/'+op.value.ipk); + if(null === ulen) { + ulen = 0; + } else { + ulen = ulen.value; + } + await batch.put('utxi/'+op.value.ipk+'/'+ulen, len); + await batch.put('utxl/'+op.value.ipk, ulen + 1); + if(true === _this.enable_txlogs){ + console.log(`${op.key} appended. Signed length: ${_this.base.view.core.signedLength}, tx length: ${len + 1}`); } } else if(op.type === 'msg') { + // Chat apply: user-signed message + whitelist/mute checks + replay protection. if(b4a.byteLength(jsonStringify(op)) > _this.protocol_instance.msgMaxBytes()) continue; if(false === this.check.msg(op)) continue; const admin = await batch.get('admin'); @@ -206,6 +232,7 @@ export class Peer extends ReadyResource { console.log(`#${len + 1} | ${nick !== null ? nick.value : op.value.dispatch.address}: ${op.value.dispatch.msg}`); } } else if (op.type === 'feature') { + // Feature apply: admin-signed feature/contract op (replay-protected by sh/). if(b4a.byteLength(jsonStringify(op)) > _this.protocol_instance.featMaxBytes()) continue; if(false === this.check.feature(op)) continue; const str_dispatch_value = jsonStringify(op.value.dispatch.value); @@ -220,6 +247,7 @@ export class Peer extends ReadyResource { } } } else if (op.type === 'addIndexer') { + // Membership apply: admin-signed add indexer (Autobase writer with isIndexer: true). if(false === this.check.addIndexer(op)) continue; const str_msg = jsonStringify(op.value.msg); const admin = await batch.get('admin'); @@ -236,6 +264,7 @@ export class Peer extends ReadyResource { } } } else if (op.type === 'addWriter') { + // Membership apply: admin-signed add writer (Autobase writer with isIndexer: false). if(false === this.check.addWriter(op)) continue; const str_msg = jsonStringify(op.value.msg); const admin = await batch.get('admin'); @@ -248,10 +277,11 @@ export class Peer extends ReadyResource { const writerKey = b4a.from(op.key, 'hex'); await base.addWriter(writerKey, { isIndexer : false }); await batch.put('sh/'+op.hash, ''); - console.log(`Writer added: ${writerKey}`); + console.log(`Writer added: ${op.key}`); } } } else if (op.type === 'removeWriter') { + // Membership apply: admin-signed remove writer/indexer. if(false === this.check.removeWriter(op)) continue; const str_msg = jsonStringify(op.value.msg); const admin = await batch.get('admin'); @@ -268,6 +298,7 @@ export class Peer extends ReadyResource { } } } else if (op.type === 'setChatStatus') { + // Chat config apply: admin-signed chat on/off toggle. if(false === this.check.setChatStatus(op)) continue; const str_msg = jsonStringify(op.value.msg); const admin = await batch.get('admin'); @@ -283,6 +314,7 @@ export class Peer extends ReadyResource { } } } else if (op.type === 'setAutoAddWriters') { + // Membership config apply: admin-signed toggle for auto-adding writers. if(false === this.check.setAutoAddWriters(op)) continue; const str_msg = jsonStringify(op.value.msg); const admin = await batch.get('admin'); @@ -298,6 +330,7 @@ export class Peer extends ReadyResource { } } } else if (op.type === 'autoAddWriter') { + // Membership apply: when auto_add_writers is on, allow a new writer to join by key. if(false === this.check.key(op)) continue; const auto_add_writers = await batch.get('auto_add_writers'); const banned = await batch.get('bnd/'+op.key); @@ -307,13 +340,16 @@ export class Peer extends ReadyResource { } console.log(`Writer auto added: ${op.key}`); } else if (op.type === 'addAdmin') { + // Admin apply: bootstrap node can set the initial admin once. if(false === this.check.key(op)) continue; - const bootstrap = b4a.toString(node.from.key, 'hex') - if(null === await batch.get('admin') && bootstrap === _this.bootstrap){ + const bootstrapWriterKeyHex = b4a.toString(node.from.key, 'hex'); + const subnetBootstrapHex = (b4a.isBuffer(_this.bootstrap) ? _this.bootstrap.toString('hex') : (''+_this.bootstrap)).toLowerCase(); + if(null === await batch.get('admin') && bootstrapWriterKeyHex === subnetBootstrapHex){ await batch.put('admin', op.key); console.log(`Admin added: ${op.key}`); } } else if (op.type === 'updateAdmin') { + // Admin apply: current admin transfers admin rights (replay-protected by sh/). if(false === this.check.updateAdmin(op)) continue; const admin = await batch.get('admin'); const str_value = jsonStringify(op.value); @@ -327,6 +363,7 @@ export class Peer extends ReadyResource { } } } else if(op.type === 'setNick') { + // Chat apply: nickname changes (user/mod/admin-signed, uniqueness-enforced). if(false === this.check.nick(op)) continue; const taken = await batch.get('kcin/'+op.value.dispatch.nick); const chat_status = await batch.get('chat_status'); @@ -364,6 +401,7 @@ export class Peer extends ReadyResource { console.log(`Changed nick to ${op.value.dispatch.nick} (${op.value.dispatch.address})`); } } else if(op.type === 'muteStatus') { + // Chat moderation apply: admin/mod-signed mute/unmute (stored under mtd/). if(false === this.check.mute(op)) continue; const admin = await batch.get('admin'); const str_value = jsonStringify(op.value); @@ -385,6 +423,7 @@ export class Peer extends ReadyResource { } } } else if(op.type === 'deleteMessage') { + // Chat moderation apply: admin/mod/user-signed message deletion (replay-protected by sh/). if(false === this.check.deleteMessage(op)) continue; const str_value = jsonStringify(op.value); if(null !== str_value && @@ -422,6 +461,7 @@ export class Peer extends ReadyResource { } } } else if(op.type === 'unpinMessage') { + // Chat moderation apply: admin/mod-signed unpin. if(false === this.check.unpinMessage(op)) continue; const str_value = jsonStringify(op.value); if(null !== str_value && @@ -449,6 +489,7 @@ export class Peer extends ReadyResource { } } } else if(op.type === 'pinMessage') { + // Chat moderation apply: admin/mod-signed pin/unpin (by pinned flag). if(false === this.check.pinMessage(op)) continue; const str_value = jsonStringify(op.value); if(null !== str_value && @@ -484,6 +525,7 @@ export class Peer extends ReadyResource { } } } else if(op.type === 'setMod') { + // Chat moderation apply: admin-signed set/unset mod role. if(false === this.check.mod(op)) continue; const admin = await batch.get('admin'); const str_value = jsonStringify(op.value); @@ -497,6 +539,7 @@ export class Peer extends ReadyResource { } } } else if(op.type === 'setWhitelistStatus') { + // Chat whitelist apply: admin-signed add/remove address from whitelist. if(false === this.check.whitelistStatus(op)) continue; const admin = await batch.get('admin'); const str_value = jsonStringify(op.value); @@ -510,6 +553,7 @@ export class Peer extends ReadyResource { } } } else if(op.type === 'enableWhitelist') { + // Chat whitelist config apply: admin-signed enable/disable whitelist enforcement. if(false === this.check.enableWhitelist(op)) continue; const admin = await batch.get('admin'); const str_value = jsonStringify(op.value); @@ -522,6 +566,19 @@ export class Peer extends ReadyResource { console.log(`Changed whitelist enabled ${op.value.dispatch.enabled}`); } } + } else if(op.type === 'enableTransactions') { + if(false === this.check.enableTransactions(op)) continue; + const admin = await batch.get('admin'); + const str_value = jsonStringify(op.value); + if(null !== admin && null !== str_value && + null === await batch.get('sh/'+op.hash)){ + const verified = _this.wallet.verify(op.hash, str_value + op.nonce, admin.value); + if(true === verified) { + await batch.put('txen', op.value.dispatch.enabled); + await batch.put('sh/'+op.hash, ''); + console.log(`Changed transactions enabled ${op.value.dispatch.enabled}`); + } + } } } await batch.flush(); @@ -532,6 +589,12 @@ export class Peer extends ReadyResource { } async sendTx(msg){ + if(this.msbClient.isReady()){ + if(msg && typeof msg === 'object' && typeof msg.type === 'number') { + try { await this.msbClient.broadcastTransaction(msg); } catch(_e) { } + } + return; + } if(this.msb.getNetwork().validator_stream === null) return; let _msg = safeClone(msg); if(_msg['ts'] !== undefined) delete _msg['ts']; @@ -540,6 +603,9 @@ export class Peer extends ReadyResource { async _open() { await this.base.ready(); + if (this.bootstrap === null) { + this.bootstrap = this.base.key; + } await this.wallet.initKeyPair(this.KEY_PAIR_PATH, this.readline_instance); this.writerLocalKey = b4a.toString(this.base.local.key, 'hex'); if(!this.init_contract_starting){ @@ -548,19 +614,11 @@ export class Peer extends ReadyResource { if (this.replicate) await this._replicate(); this.on('tx', async (msg) => { if(Object.keys(this.tx_pool).length <= this.tx_pool_max_size && !this.tx_pool[msg.tx]){ - await this.sendTx(msg); msg['ts'] = Math.floor(Date.now() / 1000); this.tx_pool[msg.tx] = msg; } }); - this.updater(); - } - - async initContract(){ - this.init_contract_starting = true; - this.protocol_instance = new this.protocol(this, this.base, this.options); - await this.protocol_instance.extendApi(); - this.contract_instance = new this.contract(this.protocol_instance); + if (this.enable_updater) this.updater(); } async updater() { @@ -574,6 +632,13 @@ export class Peer extends ReadyResource { } } + async initContract(){ + this.init_contract_starting = true; + this.protocol_instance = new this.protocol(this, this.base, this.options); + await this.protocol_instance.extendApi(); + this.contract_instance = new this.contract(this.protocol_instance); + } + async close() { if (this.swarm) { await this.swarm.destroy(); @@ -582,6 +647,9 @@ export class Peer extends ReadyResource { } async validator_observer(){ + if(this.msbClient.isReady()){ + return; + } while(true){ if(this.msb.getSwarm() !== null && this.msb.getNetwork().validator_stream === null) { console.log('Looking for available validators, please wait...'); @@ -613,9 +681,9 @@ export class Peer extends ReadyResource { } } const promises = []; - for(let i = 0; i < 10; i++){ + for(let i = 0; i < 2; i++){ promises.push(findSome()); - await this.sleep(250); + await this.sleep(500); } await Promise.all(promises); } @@ -625,6 +693,7 @@ export class Peer extends ReadyResource { async tx_observer(){ while(true){ + let backoff = 1; const ts = Math.floor(Date.now() / 1000); for(let tx in this.tx_pool){ if(ts - this.tx_pool[tx].ts > this.max_tx_delay){ @@ -633,18 +702,31 @@ export class Peer extends ReadyResource { delete this.protocol_instance.prepared_transactions_content[tx]; continue; } - const msbsl = this.msb.base.view.core.signedLength; - const view_session = this.msb.base.view.checkout(msbsl); + if(!this.msbClient.isReady()) continue; + const msbsl = this.msbClient.getSignedLength(); + const view_session = this.msb.state.base.view.checkout(msbsl); const msb_tx = await view_session.get(tx); await view_session.close(); - if(null !== msb_tx){ - msb_tx['msbsl'] = msbsl; - msb_tx['dispatch'] = this.protocol_instance.prepared_transactions_content[tx].dispatch; - msb_tx['ipk'] = this.protocol_instance.prepared_transactions_content[tx].ipk; - msb_tx['wp'] = this.protocol_instance.prepared_transactions_content[tx].validator; + if (null !== msb_tx && b4a.isBuffer(msb_tx.value)) { + const decoded = safeDecodeApplyOperation(msb_tx.value); + if (decoded?.type !== 12 || decoded?.txo === undefined) continue; + if (decoded.txo.tx === undefined || decoded.txo.tx.toString('hex') !== tx) continue; + const invokerAddress = decoded?.address ? decoded.address.toString('ascii') : null; + const validatorAddress = decoded?.txo?.va ? decoded.txo.va.toString('ascii') : null; + const ipk = invokerAddress ? this.msbClient.addressToPubKeyHex(invokerAddress) : null; + const wp = validatorAddress ? this.msbClient.addressToPubKeyHex(validatorAddress) : null; + if (null === ipk || null === wp) continue; + const prepared = this.protocol_instance.prepared_transactions_content[tx]; + if (prepared === undefined) continue; + const subnet_tx = { + msbsl: msbsl, + dispatch: prepared.dispatch, + ipk: ipk, + wp: wp, + }; delete this.tx_pool[tx]; delete this.protocol_instance.prepared_transactions_content[tx]; - await this.base.append({ type: 'tx', key: tx, value: msb_tx }); + await this.base.append({ type: 'tx', key: tx, value: subnet_tx }); } await this.sleep(5); } @@ -657,6 +739,10 @@ export class Peer extends ReadyResource { } async createHash(type, message){ + if(type === 'blake3'){ + const out = await blake3Hash(message); + return out.toString('hex'); + } if(type === 'sha256'){ const out = b4a.alloc(sodium.crypto_hash_sha256_BYTES); sodium.crypto_hash_sha256(out, b4a.from(message)); @@ -745,8 +831,9 @@ export class Peer extends ReadyResource { const remotePublicKey = b4a.toString(connection.remotePublicKey, 'hex'); this.connectedPeers.add(remotePublicKey); - wakeup.addStream(connection); - this.store.replicate(connection); + const stream = this.store.replicate(connection); + stream.on('error', (error) => { }); + wakeup.addStream(stream); this.connectedNodes++; connection.on('close', () => { @@ -803,115 +890,8 @@ export class Peer extends ReadyResource { console.error('Error during DAG monitoring:', error.message); } } - - printHelp(){ - console.log('Node started. Available commands:'); - console.log(' '); - console.log('- Setup Commands:'); - console.log('- /add_admin | Works only once and only on bootstrap node! Enter a wallet address to assign admin rights: \'/add_admin --address "
"\'.'); - console.log('- /update_admin | Existing admins may transfer admin ownership. Enter "null" as address to waive admin rights for this peer entirely: \'/update_admin --address "
"\'.'); - console.log('- /add_indexer | Only admin. Enter a peer writer key to get included as indexer for this network: \'/add_indexer --key ""\'.'); - console.log('- /add_writer | Only admin. Enter a peer writer key to get included as writer for this network: \'/add_writer --key ""\'.'); - console.log('- /remove_writer | Only admin. Enter a peer writer key to get removed as writer or indexer for this network: \'/remove_writer --key ""\'.'); - console.log('- /set_auto_add_writers | Only admin. Allow any peer to join as writer automatically: \'/set_auto_add_writers --enabled 1\''); - console.log(' '); - console.log('- Chat Commands:'); - console.log('- /set_chat_status | Only admin. Enable/disable the built-in chat system: \'/set_chat_status --enabled 1\'. The chat system is disabled by default.'); - console.log('- /post | Post a message: \'/post --message "Hello"\'. Chat must be enabled. Optionally use \'--reply_to \' to respond to a desired message.'); - console.log('- /set_nick | Change your nickname like this \'/set_nick --nick "Peter"\'. Chat must be enabled. Can be edited by admin and mods using the optional --user
flag.'); - console.log('- /mute_status | Only admin and mods. Mute or unmute a user by their address: \'/mute_status --user "
" --muted 1\'.'); - console.log('- /set_mod | Only admin. Set a user as mod: \'/set_mod --user "
" --mod 1\'.'); - console.log('- /delete_message | Delete a message: \'/delete_message --id 1\'. Chat must be enabled.'); - console.log('- /pin_message | Set the pin status of a message: \'/pin_message --id 1 --pin 1\'. Chat must be enabled.'); - console.log('- /unpin_message | Unpin a message by its pin id: \'/unpin_message --pin_id 1\'. Chat must be enabled.'); - console.log('- /enable_whitelist | Only admin. Enable/disable chat whitelists: \'/enable_whitelist --enabled 1\'.'); - console.log('- /set_whitelist_status | Only admin. Add/remove users to/from the chat whitelist: \'/set_whitelist_status --user "
" --status 1\'.'); - console.log(' '); - console.log('- System Commands:'); - console.log('- /tx | Perform a contract transaction. The command flag contains contract commands (format is protocol dependent): \'/tx --command ""\'. To simulate a tx, additionally use \'--sim 1\'.'); - console.log('- /join_validator | Try to connect to a specific validator with its MSB address: \'/join_validator --address "
"\'.'); - console.log('- /stats | check system properties such as writer key, DAG, etc.'); - console.log('- /get_keys | prints your public and private keys. Be careful and never share your private key!'); - console.log('- /exit | Exit the program'); - console.log('- /help | This help text'); - - this.protocol_instance.printOptions(); - } - - async interactiveMode() { - if(this.readline_instance === null || (global.Pear !== undefined && global.Pear.config.options.type === 'desktop')) return; - - const rl = this.readline_instance; - - this.printHelp(); - - rl.on('line', async (input) => { - switch (input) { - case '/stats': - await this.verifyDag(); - break; - case '/help': - await this.printHelp(); - break; - case '/exit': - console.log('Exiting...'); - rl.close(); - await this.close(); - typeof process !== "undefined" ? process.exit(0) : Pear.exit(0); - case '/get_keys': - console.log("Public Key: ", this.wallet.publicKey); - console.log("Secret Key: ", this.wallet.secretKey); - break; - default: - try { - if (input.startsWith('/tx')) { - await tx(input, this); - } else if (input.startsWith('/add_indexer') || input.startsWith('/add_writer')) { - await addWriter(input, this); - } else if (input.startsWith('/remove_writer')) { - await removeWriter(input, this); - } else if (input.startsWith('/add_admin')) { - await addAdmin(input, this); - } else if (input.startsWith('/update_admin')) { - await updateAdmin(input, this); - } else if (input.startsWith('/set_auto_add_writers')) { - await setAutoAddWriters(input, this); - } else if (input.startsWith('/set_chat_status')) { - await setChatStatus(input, this); - } else if (input.startsWith('/post')) { - await postMessage(input, this); - } else if (input.startsWith('/set_nick')) { - await setNick(input, this); - } else if (input.startsWith('/mute_status')) { - await muteStatus(input, this); - } else if (input.startsWith('/pin_message')) { - await pinMessage(input, this); - } else if (input.startsWith('/unpin_message')) { - await unpinMessage(input, this); - } else if (input.startsWith('/set_mod')) { - await setMod(input, this); - } else if (input.startsWith('/delete_message')) { - await deleteMessage(input, this); - } else if (input.startsWith('/enable_whitelist')) { - await enableWhitelist(input, this); - } else if (input.startsWith('/set_whitelist_status')) { - await setWhitelistStatus(input, this); - } else if (input.startsWith('/join_validator')) { - await joinValidator(input, this); - } else { - await this.protocol_instance.customCommand(input); - } - } catch(e) { - console.log('Command failed:', e.message); - } - } - rl.prompt(); - }); - - rl.prompt(); - } } function noop() { } -export default Peer; \ No newline at end of file +export default Peer; diff --git a/src/msbClient.js b/src/msbClient.js new file mode 100644 index 0000000..7ad5aa8 --- /dev/null +++ b/src/msbClient.js @@ -0,0 +1,111 @@ +import b4a from 'b4a'; +import { TRAC_NETWORK_MSB_MAINNET_PREFIX } from 'trac-wallet/constants.js'; +import PeerWallet from 'trac-wallet'; + +export const MSB_OPERATION_TYPE = Object.freeze({ + BOOTSTRAP_DEPLOYMENT: 11, + TX: 12, +}); + +export class MsbClient { + constructor(msbInstance) { + this.msb = msbInstance || null; + } + + #orchestratorCompatiblePayload(payload) { + if (!payload || typeof payload !== 'object') return payload; + if (payload.tro && payload.tro.tx) return payload; + const tx = + payload?.tro?.tx ?? + payload?.txo?.tx ?? + payload?.bdo?.tx ?? + payload?.rao?.tx ?? + null; + if (!tx) return payload; + return { ...payload, tro: { ...(payload.tro || {}), tx } }; + } + + isReady() { + return !!(this.msb && this.msb.state && this.msb.network && (this.msb.config || this.msb.options || this.msb.bootstrap)); + } + + get addressPrefix() { + if (!this.isReady()) return null; + const fromConfig = this.msb.config?.addressPrefix; + if (fromConfig) return fromConfig; + const addr = this.msb.wallet?.address; + if (typeof addr === 'string') { + const i = addr.indexOf('1'); + if (i > 0) return addr.slice(0, i); + } + return TRAC_NETWORK_MSB_MAINNET_PREFIX; + } + + get networkId() { + if (!this.isReady()) return null; + return this.msb.config?.networkId ?? this.msb.options?.networkId ?? this.msb.options?.network_id ?? 918; + } + + get bootstrapHex() { + if (!this.isReady()) return null; + const buf = this.msb.config?.bootstrap ?? this.msb.bootstrap; + return b4a.isBuffer(buf) ? buf.toString('hex') : null; + } + + async getTxvHex() { + if (!this.isReady()) return null; + const txv = await this.msb.state.getIndexerSequenceState(); + return txv.toString('hex'); + } + + pubKeyHexToAddress(pubKeyHex) { + if (!this.addressPrefix) return null; + return PeerWallet.encodeBech32mSafe(this.addressPrefix, b4a.from(pubKeyHex, 'hex')); + } + + addressToPubKeyHex(address) { + const decoded = PeerWallet.decodeBech32mSafe(address); + if (!decoded) return null; + return b4a.toString(decoded, 'hex'); + } + + getSignedLength() { + if (!this.isReady()) return 0; + return this.msb.state.getSignedLength(); + } + + async getSignedAtLength(key, signedLength) { + if (!this.isReady()) return null; + const viewSession = this.msb.state.base.view.checkout(signedLength); + try { + return await viewSession.get(key); + } finally { + await viewSession.close(); + } + } + + async broadcastTransaction(payload) { + if (!this.isReady()) throw new Error('MSB is not ready.'); + const safePayload = this.#orchestratorCompatiblePayload(payload); + if (typeof this.msb.broadcastTransactionCommand === 'function') { + return await this.msb.broadcastTransactionCommand(safePayload); + } + if (this.msb.network?.validatorMessageOrchestrator?.send) { + const ok = await this.msb.network.validatorMessageOrchestrator.send(safePayload); + return { message: ok ? 'Transaction broadcasted successfully.' : 'Transaction broadcast failed.', tx: null }; + } + throw new Error('MSB does not support transaction broadcasting.'); + } + + async broadcastBootstrapDeployment(payload) { + if (!this.isReady()) throw new Error('MSB is not ready.'); + const safePayload = this.#orchestratorCompatiblePayload(payload); + if (this.msb.network?.validatorMessageOrchestrator?.send) { + return await this.msb.network.validatorMessageOrchestrator.send(safePayload); + } + if (typeof this.msb.broadcastPartialTransaction === 'function') { + return await this.msb.broadcastPartialTransaction(safePayload); + } + throw new Error('MSB does not support bootstrap deployment broadcasting.'); + } +} diff --git a/src/peerRunnerConfig.js b/src/peerRunnerConfig.js new file mode 100644 index 0000000..ff9968b --- /dev/null +++ b/src/peerRunnerConfig.js @@ -0,0 +1,121 @@ +import fs from "fs"; +import path from "path"; +import { ensureTrailingSlash } from "./runnerArgs.js"; + +const readHexFile = (filePath, byteLength) => { + try { + if (fs.existsSync(filePath)) { + const hex = fs.readFileSync(filePath, "utf8").trim().toLowerCase(); + if (/^[0-9a-f]+$/.test(hex) && hex.length === byteLength * 2) return hex; + } + } catch (_e) {} + return null; +}; + +export const resolvePeerRunnerConfig = ({ env = {}, storeLabel = null, flags = {} }) => { + const msbStoresDirectory = ensureTrailingSlash( + (flags["msb-stores-directory"] && String(flags["msb-stores-directory"])) || + env.MSB_STORES_DIRECTORY || + "stores/" + ); + + const peerStoresDirectory = ensureTrailingSlash( + (flags["peer-stores-directory"] && String(flags["peer-stores-directory"])) || + env.PEER_STORES_DIRECTORY || + "stores/" + ); + + const peerStoreNameRaw = + (flags["peer-store-name"] && String(flags["peer-store-name"])) || + env.PEER_STORE_NAME || + storeLabel || + "peer"; + const peerStoreName = ensureTrailingSlash(peerStoreNameRaw); + + const msbStoreName = + (flags["msb-store-name"] && String(flags["msb-store-name"])) || + env.MSB_STORE_NAME || + `${peerStoreNameRaw}-msb`; + + const msbBootstrap = + (flags["msb-bootstrap"] && String(flags["msb-bootstrap"])) || env.MSB_BOOTSTRAP || null; + const msbChannel = (flags["msb-channel"] && String(flags["msb-channel"])) || env.MSB_CHANNEL || null; + + const msbBootstrapHex = msbBootstrap ? String(msbBootstrap).trim().toLowerCase() : null; + if (msbBootstrapHex && !/^[0-9a-f]{64}$/.test(msbBootstrapHex)) { + throw new Error( + `Invalid --msb-bootstrap. Expected 32-byte hex (64 chars), got length ${msbBootstrapHex.length}.` + ); + } + if (!msbBootstrapHex || !msbChannel) { + throw new Error( + "Missing MSB network params. Provide --msb-bootstrap= and --msb-channel= (or env MSB_BOOTSTRAP/MSB_CHANNEL)." + ); + } + + const subnetChannel = + (flags["subnet-channel"] && String(flags["subnet-channel"])) || + env.SUBNET_CHANNEL || + "trac-peer-subnet"; + + const subnetBootstrapHex = + (flags["subnet-bootstrap"] && String(flags["subnet-bootstrap"])) || env.SUBNET_BOOTSTRAP || null; + + const subnetBootstrapFile = path.join(peerStoresDirectory, peerStoreNameRaw, "subnet-bootstrap.hex"); + + let subnetBootstrap = subnetBootstrapHex ? subnetBootstrapHex.trim().toLowerCase() : null; + if (subnetBootstrap) { + if (!/^[0-9a-f]{64}$/.test(subnetBootstrap)) { + throw new Error("Invalid --subnet-bootstrap. Provide 32-byte hex (64 chars)."); + } + if (subnetBootstrap === msbBootstrapHex) { + throw new Error("Subnet bootstrap cannot equal MSB bootstrap."); + } + } else { + subnetBootstrap = readHexFile(subnetBootstrapFile, 32); + if (subnetBootstrap && subnetBootstrap === msbBootstrapHex) { + throw new Error("Stored subnet bootstrap equals MSB bootstrap. Delete the file and rerun."); + } + } + + const msbStoresFullPath = `${msbStoresDirectory}/${msbStoreName}`; + const msbKeyPairPath = `${msbStoresFullPath}/db/keypair.json`; + + const peerKeyPairPath = path.join(peerStoresDirectory, peerStoreName, "db", "keypair.json"); + + const rpcEnabled = flags.rpc === true || flags.rpc === "true" || env.PEER_RPC === "true" || env.PEER_RPC === "1"; + const rpcHost = + (flags["rpc-host"] && String(flags["rpc-host"])) || env.PEER_RPC_HOST || "127.0.0.1"; + const rpcPortRaw = (flags["rpc-port"] && String(flags["rpc-port"])) || env.PEER_RPC_PORT || "5001"; + const rpcPort = parseInt(rpcPortRaw); + if (isNaN(rpcPort) || rpcPort < 1 || rpcPort > 65535) { + throw new Error("Invalid --rpc-port. Expected integer 1-65535."); + } + const rpcAllowOrigin = (flags["rpc-allow-origin"] && String(flags["rpc-allow-origin"])) || env.PEER_RPC_ALLOW_ORIGIN || "*"; + const rpcMaxBodyBytesRaw = + (flags["rpc-max-body-bytes"] && String(flags["rpc-max-body-bytes"])) || env.PEER_RPC_MAX_BODY_BYTES || "1000000"; + const rpcMaxBodyBytes = parseInt(rpcMaxBodyBytesRaw); + if (isNaN(rpcMaxBodyBytes) || rpcMaxBodyBytes < 1) { + throw new Error("Invalid --rpc-max-body-bytes. Expected a positive integer."); + } + + return { + msbStoresDirectory, + peerStoresDirectory, + peerStoreNameRaw, + peerStoreName, + msbStoreName, + msbBootstrapHex, + msbChannel, + subnetChannel, + subnetBootstrap, + subnetBootstrapFile, + msbKeyPairPath, + peerKeyPairPath, + rpcEnabled, + rpcHost, + rpcPort, + rpcAllowOrigin, + rpcMaxBodyBytes, + }; +}; diff --git a/src/permissions.js b/src/permissions.js new file mode 100644 index 0000000..0d8e61f --- /dev/null +++ b/src/permissions.js @@ -0,0 +1,63 @@ +export async function getAdminKey(peer) { + if (!peer?.base?.view) return null; + const admin = await peer.base.view.get('admin'); + return admin?.value ?? null; +} + +export async function isAdmin(peer, address = null) { + const target = address ?? peer?.wallet?.publicKey ?? null; + if (!target) return false; + const admin = await getAdminKey(peer); + return admin !== null && admin === target; +} + +export async function isMod(peer, address = null) { + if (!peer?.base?.view) return false; + const target = address ?? peer?.wallet?.publicKey ?? null; + if (!target) return false; + const mod = await peer.base.view.get('mod/' + target); + return mod !== null && mod.value === true; +} + +export async function requireAdmin(peer) { + if (await isAdmin(peer)) return; + throw new Error('Only admin may perform this operation.'); +} + +export async function requireAdminOrMod(peer) { + if (await isAdmin(peer)) return; + if (await isMod(peer)) return; + throw new Error('Only admin or mod may perform this operation.'); +} + +export function isHex32(value) { + return /^[0-9a-f]{64}$/.test(String(value ?? '').trim().toLowerCase()); +} + +export function getSubnetBootstrapHex(peer) { + const b = peer?.bootstrap; + if (!b) return null; + if (typeof b === 'string') return b.toLowerCase(); + if (b?.toString) return b.toString('hex').toLowerCase(); + return null; +} + +export function getLocalWriterKeyHex(peer) { + try { + return peer?.base?.local?.key?.toString('hex') ?? peer?.writerLocalKey ?? null; + } catch (_e) { + return peer?.writerLocalKey ?? null; + } +} + +export async function requireBootstrapNodeForAdminSet(peer) { + const bootstrapHex = getSubnetBootstrapHex(peer); + const writerKeyHex = getLocalWriterKeyHex(peer); + if (!bootstrapHex || !writerKeyHex) throw new Error('Peer is not initialized.'); + if (bootstrapHex !== writerKeyHex.toLowerCase()) { + throw new Error('Only the subnet bootstrap node may set the initial admin.'); + } + const admin = await getAdminKey(peer); + if (admin !== null) throw new Error('Admin is already set.'); +} + diff --git a/src/protocol.js b/src/protocol.js index 11dd1ef..316ae6f 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -1,6 +1,10 @@ import { formatNumberString, resolveNumberString, jsonStringify, jsonParse, safeClone } from "./functions.js"; import {ProtocolApi} from './api.js'; import Wallet from 'trac-wallet'; +import b4a from 'b4a'; +import { createMessage } from 'trac-msb/src/utils/buffer.js'; +import { blake3Hash } from 'trac-msb/src/utils/crypto.js'; +import { MSB_OPERATION_TYPE } from './msbClient.js'; class Protocol{ constructor(peer, base, options = {}) { @@ -81,92 +85,131 @@ class Protocol{ } async addFeature(key, feature){ - const pk1 = this.peer.wallet.publicKey; - const pk2 = await this.base.view.get('admin'); - if(null === pk2 || pk1 !== pk2.value) throw new Error('addFeature(key, feature): Features only allowed for admin.'); - if(typeof this.features[key] !== "undefined") throw new Error('addFeature(key, feature): Feature key exists already.'); + //const pk1 = this.peer.wallet.publicKey; + //const pk2 = await this.base.view.get('admin'); + //if(null === pk2 || pk1 !== pk2.value) throw new Error('addFeature(key, feature): Features only allowed for admin.'); feature.key = key; + if(typeof this.features[key] !== "undefined") key = Math.random(); this.features[key] = feature; } - async generateTx(bootstrap, msb_bootstrap, validator_public_key, local_writer_key, local_public_key, content_hash, nonce){ - let tx = bootstrap + '-' + - msb_bootstrap + '-' + - validator_public_key + '-' + - local_writer_key + '-' + - local_public_key + '-' + - content_hash + '-' + - nonce; - return await this.peer.createHash('sha256', await this.peer.createHash('sha256', tx)); + async generateTx(networkId, txvHex, incomingWritingKeyHex, contentHashHex, externalBootstrapHex, msbBootstrapHex, nonceHex){ + const msg = createMessage( + networkId, + b4a.from(txvHex, 'hex'), + b4a.from(incomingWritingKeyHex, 'hex'), + b4a.from(contentHashHex, 'hex'), + b4a.from(externalBootstrapHex, 'hex'), + b4a.from(msbBootstrapHex, 'hex'), + b4a.from(nonceHex, 'hex'), + MSB_OPERATION_TYPE.TX + ); + const tx = await blake3Hash(msg); + return tx.toString('hex'); } async simulateTransaction(validator_pub_key, obj, surrogate = null){ const storage = new SimStorage(this.peer); let nonce = this.generateNonce(); - const content_hash = await this.peer.createHash('sha256', this.safeJsonStringify(obj)); - let tx = await this.generateTx(this.peer.bootstrap, this.peer.msb.bootstrap, validator_pub_key, - this.peer.writerLocalKey, surrogate !== null ? surrogate.address : this.peer.wallet.publicKey, content_hash, nonce); + const content_hash = await this.peer.createHash('blake3', this.safeJsonStringify(obj)); + const txvHex = await this.peer.msbClient.getTxvHex(); + const msbBootstrapHex = this.peer.msbClient.bootstrapHex; + const subnetBootstrapHex = (b4a.isBuffer(this.peer.bootstrap) ? this.peer.bootstrap.toString('hex') : (''+this.peer.bootstrap)).toLowerCase(); + const tx = await this.generateTx( + this.peer.msbClient.networkId, + txvHex, + this.peer.writerLocalKey, + content_hash, + subnetBootstrapHex, + msbBootstrapHex, + nonce + ); const op = { type : 'tx', key : tx, value : { dispatch : obj, - value : { - ipk : surrogate !== null ? surrogate.address : this.peer.wallet.publicKey, - wp : validator_pub_key - } + ipk : surrogate !== null ? surrogate.address : this.peer.wallet.publicKey, + wp : validator_pub_key } } return await this.peer.contract_instance.execute(op, storage); } - async broadcastTransaction(validator_pub_key, obj, sim = false, surrogate = null){ - if(this.peer.msb.getNetwork().validator_stream !== null && - this.peer.wallet.publicKey !== null && - this.peer.wallet.secretKey !== null && - this.base.localWriter !== null && - obj.type !== undefined && - obj.value !== undefined) - { - if(true === sim) { - return await this.simulateTransaction(validator_pub_key, obj, surrogate); - } + // async broadcastTransaction(validator_pub_key, obj, sim = false, surrogate = null){ + // const tx_enabled = await this.peer.base.view.get('txen'); + // if( (null === tx_enabled || true === tx_enabled.value ) && + // this.peer.msb.getNetwork().validator_stream !== null && + // this.peer.wallet.publicKey !== null && + // this.peer.wallet.secretKey !== null && + // this.base.localWriter !== null && + // obj.type !== undefined && + // obj.value !== undefined) + // { + // if(true === sim) { + // return await this.simulateTransaction(validator_pub_key, obj, surrogate); + // } + async broadcastTransaction(obj, sim = false, surrogate = null){ + if(!this.peer.msbClient.isReady()) throw new Error('MSB is not ready.'); + const tx_enabled = await this.peer.base.view.get('txen'); + if (null === tx_enabled || true !== tx_enabled.value ) throw new Error('Tx is not enabled.'); + if(this.peer.wallet.publicKey === null || this.peer.wallet.secretKey === null) throw new Error('Wallet is not initialized.'); + if(this.peer.writerLocalKey === null) throw new Error('Local writer is not initialized.'); + if(obj.type === undefined || obj.value === undefined) throw new Error('Invalid transaction object.'); - let tx, signature, nonce, publicKey; - const content_hash = await this.peer.createHash('sha256', this.safeJsonStringify(obj)); - - if(surrogate !== null) { - nonce = surrogate.nonce; - tx = surrogate.tx; - signature = surrogate.signature; - publicKey = surrogate.address; - } else { - nonce = this.generateNonce(); - tx = await this.generateTx(this.peer.bootstrap, this.peer.msb.bootstrap, validator_pub_key, - this.peer.writerLocalKey, this.peer.wallet.publicKey, content_hash, nonce); - signature = this.peer.wallet.sign(tx + nonce); - publicKey = this.peer.wallet.publicKey; - } + const validator_pub_key = '0'.repeat(64); + if(true === sim) { + return await this.simulateTransaction(validator_pub_key, obj, surrogate); + } + + const txvHex = await this.peer.msbClient.getTxvHex(); + const msbBootstrapHex = this.peer.msbClient.bootstrapHex; + const subnetBootstrapHex = (b4a.isBuffer(this.peer.bootstrap) ? this.peer.bootstrap.toString('hex') : (''+this.peer.bootstrap)).toLowerCase(); + const content_hash = await this.peer.createHash('blake3', this.safeJsonStringify(obj)); - const _tx = { - op: 'pre-tx', - tx: tx, - is: signature, - wp : validator_pub_key, - i: this.peer.writerLocalKey, - ipk: publicKey, - ch : content_hash, - in : nonce, - bs : this.peer.bootstrap, - mbs : this.peer.msb.bootstrap - }; - - this.peer.emit('tx', _tx); - this.prepared_transactions_content[tx] = { dispatch : obj, ipk : publicKey, validator : validator_pub_key }; - return _tx; + let nonceHex, txHex, signatureHex, pubKeyHex; + if(surrogate !== null) { + nonceHex = surrogate.nonce; + txHex = surrogate.tx; + signatureHex = surrogate.signature; + pubKeyHex = surrogate.address; } else { - throw Error('broadcastTransaction(writer, obj): Cannot prepare transaction. Please make sure inputs and local writer are set.'); + nonceHex = this.generateNonce(); + txHex = await this.generateTx( + this.peer.msbClient.networkId, + txvHex, + this.peer.writerLocalKey, + content_hash, + subnetBootstrapHex, + msbBootstrapHex, + nonceHex + ); + signatureHex = this.peer.wallet.sign(b4a.from(txHex, 'hex')); + pubKeyHex = this.peer.wallet.publicKey; } + + const address = this.peer.msbClient.pubKeyHexToAddress(pubKeyHex); + if(address === null) throw new Error('Failed to create MSB address from public key.'); + + const payload = { + type: MSB_OPERATION_TYPE.TX, + address: address, + txo: { + tx: txHex, + txv: txvHex, + iw: this.peer.writerLocalKey, + in: nonceHex, + ch: content_hash, + is: signatureHex, + bs: subnetBootstrapHex, + mbs: msbBootstrapHex + } + }; + + await this.peer.msbClient.broadcastTransaction(payload); + this.prepared_transactions_content[txHex] = { dispatch : obj, ipk : pubKeyHex, address : address }; + this.peer.emit('tx', { tx : txHex }); + return payload; } async tokenizeInput(input){ @@ -179,8 +222,10 @@ class Protocol{ } getError(value){ - if (value === false || (value !== undefined && value.stack !== undefined && value.message !== undefined)) { - return value === false ? new Error('Error') : value; + if (value === false) return new Error('Error'); + if (value === null || value === undefined) return null; + if (typeof value === 'object' && value.stack !== undefined && value.message !== undefined) { + return value; } return null; } @@ -190,13 +235,24 @@ class Protocol{ } async tx(subject, sim = false, surrogate = null){ - if(this.peer.msb.getNetwork().validator_stream === null) throw new Error('HyperMallProtocol::tx(): No validator available.'); const obj = this.mapTxCommand(subject.command); if(null !== obj && typeof obj.type === 'string' && obj.value !== undefined) { - return await this.broadcastTransaction(this.peer.msb.getNetwork().validator,{ + return await this.broadcastTransaction({ type : obj.type, value : obj.value }, sim, surrogate); + const _this = this; + async function push(){ + await _this.peer.sleep(10_000); + try{ + await _this.broadcastTransaction({ + type : 'p', + value : '' + }); + } catch(e) { } + } + push(); + return ret; } throw new Error('HyperMallProtocol::tx(): command not found.'); } @@ -210,6 +266,7 @@ class Protocol{ async getSigned(key){ const view_session = this.peer.base.view.checkout(this.peer.base.view.core.signedLength); const result = await view_session.get(key); + await view_session.close(); if(result === null) return null; return result.value; } @@ -247,4 +304,4 @@ class SimStorage{ } } -export default Protocol; \ No newline at end of file +export default Protocol; diff --git a/src/runnerArgs.js b/src/runnerArgs.js new file mode 100644 index 0000000..17d7016 --- /dev/null +++ b/src/runnerArgs.js @@ -0,0 +1,38 @@ +export const toArgMap = (argv) => { + const out = {}; + for (let i = 0; i < argv.length; i++) { + const raw = argv[i]; + if (!raw.startsWith("--")) continue; + const eq = raw.indexOf("="); + if (eq !== -1) { + const k = raw.slice(2, eq); + const v = raw.slice(eq + 1); + out[k] = v; + continue; + } + const k = raw.slice(2); + const next = argv[i + 1]; + if (next !== undefined && !String(next).startsWith("--")) { + out[k] = next; + i++; + } else { + out[k] = true; + } + } + return out; +}; + +export const ensureTrailingSlash = (value) => (value.endsWith("/") ? value : `${value}/`); + +export const getPearRuntime = () => { + const pearApp = typeof Pear !== "undefined" ? (Pear.app ?? Pear.config) : undefined; + const runtimeArgs = typeof process !== "undefined" ? process.argv.slice(2) : []; + const argv = pearApp?.args ?? runtimeArgs; + const env = typeof process !== "undefined" && process.env ? process.env : {}; + + const storeLabel = argv[0] && !String(argv[0]).startsWith("--") ? String(argv[0]) : null; + const flags = toArgMap(argv.slice(storeLabel ? 1 : 0)); + + return { argv, env, storeLabel, flags }; +}; + diff --git a/src/textCodec.js b/src/textCodec.js new file mode 100644 index 0000000..d712ed0 --- /dev/null +++ b/src/textCodec.js @@ -0,0 +1,49 @@ +import { Buffer } from "buffer"; + +const isConstructable = (Ctor) => { + if (typeof Ctor !== "function") return false; + try { + // eslint-disable-next-line no-new + new Ctor(); + return true; + } catch (_e) { + return false; + } +}; + +const ensureGlobals = () => { + // Pear/Bare runtime can provide non-constructable TextEncoder/TextDecoder. + if (!isConstructable(globalThis.TextEncoder)) { + globalThis.TextEncoder = class TextEncoder { + encode(input = "") { + return Buffer.from(String(input), "utf8"); + } + }; + } + + if (!isConstructable(globalThis.TextDecoder)) { + globalThis.TextDecoder = class TextDecoder { + decode(input) { + return Buffer.from(input ?? []).toString("utf8"); + } + }; + } +}; + +export const ensureTextCodecs = async () => { + // 1) Ensure globals exist and are constructable. + ensureGlobals(); + + // 2) trac-crypto-api overwrites globals from `util.TextEncoder/TextDecoder` in bare runtime. + // Patch util's exports too so that overwrite remains constructable. + try { + const utilMod = await import("util"); + const utilObj = utilMod?.default ?? utilMod; + if (utilObj && typeof utilObj === "object") { + utilObj.TextEncoder = globalThis.TextEncoder; + utilObj.TextDecoder = globalThis.TextDecoder; + } + } catch (_e) { + // Ignore if util is unavailable in this runtime. + } +}; diff --git a/src/wallet.js b/src/wallet.js index 485bc99..0c56a18 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -1,9 +1,35 @@ import PeerWallet from "trac-wallet" +import b4a from "b4a"; class Wallet extends PeerWallet{ constructor() { super(); } + + get publicKey() { + const pk = super.publicKey; + if (!pk) return null; + return b4a.toString(pk, "hex"); + } + + get secretKey() { + const sk = super.secretKey; + if (!sk) return null; + return b4a.toString(sk, "hex"); + } + + sign(message) { + const msgBuf = b4a.isBuffer(message) ? message : b4a.from(String(message)); + const signatureBuf = super.sign(msgBuf); + return b4a.toString(signatureBuf, "hex"); + } + + verify(signature, message, publicKey = this.publicKey) { + const sigBuf = b4a.isBuffer(signature) ? signature : b4a.from(String(signature), "hex"); + const msgBuf = b4a.isBuffer(message) ? message : b4a.from(String(message)); + const pkBuf = b4a.isBuffer(publicKey) ? publicKey : b4a.from(String(publicKey), "hex"); + return PeerWallet.verify(sigBuf, msgBuf, pkBuf); + } } -export default Wallet; \ No newline at end of file +export default Wallet; diff --git a/tests/acceptance/acceptance.test.js b/tests/acceptance/acceptance.test.js new file mode 100644 index 0000000..44296c2 --- /dev/null +++ b/tests/acceptance/acceptance.test.js @@ -0,0 +1,7 @@ +// This runner is intentionally small (mirrors MSB's brittle runner style). +import test from "brittle"; + +test.pause(); +await import("./rpc.test.js"); +test.resume(); + diff --git a/tests/acceptance/rpc.test.js b/tests/acceptance/rpc.test.js new file mode 100644 index 0000000..1e98812 --- /dev/null +++ b/tests/acceptance/rpc.test.js @@ -0,0 +1,204 @@ +import test from "brittle"; +import path from "path"; +import os from "os"; +import fs from "fs/promises"; +import b4a from "b4a"; + +import { createServer } from "../../rpc/create_server.js"; +import { Peer, Protocol, Contract } from "../../src/index.js"; +import Wallet from "../../src/wallet.js"; + +async function withTempDir(fn) { + const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), "trac-peer-acceptance-")); + const storesDirectory = tmpRoot.endsWith(path.sep) ? tmpRoot : tmpRoot + path.sep; + try { + return await fn({ tmpRoot, storesDirectory }); + } finally { + await fs.rm(tmpRoot, { recursive: true, force: true }); + } +} + +async function prepareWallet(storesDirectory, storeName) { + const wallet = new Wallet(); + await wallet.generateKeyPair(); + const keypairPath = path.join(storesDirectory, storeName, "db", "keypair.json"); + await fs.mkdir(path.dirname(keypairPath), { recursive: true }); + await wallet.exportToFile(keypairPath, b4a.alloc(0)); + return wallet; +} + +async function closePeer(peer) { + if (!peer) return; + try { + await peer.close(); + } catch (_e) {} + try { + await peer.store.close(); + } catch (_e) {} +} + +async function startRpc(peer, { maxBodyBytes = 1_000_000 } = {}) { + const server = createServer(peer, { maxBodyBytes }); + await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); + const addr = server.address(); + const baseUrl = `http://127.0.0.1:${addr.port}`; + return { server, baseUrl }; +} + +async function httpJson(method, url, body = null) { + const res = await fetch(url, { + method, + headers: body ? { "Content-Type": "application/json" } : undefined, + body: body ? JSON.stringify(body) : undefined, + }); + const text = await res.text(); + let json = null; + try { + json = text ? JSON.parse(text) : null; + } catch (_e) {} + return { status: res.status, json, text }; +} + +test("rpc: health/status/state + chat flow", async (t) => { + await withTempDir(async ({ storesDirectory }) => { + const storeName = "peer"; + const wallet = await prepareWallet(storesDirectory, storeName); + + const peer = new Peer({ + stores_directory: storesDirectory, + store_name: storeName, + wallet, + protocol: Protocol, + contract: Contract, + msb: null, + replicate: false, + enable_interactive_mode: false, + enable_background_tasks: false, + enable_updater: false, + }); + + let server = null; + try { + await peer.ready(); + const rpc = await startRpc(peer); + server = rpc.server; + const baseUrl = rpc.baseUrl; + + // Unversioned health route should not exist. + { + const r = await httpJson("GET", `${baseUrl}/health`); + t.is(r.status, 404); + } + + // Versioned health route exists. + { + const r = await httpJson("GET", `${baseUrl}/v1/health`); + t.is(r.status, 200); + t.is(r.json?.ok, true); + } + + // Status includes our pubkey and no MSB readiness. + { + const r = await httpJson("GET", `${baseUrl}/v1/status`); + t.is(r.status, 200); + t.is(r.json?.msb?.ready, false); + t.is(r.json?.peer?.pubKeyHex, wallet.publicKey); + } + + // Chat status is null by default. + { + const r = await httpJson("GET", `${baseUrl}/v1/state?key=chat_status&confirmed=true`); + t.is(r.status, 200); + t.is(r.json?.value ?? null, null); + } + + // Set admin to ourselves. + { + const r = await httpJson("POST", `${baseUrl}/v1/admin/add-admin`, { address: wallet.publicKey }); + t.is(r.status, 200); + } + { + const r = await httpJson("GET", `${baseUrl}/v1/state?key=admin&confirmed=true`); + t.is(r.status, 200); + t.is(r.json?.value, wallet.publicKey); + } + + // Enable chat. + { + const r = await httpJson("POST", `${baseUrl}/v1/chat/status`, { enabled: true }); + t.is(r.status, 200); + } + { + const r = await httpJson("GET", `${baseUrl}/v1/state?key=chat_status&confirmed=true`); + t.is(r.status, 200); + t.is(r.json?.value, "on"); + } + + // Set nick. + { + const r = await httpJson("POST", `${baseUrl}/v1/chat/nick`, { nick: "alice" }); + t.is(r.status, 200); + } + { + const r = await httpJson("GET", `${baseUrl}/v1/state?key=${encodeURIComponent(`nick/${wallet.publicKey}`)}&confirmed=true`); + t.is(r.status, 200); + t.is(r.json?.value, "alice"); + } + + // Post message. + { + const r = await httpJson("POST", `${baseUrl}/v1/chat/post`, { message: "hello" }); + t.is(r.status, 200); + } + { + const r = await httpJson("GET", `${baseUrl}/v1/state?key=msgl&confirmed=true`); + t.is(r.status, 200); + t.is(r.json?.value, 1); + } + { + const r = await httpJson("GET", `${baseUrl}/v1/state?key=msg%2F0&confirmed=true`); + t.is(r.status, 200); + t.is(r.json?.value?.msg, "hello"); + t.is(r.json?.value?.address, wallet.publicKey); + } + } finally { + if (server) await new Promise((resolve) => server.close(resolve)); + await closePeer(peer); + } + }); +}); + +test("rpc: body size limit returns 413", async (t) => { + await withTempDir(async ({ storesDirectory }) => { + const storeName = "peer"; + const wallet = await prepareWallet(storesDirectory, storeName); + + const peer = new Peer({ + stores_directory: storesDirectory, + store_name: storeName, + wallet, + protocol: Protocol, + contract: Contract, + msb: null, + replicate: false, + enable_interactive_mode: false, + enable_background_tasks: false, + enable_updater: false, + }); + + let server = null; + try { + await peer.ready(); + const rpc = await startRpc(peer, { maxBodyBytes: 32 }); + server = rpc.server; + const baseUrl = rpc.baseUrl; + + const big = "x".repeat(100); + const r = await httpJson("POST", `${baseUrl}/v1/chat/post`, { message: big }); + t.is(r.status, 413); + } finally { + if (server) await new Promise((resolve) => server.close(resolve)); + await closePeer(peer); + } + }); +}); diff --git a/tests/unit/applyGuards.test.js b/tests/unit/applyGuards.test.js new file mode 100644 index 0000000..447b34e --- /dev/null +++ b/tests/unit/applyGuards.test.js @@ -0,0 +1,261 @@ +import test from 'brittle'; +import b4a from 'b4a'; +import path from 'path'; +import os from 'os'; +import fs from 'fs/promises'; +import PeerWallet from 'trac-wallet'; +import { safeEncodeApplyOperation } from 'trac-msb/src/utils/protobuf/operationHelpers.js'; +import { blake3Hash } from 'trac-msb/src/utils/crypto.js'; + +import { Peer } from '../../src/index.js'; +import Wallet from '../../src/wallet.js'; + +class TestProtocol { + constructor(peer, base, options) { + this.prepared_transactions_content = {}; + } + async extendApi() {} + getError(value) { + return value ?? null; + } + txMaxBytes() { + return 1024 * 1024; + } + msgMaxBytes() { + return 1024 * 1024; + } + featMaxBytes() { + return 1024 * 1024; + } +} + +class TestContract { + constructor(protocol) {} + async execute(op, batch) { + return null; + } +} + +async function withTempDir(fn) { + const tmpRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'trac-peer-tests-')); + const storesDirectory = tmpRoot.endsWith(path.sep) ? tmpRoot : tmpRoot + path.sep; + try { + return await fn({ tmpRoot, storesDirectory }); + } finally { + await fs.rm(tmpRoot, { recursive: true, force: true }); + } +} + +async function closePeer(peer) { + if (!peer) return; + try { + await peer.close(); + } catch (_e) {} + try { + await peer.store.close(); + } catch (_e) {} +} + +async function prepareWallet(storesDirectory, storeName) { + const wallet = new Wallet(); + await wallet.generateKeyPair(); + const keypairPath = path.join(storesDirectory, storeName, 'db', 'keypair.json'); + await fs.mkdir(path.dirname(keypairPath), { recursive: true }); + await wallet.exportToFile(keypairPath, b4a.alloc(0)); + return wallet; +} + +function makeHex32(fill) { + return b4a.alloc(32).fill(fill).toString('hex'); +} + +function makeMsbStub({ msbBootstrapBuf, signedLength, getEntry }) { + const core = { + signedLength, + once(event, cb) { + // Used only if apply has to wait; tests will override if needed. + }, + }; + + return { + config: { + bootstrap: msbBootstrapBuf, + addressPrefix: 'trac', + networkId: 918, + }, + network: {}, + state: { + base: { + view: { + core, + checkout(_signedLength) { + return { + async get(key) { + return await getEntry(key, _signedLength); + }, + async close() {}, + }; + }, + }, + }, + getSignedLength() { + return core.signedLength; + }, + }, + }; +} + +test('apply: tx msbsl stall guard skips waiting', async (t) => { + await withTempDir(async ({ storesDirectory }) => { + const msbBootstrapBuf = b4a.alloc(32).fill(7); + const msb = makeMsbStub({ + msbBootstrapBuf, + signedLength: 0, + async getEntry() { + return null; + }, + }); + + // If the code ever tries to wait for msbCore.append, fail fast. + msb.state.base.view.core.once = () => { + throw new Error('apply should not wait for msbCore.append in this test'); + }; + + const storeName = 'peer-stall-guard'; + const wallet = await prepareWallet(storesDirectory, storeName); + const peer = new Peer({ + stores_directory: storesDirectory, + store_name: storeName, + channel: 'unit-test', + msb, + protocol: TestProtocol, + contract: TestContract, + wallet, + replicate: false, + enable_interactive_mode: false, + enable_background_tasks: false, + enable_updater: false, + max_msb_signed_length: 10, + }); + + try { + await peer.ready(); + + const txHashHex = makeHex32(1); + const op = { + type: 'tx', + key: txHashHex, + value: { + dispatch: { type: 'ping', value: { msg: 'hi' } }, + msbsl: 100, // above max_msb_signed_length => should be skipped before any waiting + ipk: makeHex32(2), + wp: makeHex32(3), + }, + }; + + const timeout = new Promise((_, reject) => + setTimeout(() => reject(new Error('append timed out (possible apply stall)')), 2000) + ); + await Promise.race([peer.base.append(op), timeout]); + + const txl = await peer.bee.get('txl'); + t.is(txl, null, 'tx should not be indexed when msbsl exceeds max_msb_signed_length'); + } finally { + await closePeer(peer); + } + }); +}); + +test('apply: tx MSB payload size guard blocks otherwise-valid tx', async (t) => { + await withTempDir(async ({ storesDirectory }) => { + const msbBootstrapBuf = b4a.alloc(32).fill(7); + + const txHashHex = makeHex32(1); + const ipkHex = makeHex32(2); + const wpHex = makeHex32(3); + const invokerAddress = PeerWallet.encodeBech32mSafe('trac', b4a.from(ipkHex, 'hex')); + const validatorAddress = PeerWallet.encodeBech32mSafe('trac', b4a.from(wpHex, 'hex')); + + const dispatch = { type: 'ping', value: { msg: 'hello' } }; + const ch = (await blake3Hash(JSON.stringify(dispatch))).toString('hex'); + + const makePeer = async (maxBytes, storeName) => { + let msbOperation = null; + const msb = makeMsbStub({ + msbBootstrapBuf, + signedLength: 1, + async getEntry(key, _signedLength) { + if (key !== txHashHex) return null; + if (!msbOperation) return null; + return { value: msbOperation }; + }, + }); + + const wallet = await prepareWallet(storesDirectory, storeName); + const peer = new Peer({ + stores_directory: storesDirectory, + store_name: storeName, + channel: 'unit-test', + msb, + protocol: TestProtocol, + contract: TestContract, + wallet, + replicate: false, + enable_interactive_mode: false, + enable_background_tasks: false, + enable_updater: false, + max_msb_apply_operation_bytes: maxBytes, + }); + await peer.ready(); + + // Bind an MSB operation that matches this specific peer's subnet bootstrap. + const subnetBootstrapBuf = peer.bootstrap; + msbOperation = safeEncodeApplyOperation({ + type: 12, + address: b4a.from(invokerAddress, 'ascii'), + txo: { + tx: b4a.from(txHashHex, 'hex'), + bs: subnetBootstrapBuf, + mbs: msbBootstrapBuf, + ch: b4a.from(ch, 'hex'), + va: b4a.from(validatorAddress, 'ascii'), + }, + }); + + t.ok(msbOperation.byteLength > 0, 'fixture MSB operation encodes'); + return peer; + }; + + const op = { + type: 'tx', + key: txHashHex, + value: { + dispatch, + msbsl: 1, + ipk: ipkHex, + wp: wpHex, + }, + }; + + const peerBlocked = await makePeer(1, 'peer-maxbytes-blocked'); + try { + const maxBytes = peerBlocked.max_msb_apply_operation_bytes; + const msbLen = (await peerBlocked.msb.state.base.view.checkout(1).get(txHashHex))?.value?.byteLength ?? null; + t.ok(msbLen !== null && msbLen > maxBytes, 'fixture MSB payload is larger than max bytes'); + await peerBlocked.base.append(op); + const txl = await peerBlocked.bee.get('txl'); + t.is(txl, null, 'tx should not be indexed when MSB apply payload exceeds max bytes'); + } finally { + await closePeer(peerBlocked); + } + + const peerAllowed = await makePeer(1024 * 1024, 'peer-maxbytes-allowed'); + try { + await peerAllowed.base.append(op); + const txl = await peerAllowed.bee.get('txl'); + t.is(txl?.value ?? null, 1, 'tx should be indexed when MSB apply payload is within max bytes'); + } finally { + await closePeer(peerAllowed); + } + }); +}); diff --git a/tests/unit/unit.test.js b/tests/unit/unit.test.js new file mode 100644 index 0000000..7fe5bab --- /dev/null +++ b/tests/unit/unit.test.js @@ -0,0 +1,7 @@ +// This runner is intentionally small (mirrors MSB's brittle runner style). +import test from 'brittle'; + +test.pause(); +await import('./applyGuards.test.js'); +test.resume(); +