mirror of
https://github.com/luxfi/node.git
synced 2026-07-29 00:26:40 +00:00
Compare commits
50
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2540a99d62 | ||
|
|
54af785ed5 | ||
|
|
353dbdd339 | ||
|
|
e010ed54da | ||
|
|
9a47ef7f5c | ||
|
|
2bf64cc21a | ||
|
|
f8ca4547a7 | ||
|
|
07bb1771c2 | ||
|
|
08465c2f63 | ||
|
|
ba14e55f2f | ||
|
|
d84b5397ed | ||
|
|
98911842db | ||
|
|
0f185bd813 | ||
|
|
a2f5c6c37b | ||
|
|
b3e92ce979 | ||
|
|
0428445890 | ||
|
|
86a342ae7b | ||
|
|
52fd2cef35 | ||
|
|
89bb0e718c | ||
|
|
6c6b256418 | ||
|
|
c7e0eef6f6 | ||
|
|
b4720c9f57 | ||
|
|
f840336167 | ||
|
|
d4323da799 | ||
|
|
97d9745ea9 | ||
|
|
6e7f0b3d7e | ||
|
|
6a87c69010 | ||
|
|
3db595a637 | ||
|
|
15ff32fbdc | ||
|
|
ec5cf94145 | ||
|
|
5e44cd67dc | ||
|
|
1b6487e0b7 | ||
|
|
67272bc7c9 | ||
|
|
2e98019144 | ||
|
|
6424671eb7 | ||
|
|
29c623880c | ||
|
|
ab4201301e | ||
|
|
29a263eb14 | ||
|
|
c3f037c737 | ||
|
|
d1400a4c65 | ||
|
|
bd996c41b1 | ||
|
|
feef2bcb1e | ||
|
|
99c690f307 | ||
|
|
00fa804fc0 | ||
|
|
65efff0628 | ||
|
|
5921a24dd9 | ||
|
|
7397da43ae | ||
|
|
a63a18bf71 | ||
|
|
363a5a8591 | ||
|
|
51a304804c |
+47
-12
@@ -257,7 +257,13 @@ RUN . ./build_env.sh && \
|
||||
# failed ValidateState, and BRICKED the node. Proven on-node: real swap → kill -9 →
|
||||
# clean reboot, state intact. v1.99.40 = v1.99.39 + deps to latest. consensus v1.25.21 =
|
||||
# stake-weighted alpha-of-K quorum finality + per-height single-finalize + epoch-bound certs.
|
||||
ARG EVM_VERSION=v1.101.2
|
||||
# EVM_VERSION MUST track node's go.mod luxfi/evm — the C-Chain 0x9999 DEX precompile
|
||||
# executes in THIS plugin (built by `git clone --branch ${EVM_VERSION}`), NOT the luxd
|
||||
# node binary. v1.104.4 pins precompile v0.19.1 = the settle-only decomplect (embedded
|
||||
# matcher removed, 0x9012 value ops → PRECOMPILE_MOVED). A stale pin here silently ships
|
||||
# the OLD matcher-bearing plugin even when the node binary is current (devnet canary caught
|
||||
# exactly this: node v0.19.1 but plugin v1.104.3/precompile v0.19.0).
|
||||
ARG EVM_VERSION=v1.104.6
|
||||
ARG EVM_VM_ID=mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6
|
||||
# the pinned evm go.mod may pin a dead luxfi/upgrade pseudo-version
|
||||
# (v1.0.1-0.20260603055252-f51810805436 — commit pruned from origin). Heal it to
|
||||
@@ -281,7 +287,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
# byte-identity; DEX big.Rat + determinism). Pin chains v1.4.8 (warp
|
||||
# consolidated to one luxfi/warp helper; graphvm genesis-last-accepted fix)
|
||||
# to match the chain-VM plugin stage (CHAINS_REF) below.
|
||||
go mod edit -require=github.com/luxfi/chains@v1.4.8 && \
|
||||
go mod edit -require=github.com/luxfi/chains@v1.7.0 && \
|
||||
find /tmp/evm -name go.sum -exec sed -i -E '/^github.com\/(luxfi|hanzoai)\//d' {} + && \
|
||||
GOARCH=$(echo ${TARGETPLATFORM} | cut -d / -f2) \
|
||||
CGO_ENABLED=0 GOFLAGS=-mod=mod \
|
||||
@@ -302,16 +308,28 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
# oraclevm -> r5m1ujrmXxVcQetG3CQfuDLHp2RHKh6vCDaFgBRQfUcTZh7eS
|
||||
# quantumvm -> ry9Sg8rZdT26iEKvJDmC2wkESs4SDKgZEhk5BgLSwg1EpcNug
|
||||
# relayvm -> sP6dLqrrBR9w3soP18fbJ3YzZecZdD7DDdfH2cFhhLq7Hy9bz
|
||||
# thresholdvm -> tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t
|
||||
# mpcvm (M-Chain) -> qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS
|
||||
# fhevm (F-Chain) -> n6sSsSfbpQBrU9sY4R29U6z8VrmnTo2CntW6da4rRS7qmnGdv
|
||||
# zkvm -> vv3qPfyTVXZ5ArRZA9Jh4hbYDTBe43f7sgQg4CHfNg1rnnvX9
|
||||
#
|
||||
# M-Chain (MPCVMID) and F-Chain (FHEVMID) are the SAME shared ThresholdVM
|
||||
# substrate (github.com/luxfi/chains/mpcvm; factory.go documents it as "the
|
||||
# shared library substrate that M-Chain (MPC) and F-Chain (FHE) run; it is not
|
||||
# itself a chain"). One byte-identical binary is installed under BOTH chain VM
|
||||
# IDs; the mpc-vs-fhe split is driven by each chain's genesis ("mode":"mpc" in
|
||||
# mchain.json vs "mode":"fhe" in fchain.json). The plugin's filename must equal
|
||||
# the chain's VMID (vms/registry parses ids.FromString(file.Name())), so the old
|
||||
# thresholdvm-ID name (tGVBwRxpmD2...) matched NEITHER chain and left both
|
||||
# unloadable — GetFactory(MPCVMID/FHEVMID) missed. Baking at the two chain IDs
|
||||
# is what makes M-Chain and F-Chain registrable.
|
||||
|
||||
# MUST track node's go.mod luxfi/chains (the D-Chain dexvm + 10 VM plugins).
|
||||
# Bump with every chains release or the bundled VM plugins go stale vs node's deps.
|
||||
# v1.4.7 == node go.mod's luxfi/chains pin: warp consolidated to ONE luxfi/warp
|
||||
# helper (bridgevm/zkvm/thresholdvm), graphvm genesis-last-accepted fix, built on
|
||||
# helper (bridgevm/zkvm/mpcvm), graphvm genesis-last-accepted fix, built on
|
||||
# evm v1.99.48 + precompile v0.16.0 (enable-everything builder surface). Keeps the
|
||||
# baked VM plugins in lockstep with the host node.
|
||||
ARG CHAINS_REF=v1.4.8
|
||||
ARG CHAINS_REF=v1.7.2
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
git clone --depth 1 --branch ${CHAINS_REF} https://github.com/luxfi/chains.git /tmp/chains && \
|
||||
find /tmp/chains -name go.sum -exec sed -i -E '/^github.com\/(luxfi|hanzoai)\//d' {} +
|
||||
@@ -345,13 +363,29 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
-o /luxd/build/plugins/ry9Sg8rZdT26iEKvJDmC2wkESs4SDKgZEhk5BgLSwg1EpcNug ./cmd/plugin ) || echo "WARN: quantumvm plugin build skipped" ; \
|
||||
( cd /tmp/chains/relayvm && CGO_ENABLED=0 GOFLAGS=-mod=mod go build -ldflags="-s -w" \
|
||||
-o /luxd/build/plugins/sP6dLqrrBR9w3soP18fbJ3YzZecZdD7DDdfH2cFhhLq7Hy9bz ./cmd/plugin ) || echo "WARN: relayvm plugin build skipped" ; \
|
||||
( cd /tmp/chains/thresholdvm && CGO_ENABLED=0 GOFLAGS=-mod=mod go build -ldflags="-s -w" \
|
||||
-o /luxd/build/plugins/tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t ./cmd/plugin ) || echo "WARN: thresholdvm plugin build skipped" ; \
|
||||
( cd /tmp/chains/mpcvm && CGO_ENABLED=0 GOFLAGS=-mod=mod go build -ldflags="-s -w" \
|
||||
-o /luxd/build/plugins/qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS ./cmd/plugin && \
|
||||
cp /luxd/build/plugins/qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS \
|
||||
/luxd/build/plugins/n6sSsSfbpQBrU9sY4R29U6z8VrmnTo2CntW6da4rRS7qmnGdv ) \
|
||||
|| echo "WARN: mpcvm(M-Chain)/fhevm(F-Chain) plugin build skipped" ; \
|
||||
( cd /tmp/chains/zkvm && CGO_ENABLED=0 GOFLAGS=-mod=mod go build -ldflags="-s -w" \
|
||||
-o /luxd/build/plugins/vv3qPfyTVXZ5ArRZA9Jh4hbYDTBe43f7sgQg4CHfNg1rnnvX9 ./cmd/plugin ) || echo "WARN: zkvm plugin build skipped" ; \
|
||||
( chmod +x /luxd/build/plugins/* 2>/dev/null || true ) && \
|
||||
test -s /luxd/build/plugins/kMhHABHM8j4bH94MCc4rsTNdo5E9En37MMyiujk4WdNxgXFsY \
|
||||
|| { echo "FATAL: bridgevm (B-Chain) plugin missing — the v1.30.16 regression would recur"; exit 1; } && \
|
||||
for p in \
|
||||
juFxSrbCM4wszxddKepj1GWwmrn9YgN1g4n3VUWPpRo9JjERA \
|
||||
kMhHABHM8j4bH94MCc4rsTNdo5E9En37MMyiujk4WdNxgXFsY \
|
||||
nZQm4Dmg1rjX18rb8maL9gamYyXPf1xCvF7ymWzxp6a1nSQTt \
|
||||
oR6tnZHezwogyf9fRnomNXC9ojwCEBAU6jdUzpgy2PB1tD7fM \
|
||||
pJJCSV7hHYVY6TUZwR8qUPAfuhX8JLb2C1AzNSezrYNbgau8M \
|
||||
r5m1ujrmXxVcQetG3CQfuDLHp2RHKh6vCDaFgBRQfUcTZh7eS \
|
||||
ry9Sg8rZdT26iEKvJDmC2wkESs4SDKgZEhk5BgLSwg1EpcNug \
|
||||
sP6dLqrrBR9w3soP18fbJ3YzZecZdD7DDdfH2cFhhLq7Hy9bz \
|
||||
qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS \
|
||||
n6sSsSfbpQBrU9sY4R29U6z8VrmnTo2CntW6da4rRS7qmnGdv \
|
||||
vv3qPfyTVXZ5ArRZA9Jh4hbYDTBe43f7sgQg4CHfNg1rnnvX9 ; do \
|
||||
test -s /luxd/build/plugins/$p \
|
||||
|| { echo "FATAL: required chain-VM plugin $p missing/empty — its build failed above (see the matching WARN line); the runtime-stage hard COPY would otherwise fail cryptically. Surface & fix the real Go build error, or remove the plugin from BOTH the build list and the runtime COPY."; exit 1; } ; \
|
||||
done && \
|
||||
rm -rf /tmp/chains
|
||||
|
||||
# ============= Native D-Chain DEX VM Plugin Stage ================
|
||||
@@ -367,7 +401,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
# accelerator in pkg/lx is a separate concern gated by its own cuda/metal tags and
|
||||
# is NOT linked here. v1.5.10 is the first tag whose cmd/dchain builds CGO=0
|
||||
# (drops the phantom dchain+cgo gate); v1.5.11 wires CLOB order ingestion over the
|
||||
# node HTTP router (VM.CreateHandlers -> /ext/bc/D/dex/<method>, pkg/dchain/ingest.go)
|
||||
# node HTTP router (VM.CreateHandlers -> /v1/bc/D/dex/<method>, pkg/dchain/ingest.go)
|
||||
# so an order POSTed to the node flows submitTx -> mempool -> consensus -> Verify
|
||||
# (match) -> Accept; v1.5.12 persists the head block so the VM survives a restart
|
||||
# once advanced past genesis (GetBlock(lastAccepted) no longer ErrNotFound);
|
||||
@@ -379,7 +413,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
# DB (every plugin VM via vm/rpc) — that stranded the native D-Chain order book
|
||||
# (rebuildBookFromDB folded empty -> 0 fills despite committed asks). v1.5.15 adds
|
||||
# the committed-state READ surface (clob_get_trades/orders/markets/book over
|
||||
# /ext/bc/D/dex/<method>, pkg/dchain/read.go): read-only JSON of the durable trade
|
||||
# /v1/bc/D/dex/<method>, pkg/dchain/read.go): read-only JSON of the durable trade
|
||||
# log / resting book / markets, served beside the writes with ZERO consensus
|
||||
# impact. Needed to VERIFY a fill replicated identically across validators (query
|
||||
# every node, diff the trade rows + head root) and to feed markets-display (native
|
||||
@@ -452,7 +486,8 @@ COPY --from=builder \
|
||||
/luxd/build/plugins/r5m1ujrmXxVcQetG3CQfuDLHp2RHKh6vCDaFgBRQfUcTZh7eS \
|
||||
/luxd/build/plugins/ry9Sg8rZdT26iEKvJDmC2wkESs4SDKgZEhk5BgLSwg1EpcNug \
|
||||
/luxd/build/plugins/sP6dLqrrBR9w3soP18fbJ3YzZecZdD7DDdfH2cFhhLq7Hy9bz \
|
||||
/luxd/build/plugins/tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t \
|
||||
/luxd/build/plugins/qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS \
|
||||
/luxd/build/plugins/n6sSsSfbpQBrU9sY4R29U6z8VrmnTo2CntW6da4rRS7qmnGdv \
|
||||
/luxd/build/plugins/vv3qPfyTVXZ5ArRZA9Jh4hbYDTBe43f7sgQg4CHfNg1rnnvX9 \
|
||||
/luxd/build/plugins/
|
||||
WORKDIR /luxd/build
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ Target: validate all consensus, EVM, and staking behavior with K=11 validators.
|
||||
- [ ] Verify all precompile activation timestamps are after 2025-12-25
|
||||
- [ ] Deploy via PaaS (platform.hanzo.ai), not manual kubectl
|
||||
- [ ] Verify all 11 pods reach Running state
|
||||
- [ ] Verify all 11 nodes report healthy via `/ext/health/liveness`
|
||||
- [ ] Verify all 11 nodes report healthy via `/v1/health/liveness`
|
||||
|
||||
### 1.2 Bootstrap and Connectivity
|
||||
|
||||
|
||||
@@ -10,3 +10,24 @@ For the canonical Lux IP and licensing strategy, see:
|
||||
|
||||
For commercial inquiries that go beyond BSD-3 (e.g. private moat
|
||||
acceleration kernels), contact `licensing@lux.network`.
|
||||
|
||||
## Upstream attribution
|
||||
|
||||
See [NOTICE](NOTICE) for the full attribution. In summary:
|
||||
|
||||
- **avalanchego** (Ava Labs, Inc.) — this repository is derived from
|
||||
[ava-labs/avalanchego](https://github.com/ava-labs/avalanchego), licensed
|
||||
under the **BSD 3-Clause License** (© 2019 Ava Labs, Inc.). BSD-3 is
|
||||
permissive; the Lux additions here are likewise BSD-3-Clause.
|
||||
- **go-ethereum** (The go-ethereum Authors) — EVM support derives from
|
||||
[go-ethereum](https://github.com/ethereum/go-ethereum). It is **not**
|
||||
vendored in-tree; it is consumed as the external Go module
|
||||
`github.com/luxfi/geth`, which retains go-ethereum's original licenses:
|
||||
the library is **LGPL-3.0-or-later** and the command-line tools are
|
||||
**GPL-3.0**.
|
||||
|
||||
**Copyleft flag:** the LGPL-3.0/GPL-3.0 terms of the go-ethereum-derived code
|
||||
(via `github.com/luxfi/geth`) are **not** superseded by this repository's
|
||||
BSD-3-Clause license. Distributing compiled node binaries must honor LGPL-3.0
|
||||
for the linked geth library (published source of that code and its
|
||||
modifications at <https://github.com/luxfi/geth>, and user ability to relink).
|
||||
|
||||
@@ -65,10 +65,10 @@ selection, and EVM contract auth.
|
||||
### Where to look for X
|
||||
- Profile resolve at boot: `node/node.go:initSecurityProfile`
|
||||
- Profile RPC + REST + metrics: `service/security/`
|
||||
- JSON-RPC namespace: `security` at `POST /ext/security`
|
||||
- JSON-RPC namespace: `security` at `POST /v1/security`
|
||||
(methods `securityProfile`, `blockSecurity`)
|
||||
- REST sidecars: `GET /ext/security/profile`, `GET /ext/security/block/{n}`
|
||||
- Prometheus gauges: `/ext/metrics` under the `security_*` family
|
||||
- REST sidecars: `GET /v1/security/profile`, `GET /v1/security/block/{n}`
|
||||
- Prometheus gauges: `/v1/metrics` under the `security_*` family
|
||||
- Peer scheme gate: `network/peer/scheme_gate.go`
|
||||
- Classical-compat registry: `vms/txs/auth/policy.go`
|
||||
- Mempool gate (P-Chain): `vms/platformvm/mempool/*.go`
|
||||
@@ -85,6 +85,29 @@ selection, and EVM contract auth.
|
||||
|
||||
## FeePolicy — canonical user-tx fee gate
|
||||
|
||||
> **Topology + UTXO ownership + cross-chain fee model** are normatively
|
||||
> specified by [**LP-0130** (Chain Topology, UTXO Ownership, and Fee
|
||||
> Model)](https://github.com/luxfi/lps/blob/main/LPs/lp-0130-chain-topology-utxo-ownership-and-fee-model.md).
|
||||
> Read that LP before touching any VM's fee/settlement path or any
|
||||
> cross-chain import/export flow. In particular:
|
||||
>
|
||||
> - Only **P** and **X** are canonical UTXO state machines (LP-0130 §2).
|
||||
> - **X** is the money rail; **P** is the staking/reward rail; **LUX**
|
||||
> is the fee currency everywhere (LP-0130 §3, §5).
|
||||
> - **Q-Chain has no user-payable blockspace** — finality is a
|
||||
> validator obligation paid via P (LP-0130 §6). `quantumvm` MUST
|
||||
> use `NoUserTxPolicy{}` — enforced in chains/quantumvm/feegate.go as of 2026-07-03.
|
||||
> - **M-Chain fees are service fees** deducted from the originating
|
||||
> chain's fee pool, not a user M-balance (LP-0130 §7). `mpcvm`
|
||||
> already runs `NoUserTxPolicy{}` — correct.
|
||||
> - **B-Chain fees** are deducted from the bridged amount (LP-0130 §8).
|
||||
> - Every non-P/X chain settles worker rewards to X (asset payouts) or
|
||||
> P (staker rewards) via epoch fee roots reconciled at Q finality
|
||||
> (LP-0130 §4, §11).
|
||||
> - **Σ-escrow invariant** (LP-0130 I-8): `Σ non-P/X fee balances ==
|
||||
> Σ X-side fee escrow` at every Q checkpoint. Drift is a
|
||||
> finality-blocking fault.
|
||||
|
||||
Every Lux VM that accepts user-submitted txs declares a `fee.Policy`
|
||||
(package `vms/types/fee`). There is one interface and one validator —
|
||||
no per-VM bespoke fee structs.
|
||||
@@ -97,14 +120,14 @@ no per-VM bespoke fee structs.
|
||||
| zkvm | Z-Chain | user-tx | `FlatPolicy{Fee: MinTxFeeFloor, ...}` |
|
||||
| aivm | A-Chain | user-tx | `FlatPolicy{Fee: MinTxFeeFloor, ...}` |
|
||||
| keyvm | K-Chain | user-tx | `FlatPolicy{Fee: MinTxFeeFloor, ...}` |
|
||||
| bridgevm | B-Chain | user-tx | `FlatPolicy{Fee: MinTxFeeFloor, ...}` |
|
||||
| quantumvm | Q-Chain | user-tx | `FlatPolicy{Fee: MinTxFeeFloor, ...}` |
|
||||
| bridgevm | B-Chain | user-tx | `FlatPolicy{Fee: MinTxFeeFloor, ...}` (deducted from bridged amount, LP-0130 §8) |
|
||||
| quantumvm | Q-Chain | **service-only** (LP-0130 §6) | `NoUserTxPolicy{}` — validator obligation, no user-payable blockspace |
|
||||
| identityvm | I-Chain | user-tx | `FlatPolicy{Fee: MinTxFeeFloor, ...}` |
|
||||
| thresholdvm | M-Chain | service-only | `NoUserTxPolicy{}` |
|
||||
| mpcvm | M-Chain | service-only (LP-0130 §7) | `NoUserTxPolicy{}` — fees pulled from originating chain's fee pool |
|
||||
| oraclevm | O-Chain | service-only | `NoUserTxPolicy{}` |
|
||||
| relayvm | R-Chain | service-only | `NoUserTxPolicy{}` |
|
||||
| graphvm | G-Chain | read-only | `NoUserTxPolicy{}` (GraphQL refuses `mutation`) |
|
||||
| evm | C-Chain | user-tx | native EVM gas (gas * gasPrice >= 0 enforced upstream) |
|
||||
| evm | C-Chain | user-tx | native EVM gas (gas * gasPrice >= 0 enforced upstream); balance is X-imported LUX (LP-0130 §10) |
|
||||
| platformvm | P-Chain | user-tx | native `TxFee` field on Config |
|
||||
| avm | X-Chain | user-tx | native `TxFee` field on Config |
|
||||
|
||||
@@ -230,7 +253,7 @@ Located in `/vms/`:
|
||||
- **platformvm**: Staking, validation, network management
|
||||
- **xvm**: Asset transfers, UTXO model
|
||||
- **dexvm**: DEX with order book, perpetuals, AMM
|
||||
- **thresholdvm**: Threshold MPC and FHE for confidential computing
|
||||
- **mpcvm**: Threshold MPC and FHE for confidential computing
|
||||
- **quantumvm**: PQ consensus coordination (ML-DSA, Corona)
|
||||
- **identityvm**: Decentralized identity (DID, verifiable credentials)
|
||||
- **keyvm**: Post-quantum key management (ML-KEM, ML-DSA)
|
||||
@@ -320,11 +343,11 @@ github.com/luxfi/genesis (JSON config) → github.com/luxfi/node/genesis/build
|
||||
### CGO Dependencies
|
||||
These require CGO for full functionality (graceful fallback when disabled):
|
||||
- `consensus/quasar` - GPU NTT acceleration
|
||||
- `vms/thresholdvm/fhe` - GPU FHE operations
|
||||
- `vms/mpcvm/fhe` - GPU FHE operations
|
||||
- `x/blockdb` - zstd compression
|
||||
|
||||
### FHE (Fully Homomorphic Encryption)
|
||||
Located in `vms/thresholdvm/fhe/`:
|
||||
Located in `vms/mpcvm/fhe/`:
|
||||
- Uses `github.com/luxfi/lattice/multiparty` for DKG
|
||||
- Lattice-based cryptography only (no fallbacks)
|
||||
- Threshold decryption via Warp messaging
|
||||
@@ -639,7 +662,7 @@ strings.Contains(errStr, "not found") // parent block not in local state
|
||||
### Known CGO Stubs
|
||||
When CGO disabled, these use CPU fallbacks:
|
||||
- `consensus/quasar/gpu_ntt_nocgo.go`
|
||||
- `vms/thresholdvm/fhe/gpu_fhe_nocgo.go`
|
||||
- `vms/mpcvm/fhe/gpu_fhe_nocgo.go`
|
||||
- `vms/zkvm/accel/accel_mlx.go`
|
||||
|
||||
### 8. ZAP CreateHandlers for VM HTTP Endpoints
|
||||
@@ -659,7 +682,7 @@ When CGO disabled, these use CPU fallbacks:
|
||||
```bash
|
||||
curl -s -X POST -H "Content-Type: application/json" \
|
||||
-d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \
|
||||
http://localhost:9640/ext/bc/C/rpc
|
||||
http://localhost:9640/v1/bc/C/rpc
|
||||
# Returns: {"jsonrpc":"2.0","id":1,"result":"0x17870"}
|
||||
```
|
||||
|
||||
@@ -668,7 +691,7 @@ curl -s -X POST -H "Content-Type: application/json" \
|
||||
|
||||
**Behavior**:
|
||||
- **GET /**: Returns JSON node information (nodeId, networkId, version, chains, endpoints)
|
||||
- **POST /**: Proxies JSON-RPC requests directly to C-chain `/ext/bc/C/rpc`
|
||||
- **POST /**: Proxies JSON-RPC requests directly to C-chain `/v1/bc/C/rpc`
|
||||
- **OPTIONS /**: Returns CORS preflight headers
|
||||
|
||||
**Files Modified**: `server/http/router.go`, `server/http/server.go`
|
||||
@@ -735,7 +758,7 @@ if s.validators.NumNets() != 0 {
|
||||
|
||||
**Verification**:
|
||||
```bash
|
||||
curl -s http://localhost:9650/ext/health | jq '.checks.bls'
|
||||
curl -s http://localhost:9650/v1/health | jq '.checks.bls'
|
||||
# Should show: "message": "node has the correct BLS key"
|
||||
```
|
||||
|
||||
@@ -769,7 +792,7 @@ Testing conducted on a single Lux validator node (testnet mode, macOS):
|
||||
**Benchmark Command:**
|
||||
```bash
|
||||
cd ~/work/lux/benchmarks
|
||||
NODE_ENDPOINT="http://localhost:9640/ext/bc/C/rpc" \
|
||||
NODE_ENDPOINT="http://localhost:9640/v1/bc/C/rpc" \
|
||||
PRIVATE_KEY="<funded_key>" \
|
||||
./bin/bench tps --chains=lux --duration=60s --concurrency=5
|
||||
```
|
||||
|
||||
@@ -212,7 +212,7 @@ run-testnet: build-fips init-chains
|
||||
node-status:
|
||||
@echo "$(GREEN)Checking node status...$(NC)"
|
||||
@curl -s -X POST --data '{"jsonrpc":"2.0","id":1,"method":"info.isBootstrapped","params":{}}' \
|
||||
-H 'content-type:application/json;' http://localhost:9630/ext/info | jq
|
||||
-H 'content-type:application/json;' http://localhost:9630/v1/info | jq
|
||||
|
||||
stop-node:
|
||||
@echo "$(YELLOW)Stopping Lux node...$(NC)"
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
Lux Node
|
||||
Copyright (c) 2019-2025 Lux Industries Inc.
|
||||
|
||||
This product includes software from avalanchego by Ava Labs, Inc.
|
||||
(https://github.com/ava-labs/avalanchego), licensed under the BSD 3-Clause
|
||||
License:
|
||||
|
||||
Copyright (C) 2019, Ava Labs, Inc.
|
||||
|
||||
Lux Node is derived from avalanchego. The Lux additions and modifications in
|
||||
this repository are licensed under the BSD 3-Clause License (see the LICENSE
|
||||
file). The BSD 3-Clause terms of the upstream avalanchego code are retained;
|
||||
this NOTICE preserves the required Ava Labs copyright attribution at the
|
||||
repository level.
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
Ethereum Virtual Machine support is derived from go-ethereum by The
|
||||
go-ethereum Authors (https://github.com/ethereum/go-ethereum). In this
|
||||
repository that code is NOT vendored in-tree; it is consumed as an external
|
||||
Go module through the Lux fork github.com/luxfi/geth, which retains
|
||||
go-ethereum's original licenses:
|
||||
|
||||
* The go-ethereum library packages are licensed under the GNU Lesser
|
||||
General Public License, version 3 (LGPL-3.0-or-later).
|
||||
* The go-ethereum command-line tools are licensed under the GNU General
|
||||
Public License, version 3 (GPL-3.0).
|
||||
|
||||
Copyright (C) The go-ethereum Authors
|
||||
|
||||
COPYLEFT NOTICE: The LGPL-3.0/GPL-3.0 terms continue to apply to the
|
||||
go-ethereum-derived portions provided via github.com/luxfi/geth, and are NOT
|
||||
superseded by the BSD 3-Clause license of this repository. Distribution of
|
||||
compiled Lux Node binaries must honor LGPL-3.0 for the linked go-ethereum
|
||||
library code (source availability for that code and its modifications, and
|
||||
the ability for users to relink against a modified library). The luxfi/geth
|
||||
source, including Lux modifications, is published at
|
||||
https://github.com/luxfi/geth.
|
||||
+1
-1
@@ -3066,7 +3066,7 @@ This version is backwards compatible to [v1.9.0](https://github.com/luxfi/node/r
|
||||
- Fixed `x/merkledb.ChangeProof#getLargestKey` to correctly handle no changes
|
||||
- Added test for `xvm/txs/executor.SemanticVerifier#verifyFxUsage` with multiple valid fxs
|
||||
- Fixed CPU + bandwidth performance regression during vertex processing
|
||||
- Added example usage of the `/ext/index/X/block` API
|
||||
- Added example usage of the `/v1/index/X/block` API
|
||||
- Reduced the default value of `--consensus-optimal-processing` from `50` to `10`
|
||||
- Updated the year in the license header
|
||||
|
||||
|
||||
+1
-1
@@ -145,7 +145,7 @@ Production implementations live in `lux/crypto/` and `lux/lattice/`. Formal veri
|
||||
| `audits/2025-12-30-other-vms-audit.md` | Secondary VMs |
|
||||
| `audits/2025-12-30-platformvm-audit.md` | PlatformVM (P-Chain) |
|
||||
| `audits/2025-12-30-proposervm-evm-audit.md` | ProposerVM and EVM integration |
|
||||
| `audits/2025-12-30-thresholdvm-audit.md` | ThresholdVM (T-Chain) |
|
||||
| `audits/2025-12-30-mpcvm-audit.md` | ThresholdVM (T-Chain) |
|
||||
| `audits/2025-12-30-warp-audit.md` | Warp cross-chain messaging |
|
||||
| `audits/2025-12-30-zkvm-audit.md` | ZKVM (Z-Chain) |
|
||||
|
||||
|
||||
+63
-16
@@ -157,8 +157,27 @@ func (b *blockHandler) sampleAncestorBeacons() (set.Set[ids.NodeID], bool) {
|
||||
if len(connected) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
start := int(b.bsRotor.Add(1)-1) % len(connected)
|
||||
// Prefer beacons that reported an ahead tip in the last frontier round — they HOLD the
|
||||
// ancestry the descent needs, so asking them (rather than a rotated peer that may be at
|
||||
// genesis) is what lets a re-bootstrapping node obtain ancestry even when ≥2 peers are still
|
||||
// at genesis. This is ava's PeerTracker "ask a prover" bias sourced from the frontier replies
|
||||
// we already have (no separate tracker). Fill any remaining slots from the rotated full set so
|
||||
// the sample never shrinks below what blind rotation would pick (defense against a stale/empty
|
||||
// ahead-set). Empty ahead-set ⇒ pure rotation, identical to prior behavior.
|
||||
b.bsMu.Lock()
|
||||
ahead := b.bsAheadBeacons
|
||||
b.bsMu.Unlock()
|
||||
|
||||
sample := set.NewSet[ids.NodeID](bootstrapAncestorSample)
|
||||
for _, id := range connected {
|
||||
if sample.Len() >= bootstrapAncestorSample {
|
||||
break
|
||||
}
|
||||
if ahead != nil && ahead.Contains(id) {
|
||||
sample.Add(id)
|
||||
}
|
||||
}
|
||||
start := int(b.bsRotor.Add(1)-1) % len(connected)
|
||||
for i := 0; i < len(connected) && sample.Len() < bootstrapAncestorSample; i++ {
|
||||
sample.Add(connected[(start+i)%len(connected)])
|
||||
}
|
||||
@@ -318,7 +337,7 @@ func (b *blockHandler) FrontierTip(ctx context.Context) (ids.ID, chainbootstrap.
|
||||
switch {
|
||||
case err == nil:
|
||||
// A configured-beacon quorum named a safe sync anchor (NOT a finality cert — the loop
|
||||
// re-executes the descent and re-enters consensus, where ConsensusQuorum alone governs).
|
||||
// re-executes the descent and re-enters consensus, where FinalityQuorum alone governs).
|
||||
// With the P-ready gate above, this judgement is over the TRUE FULL staked set, so a tip the
|
||||
// quorum actively names is a real frontier (when it equals our own held tip, a ⅔-of-responders
|
||||
// supermajority is AT our height, so we ARE at the network frontier — the loop's Accepted()-shortcut
|
||||
@@ -538,6 +557,17 @@ func (b *blockHandler) collectFrontierReplies(ctx context.Context, connected []i
|
||||
|
||||
replies := make([]BeaconReply, 0, len(connected))
|
||||
seen := make(map[ids.NodeID]struct{}, len(connected))
|
||||
// ahead = beacons reporting a tip this node has NOT accepted (they hold blocks we lack, so
|
||||
// they can SERVE the ancestry the descent needs). sampleAncestorBeacons prefers them so the
|
||||
// GetAncestors sample targets peers that can serve — never a peer still at genesis (our own
|
||||
// tip). Recorded per round; publishes into b.bsAheadBeacons before returning.
|
||||
ahead := set.NewSet[ids.NodeID](len(connected))
|
||||
record := func() []BeaconReply {
|
||||
b.bsMu.Lock()
|
||||
b.bsAheadBeacons = ahead
|
||||
b.bsMu.Unlock()
|
||||
return replies
|
||||
}
|
||||
deadline := time.After(bootstrapFrontierWindow)
|
||||
for {
|
||||
select {
|
||||
@@ -551,13 +581,16 @@ func (b *blockHandler) collectFrontierReplies(ctx context.Context, connected []i
|
||||
}
|
||||
seen[rep.nodeID] = struct{}{}
|
||||
replies = append(replies, BeaconReply{NodeID: rep.nodeID, Tip: rep.tip, Weight: w})
|
||||
if _, accepted := b.acceptedHeight(rep.tip); !accepted {
|
||||
ahead.Add(rep.nodeID) // reported a tip we have not accepted → genuinely ahead
|
||||
}
|
||||
if len(seen) >= len(connected) {
|
||||
return replies // every connected beacon answered — resolve now, do not wait the window
|
||||
return record() // every connected beacon answered — resolve now, do not wait the window
|
||||
}
|
||||
case <-deadline:
|
||||
return replies
|
||||
return record()
|
||||
case <-ctx.Done():
|
||||
return replies
|
||||
return record()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -581,7 +614,15 @@ func (b *blockHandler) Ancestors(ctx context.Context, blockID ids.ID, maxBlocks
|
||||
requestID := b.requestIDCounter
|
||||
b.contextRequestMu.Unlock()
|
||||
|
||||
ch := make(chan [][]byte, 1)
|
||||
// Buffer the whole sample so EVERY sampled beacon's reply can queue — the loop
|
||||
// then skips the EMPTY ones (a beacon that lacks the requested block, e.g. a peer
|
||||
// still at genesis) and returns the first NON-EMPTY batch. With a size-1 channel a
|
||||
// fast empty reply won the race and starved a slower peer that actually held the
|
||||
// ancestry, so a re-bootstrapping node with ≥2 peers at genesis could keep drawing
|
||||
// empties and stall. This mirrors the proven avalanchego contract: an empty Ancestors
|
||||
// reply means "this peer can't serve — take another's," never "done" (getter serves an
|
||||
// EXPLICIT empty batch when it lacks the block; see GetContext).
|
||||
ch := make(chan [][]byte, bootstrapAncestorSample)
|
||||
b.bsMu.Lock()
|
||||
b.bsAncestorCh[requestID] = ch
|
||||
b.bsMu.Unlock()
|
||||
@@ -597,13 +638,19 @@ func (b *blockHandler) Ancestors(ctx context.Context, blockID ids.ID, maxBlocks
|
||||
}
|
||||
b.net.Send(msg, sample, b.networkID, 0)
|
||||
|
||||
select {
|
||||
case blocks := <-ch:
|
||||
return blocks, nil
|
||||
case <-time.After(bootstrapAncestorsTimeout):
|
||||
return nil, nil
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
deadline := time.After(bootstrapAncestorsTimeout)
|
||||
for {
|
||||
select {
|
||||
case blocks := <-ch:
|
||||
if len(blocks) == 0 {
|
||||
continue // this beacon can't serve the block — wait for a peer that can
|
||||
}
|
||||
return blocks, nil
|
||||
case <-deadline:
|
||||
return nil, nil // no beacon in the sample served — the loop re-samples (rotated)
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -816,7 +863,7 @@ func (b *blockHandler) BootstrapFailure() error {
|
||||
// is correctly failing safe DOWN (VM in Bootstrapping, serving nothing as head) and waiting for the
|
||||
// quorum to return; the network cannot make progress without it. monitorBootstrap's no-progress
|
||||
// watchdog polls this so it does NOT force-STOP a node that is deliberately waiting — which, given
|
||||
// the K8s probes only poll the always-green /ext/health/liveness, would be a permanent brick. It is
|
||||
// the K8s probes only poll the always-green /v1/health/liveness, would be a permanent brick. It is
|
||||
// the discriminator between "stuck on a served gap" (a real stall → stop) and "waiting for the
|
||||
// quorum" (self-heal → keep waiting). Distinct from BootstrapFailed (a terminal/structural fail).
|
||||
func (b *blockHandler) BootstrapConnecting() bool { return b.bootstrapConnecting.Load() }
|
||||
@@ -921,7 +968,7 @@ func (b *blockHandler) runBootstrapThenPoll(ctx context.Context) {
|
||||
// no-progress watchdog treats it as a deliberate WAIT, not a stall: the node stays in Bootstrapping
|
||||
// (serving nothing as head, NEVER live at the stale height) and CONVERGES the instant the quorum
|
||||
// returns — the in-process self-heal the K8s probes do NOT provide (they poll the always-green
|
||||
// /ext/health/liveness, so a fail-safe-DOWN node is never restarted). A STRUCTURAL failure (deep gap
|
||||
// /v1/health/liveness, so a fail-safe-DOWN node is never restarted). A STRUCTURAL failure (deep gap
|
||||
// → state-sync) or an exhausted attempt bound returns false WITHOUT going Ready, bootstrapFailed
|
||||
// recording the reason so monitorBootstrap surfaces it. The node NEVER false-completes at its stale
|
||||
// height, and a transient outage NEVER becomes a permanent brick.
|
||||
@@ -963,7 +1010,7 @@ func (b *blockHandler) runInitialSync(ctx context.Context) bool {
|
||||
// (eclipse / partition / a majority co-restart still in flight) is RE-ATTEMPTED — the node stays
|
||||
// in Bootstrapping (engine alive, VM serving nothing as head, never live at the stale height)
|
||||
// and CONVERGES the instant the quorum returns. This is the recovery the K8s probes do NOT
|
||||
// provide (they all poll the always-green /ext/health/liveness, so a fail-safe-DOWN node is
|
||||
// provide (they all poll the always-green /v1/health/liveness, so a fail-safe-DOWN node is
|
||||
// never restarted). bootstrapMaxAttempts ≤ 0 ⇒ retry until the quorum returns or shutdown; a
|
||||
// test pins it to 1 to assert the single-attempt terminal fail-safe. A STRUCTURAL failure (deep
|
||||
// gap → state-sync) is NOT retried — a retry cannot fix it; it is surfaced for the operator.
|
||||
|
||||
@@ -217,6 +217,13 @@ type bsBeaconNet struct {
|
||||
serveAncestors bool // beacons serve ancestry (false models name-only beacons)
|
||||
ancestorsEmpty bool // beacons REPLY to GetAncestors but serve an EMPTY batch (cross-version / withholding)
|
||||
|
||||
// emptyResponders models a MIXED fleet on a WIPE-path recovery: the named beacons REPLY to
|
||||
// GetAncestors with an EMPTY batch (they lack the block — e.g. still at genesis after a
|
||||
// simultaneous wipe), while the rest serve the real ancestry. When set, the mock delivers the
|
||||
// empties FIRST, proving the fetcher (blockHandler.Ancestors) skips them and still returns the
|
||||
// non-empty batch — never starved by a peer that can't serve. Requires serveAncestors=true.
|
||||
emptyResponders set.Set[ids.NodeID]
|
||||
|
||||
// tipFor optionally overrides the tip a specific beacon reports (models DISAGREEMENT —
|
||||
// beacons connected but split across tips, so no ⅔ quorum forms → FrontierNoQuorum).
|
||||
tipFor map[ids.NodeID]ids.ID
|
||||
@@ -334,6 +341,23 @@ func (n *bsBeaconNet) Send(msg message.OutboundMessage, nodeIDs set.Set[ids.Node
|
||||
n.bh.deliverBootstrapFrontier(n.malicious, n.forgedTip)
|
||||
}
|
||||
case "ancestors":
|
||||
if n.emptyResponders != nil {
|
||||
// Mixed fleet: emptyResponders reply EMPTY (they lack the block), the rest serve.
|
||||
// Deliver the empties FIRST so the test proves Ancestors skips them and returns the
|
||||
// slower non-empty batch (the WIPE-path ≥2-at-genesis case).
|
||||
var servers []ids.NodeID
|
||||
for id := range nodeIDs {
|
||||
if n.emptyResponders.Contains(id) {
|
||||
n.bh.deliverBootstrapAncestors(m.requestID, nil)
|
||||
} else {
|
||||
servers = append(servers, id)
|
||||
}
|
||||
}
|
||||
for range servers {
|
||||
n.bh.deliverBootstrapAncestors(m.requestID, n.frame(m.blockID))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if n.serveAncestors {
|
||||
n.bh.deliverBootstrapAncestors(m.requestID, n.frame(m.blockID))
|
||||
} else if n.ancestorsEmpty {
|
||||
@@ -512,6 +536,66 @@ func TestNodeBootstrap_EmptyNodeConvergesViaTransport(t *testing.T) {
|
||||
require.True(t, bh.Accepted(ctx, chain[N].id), "node must hold the tip after sync")
|
||||
}
|
||||
|
||||
// TestNodeBootstrap_WipePath_MixedGenesisPeers_ObtainsAncestry is the regression guard for the
|
||||
// WIPE-path ≥2-at-genesis stall (deliverable 3). A re-bootstrapping node samples a fleet where
|
||||
// SOME beacons reply to GetAncestors with an EMPTY batch (they lack the block — still at genesis
|
||||
// after a simultaneous wipe) while the rest serve the real ancestry, and the empties arrive FIRST.
|
||||
// With the pre-fix size-1 reply channel + first-reply-wins, the empty reply won the race and the
|
||||
// good peer's non-empty batch was dropped, so the descent got nothing every round and stalled. The
|
||||
// fix buffers the whole sample and SKIPS empty batches, returning the first non-empty one — so the
|
||||
// node obtains ancestry from the peers that CAN serve, even when ≥2 peers are at genesis.
|
||||
func TestNodeBootstrap_WipePath_MixedGenesisPeers_ObtainsAncestry(t *testing.T) {
|
||||
const N = 30
|
||||
chain, byID := buildBSChain(N, -1)
|
||||
vm := newBSVM(chain)
|
||||
bh, chainID, beacons := newBSHandlerAndEngine(t, vm, 5)
|
||||
|
||||
// 2 of 5 beacons are "still at genesis" — they reply EMPTY to GetAncestors. The mock
|
||||
// delivers those empties FIRST so a first-reply-wins fetcher would be starved.
|
||||
empties := set.NewSet[ids.NodeID](2)
|
||||
empties.Add(beacons[0], beacons[1])
|
||||
bh.net = &bsBeaconNet{
|
||||
bh: bh, chainID: chainID, connected: beacons, byID: byID, tip: chain[N],
|
||||
serveAncestors: true, emptyResponders: empties,
|
||||
}
|
||||
bh.msgCreator = bsMsgBuilder{}
|
||||
|
||||
ctx := context.Background()
|
||||
require.NoError(t, runBS(t, bh), "node must converge despite ≥2 peers serving empty ancestry")
|
||||
|
||||
last, _ := vm.LastAccepted(ctx)
|
||||
require.Equal(t, chain[N].id, last, "node must sync to the tip via the peers that CAN serve")
|
||||
require.True(t, bh.Accepted(ctx, chain[N].id))
|
||||
}
|
||||
|
||||
// TestSampleAncestorBeacons_PrefersAheadBeacons proves change 2: the ancestry sample PREFERS
|
||||
// beacons the frontier round found genuinely AHEAD (they hold the ancestry), so a re-bootstrapping
|
||||
// node asks peers that can serve rather than wasting the bounded sample on genesis peers. With more
|
||||
// connected beacons than the sample size, blind rotation would periodically EXCLUDE any given
|
||||
// beacon; the preference guarantees the recorded ahead-beacon is ALWAYS sampled.
|
||||
func TestSampleAncestorBeacons_PrefersAheadBeacons(t *testing.T) {
|
||||
const numBeacons = 8 // > bootstrapAncestorSample (4), so rotation alone would sometimes miss one
|
||||
chain, byID := buildBSChain(1, -1)
|
||||
vm := newBSVM(chain)
|
||||
bh, chainID, beacons := newBSHandlerAndEngine(t, vm, numBeacons)
|
||||
bh.net = &bsBeaconNet{bh: bh, chainID: chainID, connected: beacons, byID: byID, tip: chain[1]}
|
||||
bh.msgCreator = bsMsgBuilder{}
|
||||
|
||||
// Record ONE beacon as ahead (the frontier round's output). It must appear in EVERY sample.
|
||||
ahead := beacons[numBeacons-1]
|
||||
bh.bsMu.Lock()
|
||||
bh.bsAheadBeacons = set.Of(ahead)
|
||||
bh.bsMu.Unlock()
|
||||
|
||||
for i := 0; i < 2*numBeacons; i++ {
|
||||
sample, ok := bh.sampleAncestorBeacons()
|
||||
require.True(t, ok)
|
||||
require.LessOrEqual(t, sample.Len(), bootstrapAncestorSample)
|
||||
require.True(t, sample.Contains(ahead),
|
||||
"the recorded ahead-beacon must be preferred into every ancestry sample (call %d)", i)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRED_FrozenVMLastAccepted_ConvergesOffFinalizedLedger is the regression guard for red
|
||||
// HIGH-1: the convergence-recognition must ride the IN-PROCESS consensus finalized ledger, NOT
|
||||
// the VM's LastAccepted cache — which the real ZAP client FREEZES at the boot snapshot for the
|
||||
@@ -775,9 +859,9 @@ func TestNodeBootstrap_NoBeaconSet_ReportsNoBeacons(t *testing.T) {
|
||||
func TestNodeBootstrap_FreshNet_SelfVoteUnderFullConnectivity_CaughtUp(t *testing.T) {
|
||||
const N = 5
|
||||
chain, byID := buildBSChain(N, -1)
|
||||
vm := newBSVM(chain) // the node is at genesis (lastAccepted = genesis)
|
||||
vm := newBSVM(chain) // the node is at genesis (lastAccepted = genesis)
|
||||
bh, chainID, beacons := newBSHandlerAndEngine(t, vm, 2) // 2 equal-weight (100) beacons
|
||||
bh.selfNodeID = beacons[0] // THIS node is beacon 0 — it is itself a validator
|
||||
bh.selfNodeID = beacons[0] // THIS node is beacon 0 — it is itself a validator
|
||||
bh.msgCreator = bsMsgBuilder{}
|
||||
|
||||
// FULL connectivity: the ONE other beacon is connected and reports GENESIS (a fresh net — every
|
||||
@@ -824,7 +908,7 @@ func TestNodeBootstrap_SelfVote_PeerAheadDefeatsCaughtUp(t *testing.T) {
|
||||
func TestNodeBootstrap_SelfVote_PartialConnectivityFailsSafe(t *testing.T) {
|
||||
const N = 5
|
||||
chain, byID := buildBSChain(N, -1)
|
||||
vm := newBSVM(chain) // node at genesis
|
||||
vm := newBSVM(chain) // node at genesis
|
||||
bh, chainID, beacons := newBSHandlerAndEngine(t, vm, 3) // self + 2 others
|
||||
bh.selfNodeID = beacons[0]
|
||||
bh.msgCreator = bsMsgBuilder{}
|
||||
@@ -1528,7 +1612,7 @@ func TestRED_TipHolderCoRestartGoesReadyAtOwnTip(t *testing.T) {
|
||||
// It must NOT go live at its stale height (safety) and must NOT permanently give up (liveness): it
|
||||
// stays in Bootstrapping, RE-ATTEMPTS (bootstrapMaxAttempts ≤ 0 ⇒ until the quorum returns), and
|
||||
// CONVERGES the instant the quorum comes back — all IN-PROCESS, with no pod restart (the K8s probes
|
||||
// poll the always-green /ext/health/liveness and would never restart it). This is the bounded
|
||||
// poll the always-green /v1/health/liveness and would never restart it). This is the bounded
|
||||
// re-bootstrap retry that closes the "permanent brick" RED flagged.
|
||||
func TestRED_MajorityOutageSelfHealsWhenQuorumReturns(t *testing.T) {
|
||||
const N, K = 30, 16 // stale at N; the live frontier (once the quorum returns) is N+K
|
||||
@@ -1852,8 +1936,8 @@ func TestBootstrap_AcceptedHeight_StoreVsAcceptance(t *testing.T) {
|
||||
const M = 30
|
||||
const Top = 40
|
||||
chain, _ := buildBSChain(Top, -1)
|
||||
vm := newBSVMAt(chain, M) // accepted 0..M
|
||||
vm.store(chain[M+1 : Top+1]...) // M+1..Top GOSSIPED into the store but UNACCEPTED
|
||||
vm := newBSVMAt(chain, M) // accepted 0..M
|
||||
vm.store(chain[M+1 : Top+1]...) // M+1..Top GOSSIPED into the store but UNACCEPTED
|
||||
bh := &blockHandler{logger: log.NewNoOpLogger(), vm: vm}
|
||||
ctx := context.Background()
|
||||
|
||||
|
||||
+63
-10
@@ -12,7 +12,7 @@
|
||||
// to recover from. Bootstrap trust was braided into consensus finality, and finality's ⅔ rule
|
||||
// is mathematically unsatisfiable during a mass outage.
|
||||
//
|
||||
// The fix is a type split, NOT a renamed threshold. ConsensusQuorum decides FINALITY
|
||||
// The fix is a type split, NOT a renamed threshold. FinalityQuorum decides FINALITY
|
||||
// (> ⅔ of CURRENT stake — UNCHANGED). BootstrapTrust decides whether a fetched frontier is
|
||||
// SAFE TO BEGIN SYNC FROM: a quorum of AUTHENTICATED CONFIGURED beacons that RESPOND, gated by
|
||||
// a response FLOOR (MinResponses) and an agreement threshold over the RESPONDERS (not over the
|
||||
@@ -24,6 +24,7 @@ package chains
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
@@ -37,30 +38,30 @@ import (
|
||||
|
||||
// BootstrapTrust is not a consensus-finality oracle.
|
||||
// It selects a weak-subjective sync frontier from authenticated configured beacons.
|
||||
// Live block acceptance remains governed exclusively by ConsensusQuorum.
|
||||
// Live block acceptance remains governed exclusively by FinalityQuorum.
|
||||
type BootstrapTrust interface {
|
||||
// AcceptsFrontier returns the block an empty/behind node may BEGIN SYNCING FROM, selected
|
||||
// from the authenticated configured beacons' frontier replies — or an error
|
||||
// (ErrInsufficientBootstrapResponses / ErrNoBootstrapQuorum) when no trusted frontier can be
|
||||
// named this round. The returned Frontier is a sync ANCHOR, never a consensus certificate
|
||||
// (see the type comment): the node must still re-execute every block it descends to before
|
||||
// re-entering live consensus, where ConsensusQuorum alone governs acceptance.
|
||||
// re-entering live consensus, where FinalityQuorum alone governs acceptance.
|
||||
AcceptsFrontier(ctx context.Context, replies []BeaconReply) (*Frontier, error)
|
||||
}
|
||||
|
||||
// ConsensusQuorum decides FINALITY: whether a weight is a finalizing supermajority (> ⅔) of the
|
||||
// FinalityQuorum decides FINALITY: whether a weight is a finalizing supermajority (> ⅔) of the
|
||||
// CURRENT validator set. This is the live-consensus rule; bootstrap does NOT change it. It is a
|
||||
// SEPARATE named type from BootstrapTrust precisely so the distinction is explicit and testable:
|
||||
// a frontier that AcceptsFrontier admits is "safe to sync from", and in general it does NOT
|
||||
// satisfy HasFinality (3 of 5 responders is a valid sync anchor; 3 of 5 stake is not finality).
|
||||
type ConsensusQuorum interface {
|
||||
type FinalityQuorum interface {
|
||||
HasFinality(weight, total StakeWeight) bool
|
||||
}
|
||||
|
||||
// StakeWeight is validator stake in the units the validator manager reports (Weight/Light).
|
||||
type StakeWeight = uint64
|
||||
|
||||
// twoThirdsFinality is the production ConsensusQuorum: > ⅔ of the CURRENT total stake, exactly
|
||||
// twoThirdsFinality is the production FinalityQuorum: > ⅔ of the CURRENT total stake, exactly
|
||||
// the rule the live cert-gate uses (consensusconfig.TwoThirdsStakeFloor). Defined here only to
|
||||
// give the live rule a name to CONTRAST bootstrap trust against — it is not wired into the live
|
||||
// path (that already enforces ⅔ inside consensus), and bootstrap never calls it to ACCEPT.
|
||||
@@ -70,10 +71,10 @@ func (twoThirdsFinality) HasFinality(weight, total StakeWeight) bool {
|
||||
return weight > consensusconfig.TwoThirdsStakeFloor(total)
|
||||
}
|
||||
|
||||
// DefaultConsensusQuorum returns the live ⅔-of-current-stake finality rule — the thing bootstrap
|
||||
// DefaultFinalityQuorum returns the live ⅔-of-current-stake finality rule — the thing bootstrap
|
||||
// trust is explicitly NOT. Used by the test suite to prove a bootstrap-accepted frontier does
|
||||
// not constitute finality.
|
||||
func DefaultConsensusQuorum() ConsensusQuorum { return twoThirdsFinality{} }
|
||||
func DefaultFinalityQuorum() FinalityQuorum { return twoThirdsFinality{} }
|
||||
|
||||
var (
|
||||
// ErrInsufficientBootstrapResponses: fewer than MinResponses configured beacons answered.
|
||||
@@ -127,9 +128,48 @@ type AncestrySource interface {
|
||||
// Checkpoint is an operator-pinned (id, height) the recovering node may anchor to when too few
|
||||
// beacons respond to form a quorum — the EXPLICIT override for INVARIANT 2's "1 of N reachable"
|
||||
// case. Absent (nil) ⇒ the default policy REJECTS rather than trusting a captured minority.
|
||||
//
|
||||
// INVARIANT 4 (a checkpoint is a SIGNED weak-subjectivity anchor, not a bare config value): the
|
||||
// checkpoint carries a cryptographic Signature by the configured checkpoint AUTHORITY over its
|
||||
// (id, height), and AcceptsFrontier trusts it ONLY when CheckpointVerifier authenticates that
|
||||
// signature. A (id,height) present in a flag/config but UNSIGNED — or signed by a non-authority key
|
||||
// — is REJECTED (fail closed). This is the crucial hardening: the checkpoint is the one path that
|
||||
// bypasses the beacon quorum, so a compromised flag must NOT be able to inject a false sync anchor
|
||||
// without ALSO forging the authority's signature. It is a cryptographic vouch, NEVER a ⅔-live-stake
|
||||
// tally (that conflation is the very deadlock BootstrapTrust exists to avoid).
|
||||
type Checkpoint struct {
|
||||
ID ids.ID
|
||||
Height uint64
|
||||
// Signature is the checkpoint authority's signature over this checkpoint's canonical (id,height)
|
||||
// bytes. Verified by CheckpointVerifier before the anchor is trusted; an empty signature is
|
||||
// never accepted.
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
// CheckpointVerifier authenticates a Checkpoint's Signature against the configured checkpoint
|
||||
// AUTHORITY key(s). The node injects a real implementation backed by a PROVEN primitive (Ed25519 /
|
||||
// BLS — never custom crypto); the policy stays free of any crypto dependency, exactly like
|
||||
// AncestrySource and heightOf. A nil verifier means no signed anchor is configured, so any
|
||||
// Checkpoint is untrusted and the below-floor case fails closed.
|
||||
type CheckpointVerifier interface {
|
||||
// VerifyCheckpoint reports whether sig is a valid signature over (id, height) by the configured
|
||||
// checkpoint authority. It MUST reject an empty signature and be signature-safe (constant-time
|
||||
// compare on the primitive). It is the sole authority on whether a pinned anchor may be trusted.
|
||||
VerifyCheckpoint(id ids.ID, height uint64, sig []byte) bool
|
||||
}
|
||||
|
||||
// CanonicalCheckpointMessage is the exact byte string a checkpoint authority signs and
|
||||
// CheckpointVerifier authenticates: a domain-separated, fixed-layout encoding of (id, height) so a
|
||||
// signature can never be transplanted from another context. 8-byte big-endian height after the
|
||||
// 32-byte id, under a distinct domain tag.
|
||||
func CanonicalCheckpointMessage(id ids.ID, height uint64) []byte {
|
||||
const domain = "lux-bootstrap-checkpoint-v1\x00"
|
||||
msg := make([]byte, 0, len(domain)+len(id)+8)
|
||||
msg = append(msg, domain...)
|
||||
msg = append(msg, id[:]...)
|
||||
var h [8]byte
|
||||
binary.BigEndian.PutUint64(h[:], height)
|
||||
return append(msg, h[:]...)
|
||||
}
|
||||
|
||||
// Ratio is an exact rational threshold (e.g. 2/3, 3/4). A value clears it iff
|
||||
@@ -152,7 +192,7 @@ func (r Ratio) floorOf(whole uint64) uint64 {
|
||||
}
|
||||
|
||||
// BootstrapPolicy is the default BootstrapTrust: a CONFIGURED-BEACON quorum with a response
|
||||
// FLOOR and an agreement threshold over the RESPONDERS — a SEPARATE object from ConsensusQuorum
|
||||
// FLOOR and an agreement threshold over the RESPONDERS — a SEPARATE object from FinalityQuorum
|
||||
// with a SEPARATE threat model. It does NOT pass "reachable stake" into the ⅔-of-current-stake
|
||||
// finality rule (that conflation IS the mass-recovery deadlock). It reuses the ancestor-tolerant
|
||||
// common-ancestor tally only for HOW to find the agreed frontier; the ACCEPTANCE gate is the
|
||||
@@ -197,8 +237,12 @@ type BootstrapPolicy struct {
|
||||
// network, or a fleet unanimously AT the tip).
|
||||
MinFrontierHeight uint64
|
||||
// Checkpoint is the OPTIONAL operator override for the below-floor case (INVARIANT 2). nil ⇒
|
||||
// reject below the floor.
|
||||
// reject below the floor. When set, it is trusted ONLY if CheckpointVerifier authenticates its
|
||||
// signature (INVARIANT 4).
|
||||
Checkpoint *Checkpoint
|
||||
// CheckpointVerifier authenticates the Checkpoint's authority signature (INVARIANT 4). nil ⇒ a
|
||||
// configured Checkpoint is NOT trusted (fail closed) — a bare (id,height) is never enough.
|
||||
CheckpointVerifier CheckpointVerifier
|
||||
// NamingWindow bounds the ancestry fetched per anchor; MaxAnchors bounds how many distinct
|
||||
// reported tips are resolved. Both default to the package constants when zero.
|
||||
NamingWindow int
|
||||
@@ -330,6 +374,15 @@ func (p *BootstrapPolicy) AcceptsFrontier(ctx context.Context, replies []BeaconR
|
||||
// operator explicitly pinned a checkpoint to anchor from.
|
||||
if !p.floorMet(responders, responderWeight) {
|
||||
if p.Checkpoint != nil {
|
||||
// INVARIANT 4: the checkpoint bypasses the beacon quorum, so trust it ONLY when the
|
||||
// configured authority SIGNED this exact (id,height). A checkpoint present in config but
|
||||
// unsigned, or signed by a non-authority key, is REJECTED (fail closed) — a compromised
|
||||
// flag cannot inject a false sync anchor without also forging the authority's signature.
|
||||
if p.CheckpointVerifier == nil || len(p.Checkpoint.Signature) == 0 ||
|
||||
!p.CheckpointVerifier.VerifyCheckpoint(p.Checkpoint.ID, p.Checkpoint.Height, p.Checkpoint.Signature) {
|
||||
return nil, fmt.Errorf("%w: a checkpoint is pinned but its authority signature did not verify",
|
||||
ErrInsufficientBootstrapResponses)
|
||||
}
|
||||
return &Frontier{
|
||||
ID: p.Checkpoint.ID,
|
||||
Height: p.Checkpoint.Height,
|
||||
|
||||
+100
-12
@@ -16,6 +16,7 @@ package chains
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -302,7 +303,7 @@ func TestBootstrapTrust_F_SplitReachableAncestrySelectsCommonAncestor(t *testing
|
||||
|
||||
// TestBootstrapTrust_G_FinalityUnchanged proves INVARIANT 3: a bootstrap-accepted frontier is NOT
|
||||
// finality. The SAME 3-of-5 support that AcceptsFrontier admits as a sync anchor does NOT satisfy
|
||||
// ConsensusQuorum.HasFinality — live block acceptance still requires > ⅔ of CURRENT validator
|
||||
// FinalityQuorum.HasFinality — live block acceptance still requires > ⅔ of CURRENT validator
|
||||
// stake (4 of 5 here). The bootstrap quorum cannot finalize a block.
|
||||
func TestBootstrapTrust_G_FinalityUnchanged(t *testing.T) {
|
||||
const w uint64 = 100
|
||||
@@ -321,39 +322,76 @@ func TestBootstrapTrust_G_FinalityUnchanged(t *testing.T) {
|
||||
require.Equal(t, frontier, f.ID)
|
||||
require.Equal(t, StakeWeight(3*w), f.Weight, "the frontier is backed by exactly the 3 responders")
|
||||
|
||||
// ConsensusQuorum says that SAME 3-of-5 weight is NOT finality — the decisions are different
|
||||
// FinalityQuorum says that SAME 3-of-5 weight is NOT finality — the decisions are different
|
||||
// objects with different thresholds. Finality is unchanged: it still needs > ⅔ (4 of 5).
|
||||
cq := DefaultConsensusQuorum()
|
||||
cq := DefaultFinalityQuorum()
|
||||
require.False(t, cq.HasFinality(3*w, total),
|
||||
"INVARIANT 3: a bootstrap-accepted frontier (3 of 5) is NOT a finalizing supermajority")
|
||||
require.True(t, cq.HasFinality(4*w, total),
|
||||
"finality UNCHANGED: > ⅔ of current stake (4 of 5) still finalizes")
|
||||
require.False(t, cq.HasFinality(f.Weight, total),
|
||||
"the bootstrap quorum's own backing weight cannot finalize a block")
|
||||
|
||||
// AFTER-SYNC (the owner's "bootstrap is not a finality bypass"): the node has now SYNCED to the
|
||||
// frontier via BootstrapTrust and re-entered live consensus. A NEW block that collects the SAME
|
||||
// 3-of-5 stake STILL does not finalize — bootstrap admitted a sync ANCHOR, it did not lower the
|
||||
// finality bar. Live acceptance returns to strict > ⅔ of CURRENT stake, exactly as before any
|
||||
// bootstrap. HasFinality is stateless in the bootstrap outcome, which is the whole point: there
|
||||
// is no code path by which "we bootstrapped from 3/5" leaks into the finality decision.
|
||||
require.False(t, cq.HasFinality(3*w, total),
|
||||
"AFTER syncing from a 3-of-5 bootstrap frontier, live finality STILL needs > ⅔ (4 of 5) — no bypass")
|
||||
}
|
||||
|
||||
// ----- checkpoint override (complements B) ----------------------------------
|
||||
|
||||
// edCheckpointAuthority is a test checkpoint authority backed by Ed25519 — a PROVEN primitive, no
|
||||
// custom crypto. It signs a checkpoint's canonical (id,height) message and verifies against its own
|
||||
// public key, rejecting an empty signature and any key that is not the configured authority.
|
||||
type edCheckpointAuthority struct {
|
||||
priv ed25519.PrivateKey
|
||||
pub ed25519.PublicKey
|
||||
}
|
||||
|
||||
func newEdCheckpointAuthority(t *testing.T) *edCheckpointAuthority {
|
||||
t.Helper()
|
||||
pub, priv, err := ed25519.GenerateKey(nil)
|
||||
require.NoError(t, err)
|
||||
return &edCheckpointAuthority{priv: priv, pub: pub}
|
||||
}
|
||||
|
||||
func (a *edCheckpointAuthority) sign(id ids.ID, height uint64) []byte {
|
||||
return ed25519.Sign(a.priv, CanonicalCheckpointMessage(id, height))
|
||||
}
|
||||
|
||||
// VerifyCheckpoint implements CheckpointVerifier: authenticate against the authority's public key.
|
||||
func (a *edCheckpointAuthority) VerifyCheckpoint(id ids.ID, height uint64, sig []byte) bool {
|
||||
return len(sig) != 0 && ed25519.Verify(a.pub, CanonicalCheckpointMessage(id, height), sig)
|
||||
}
|
||||
|
||||
// TestBootstrapTrust_CheckpointOverride: below the response floor (1 of 5), the DEFAULT is reject
|
||||
// (test B), but an operator who pins a checkpoint gets the explicit override — the node anchors to
|
||||
// the pinned (id,height) instead of trusting the lone beacon. This is the sanctioned escape hatch
|
||||
// for a deeply-partitioned node, NEVER an open-ended ≥1-beacon acceptance.
|
||||
// (test B), but an operator who pins a SIGNED checkpoint gets the explicit override — the node
|
||||
// anchors to the authenticated (id,height) instead of trusting the lone beacon. This is the
|
||||
// sanctioned escape hatch for a deeply-partitioned node, NEVER an open-ended ≥1-beacon acceptance,
|
||||
// and (INVARIANT 4) NEVER a bare unsigned config value.
|
||||
func TestBootstrapTrust_CheckpointOverride(t *testing.T) {
|
||||
beacons := nodeIDs(5)
|
||||
authority := newEdCheckpointAuthority(t)
|
||||
ckptID := ids.GenerateTestID()
|
||||
const ckptHeight = uint64(1_082_796)
|
||||
policy := &BootstrapPolicy{
|
||||
TrustedBeacons: equalBeacons(beacons, equalStake),
|
||||
MinResponses: 3,
|
||||
Checkpoint: &Checkpoint{ID: ckptID, Height: 1_082_796},
|
||||
TrustedBeacons: equalBeacons(beacons, equalStake),
|
||||
MinResponses: 3,
|
||||
Checkpoint: &Checkpoint{ID: ckptID, Height: ckptHeight, Signature: authority.sign(ckptID, ckptHeight)},
|
||||
CheckpointVerifier: authority,
|
||||
}
|
||||
// 1 reachable beacon — below the floor — but a checkpoint is pinned.
|
||||
// 1 reachable beacon — below the floor — but a SIGNED checkpoint is pinned.
|
||||
f, err := policy.AcceptsFrontier(context.Background(), []BeaconReply{
|
||||
reply(beacons[0], ids.GenerateTestID(), equalStake),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.True(t, f.FromCheckpoint, "below the floor with a pinned checkpoint → anchor to the checkpoint")
|
||||
require.True(t, f.FromCheckpoint, "below the floor with a SIGNED checkpoint → anchor to the checkpoint")
|
||||
require.Equal(t, ckptID, f.ID)
|
||||
require.Equal(t, uint64(1_082_796), f.Height)
|
||||
require.Equal(t, ckptHeight, f.Height)
|
||||
|
||||
// Without the checkpoint the same 1-of-5 is rejected (the default — never trust the lone beacon).
|
||||
policy.Checkpoint = nil
|
||||
@@ -363,6 +401,56 @@ func TestBootstrapTrust_CheckpointOverride(t *testing.T) {
|
||||
require.ErrorIs(t, err, ErrInsufficientBootstrapResponses)
|
||||
}
|
||||
|
||||
// TestBootstrapTrust_CheckpointMustBeSigned is INVARIANT 4: a checkpoint that is present but not
|
||||
// AUTHENTICATED is REJECTED (fail closed). A compromised flag/config that pins a false (id,height)
|
||||
// cannot inject a sync anchor without the authority's signature. Four rejection modes, one accept.
|
||||
func TestBootstrapTrust_CheckpointMustBeSigned(t *testing.T) {
|
||||
beacons := nodeIDs(5)
|
||||
authority := newEdCheckpointAuthority(t)
|
||||
attacker := newEdCheckpointAuthority(t) // a DIFFERENT key — not the configured authority
|
||||
ckptID := ids.GenerateTestID()
|
||||
const h = uint64(500_000)
|
||||
lone := []BeaconReply{reply(beacons[0], ids.GenerateTestID(), equalStake)} // 1-of-5, below floor
|
||||
|
||||
base := func() *BootstrapPolicy {
|
||||
return &BootstrapPolicy{TrustedBeacons: equalBeacons(beacons, equalStake), MinResponses: 3, CheckpointVerifier: authority}
|
||||
}
|
||||
|
||||
// (1) UNSIGNED checkpoint (empty signature) → rejected even with a verifier wired.
|
||||
p := base()
|
||||
p.Checkpoint = &Checkpoint{ID: ckptID, Height: h}
|
||||
_, err := p.AcceptsFrontier(context.Background(), lone)
|
||||
require.ErrorIs(t, err, ErrInsufficientBootstrapResponses, "an UNSIGNED checkpoint must be rejected")
|
||||
|
||||
// (2) signed by a NON-AUTHORITY (attacker) key → rejected.
|
||||
p = base()
|
||||
p.Checkpoint = &Checkpoint{ID: ckptID, Height: h, Signature: attacker.sign(ckptID, h)}
|
||||
_, err = p.AcceptsFrontier(context.Background(), lone)
|
||||
require.ErrorIs(t, err, ErrInsufficientBootstrapResponses, "a checkpoint signed by a non-authority key must be rejected")
|
||||
|
||||
// (3) authority signature over a DIFFERENT (id,height) — replay onto a forged anchor → rejected.
|
||||
p = base()
|
||||
forgedID := ids.GenerateTestID()
|
||||
p.Checkpoint = &Checkpoint{ID: forgedID, Height: h, Signature: authority.sign(ckptID, h)} // sig binds ckptID, not forgedID
|
||||
_, err = p.AcceptsFrontier(context.Background(), lone)
|
||||
require.ErrorIs(t, err, ErrInsufficientBootstrapResponses, "a signature transplanted to a different (id,height) must be rejected")
|
||||
|
||||
// (4) NO verifier configured → any checkpoint is untrusted (fail closed).
|
||||
p = base()
|
||||
p.CheckpointVerifier = nil
|
||||
p.Checkpoint = &Checkpoint{ID: ckptID, Height: h, Signature: authority.sign(ckptID, h)}
|
||||
_, err = p.AcceptsFrontier(context.Background(), lone)
|
||||
require.ErrorIs(t, err, ErrInsufficientBootstrapResponses, "no verifier ⇒ even a validly-signed checkpoint is untrusted")
|
||||
|
||||
// (accept) authority signs the exact pinned (id,height) → trusted.
|
||||
p = base()
|
||||
p.Checkpoint = &Checkpoint{ID: ckptID, Height: h, Signature: authority.sign(ckptID, h)}
|
||||
f, err := p.AcceptsFrontier(context.Background(), lone)
|
||||
require.NoError(t, err)
|
||||
require.True(t, f.FromCheckpoint)
|
||||
require.Equal(t, ckptID, f.ID)
|
||||
}
|
||||
|
||||
// ----- safety guard for the global ancestor-tolerant tally ------------------
|
||||
|
||||
// TestBootstrapTrust_ForkAtSharedGenesisFailsSafe is the load-bearing guard for the
|
||||
|
||||
@@ -46,6 +46,30 @@ func (s *Nets) GetOrCreate(chainID ids.ID) (nets.Net, bool) {
|
||||
return chain, true
|
||||
}
|
||||
|
||||
// IsChainBootstrapped reports whether the given chain has finished initial sync
|
||||
// (reached the network frontier and transitioned its VM to normal operation) on
|
||||
// this node — Bootstrapped(chainID) was called for it in its validation net. A
|
||||
// chain that is merely tracked (its sync goroutine launched) but has NOT converged
|
||||
// reads false. This is the per-chain truth manager.IsBootstrapped / info.isBootstrapped
|
||||
// key on, replacing the mere-existence test that returned true the instant a chain
|
||||
// was added to the manager (the premature-true masking bug: a C-Chain stalled at
|
||||
// genesis reported bootstrapped=true). A chainID is added to exactly one net's
|
||||
// tracking, so the first net that reports it bootstrapped is authoritative.
|
||||
func (s *Nets) IsChainBootstrapped(chainID ids.ID) bool {
|
||||
if s == nil {
|
||||
return false // no net tracking wired ⇒ nothing has been marked bootstrapped
|
||||
}
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
for _, chain := range s.chains {
|
||||
if chain.IsChainBootstrapped(chainID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Bootstrapping returns the chainIDs of any chains that are still
|
||||
// bootstrapping.
|
||||
func (s *Nets) Bootstrapping() []ids.ID {
|
||||
|
||||
+206
-24
@@ -15,6 +15,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
gatomic "sync/atomic"
|
||||
@@ -219,7 +220,7 @@ type ChainParameters struct {
|
||||
FxIDs []ids.ID
|
||||
// Invariant: Only used when [ID] is the P-chain ID.
|
||||
CustomBeacons validators.Manager
|
||||
// Name of the chain (used for HTTP routing alias, e.g., /ext/bc/zoo/rpc)
|
||||
// Name of the chain (used for HTTP routing alias, e.g., /v1/bc/zoo/rpc)
|
||||
Name string
|
||||
}
|
||||
|
||||
@@ -743,7 +744,7 @@ func (m *manager) createChain(chainParams ChainParameters) {
|
||||
// plugin shows up later (e.g. via lpm install).
|
||||
//
|
||||
// The old behavior (always register a failing health check) made
|
||||
// /ext/health return 503 for any opted-out chain, which made
|
||||
// /v1/health return 503 for any opted-out chain, which made
|
||||
// kubelet liveness probes kill the validator pod. That made
|
||||
// chain participation effectively all-or-nothing per validator.
|
||||
// Now: validators participate per-plugin, opt-in.
|
||||
@@ -855,7 +856,7 @@ func (m *manager) createChain(chainParams ChainParameters) {
|
||||
m.Server.AddRoute(handler, chainIDBase, endpoint)
|
||||
}
|
||||
|
||||
// Also register with chain name alias for user-friendly routing (e.g., /ext/bc/zoo/rpc)
|
||||
// Also register with chain name alias for user-friendly routing (e.g., /v1/bc/zoo/rpc)
|
||||
if chainParams.Name != "" {
|
||||
nameLower := strings.ToLower(chainParams.Name)
|
||||
nameBase := fmt.Sprintf("bc/%s", nameLower)
|
||||
@@ -928,9 +929,9 @@ func (m *manager) createChain(chainParams ChainParameters) {
|
||||
m.Log.Info("║ VM ID:", log.Stringer("vmID", chainParams.VMID))
|
||||
m.Log.Info("║ Network ID:", log.Stringer("chainID", chainParams.ChainID))
|
||||
m.Log.Info("║ Endpoints available at:")
|
||||
m.Log.Info("║ → /ext/bc/" + chainParams.ID.String())
|
||||
m.Log.Info("║ → /v1/bc/" + chainParams.ID.String())
|
||||
if chainAlias != chainParams.ID.String() {
|
||||
m.Log.Info("║ → /ext/bc/" + chainAlias)
|
||||
m.Log.Info("║ → /v1/bc/" + chainAlias)
|
||||
}
|
||||
m.Log.Info("╚══════════════════════════════════════════════════════════════════╝")
|
||||
|
||||
@@ -1199,6 +1200,19 @@ func (m *manager) buildChain(chainParams ChainParameters, sb nets.Net) (*chainIn
|
||||
return nil, fmt.Errorf("refusing to start multi-node chain %s with non-BFT consensus params: %w", chainParams.ID, err)
|
||||
}
|
||||
}
|
||||
// Round-scoped view-change (restores liveness under competing siblings + a zero-margin
|
||||
// quorum — the 415→416 freeze). OPT-IN per deployment via LUX_CONSENSUS_VIEW_CHANGE=true
|
||||
// so devnet/testnet can enable it without a mainnet default (mainnet is owner-gated). Only
|
||||
// meaningful on a multi-validator (K>1) chain; K==1 has no competing proposers. The engine
|
||||
// itself fail-secure HALTS the view-change if the committee fails the 2α−n>f bound, so
|
||||
// enabling it can never weaken safety — at worst it halts (never forks).
|
||||
if consensusParams.K > 1 && strings.EqualFold(os.Getenv("LUX_CONSENSUS_VIEW_CHANGE"), "true") {
|
||||
consensusParams.ViewChange = true
|
||||
m.Log.Info("round-scoped view-change ENABLED for chain",
|
||||
log.Stringer("chainID", chainParams.ID),
|
||||
log.Int("K", consensusParams.K),
|
||||
log.Int("alpha", consensusParams.AlphaConfidence))
|
||||
}
|
||||
_, innerIsDAGNative := vmTyped.(interface {
|
||||
Linearize(context.Context, ids.ID, chan<- vm.Message) error
|
||||
})
|
||||
@@ -1516,8 +1530,67 @@ func (m *manager) buildChain(chainParams ChainParameters, sb nets.Net) (*chainIn
|
||||
log.Stringer("networkID", networkID))
|
||||
}
|
||||
}
|
||||
// DEFENSIVE (view-change hygiene): a view-change chain that cannot BROADCAST its
|
||||
// prevotes/precommits (its gossiper is not a QuorumGossiper) or cannot SIGN them (no
|
||||
// VoteSigner) would emit votes into the void — every node tallies only its OWN vote, no
|
||||
// peer ever receives one, α is never reached, and finality silently stalls. Refuse to
|
||||
// start such a chain LOUDLY rather than freeze in production.
|
||||
if consensusParams.ViewChange {
|
||||
if _, ok := netCfg.Gossiper.(consensuschain.QuorumGossiper); !ok {
|
||||
return nil, fmt.Errorf("refusing to start view-change chain %s: gossiper %T does not implement "+
|
||||
"QuorumGossiper — prevotes/precommits could not be broadcast and finality would silently stall",
|
||||
chainParams.ID, netCfg.Gossiper)
|
||||
}
|
||||
if netCfg.VoteSigner == nil {
|
||||
return nil, fmt.Errorf("refusing to start view-change chain %s: no VoteSigner wired — this node "+
|
||||
"could not sign its prevotes/precommits and finality would silently stall", chainParams.ID)
|
||||
}
|
||||
}
|
||||
consensusEngine := consensuschain.NewRuntime(netCfg)
|
||||
|
||||
// STALE-LOCK MIGRATION (one-shot, operator-gated). AFTER NewRuntime seeds the durable
|
||||
// view-change locks and BEFORE Start drives any view, optionally CANONICALIZE (or prune)
|
||||
// pre-fix OUTER-wrapper view-change lock metadata ABOVE the decided floor — the block-1082880
|
||||
// durable split-lock: locks stored by the proposervm outer id that the canonical=inner roll
|
||||
// never re-canonicalized, so honest nodes stay locked on stale aliases, split below α, and
|
||||
// never form a POL. All finalized + vote-guard state AT OR BELOW the floor is preserved
|
||||
// verbatim and the floor is never lowered — a narrow metadata migration, not a state reset.
|
||||
// LUX_CONSENSUS_MIGRATE_STALE_LOCKS: "inspect" prints the plan and mutates NOTHING (the
|
||||
// per-pod audit gate); "apply:<floor>" performs the idempotent, safety-gated repair, where
|
||||
// <floor> is the decided floor the operator OBSERVED via inspect (1082879 for the mainnet
|
||||
// one-shot). The explicit target makes the apply SELF-DISARMING: once the chain recovers
|
||||
// and the floor advances past the target, a lingering env no-ops instead of degrading into
|
||||
// an every-boot prune of genuine crash locks (the HIGH-1 regression). A bare "apply" is
|
||||
// REFUSED. Only for a K>1 view-change chain (the only chains that carry these locks).
|
||||
if consensusParams.K > 1 && consensusParams.ViewChange {
|
||||
env := strings.ToLower(os.Getenv("LUX_CONSENSUS_MIGRATE_STALE_LOCKS"))
|
||||
switch {
|
||||
case env == "inspect":
|
||||
logStaleLockReport(m.Log, chainParams.ID, "inspect (no write)", consensusEngine.InspectLocks(context.Background()))
|
||||
case env == "apply" || strings.HasPrefix(env, "apply:"):
|
||||
target, tErr := strconv.ParseUint(strings.TrimPrefix(env, "apply:"), 10, 64)
|
||||
if env == "apply" || tErr != nil {
|
||||
m.Log.Error("stale-lock migration REFUSED: apply requires an explicit floor target — "+
|
||||
"run inspect, read decidedFloor, then set LUX_CONSENSUS_MIGRATE_STALE_LOCKS=apply:<decidedFloor>",
|
||||
log.Stringer("chainID", chainParams.ID), log.String("env", env))
|
||||
break
|
||||
}
|
||||
rep, mErr := consensusEngine.MigrateStaleLocks(context.Background(), target)
|
||||
logStaleLockReport(m.Log, chainParams.ID, "apply", rep)
|
||||
if mErr != nil {
|
||||
return nil, fmt.Errorf("stale-lock migration for chain %s failed (fail-closed, nothing changed): %w", chainParams.ID, mErr)
|
||||
}
|
||||
if rep.Stop {
|
||||
m.Log.Error("stale-lock migration STOPPED — manual recovery required (no write performed)",
|
||||
log.Stringer("chainID", chainParams.ID), log.String("reason", rep.StopReason))
|
||||
}
|
||||
if rep.Skipped {
|
||||
m.Log.Warn("stale-lock migration self-disarmed (no write) — unset LUX_CONSENSUS_MIGRATE_STALE_LOCKS",
|
||||
log.Stringer("chainID", chainParams.ID), log.String("reason", rep.SkipReason))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the consensus engine with a LIFETIME context (not a timeout):
|
||||
// engine.Start parents all four long-running loops (poll, vote, pipeline,
|
||||
// re-poll) to this ctx, so a WithTimeout here kills them ~30s after the
|
||||
@@ -2080,7 +2153,7 @@ func (m *manager) monitorBootstrap(engine Engine, h handler.Handler, sb nets.Net
|
||||
// this true. The no-progress watchdog treats it as a deliberate quorum WAIT, not a stall: it must
|
||||
// NOT force-STOP a node that is correctly failing safe DOWN and waiting for its quorum to return
|
||||
// (the network cannot progress without the quorum, and the K8s probes — all polling the
|
||||
// always-green /ext/health/liveness — would never restart it, so a stop here is a permanent
|
||||
// always-green /v1/health/liveness — would never restart it, so a stop here is a permanent
|
||||
// brick). The node stays in Bootstrapping (serving nothing as head) and converges when the quorum
|
||||
// returns. nil for degenerate handlers (legacy behavior).
|
||||
type bootstrapConnector interface{ BootstrapConnecting() bool }
|
||||
@@ -2252,6 +2325,17 @@ func watchBootstrapProgress(
|
||||
}
|
||||
}
|
||||
|
||||
// IsBootstrapped reports whether [id] has ACTUALLY finished initial sync on this
|
||||
// node: the chain exists AND its validation net has marked it bootstrapped —
|
||||
// monitorBootstrap called sb.Bootstrapped(id) only after runInitialSync reached the
|
||||
// named network frontier and transitioned the VM to normal operation (head advanced
|
||||
// to the frontier, eth-RPC serving live). The old body returned true the instant the
|
||||
// chain merely EXISTED in m.chains — set right after createChain launched the (async,
|
||||
// possibly-stalling) bootstrap goroutine — so a C-Chain stalled at genesis (head 0x0,
|
||||
// never converged) still reported info.isBootstrapped(C)=true, masking the stall from
|
||||
// any readiness gate keyed on it. Keying on the SAME sb.Bootstrapped signal the health
|
||||
// check (m.Nets.Bootstrapping) already uses makes info.isBootstrapped track real state,
|
||||
// so a hands-off rolling upgrade's wait-for-healthy gate can trust it.
|
||||
func (m *manager) IsBootstrapped(id ids.ID) bool {
|
||||
m.chainsLock.Lock()
|
||||
_, exists := m.chains[id]
|
||||
@@ -2259,9 +2343,7 @@ func (m *manager) IsBootstrapped(id ids.ID) bool {
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
// Bootstrapped chains start in NormalOp
|
||||
return true
|
||||
return m.Nets.IsChainBootstrapped(id)
|
||||
}
|
||||
|
||||
func (m *manager) GetChains() []ChainInfo {
|
||||
@@ -2271,10 +2353,12 @@ func (m *manager) GetChains() []ChainInfo {
|
||||
result := make([]ChainInfo, 0, len(m.chains))
|
||||
for id, info := range m.chains {
|
||||
result = append(result, ChainInfo{
|
||||
ID: id,
|
||||
Name: info.Name,
|
||||
VMID: info.VMID,
|
||||
Bootstrapped: true,
|
||||
ID: id,
|
||||
Name: info.Name,
|
||||
VMID: info.VMID,
|
||||
// Real per-chain convergence, not mere existence (same fix as IsBootstrapped):
|
||||
// a tracked-but-still-syncing chain reports Bootstrapped=false.
|
||||
Bootstrapped: m.Nets.IsChainBootstrapped(id),
|
||||
})
|
||||
}
|
||||
return result
|
||||
@@ -2723,13 +2807,21 @@ type blockHandler struct {
|
||||
// tells monitorBootstrap's no-progress watchdog this is a deliberate WAIT for the quorum to
|
||||
// return (the network cannot make progress without it), NOT a stall — so the watchdog does not
|
||||
// force-STOP a node that is correctly failing safe and waiting, which (given the K8s probes only
|
||||
// poll the always-green /ext/health/liveness) would otherwise be a permanent brick.
|
||||
// poll the always-green /v1/health/liveness) would otherwise be a permanent brick.
|
||||
bootstrapConnecting gatomic.Bool
|
||||
bsActive gatomic.Bool // true while the bootstrap loop is driving
|
||||
bsMu sync.Mutex // guards bsFrontierCh + bsAncestorCh
|
||||
bsMu sync.Mutex // guards bsFrontierCh + bsAncestorCh + bsAheadBeacons
|
||||
bsFrontierCh chan bsFrontierReply // weighted frontier replies for the current FrontierTip
|
||||
bsAncestorCh map[uint32]chan [][]byte // requestID -> ancestors reply for the current Ancestors
|
||||
bsRotor gatomic.Uint32 // round-robins the Ancestors peer sample (M1: no monopoly)
|
||||
// bsAheadBeacons is the set of beacons whose accepted tip, reported in the most recent
|
||||
// FrontierTip round, is a block this node does NOT hold — i.e. beacons genuinely AHEAD that
|
||||
// therefore HAVE the ancestry the descent needs. sampleAncestorBeacons prefers them so a
|
||||
// re-bootstrapping node asks GetAncestors of peers that can serve, never wasting the sample on
|
||||
// peers still at genesis (the ava PeerTracker "ask a prover" bias, without a full tracker).
|
||||
// Recorded in collectFrontierReplies; empty ⇒ sampleAncestorBeacons falls back to the full
|
||||
// rotated set (identical to prior behavior). Guarded by bsMu.
|
||||
bsAheadBeacons set.Set[ids.NodeID]
|
||||
|
||||
// vmReady transitions the VM to NORMAL OPERATION (vm.Ready → the EVM's
|
||||
// onNormalOperationsStarted: block building, mempool gossip, validator dispatch).
|
||||
@@ -2826,7 +2918,7 @@ type blockHandler struct {
|
||||
// DOWN — never live at a stale height) and CONVERGES the instant the quorum returns. ≤0 ⇒
|
||||
// UNLIMITED (retry until the quorum returns or shutdown) — the production default, because a
|
||||
// node without a quorum must keep trying to rejoin and the K8s liveness probe does NOT restart
|
||||
// it (all luxd probes poll the always-green /ext/health/liveness). A STRUCTURAL failure (deep
|
||||
// it (all luxd probes poll the always-green /v1/health/liveness). A STRUCTURAL failure (deep
|
||||
// gap → state-sync) is never retried regardless. Tests pin it to 1 to assert the single-attempt
|
||||
// terminal fail-safe in isolation.
|
||||
bootstrapMaxAttempts int
|
||||
@@ -3094,14 +3186,45 @@ func (b *blockHandler) requestContext(ctx context.Context, nodeID ids.NodeID, bl
|
||||
return
|
||||
}
|
||||
|
||||
nodeSet := set.NewSet[ids.NodeID](1)
|
||||
nodeSet.Add(nodeID)
|
||||
// PEER SELECTION (defect #3). The consensus layer signals "I hold a VERIFIED cert
|
||||
// for a block I don't track — fetch it" by passing ids.EmptyNodeID (topology.go:
|
||||
// requestCatchup(cert.Position.BlockID, ids.EmptyNodeID)); picking a real peer is
|
||||
// the node layer's job. The prior code blindly Add(EmptyNodeID) + Send, so
|
||||
// GetAncestors went to ZERO peers (the "sentTo=0" spam on the frozen fleet) and the
|
||||
// certified-but-untracked block was NEVER fetched — the node saw the cert, could not
|
||||
// finalize, and never recovered. When nodeID is Empty, sample real connected peers
|
||||
// that track this chain's network (the SAME selection pollFrontierOnce uses); a valid
|
||||
// cert already gated this request, so asking any network peer is sound (the served
|
||||
// gap is cert-verified on accept).
|
||||
nodeSet := set.NewSet[ids.NodeID](frontierPollSample)
|
||||
if nodeID == ids.EmptyNodeID {
|
||||
for _, p := range b.net.PeerInfo(nil) {
|
||||
if p.TrackedChains.Contains(b.networkID) {
|
||||
nodeSet.Add(p.ID)
|
||||
if nodeSet.Len() >= frontierPollSample {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nodeSet.Add(nodeID)
|
||||
}
|
||||
if nodeSet.Len() == 0 {
|
||||
// No reachable peer to serve the block. Release the pending slot so a later tick
|
||||
// (frontier poll → AcceptedFrontier, or a re-gossiped cert) can retry — otherwise
|
||||
// the block stays pinned unrequestable until the TTL reap.
|
||||
b.contextRequestMu.Lock()
|
||||
delete(b.pendingContext, blockID)
|
||||
b.contextRequestMu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
sentTo := b.net.Send(msg, nodeSet, b.networkID, 0)
|
||||
b.logger.Info("requested context for missing prerequisites",
|
||||
log.Stringer("from", nodeID),
|
||||
log.Stringer("blockID", blockID),
|
||||
log.Uint32("requestID", requestID),
|
||||
log.Int("asked", nodeSet.Len()),
|
||||
log.Int("sentTo", sentTo.Len()))
|
||||
}
|
||||
|
||||
@@ -3397,15 +3520,28 @@ func (b *blockHandler) AcceptedFrontier(ctx context.Context, nodeID ids.NodeID,
|
||||
if b.deliverBootstrapFrontier(nodeID, containerID) {
|
||||
return nil
|
||||
}
|
||||
if _, err := b.vm.GetBlock(ctx, containerID); err == nil {
|
||||
return nil // we already have the peer's tip — not behind
|
||||
}
|
||||
if b.engine != nil {
|
||||
if blk, err := b.vm.GetBlock(ctx, containerID); err == nil {
|
||||
// HAVE-BLOCK, LACK-FINALIZATION (defect #5). Holding the peer's tip block does NOT
|
||||
// mean we are caught up. A verified-but-unfinalized block (we voted for it, but the
|
||||
// α-of-K cert never reached us) leaves us behind on the CERT, not the block bytes.
|
||||
// The prior check returned "not behind" on GetBlock success, so such a node NEVER
|
||||
// fetched the missing cert and sat stuck at its unfinalized height forever (the exact
|
||||
// "verified 288 but no cert" condition). Only "have the block AND it is finalized
|
||||
// here" is truly not-behind; otherwise fall through to fetch the cert-carrying gap so
|
||||
// AcceptCatchupBlock can finalize it on its verified cert (no re-vote).
|
||||
if b.engine == nil {
|
||||
return nil
|
||||
}
|
||||
if fin, ok := b.engine.FinalizedBlockAtHeight(blk.Height()); ok && fin == containerID {
|
||||
return nil // have the block AND finalized it — truly not behind
|
||||
}
|
||||
// have the block but not finalized here → behind on the cert → fetch below
|
||||
} else if b.engine != nil {
|
||||
if _, found := b.engine.GetPendingBlock(containerID); found {
|
||||
return nil // already tracked
|
||||
return nil // already tracked (pending) — the live path is handling it
|
||||
}
|
||||
}
|
||||
b.requestContext(ctx, nodeID, containerID) // behind → fetch the gap
|
||||
b.requestContext(ctx, nodeID, containerID) // behind (missing block OR its cert) → fetch the gap
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3791,6 +3927,10 @@ func (b *blockHandler) Gossip(ctx context.Context, nodeID ids.NodeID, msg []byte
|
||||
b.engine.HandleIncomingVote(blockID, payload)
|
||||
case quorumKindCert:
|
||||
b.engine.HandleIncomingCert(payload)
|
||||
case quorumKindPrevote:
|
||||
// Round-scoped view-change prevote: the engine decodes+verifies
|
||||
// (height,round,canonical,sig) from the payload and tallies it toward a POL.
|
||||
b.engine.HandleIncomingPrevote(payload)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -4036,6 +4176,30 @@ func (n *noopWarpSender) SendGossip(ctx context.Context, config warp.SendConfig,
|
||||
return nil
|
||||
}
|
||||
|
||||
// logStaleLockReport renders a stale-lock migration plan as an auditable per-height trace table in
|
||||
// the node log (one line per lock above the decided floor: the persisted outer id, the inner
|
||||
// canonical it resolves to, the lock round, whether a cert binds the height, and the planned
|
||||
// disposition in the Reason). Used by both the read-only inspect mode and the apply mode so the
|
||||
// operator sees the identical plan before and after a write.
|
||||
func logStaleLockReport(logger log.Logger, chainID ids.ID, mode string, rep consensuschain.LockMigrationReport) {
|
||||
logger.Warn("stale-lock migration report",
|
||||
log.Stringer("chainID", chainID), log.String("mode", mode),
|
||||
log.Uint64("decidedFloor", rep.DecidedFloor), log.Uint64("finalizedThrough", rep.FinalizedThrough),
|
||||
log.Int("locksAboveFloor", len(rep.Entries)), log.Bool("changed", rep.Changed),
|
||||
log.Bool("stop", rep.Stop), log.String("stopReason", rep.StopReason))
|
||||
for _, e := range rep.Entries {
|
||||
logger.Warn("stale-lock entry",
|
||||
log.Stringer("chainID", chainID),
|
||||
log.Uint64("height", e.Height),
|
||||
log.Stringer("lockOuter", e.LockOuter),
|
||||
log.Stringer("lockInner", e.LockCanon),
|
||||
log.Uint32("lockRound", e.LockRound),
|
||||
log.Bool("hasRound", e.HasRound),
|
||||
log.Bool("certAt", e.CertAt),
|
||||
log.String("plan", e.Reason))
|
||||
}
|
||||
}
|
||||
|
||||
// networkGossiper implements consensuschain.Gossiper for Lux consensus integration.
|
||||
// It adapts the node's network layer to the minimal Gossiper interface used by
|
||||
// the integrated consensus engine.
|
||||
@@ -4105,6 +4269,24 @@ func (g *networkGossiper) BroadcastVote(chainID ids.ID, networkID ids.ID, blockI
|
||||
return g.net.Gossip(msg, nil, g.networkID, -1, 0, 0).Len()
|
||||
}
|
||||
|
||||
// BroadcastPrevote sends this node's signed ROUND-SCOPED view-change prevote (the
|
||||
// non-binding preference signal) for `canonical` at (height, round) to ALL validators,
|
||||
// framed in a quorum envelope (kind 3) and decoded by blockHandler.Gossip into
|
||||
// engine.HandleIncomingPrevote. Prevotes never finalize anything — they drive the POL +
|
||||
// the lock/unlock rule that lets a competing-sibling split RE-CONVERGE (liveness under a
|
||||
// down proposer + zero-margin quorum). Only emitted when the chain runs params.ViewChange.
|
||||
func (g *networkGossiper) BroadcastPrevote(chainID ids.ID, networkID ids.ID, height uint64, round uint32, canonical ids.ID, voteBytes []byte) int {
|
||||
if g.net == nil || g.msgCreator == nil {
|
||||
return 0
|
||||
}
|
||||
envelope := encodeQuorumGossip(quorumKindPrevote, canonical, voteBytes)
|
||||
msg, err := g.msgCreator.Gossip(chainID, envelope)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return g.net.Gossip(msg, nil, g.networkID, -1, 0, 0).Len()
|
||||
}
|
||||
|
||||
// GossipCert broadcasts an assembled α-of-K finality cert to ALL validators so
|
||||
// followers finalize blockID on a verifiable proof (HandleIncomingCert), not a
|
||||
// fast-follow guess. Best effort: the gossiping node's own finality is already
|
||||
|
||||
@@ -123,8 +123,14 @@ func (m *manager) authorizeChainActivation(chainID ids.ID) (authorized bool, rea
|
||||
// authorization from an NFT held at an address it does not control.
|
||||
|
||||
// The gate consults the X-Chain UTXO set, so the X-Chain must already be
|
||||
// bootstrapped (created and tracked) on this node. If not, we cannot decide
|
||||
// yet — signal the caller to defer.
|
||||
// bootstrapped on this node. IsBootstrapped now keys on REAL convergence
|
||||
// (sb.Bootstrapped), not mere presence — safe here because X-Chain is a DAG
|
||||
// chain marked bootstrapped SYNCHRONOUSLY inside createChain (Engine == nil
|
||||
// path) before the sequential chain-creator dequeues any re-pushed gated chain,
|
||||
// so IsBootstrapped(X) is already true when a gated chain re-runs. INVARIANT: if
|
||||
// X-Chain is ever linearized into an engine chain (async Bootstrapped via
|
||||
// monitorBootstrap), retryPendingGatedChains must be made to re-drain after X
|
||||
// converges, else gated chains park forever. If not bootstrapped yet, defer.
|
||||
if !m.IsBootstrapped(m.XChainID) {
|
||||
return false, false
|
||||
}
|
||||
|
||||
@@ -9,10 +9,12 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/container/buffer"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/log"
|
||||
"github.com/luxfi/math/set"
|
||||
"github.com/luxfi/node/nets"
|
||||
lux "github.com/luxfi/utxo"
|
||||
"github.com/luxfi/utxo/nftfx"
|
||||
"github.com/luxfi/utxo/secp256k1fx"
|
||||
@@ -163,8 +165,10 @@ func TestHoldsAuthorizationNFT(t *testing.T) {
|
||||
|
||||
// newGateManager builds the minimal manager needed to exercise the gate:
|
||||
// only the fields authorizeChainActivation reads. xChainVM, when non-nil, is
|
||||
// installed as the X-Chain's tracked VM so IsBootstrapped(XChainID) is true and
|
||||
// xChainUTXOReader() resolves to it.
|
||||
// installed as the X-Chain's tracked VM AND marked bootstrapped in its net so
|
||||
// IsBootstrapped(XChainID) is true (the gate consults the X-Chain UTXO set, which
|
||||
// is only valid once the X-Chain has finished initial sync — mere presence in
|
||||
// m.chains is no longer sufficient) and xChainUTXOReader() resolves to it.
|
||||
func newGateManager(
|
||||
xChainID ids.ID,
|
||||
stakingAddr ids.ShortID,
|
||||
@@ -172,17 +176,29 @@ func newGateManager(
|
||||
critical set.Set[ids.ID],
|
||||
xChainVM xChainUTXOReader,
|
||||
) *manager {
|
||||
netsTracker, err := NewNets(ids.GenerateTestNodeID(), map[ids.ID]nets.Config{
|
||||
constants.PrimaryNetworkID: {},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
m := &manager{
|
||||
chains: make(map[ids.ID]*chainInfo),
|
||||
gatedAttempts: make(map[ids.ID]int),
|
||||
}
|
||||
m.Log = log.NewNoOpLogger()
|
||||
m.Nets = netsTracker
|
||||
m.XChainID = xChainID
|
||||
m.StakingXAddress = stakingAddr
|
||||
m.ChainAuthorizations = authz
|
||||
m.CriticalChains = critical
|
||||
if xChainVM != nil {
|
||||
m.chains[xChainID] = &chainInfo{Name: "X-Chain", VM: xChainVM}
|
||||
// The X-Chain has finished initial sync (its UTXO set is valid) — the real
|
||||
// precondition the gate depends on, now reflected in the net tracking.
|
||||
sb, _ := netsTracker.GetOrCreate(constants.PrimaryNetworkID)
|
||||
sb.AddChain(xChainID)
|
||||
sb.Bootstrapped(xChainID)
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -278,11 +294,14 @@ func TestAuthorizeChainActivation(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("gated chain, X-Chain tracked but VM lacks reader, opts out", func(t *testing.T) {
|
||||
// X-Chain is in m.chains (IsBootstrapped true) but its VM does not
|
||||
// satisfy xChainUTXOReader. The gate must fail closed (ready, !authz),
|
||||
// not panic.
|
||||
// X-Chain is bootstrapped (in m.chains AND marked bootstrapped in its net, so
|
||||
// IsBootstrapped true) but its VM does not satisfy xChainUTXOReader. The gate must
|
||||
// fail closed (ready, !authz), not panic.
|
||||
m := newGateManager(xChainID, addr, collection, nil, nil)
|
||||
m.chains[xChainID] = &chainInfo{Name: "X-Chain", VM: struct{}{}}
|
||||
sb, _ := m.Nets.GetOrCreate(constants.PrimaryNetworkID)
|
||||
sb.AddChain(xChainID)
|
||||
sb.Bootstrapped(xChainID)
|
||||
authorized, ready := m.authorizeChainActivation(gatedChain)
|
||||
require.True(t, ready)
|
||||
require.False(t, authorized)
|
||||
|
||||
@@ -177,6 +177,69 @@ func TestIsBootstrapped(t *testing.T) {
|
||||
require.False(m.IsBootstrapped(chainID))
|
||||
}
|
||||
|
||||
// TestIsBootstrappedTracksRealConvergence is the regression guard for the
|
||||
// premature-true masking bug: manager.IsBootstrapped must report true ONLY once the
|
||||
// chain has ACTUALLY finished initial sync (its net marked it Bootstrapped), NOT the
|
||||
// instant it is merely tracked (added to m.chains with its sync goroutine launched).
|
||||
// Before the fix, a C-Chain stalled at genesis (head 0x0) reported
|
||||
// info.isBootstrapped(C)=true, masking the stall from any readiness gate.
|
||||
func TestIsBootstrappedTracksRealConvergence(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
chainConfigs := map[ids.ID]nets.Config{
|
||||
constants.PrimaryNetworkID: {},
|
||||
}
|
||||
netsTracker, err := NewNets(ids.GenerateTestNodeID(), chainConfigs)
|
||||
require.NoError(err)
|
||||
|
||||
config := &ManagerConfig{
|
||||
Log: log.NewNoOpLogger(),
|
||||
Metrics: metric.NewMultiGatherer(),
|
||||
VMManager: vms.NewManager(),
|
||||
ChainDataDir: t.TempDir(),
|
||||
Nets: netsTracker,
|
||||
}
|
||||
m, err := New(config)
|
||||
require.NoError(err)
|
||||
mImpl := m.(*manager)
|
||||
|
||||
// A native chain validated by the primary network — the C-Chain shape.
|
||||
chainID := ids.GenerateTestID()
|
||||
|
||||
// Simulate createChain's tracking: the chain EXISTS in m.chains and is registered
|
||||
// as bootstrapping in its validation net — but has NOT converged (initial sync is
|
||||
// still driving, e.g. stalled at genesis fetching ancestry).
|
||||
mImpl.chainsLock.Lock()
|
||||
mImpl.chains[chainID] = &chainInfo{Name: "C-Chain"}
|
||||
mImpl.chainsLock.Unlock()
|
||||
sb, _ := netsTracker.GetOrCreate(constants.PrimaryNetworkID)
|
||||
require.True(sb.AddChain(chainID))
|
||||
|
||||
// THE FIX: exists-but-not-converged must be FALSE (was true — the masking bug).
|
||||
require.False(m.IsBootstrapped(chainID),
|
||||
"a tracked-but-still-syncing chain must not report bootstrapped")
|
||||
for _, ci := range m.(*manager).GetChains() {
|
||||
if ci.ID == chainID {
|
||||
require.False(ci.Bootstrapped, "GetChains must not report a syncing chain bootstrapped")
|
||||
}
|
||||
}
|
||||
|
||||
// Initial sync reaches the frontier → monitorBootstrap calls sb.Bootstrapped.
|
||||
sb.Bootstrapped(chainID)
|
||||
|
||||
// Now — and only now — it reports bootstrapped (head advanced to frontier, VM live).
|
||||
require.True(m.IsBootstrapped(chainID),
|
||||
"a converged chain must report bootstrapped")
|
||||
found := false
|
||||
for _, ci := range m.(*manager).GetChains() {
|
||||
if ci.ID == chainID {
|
||||
found = true
|
||||
require.True(ci.Bootstrapped, "GetChains must report a converged chain bootstrapped")
|
||||
}
|
||||
}
|
||||
require.True(found)
|
||||
}
|
||||
|
||||
// TestToEngineChannelFlow verifies the toEngine channel notification flow
|
||||
// This tests the goroutine that reads from toEngine and triggers block building
|
||||
func TestToEngineChannelFlow(t *testing.T) {
|
||||
|
||||
+9
-3
@@ -364,11 +364,17 @@ func hashValidatorSet(set map[ids.NodeID]*validators.GetValidatorOutput) ids.ID
|
||||
//
|
||||
// kind 1 = signed vote (payload = engine encodeSignedVote: nodeID+sig)
|
||||
// kind 2 = finality cert (payload = engine cert MarshalBinary)
|
||||
// kind 3 = round-scoped view-change PREVOTE (payload = engine encodeSignedPrevote:
|
||||
// nodeID+height+round+canonical+sig, domain "LUX/chain/prevote/v1"). The
|
||||
// envelope blockID field carries the canonical for routing but is advisory —
|
||||
// HandleIncomingPrevote decodes the authoritative (height,round,canonical) from
|
||||
// the payload and verifies the signature over the reconstructed message.
|
||||
var quorumGossipMagic = [4]byte{'L', 'X', 'Q', 0x01}
|
||||
|
||||
const (
|
||||
quorumKindVote byte = 1
|
||||
quorumKindCert byte = 2
|
||||
quorumKindVote byte = 1
|
||||
quorumKindCert byte = 2
|
||||
quorumKindPrevote byte = 3
|
||||
)
|
||||
|
||||
// ErrNotQuorumGossip signals a payload is not a quorum envelope (so the caller
|
||||
@@ -394,7 +400,7 @@ func decodeQuorumGossip(data []byte) (kind byte, blockID ids.ID, payload []byte,
|
||||
kind = data[4]
|
||||
copy(blockID[:], data[5:5+32])
|
||||
payload = data[5+32:]
|
||||
if kind != quorumKindVote && kind != quorumKindCert {
|
||||
if kind != quorumKindVote && kind != quorumKindCert && kind != quorumKindPrevote {
|
||||
return 0, ids.Empty, nil, ErrNotQuorumGossip
|
||||
}
|
||||
return kind, blockID, payload, nil
|
||||
|
||||
@@ -68,20 +68,20 @@ func (d *DebugTool) DiagnoseEndpoint(chainID ids.ID, alias string) *DiagnosticRe
|
||||
// getURLPatterns returns all possible URL patterns to test.
|
||||
func (d *DebugTool) getURLPatterns(chainID ids.ID, alias string) []string {
|
||||
patterns := []string{
|
||||
fmt.Sprintf("%s/ext/bc/%s/rpc", d.baseURL, chainID.String()),
|
||||
fmt.Sprintf("%s/ext/bc/%s/ws", d.baseURL, chainID.String()),
|
||||
fmt.Sprintf("%s/ext/bc/%s", d.baseURL, chainID.String()),
|
||||
fmt.Sprintf("%s/v1/bc/%s/rpc", d.baseURL, chainID.String()),
|
||||
fmt.Sprintf("%s/v1/bc/%s/ws", d.baseURL, chainID.String()),
|
||||
fmt.Sprintf("%s/v1/bc/%s", d.baseURL, chainID.String()),
|
||||
}
|
||||
|
||||
if alias != "" && alias != chainID.String() {
|
||||
patterns = append(patterns,
|
||||
fmt.Sprintf("%s/ext/bc/%s/rpc", d.baseURL, alias),
|
||||
fmt.Sprintf("%s/ext/bc/%s/ws", d.baseURL, alias),
|
||||
fmt.Sprintf("%s/ext/bc/%s", d.baseURL, alias),
|
||||
fmt.Sprintf("%s/v1/bc/%s/rpc", d.baseURL, alias),
|
||||
fmt.Sprintf("%s/v1/bc/%s/ws", d.baseURL, alias),
|
||||
fmt.Sprintf("%s/v1/bc/%s", d.baseURL, alias),
|
||||
)
|
||||
}
|
||||
|
||||
// Also test without /ext prefix (some setups might differ)
|
||||
// Also test without /v1 prefix (some setups might differ)
|
||||
patterns = append(patterns,
|
||||
fmt.Sprintf("%s/bc/%s/rpc", d.baseURL, chainID.String()),
|
||||
)
|
||||
|
||||
@@ -80,9 +80,9 @@ func IntegrationExample(
|
||||
fmt.Printf("\n✅ Chain %s RPC endpoints ready:\n", chainID)
|
||||
for _, endpoint := range info.Endpoints {
|
||||
if info.ChainAlias != "" {
|
||||
fmt.Printf(" %s/ext/bc/%s%s\n", baseURL, info.ChainAlias, endpoint)
|
||||
fmt.Printf(" %s/v1/bc/%s%s\n", baseURL, info.ChainAlias, endpoint)
|
||||
}
|
||||
fmt.Printf(" %s/ext/bc/%s%s\n", baseURL, chainID, endpoint)
|
||||
fmt.Printf(" %s/v1/bc/%s%s\n", baseURL, chainID, endpoint)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ services:
|
||||
LUXD_CONSENSUS_QUORUM_SIZE: "1"
|
||||
LUXD_LOG_LEVEL: "info"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-sf", "http://localhost:9630/ext/health"]
|
||||
test: ["CMD", "curl", "-sf", "http://localhost:9630/v1/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
+5
-5
@@ -133,12 +133,12 @@ deploy:
|
||||
|
||||
### Prometheus Metrics
|
||||
|
||||
The node exposes metrics at `http://localhost:9630/ext/metrics`
|
||||
The node exposes metrics at `http://localhost:9630/v1/metrics`
|
||||
|
||||
### Health Checks
|
||||
|
||||
- Liveness: `http://localhost:9630/ext/health`
|
||||
- Readiness: `http://localhost:9630/ext/info`
|
||||
- Liveness: `http://localhost:9630/v1/health`
|
||||
- Readiness: `http://localhost:9630/v1/info`
|
||||
|
||||
### Grafana Dashboard
|
||||
|
||||
@@ -178,12 +178,12 @@ docker exec -it luxd /bin/bash
|
||||
# Check if bootstrapped
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"info.isBootstrapped","params":{"chain":"C"}}' \
|
||||
http://localhost:9630/ext/info
|
||||
http://localhost:9630/v1/info
|
||||
|
||||
# Get block number
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \
|
||||
http://localhost:9630/ext/bc/C/rpc
|
||||
http://localhost:9630/v1/bc/C/rpc
|
||||
```
|
||||
|
||||
## CI/CD
|
||||
|
||||
@@ -287,10 +287,10 @@ echo "📝 Starting with command:"
|
||||
echo " $CMD"
|
||||
echo ""
|
||||
echo "📡 API Endpoints:"
|
||||
echo " - JSON-RPC: http://$HTTP_HOST:$HTTP_PORT/ext/bc/C/rpc"
|
||||
echo " - WebSocket: ws://$HTTP_HOST:$HTTP_PORT/ext/bc/C/ws"
|
||||
echo " - Health: http://$HTTP_HOST:$HTTP_PORT/ext/health"
|
||||
echo " - Info: http://$HTTP_HOST:$HTTP_PORT/ext/info"
|
||||
echo " - JSON-RPC: http://$HTTP_HOST:$HTTP_PORT/v1/bc/C/rpc"
|
||||
echo " - WebSocket: ws://$HTTP_HOST:$HTTP_PORT/v1/bc/C/ws"
|
||||
echo " - Health: http://$HTTP_HOST:$HTTP_PORT/v1/health"
|
||||
echo " - Info: http://$HTTP_HOST:$HTTP_PORT/v1/info"
|
||||
echo ""
|
||||
|
||||
# Execute
|
||||
|
||||
+1
-1
@@ -346,7 +346,7 @@ export default function HomePage() {
|
||||
</p>
|
||||
<pre className="mt-4 overflow-x-auto rounded-lg bg-black/50 p-4">
|
||||
<code className="text-sm text-green-400">
|
||||
curl -X POST --data '{"jsonrpc":"2.0","method":"health.health","id":1}' \{"\n"} -H 'content-type:application/json' \{"\n"} 127.0.0.1:9650/ext/health
|
||||
curl -X POST --data '{"jsonrpc":"2.0","method":"health.health","id":1}' \{"\n"} -H 'content-type:application/json' \{"\n"} 127.0.0.1:9650/v1/health
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
@@ -31,7 +31,7 @@ Or in configuration:
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
http://localhost:9630/ext/admin
|
||||
http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -54,7 +54,7 @@ curl -X POST --data '{
|
||||
"chain":"2S53R2ub94CV5vmSRAjqPYmRxvuiFunCb1gN2CAw3DQBfPWghX"
|
||||
},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
---
|
||||
@@ -85,7 +85,7 @@ curl -X POST --data '{
|
||||
"chain":"2S53R2ub94CV5vmSRAjqPYmRxvuiFunCb1gN2CAw3DQBfPWghX"
|
||||
},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
**Response:**
|
||||
@@ -114,7 +114,7 @@ curl -X POST --data '{
|
||||
"method":"admin.getLogLevel",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
**Response:**
|
||||
@@ -146,7 +146,7 @@ curl -X POST --data '{
|
||||
"logLevel":"debug"
|
||||
},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
---
|
||||
@@ -181,7 +181,7 @@ curl -X POST --data '{
|
||||
"method":"admin.loadVMs",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
**Response:**
|
||||
@@ -214,7 +214,7 @@ curl -X POST --data '{
|
||||
"method":"admin.startCPUProfiler",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
---
|
||||
@@ -232,7 +232,7 @@ curl -X POST --data '{
|
||||
"method":"admin.stopCPUProfiler",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
---
|
||||
@@ -250,7 +250,7 @@ curl -X POST --data '{
|
||||
"method":"admin.memoryProfile",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
---
|
||||
@@ -276,7 +276,7 @@ curl -X POST --data '{
|
||||
"method":"admin.getConfig",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
**Response:**
|
||||
@@ -311,7 +311,7 @@ curl -X POST --data '{
|
||||
"method":"admin.shutdown",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
---
|
||||
@@ -330,7 +330,7 @@ curl -X POST --data '{
|
||||
"method":"admin.db.commit",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/admin
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
## Profiling and Debugging
|
||||
@@ -344,7 +344,7 @@ curl -X POST --data '{
|
||||
"method":"admin.startCPUProfiler",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' http://localhost:9630/ext/admin
|
||||
}' http://localhost:9630/v1/admin
|
||||
|
||||
# Let it run for 30 seconds
|
||||
sleep 30
|
||||
@@ -355,7 +355,7 @@ curl -X POST --data '{
|
||||
"method":"admin.stopCPUProfiler",
|
||||
"params":{},
|
||||
"id":2
|
||||
}' http://localhost:9630/ext/admin
|
||||
}' http://localhost:9630/v1/admin
|
||||
|
||||
# Analyze profile
|
||||
go tool pprof cpu.prof
|
||||
@@ -370,7 +370,7 @@ curl -X POST --data '{
|
||||
"method":"admin.memoryProfile",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' http://localhost:9630/ext/admin
|
||||
}' http://localhost:9630/v1/admin
|
||||
|
||||
# Analyze
|
||||
go tool pprof mem.prof
|
||||
@@ -401,7 +401,7 @@ set_log_level() {
|
||||
\"method\":\"admin.setLogLevel\",
|
||||
\"params\":{\"logLevel\":\"$LEVEL\"},
|
||||
\"id\":1
|
||||
}" http://localhost:9630/ext/admin
|
||||
}" http://localhost:9630/v1/admin
|
||||
}
|
||||
|
||||
# Increase verbosity for debugging
|
||||
@@ -476,7 +476,7 @@ Enable audit logging for admin operations:
|
||||
# auto_restart.sh
|
||||
|
||||
check_health() {
|
||||
curl -s http://localhost:9630/ext/health | jq -r '.healthy'
|
||||
curl -s http://localhost:9630/v1/health | jq -r '.healthy'
|
||||
}
|
||||
|
||||
restart_node() {
|
||||
@@ -488,7 +488,7 @@ restart_node() {
|
||||
"method":"admin.shutdown",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' http://localhost:9630/ext/admin
|
||||
}' http://localhost:9630/v1/admin
|
||||
|
||||
sleep 10
|
||||
|
||||
@@ -514,7 +514,7 @@ CURRENT=$(curl -s -X POST --data '{
|
||||
"method":"admin.getConfig",
|
||||
"params":{},
|
||||
"id":1
|
||||
}' http://localhost:9630/ext/admin)
|
||||
}' http://localhost:9630/v1/admin)
|
||||
|
||||
echo "Current configuration:"
|
||||
echo $CURRENT | jq .
|
||||
@@ -525,7 +525,7 @@ curl -X POST --data '{
|
||||
"method":"admin.setLogLevel",
|
||||
"params":{"logLevel":"debug"},
|
||||
"id":2
|
||||
}' http://localhost:9630/ext/admin
|
||||
}' http://localhost:9630/v1/admin
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -10,7 +10,7 @@ The Health API provides endpoints to monitor the health and readiness of your Lu
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
http://localhost:9630/ext/health
|
||||
http://localhost:9630/v1/health
|
||||
```
|
||||
|
||||
## Health Check Types
|
||||
@@ -20,7 +20,7 @@ http://localhost:9630/ext/health
|
||||
Get the overall health status of the node:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9630/ext/health
|
||||
curl http://localhost:9630/v1/health
|
||||
```
|
||||
|
||||
**Response:**
|
||||
@@ -61,7 +61,7 @@ curl http://localhost:9630/ext/health
|
||||
Check if the node is ready to serve requests:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9630/ext/health/readiness
|
||||
curl http://localhost:9630/v1/health/readiness
|
||||
```
|
||||
|
||||
Returns:
|
||||
@@ -73,7 +73,7 @@ Returns:
|
||||
Check if the node is alive and running:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9630/ext/health/liveness
|
||||
curl http://localhost:9630/v1/health/liveness
|
||||
```
|
||||
|
||||
Returns:
|
||||
@@ -92,7 +92,7 @@ curl -X POST --data '{
|
||||
"id": 1,
|
||||
"method": "health.health",
|
||||
"params": {}
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/health
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/health
|
||||
```
|
||||
|
||||
**Response:**
|
||||
@@ -175,7 +175,7 @@ Configure per-chain health parameters:
|
||||
Export health metrics in Prometheus format:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9630/ext/metrics | grep health
|
||||
curl http://localhost:9630/v1/metrics | grep health
|
||||
```
|
||||
|
||||
Metrics:
|
||||
@@ -208,13 +208,13 @@ spec:
|
||||
image: luxfi/node:latest
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /ext/health/liveness
|
||||
path: /v1/health/liveness
|
||||
port: 9630
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ext/health/readiness
|
||||
path: /v1/health/readiness
|
||||
port: 9630
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 5
|
||||
@@ -229,7 +229,7 @@ spec:
|
||||
# health_monitor.sh
|
||||
|
||||
while true; do
|
||||
HEALTH=$(curl -s http://localhost:9630/ext/health | jq -r '.healthy')
|
||||
HEALTH=$(curl -s http://localhost:9630/v1/health | jq -r '.healthy')
|
||||
|
||||
if [ "$HEALTH" != "true" ]; then
|
||||
echo "ALERT: Node unhealthy at $(date)"
|
||||
@@ -248,7 +248,7 @@ done
|
||||
# health_analysis.sh
|
||||
|
||||
# Get detailed health
|
||||
RESPONSE=$(curl -s http://localhost:9630/ext/health)
|
||||
RESPONSE=$(curl -s http://localhost:9630/v1/health)
|
||||
|
||||
# Parse each chain
|
||||
for CHAIN in P X C Q; do
|
||||
@@ -271,7 +271,7 @@ done
|
||||
|
||||
```
|
||||
backend lux_nodes
|
||||
option httpchk GET /ext/health
|
||||
option httpchk GET /v1/health
|
||||
http-check expect status 200
|
||||
|
||||
server node1 192.168.1.10:9630 check
|
||||
@@ -289,7 +289,7 @@ upstream lux_nodes {
|
||||
}
|
||||
|
||||
location /health_check {
|
||||
proxy_pass http://lux_nodes/ext/health;
|
||||
proxy_pass http://lux_nodes/v1/health;
|
||||
proxy_connect_timeout 1s;
|
||||
proxy_read_timeout 1s;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ The Info API provides general information about the node and network status. Thi
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
http://localhost:9630/ext/info
|
||||
http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
## Format
|
||||
@@ -23,7 +23,7 @@ curl -X POST --data '{
|
||||
"method": "info.<method>",
|
||||
"params": {...},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -41,7 +41,7 @@ curl -X POST --data '{
|
||||
"method": "info.getNodeVersion",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -78,7 +78,7 @@ curl -X POST --data '{
|
||||
"method": "info.getNodeID",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -111,7 +111,7 @@ curl -X POST --data '{
|
||||
"method": "info.getNodeIP",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -140,7 +140,7 @@ curl -X POST --data '{
|
||||
"method": "info.getNetworkID",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -175,7 +175,7 @@ curl -X POST --data '{
|
||||
"method": "info.getNetworkName",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -207,7 +207,7 @@ curl -X POST --data '{
|
||||
"alias": "P"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -239,7 +239,7 @@ curl -X POST --data '{
|
||||
"chain": "P"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -269,7 +269,7 @@ curl -X POST --data '{
|
||||
"method": "info.peers",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -313,7 +313,7 @@ curl -X POST --data '{
|
||||
"method": "info.getTxFee",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -348,7 +348,7 @@ curl -X POST --data '{
|
||||
"method": "info.uptime",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response (Validator):**
|
||||
@@ -390,7 +390,7 @@ curl -X POST --data '{
|
||||
"method": "info.getVMs",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -424,7 +424,7 @@ curl -X POST --data '{
|
||||
"method": "info.getUpgrades",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -468,7 +468,7 @@ VERSION=$(curl -s -X POST --data '{
|
||||
"method": "info.getNodeVersion",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' $NODE_URL/ext/info | jq -r '.result.version')
|
||||
}' -H 'content-type:application/json;' $NODE_URL/v1/info | jq -r '.result.version')
|
||||
|
||||
echo "Version: $VERSION"
|
||||
|
||||
@@ -478,7 +478,7 @@ NODE_ID=$(curl -s -X POST --data '{
|
||||
"method": "info.getNodeID",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' $NODE_URL/ext/info | jq -r '.result.nodeID')
|
||||
}' -H 'content-type:application/json;' $NODE_URL/v1/info | jq -r '.result.nodeID')
|
||||
|
||||
echo "Node ID: $NODE_ID"
|
||||
|
||||
@@ -488,7 +488,7 @@ PEERS=$(curl -s -X POST --data '{
|
||||
"method": "info.peers",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' $NODE_URL/ext/info | jq -r '.result.numPeers')
|
||||
}' -H 'content-type:application/json;' $NODE_URL/v1/info | jq -r '.result.numPeers')
|
||||
|
||||
echo "Connected Peers: $PEERS"
|
||||
|
||||
@@ -499,7 +499,7 @@ for CHAIN in P X C Q; do
|
||||
\"method\": \"info.isBootstrapped\",
|
||||
\"params\": {\"chain\": \"$CHAIN\"},
|
||||
\"id\": 1
|
||||
}" -H 'content-type:application/json;' $NODE_URL/ext/info | jq -r '.result.isBootstrapped')
|
||||
}" -H 'content-type:application/json;' $NODE_URL/v1/info | jq -r '.result.isBootstrapped')
|
||||
|
||||
echo "$CHAIN-Chain Bootstrapped: $STATUS"
|
||||
done
|
||||
@@ -510,7 +510,7 @@ UPTIME=$(curl -s -X POST --data '{
|
||||
"method": "info.uptime",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' $NODE_URL/ext/info 2>/dev/null | jq -r '.result.rewardingStakePercentage' 2>/dev/null)
|
||||
}' -H 'content-type:application/json;' $NODE_URL/v1/info 2>/dev/null | jq -r '.result.rewardingStakePercentage' 2>/dev/null)
|
||||
|
||||
if [ "$UPTIME" != "null" ] && [ -n "$UPTIME" ]; then
|
||||
echo "Validator Uptime: $UPTIME%"
|
||||
@@ -531,7 +531,7 @@ PEERS=$(curl -s -X POST --data '{
|
||||
"method": "info.peers",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' $NODE_URL/ext/info | jq -r '.result.peers[]')
|
||||
}' -H 'content-type:application/json;' $NODE_URL/v1/info | jq -r '.result.peers[]')
|
||||
|
||||
echo "=== Peer Connection Quality ==="
|
||||
echo "Node ID | Latency (ms) | Uptime % | Version"
|
||||
@@ -560,7 +560,7 @@ while true; do
|
||||
\"method\": \"info.isBootstrapped\",
|
||||
\"params\": {\"chain\": \"$CHAIN\"},
|
||||
\"id\": 1
|
||||
}" -H 'content-type:application/json;' $NODE_URL/ext/info | jq -r '.result.isBootstrapped')
|
||||
}" -H 'content-type:application/json;' $NODE_URL/v1/info | jq -r '.result.isBootstrapped')
|
||||
|
||||
if [ "$STATUS" == "true" ]; then
|
||||
echo "✅ $CHAIN-Chain: Bootstrapped"
|
||||
@@ -575,7 +575,7 @@ while true; do
|
||||
"method": "info.peers",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' $NODE_URL/ext/info | jq -r '.result.numPeers')
|
||||
}' -H 'content-type:application/json;' $NODE_URL/v1/info | jq -r '.result.numPeers')
|
||||
|
||||
echo ""
|
||||
echo "Connected Peers: $PEERS"
|
||||
@@ -588,7 +588,7 @@ while true; do
|
||||
\"method\": \"info.isBootstrapped\",
|
||||
\"params\": {\"chain\": \"$CHAIN\"},
|
||||
\"id\": 1
|
||||
}" -H 'content-type:application/json;' $NODE_URL/ext/info | jq -r '.result.isBootstrapped')
|
||||
}" -H 'content-type:application/json;' $NODE_URL/v1/info | jq -r '.result.isBootstrapped')
|
||||
|
||||
if [ "$STATUS" != "true" ]; then
|
||||
ALL_BOOTSTRAPPED=false
|
||||
|
||||
@@ -10,7 +10,7 @@ The Platform Chain (P-Chain) is responsible for staking, validators, and chain m
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
http://localhost:9630/ext/bc/P
|
||||
http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
## Format
|
||||
@@ -23,7 +23,7 @@ curl -X POST --data '{
|
||||
"method": "platform.<method>",
|
||||
"params": {...},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -41,7 +41,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getHeight",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -73,7 +73,7 @@ curl -X POST --data '{
|
||||
"addresses": ["P-lux1q8tgunsf7sxpl2qp9fz0xjs36kstm7vdjvpzcc"]
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -114,7 +114,7 @@ curl -X POST --data '{
|
||||
"limit": 100
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -134,7 +134,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getCurrentValidators",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -190,7 +190,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getPendingValidators",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -212,7 +212,7 @@ curl -X POST --data '{
|
||||
"height": 365000
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -233,7 +233,7 @@ curl -X POST --data '{
|
||||
"height": 365000
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -255,7 +255,7 @@ curl -X POST --data '{
|
||||
"addresses": ["P-lux1q8tgunsf7sxpl2qp9fz0xjs36kstm7vdjvpzcc"]
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -274,7 +274,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getMinStake",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -305,7 +305,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getTotalStake",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -327,7 +327,7 @@ curl -X POST --data '{
|
||||
"txID": "2nmH8LithVbdjaXsxVQCQfXtzN9hBbmebrsaEYnLM9T32Uy3Y5"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -345,7 +345,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getTimestamp",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -363,7 +363,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getBlockchains",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -409,7 +409,7 @@ curl -X POST --data '{
|
||||
"blockID": "vXSY7FK7NR65Y8BrJDKQH4a6vBdJuqAMvVzWj3Zxcap5J4ZE3"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -431,7 +431,7 @@ curl -X POST --data '{
|
||||
"txID": "2nmH8LithVbdjaXsxVQCQfXtzN9hBbmebrsaEYnLM9T32Uy3Y5"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -452,7 +452,7 @@ curl -X POST --data '{
|
||||
"txID": "2nmH8LithVbdjaXsxVQCQfXtzN9hBbmebrsaEYnLM9T32Uy3Y5"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -488,7 +488,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getCurrentSupply",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -507,7 +507,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getChains",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -544,7 +544,7 @@ curl -X POST --data '{
|
||||
"password": "mypassword"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
---
|
||||
@@ -694,7 +694,7 @@ curl -s -X POST --data "{
|
||||
\"nodeIDs\": [\"$NODE_ID\"]
|
||||
},
|
||||
\"id\": 1
|
||||
}" -H 'content-type:application/json;' http://localhost:9630/ext/bc/P | jq '.result.validators[0]'
|
||||
}" -H 'content-type:application/json;' http://localhost:9630/v1/bc/P | jq '.result.validators[0]'
|
||||
```
|
||||
|
||||
### Monitor Staking Rewards
|
||||
@@ -711,7 +711,7 @@ STAKE=$(curl -s -X POST --data "{
|
||||
\"addresses\": [\"$ADDRESS\"]
|
||||
},
|
||||
\"id\": 1
|
||||
}" -H 'content-type:application/json;' http://localhost:9630/ext/bc/P)
|
||||
}" -H 'content-type:application/json;' http://localhost:9630/v1/bc/P)
|
||||
|
||||
echo "Current stake: $(echo $STAKE | jq -r '.result.staked')"
|
||||
echo "Stakeable: $(echo $STAKE | jq -r '.result.stakeable')"
|
||||
|
||||
@@ -210,14 +210,14 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id": 1,
|
||||
"method": "health.health"
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/health
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/health
|
||||
|
||||
# Get node info
|
||||
curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id": 1,
|
||||
"method": "info.getNodeVersion"
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
|
||||
# Check bootstrap status
|
||||
curl -X POST --data '{
|
||||
@@ -227,7 +227,7 @@ curl -X POST --data '{
|
||||
"params": {
|
||||
"chain": "P"
|
||||
}
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -308,7 +308,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id": 1,
|
||||
"method": "health.health"
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/health
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/health
|
||||
```
|
||||
|
||||
### Bootstrap Status
|
||||
@@ -320,7 +320,7 @@ curl -X POST --data '{
|
||||
"id": 1,
|
||||
"method": "info.isBootstrapped",
|
||||
"params": {"chain": "P"}
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
### Metrics
|
||||
@@ -328,7 +328,7 @@ curl -X POST --data '{
|
||||
Access Prometheus metrics:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9630/ext/metrics
|
||||
curl http://localhost:9630/v1/metrics
|
||||
```
|
||||
|
||||
Key metrics to monitor:
|
||||
|
||||
@@ -58,7 +58,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id": 1,
|
||||
"method": "platform.getHeight"
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
|
||||
# Check bootstrap status for all chains
|
||||
curl -X POST --data '{
|
||||
@@ -66,7 +66,7 @@ curl -X POST --data '{
|
||||
"id": 1,
|
||||
"method": "info.isBootstrapped",
|
||||
"params": {"chain": "P"}
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
### Configure Public IP
|
||||
@@ -90,7 +90,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id": 1,
|
||||
"method": "info.getNodeID"
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
Response:
|
||||
@@ -155,7 +155,7 @@ curl -X POST --data '{
|
||||
"password": "mypassword"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/keystore
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/keystore
|
||||
|
||||
# Create P-Chain address
|
||||
curl -X POST --data '{
|
||||
@@ -166,7 +166,7 @@ curl -X POST --data '{
|
||||
"password": "mypassword"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
### Transfer LUX to P-Chain
|
||||
@@ -185,7 +185,7 @@ curl -X POST --data '{
|
||||
"amount": 2000000000000
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/X
|
||||
|
||||
# Import to P-Chain
|
||||
curl -X POST --data '{
|
||||
@@ -197,7 +197,7 @@ curl -X POST --data '{
|
||||
"sourceChain": "X"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
## Step 4: Add as Validator
|
||||
@@ -221,7 +221,7 @@ curl -X POST --data '{
|
||||
"delegationFeeRate": 10
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
Parameters:
|
||||
@@ -243,7 +243,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getPendingValidators",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
|
||||
# Get current validators
|
||||
curl -X POST --data '{
|
||||
@@ -251,7 +251,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getCurrentValidators",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
## Step 5: Monitor Your Validator
|
||||
@@ -268,7 +268,7 @@ curl -X POST --data '{
|
||||
"nodeID": "NodeID-5KqnQfaFQxY9rsFBAa68377qWSherYLQ7"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
### Monitor Performance Metrics
|
||||
@@ -277,10 +277,10 @@ Key metrics to track:
|
||||
|
||||
```bash
|
||||
# Node health
|
||||
curl http://localhost:9630/ext/health
|
||||
curl http://localhost:9630/v1/health
|
||||
|
||||
# Prometheus metrics
|
||||
curl http://localhost:9630/ext/metrics | grep -E "uptime|stake|validator"
|
||||
curl http://localhost:9630/v1/metrics | grep -E "uptime|stake|validator"
|
||||
```
|
||||
|
||||
Important metrics:
|
||||
@@ -308,7 +308,7 @@ NODE_URL="http://localhost:9630"
|
||||
WEBHOOK_URL="your-webhook-url"
|
||||
|
||||
# Check if node is responsive
|
||||
if ! curl -s "$NODE_URL/ext/health" > /dev/null; then
|
||||
if ! curl -s "$NODE_URL/v1/health" > /dev/null; then
|
||||
curl -X POST "$WEBHOOK_URL" -d '{"text":"ALERT: Node is not responding!"}'
|
||||
fi
|
||||
|
||||
@@ -318,7 +318,7 @@ UPTIME=$(curl -s -X POST --data '{
|
||||
"method":"platform.getValidator",
|
||||
"params":{"nodeID":"NodeID-xxx"},
|
||||
"id":1
|
||||
}' "$NODE_URL/ext/bc/P" | jq -r '.result.uptime')
|
||||
}' "$NODE_URL/v1/bc/P" | jq -r '.result.uptime')
|
||||
|
||||
if [ "$UPTIME" -lt "80" ]; then
|
||||
curl -X POST "$WEBHOOK_URL" -d "{\"text\":\"WARNING: Uptime is $UPTIME%\"}"
|
||||
@@ -349,7 +349,7 @@ curl -X POST --data '{
|
||||
"nodeID": "NodeID-5KqnQfaFQxY9rsFBAa68377qWSherYLQ7"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
## Chain Validation
|
||||
@@ -373,7 +373,7 @@ curl -X POST --data '{
|
||||
"weight": 1000
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
### Chain Requirements
|
||||
|
||||
@@ -104,14 +104,14 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id": 1,
|
||||
"method": "info.getNetworkInfo"
|
||||
}' -H 'content-type:application/json;' http://localhost:9650/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9650/v1/info
|
||||
|
||||
# Get node ID
|
||||
curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id": 1,
|
||||
"method": "info.getNodeID"
|
||||
}' -H 'content-type:application/json;' http://localhost:9650/ext/info
|
||||
}' -H 'content-type:application/json;' http://localhost:9650/v1/info
|
||||
```
|
||||
|
||||
## Chain Management
|
||||
@@ -130,7 +130,7 @@ curl -X POST --data '{
|
||||
"genesisData": "...",
|
||||
"netID": "network-id"
|
||||
}
|
||||
}' -H 'content-type:application/json;' http://localhost:9650/ext/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9650/v1/P
|
||||
```
|
||||
|
||||
### Validator Management
|
||||
@@ -147,7 +147,7 @@ curl -X POST --data '{
|
||||
"endTime": ...,
|
||||
"stakeAmount": ...
|
||||
}
|
||||
}' -H 'content-type:application/json;' http://localhost:9650/ext/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9650/v1/P
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
@@ -175,10 +175,10 @@ curl -X POST --data '{
|
||||
|
||||
```bash
|
||||
# Prometheus metrics endpoint
|
||||
curl http://localhost:9650/ext/metrics
|
||||
curl http://localhost:9650/v1/metrics
|
||||
|
||||
# Health check
|
||||
curl http://localhost:9650/ext/health
|
||||
curl http://localhost:9650/v1/health
|
||||
```
|
||||
|
||||
### Logs
|
||||
@@ -193,7 +193,7 @@ curl -X POST --data '{
|
||||
"id": 1,
|
||||
"method": "admin.setLogLevel",
|
||||
"params": {"logLevel": "debug"}
|
||||
}' http://localhost:9650/ext/admin
|
||||
}' http://localhost:9650/v1/admin
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
@@ -595,10 +595,10 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"method":"info.peers",
|
||||
"id":1
|
||||
}' http://localhost:9630/ext/info
|
||||
}' http://localhost:9630/v1/info
|
||||
|
||||
# Check network metrics
|
||||
curl http://localhost:9630/ext/metrics | grep network
|
||||
curl http://localhost:9630/v1/metrics | grep network
|
||||
|
||||
# Monitor connections
|
||||
netstat -an | grep 9631
|
||||
|
||||
@@ -39,7 +39,7 @@ scrape_configs:
|
||||
- job_name: 'lux-node'
|
||||
static_configs:
|
||||
- targets: ['localhost:9630']
|
||||
metrics_path: '/ext/metrics'
|
||||
metrics_path: '/v1/metrics'
|
||||
|
||||
- job_name: 'node-exporter'
|
||||
static_configs:
|
||||
@@ -56,7 +56,7 @@ alerting:
|
||||
|
||||
### Available Metrics
|
||||
|
||||
The Lux node exposes 400+ Prometheus metrics at `http://localhost:9630/ext/metrics`.
|
||||
The Lux node exposes 400+ Prometheus metrics at `http://localhost:9630/v1/metrics`.
|
||||
|
||||
#### Key Metric Categories
|
||||
|
||||
@@ -419,11 +419,11 @@ groups:
|
||||
|
||||
```bash
|
||||
# Basic health check
|
||||
curl http://localhost:9630/ext/health
|
||||
curl http://localhost:9630/v1/health
|
||||
|
||||
# Detailed health with readiness/liveness
|
||||
curl http://localhost:9630/ext/health/readiness
|
||||
curl http://localhost:9630/ext/health/liveness
|
||||
curl http://localhost:9630/v1/health/readiness
|
||||
curl http://localhost:9630/v1/health/liveness
|
||||
```
|
||||
|
||||
### Custom Health Script
|
||||
@@ -443,7 +443,7 @@ send_alert() {
|
||||
}
|
||||
|
||||
# Check if node is responsive
|
||||
if ! curl -s "$NODE_URL/ext/health" > /dev/null; then
|
||||
if ! curl -s "$NODE_URL/v1/health" > /dev/null; then
|
||||
send_alert "Node is not responding!"
|
||||
exit 1
|
||||
fi
|
||||
@@ -455,7 +455,7 @@ for CHAIN in P X C Q; do
|
||||
\"method\": \"info.isBootstrapped\",
|
||||
\"params\": {\"chain\": \"$CHAIN\"},
|
||||
\"id\": 1
|
||||
}" -H 'content-type:application/json;' $NODE_URL/ext/info | jq -r '.result.isBootstrapped')
|
||||
}" -H 'content-type:application/json;' $NODE_URL/v1/info | jq -r '.result.isBootstrapped')
|
||||
|
||||
if [ "$STATUS" != "true" ]; then
|
||||
send_alert "$CHAIN-Chain is not bootstrapped!"
|
||||
@@ -468,7 +468,7 @@ PEERS=$(curl -s -X POST --data '{
|
||||
"method": "info.peers",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' $NODE_URL/ext/info | jq -r '.result.numPeers')
|
||||
}' -H 'content-type:application/json;' $NODE_URL/v1/info | jq -r '.result.numPeers')
|
||||
|
||||
if [ "$PEERS" -lt 4 ]; then
|
||||
send_alert "Low peer count: $PEERS"
|
||||
@@ -619,7 +619,7 @@ Create runbooks for common issues:
|
||||
|
||||
```bash
|
||||
# Real-time metrics
|
||||
watch -n 1 'curl -s http://localhost:9630/ext/metrics | grep -E "chain_height|network_peers"'
|
||||
watch -n 1 'curl -s http://localhost:9630/v1/metrics | grep -E "chain_height|network_peers"'
|
||||
|
||||
# Log streaming
|
||||
tail -f ~/.luxd/logs/*.log | grep --line-buffered ERROR
|
||||
|
||||
@@ -26,7 +26,7 @@ else
|
||||
fi
|
||||
|
||||
# Check API responsiveness
|
||||
if curl -s http://localhost:9630/ext/health > /dev/null 2>&1; then
|
||||
if curl -s http://localhost:9630/v1/health > /dev/null 2>&1; then
|
||||
echo "✅ API is responsive"
|
||||
else
|
||||
echo "❌ API is NOT responsive"
|
||||
@@ -53,7 +53,7 @@ PEERS=$(curl -s -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"method":"info.peers",
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/info 2>/dev/null | jq -r '.result.numPeers')
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/info 2>/dev/null | jq -r '.result.numPeers')
|
||||
|
||||
if [ -n "$PEERS" ] && [ "$PEERS" -gt 0 ]; then
|
||||
echo "✅ Connected peers: $PEERS"
|
||||
@@ -225,7 +225,7 @@ curl -X POST --data '{
|
||||
"method":"platform.getCurrentValidators",
|
||||
"params":{"nodeIDs":["<your-node-id>"]},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
|
||||
# Verify staking transaction
|
||||
curl -X POST --data '{
|
||||
@@ -233,7 +233,7 @@ curl -X POST --data '{
|
||||
"method":"platform.getTx",
|
||||
"params":{"txID":"<staking-tx-id>"},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
#### Issue: Low uptime percentage
|
||||
@@ -275,7 +275,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"method":"platform.getHeight",
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
|
||||
# Reduce consensus participation
|
||||
./build/node --consensus-gossip-concurrent=2
|
||||
@@ -334,7 +334,7 @@ grep "api" ~/.luxd/configs/node-config.json
|
||||
./build/node --http-host=0.0.0.0
|
||||
|
||||
# Check for rate limiting
|
||||
curl -I http://localhost:9630/ext/info
|
||||
curl -I http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
### Staking Issues
|
||||
@@ -369,7 +369,7 @@ curl -X POST --data '{
|
||||
"method":"platform.getBalance",
|
||||
"params":{"addresses":["P-lux1..."]},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
|
||||
# Import funds from X-Chain
|
||||
curl -X POST --data '{
|
||||
@@ -381,7 +381,7 @@ curl -X POST --data '{
|
||||
"sourceChain":"X"
|
||||
},
|
||||
"id":1
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://localhost:9630/v1/bc/P
|
||||
```
|
||||
|
||||
## Log Analysis
|
||||
@@ -518,7 +518,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"method":"info.getNodeID",
|
||||
"id":1
|
||||
}' http://localhost:9630/ext/info
|
||||
}' http://localhost:9630/v1/info
|
||||
```
|
||||
|
||||
### Support Channels
|
||||
|
||||
@@ -54,7 +54,8 @@ var (
|
||||
QChainAliases = AliasesFor("Q")
|
||||
AChainAliases = AliasesFor("A")
|
||||
BChainAliases = AliasesFor("B")
|
||||
TChainAliases = AliasesFor("T")
|
||||
MChainAliases = AliasesFor("M")
|
||||
FChainAliases = AliasesFor("F")
|
||||
ZChainAliases = AliasesFor("Z")
|
||||
GChainAliases = AliasesFor("G")
|
||||
KChainAliases = AliasesFor("K")
|
||||
@@ -604,7 +605,8 @@ func FromConfig(config *genesiscfg.Config) ([]byte, ids.ID, error) {
|
||||
{[]byte(config.CChainGenesis), constants.EVMID, "C-Chain", nil},
|
||||
{[]byte(config.DChainGenesis), constants.DexVMID, "D-Chain", nil},
|
||||
{[]byte(config.BChainGenesis), constants.BridgeVMID, "B-Chain", nil},
|
||||
{[]byte(config.TChainGenesis), constants.ThresholdVMID, "T-Chain", nil},
|
||||
{[]byte(config.MChainGenesis), constants.MPCVMID, "M-Chain", nil},
|
||||
{[]byte(config.FChainGenesis), constants.FHEVMID, "F-Chain", nil},
|
||||
{[]byte(config.QChainGenesis), constants.QuantumVMID, "Q-Chain", nil},
|
||||
{[]byte(config.ZChainGenesis), constants.ZKVMID, "Z-Chain", nil},
|
||||
// A/G/K carry genesis blobs and are deterministic genesis chains
|
||||
@@ -832,16 +834,24 @@ func Aliases(genesisBytes []byte) (map[string][]string, map[ids.ID][]string, err
|
||||
path.Join(constants.ChainAliasPrefix, "bridge"),
|
||||
}
|
||||
chainAliases[chainID] = BChainAliases
|
||||
case constants.ThresholdVMID:
|
||||
case constants.MPCVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"T",
|
||||
"threshold",
|
||||
"thresholdvm",
|
||||
"M",
|
||||
"mpc",
|
||||
path.Join(constants.ChainAliasPrefix, "T"),
|
||||
path.Join(constants.ChainAliasPrefix, "threshold"),
|
||||
"mpcvm",
|
||||
path.Join(constants.ChainAliasPrefix, "M"),
|
||||
path.Join(constants.ChainAliasPrefix, "mpc"),
|
||||
}
|
||||
chainAliases[chainID] = TChainAliases
|
||||
chainAliases[chainID] = MChainAliases
|
||||
case constants.FHEVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"F",
|
||||
"fhe",
|
||||
"fhevm",
|
||||
path.Join(constants.ChainAliasPrefix, "F"),
|
||||
path.Join(constants.ChainAliasPrefix, "fhe"),
|
||||
}
|
||||
chainAliases[chainID] = FChainAliases
|
||||
case constants.ZKVMID:
|
||||
apiAliases[endpoint] = []string{
|
||||
"Z",
|
||||
|
||||
@@ -62,7 +62,8 @@ var Registry = []ChainSpec{
|
||||
{Letter: "Q", VMID: constants.QuantumVMID, Aliases: []string{"quantum", "quantumvm", "pq"}, Name: "Q-Chain"},
|
||||
{Letter: "A", VMID: constants.AIVMID, Aliases: []string{"attest", "ai", "aivm"}, Name: "A-Chain"},
|
||||
{Letter: "B", VMID: constants.BridgeVMID, Aliases: []string{"bridge", "bridgevm"}, Name: "B-Chain"},
|
||||
{Letter: "T", VMID: constants.ThresholdVMID, Aliases: []string{"threshold", "thresholdvm", "mpc"}, Name: "T-Chain"},
|
||||
{Letter: "M", VMID: constants.MPCVMID, Aliases: []string{"mpc", "mpcvm"}, Name: "M-Chain"},
|
||||
{Letter: "F", VMID: constants.FHEVMID, Aliases: []string{"fhe", "fhevm"}, Name: "F-Chain"},
|
||||
{Letter: "Z", VMID: constants.ZKVMID, Aliases: []string{"zk", "zkvm"}, Name: "Z-Chain"},
|
||||
{Letter: "G", VMID: constants.GraphVMID, Aliases: []string{"graph", "graphvm", "dgraph"}, Name: "G-Chain"},
|
||||
{Letter: "K", VMID: constants.KeyVMID, Aliases: []string{"key", "keyvm"}, Name: "K-Chain"},
|
||||
|
||||
@@ -41,7 +41,8 @@ func TestChainAliasesRegistryParity(t *testing.T) {
|
||||
{"Q", QChainAliases, []string{"Q", "quantum", "quantumvm", "pq"}},
|
||||
{"A", AChainAliases, []string{"A", "attest", "ai", "aivm"}},
|
||||
{"B", BChainAliases, []string{"B", "bridge", "bridgevm"}},
|
||||
{"T", TChainAliases, []string{"T", "threshold", "thresholdvm", "mpc"}},
|
||||
{"M", MChainAliases, []string{"M", "mpc", "mpcvm"}},
|
||||
{"F", FChainAliases, []string{"F", "fhe", "fhevm"}},
|
||||
{"Z", ZChainAliases, []string{"Z", "zk", "zkvm"}},
|
||||
{"G", GChainAliases, []string{"G", "graph", "graphvm", "dgraph"}},
|
||||
{"K", KChainAliases, []string{"K", "key", "keyvm"}},
|
||||
@@ -72,7 +73,8 @@ func TestVMAliasesRegistryParity(t *testing.T) {
|
||||
constants.QuantumVMID: {"quantumvm", "quantum", "pq"},
|
||||
constants.AIVMID: {"aivm", "attest", "ai"},
|
||||
constants.BridgeVMID: {"bridgevm", "bridge"},
|
||||
constants.ThresholdVMID: {"thresholdvm", "threshold", "mpc"},
|
||||
constants.MPCVMID: {"mpc", "mpcvm"},
|
||||
constants.FHEVMID: {"fhe", "fhevm"},
|
||||
constants.ZKVMID: {"zkvm", "zk"},
|
||||
constants.GraphVMID: {"graphvm", "graph", "dgraph"},
|
||||
constants.KeyVMID: {"keyvm", "key"},
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// zzz_gateprobe_test.go is a BLUE-team gate instrument for the
|
||||
// mpcvm-decomplect surgery (LP-134 / LP-7050). NOT a behavioral
|
||||
// assertion — it emits a deterministic per-chain digest of the fully
|
||||
// built P-Chain genesis for every network so before/after can be diffed
|
||||
// to PROVE which chains changed and which stayed byte-identical.
|
||||
// zzz_ prefix keeps it last. Delete after RED sign-off.
|
||||
package builder
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/node/vms/platformvm/genesis"
|
||||
pchaintxs "github.com/luxfi/node/vms/platformvm/txs"
|
||||
)
|
||||
|
||||
func TestZZZ_GateProbe_PerChainDigest(t *testing.T) {
|
||||
nets := []struct {
|
||||
name string
|
||||
id uint32
|
||||
}{
|
||||
{"mainnet", 1},
|
||||
{"testnet", 2},
|
||||
{"devnet", 3},
|
||||
{"localnet", 1337},
|
||||
}
|
||||
|
||||
for _, n := range nets {
|
||||
cfg := GetConfig(n.id)
|
||||
if cfg == nil {
|
||||
t.Fatalf("%s GetConfig: nil", n.name)
|
||||
}
|
||||
gb, rootID, err := FromConfig(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("%s FromConfig: %v", n.name, err)
|
||||
}
|
||||
gen, err := genesis.Parse(gb)
|
||||
if err != nil {
|
||||
t.Fatalf("%s Parse: %v", n.name, err)
|
||||
}
|
||||
|
||||
type row struct {
|
||||
name string
|
||||
vmid string
|
||||
dataSum string
|
||||
chainID string
|
||||
}
|
||||
rows := make([]row, 0, len(gen.Chains))
|
||||
for _, c := range gen.Chains {
|
||||
u := c.Unsigned.(*pchaintxs.CreateChainTx)
|
||||
sum := sha256.Sum256(u.GenesisData)
|
||||
rows = append(rows, row{
|
||||
name: u.BlockchainName,
|
||||
vmid: u.VMID.String(),
|
||||
dataSum: hex.EncodeToString(sum[:]),
|
||||
chainID: c.ID().String(),
|
||||
})
|
||||
}
|
||||
sort.Slice(rows, func(i, j int) bool { return rows[i].name < rows[j].name })
|
||||
|
||||
fmt.Printf("=== NET %s (id=%d) P-ROOT=%s ===\n", n.name, n.id, rootID.String())
|
||||
for _, r := range rows {
|
||||
fmt.Printf(" CHAIN name=%-10s vmid=%-52s dataSHA256=%s chainTxID=%s\n",
|
||||
r.name, r.vmid, r.dataSum, r.chainID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,10 @@ require (
|
||||
github.com/huin/goupnp v1.3.0
|
||||
github.com/jackpal/gateway v1.1.1
|
||||
github.com/jackpal/go-nat-pmp v1.0.2
|
||||
github.com/luxfi/consensus v1.35.2
|
||||
github.com/luxfi/consensus v1.35.29
|
||||
github.com/luxfi/crypto v1.19.26
|
||||
github.com/luxfi/database v1.20.4
|
||||
github.com/luxfi/ids v1.3.0
|
||||
github.com/luxfi/ids v1.3.1
|
||||
github.com/luxfi/keychain v1.0.2
|
||||
github.com/luxfi/log v1.4.3
|
||||
github.com/luxfi/math v1.4.1
|
||||
@@ -120,13 +120,13 @@ require (
|
||||
github.com/luxfi/accel v1.2.4
|
||||
github.com/luxfi/api v1.0.15
|
||||
github.com/luxfi/atomic v1.0.0
|
||||
github.com/luxfi/chains v1.4.8
|
||||
github.com/luxfi/chains v1.7.3
|
||||
github.com/luxfi/codec v1.1.5
|
||||
github.com/luxfi/compress v0.0.5
|
||||
github.com/luxfi/constants v1.5.8
|
||||
github.com/luxfi/constants v1.6.1
|
||||
github.com/luxfi/container v0.0.4
|
||||
github.com/luxfi/filesystem v0.0.1
|
||||
github.com/luxfi/genesis v1.13.16
|
||||
github.com/luxfi/genesis v1.16.1
|
||||
github.com/luxfi/genesis/pkg/genesis/security v1.13.8
|
||||
github.com/luxfi/geth v1.17.12
|
||||
github.com/luxfi/go-bip39 v1.1.2
|
||||
@@ -139,12 +139,13 @@ require (
|
||||
github.com/luxfi/runtime v1.1.3
|
||||
github.com/luxfi/sdk v1.17.9
|
||||
github.com/luxfi/sys v0.1.0
|
||||
github.com/luxfi/threshold v1.12.1
|
||||
github.com/luxfi/timer v1.0.2
|
||||
github.com/luxfi/units v1.0.0
|
||||
github.com/luxfi/utils v1.2.0
|
||||
github.com/luxfi/utxo v0.3.7
|
||||
github.com/luxfi/validators v1.2.0
|
||||
github.com/luxfi/vm v1.2.5
|
||||
github.com/luxfi/vm v1.2.6
|
||||
github.com/luxfi/warp v1.24.0
|
||||
github.com/luxfi/zap v0.8.11
|
||||
github.com/luxfi/zwing v0.5.2
|
||||
@@ -176,8 +177,8 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
|
||||
github.com/aws/smithy-go v1.24.2 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.6 // indirect
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.5.0 // indirect
|
||||
github.com/btcsuite/btcd/chainhash/v2 v2.0.0 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381 // indirect
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect
|
||||
@@ -190,7 +191,6 @@ require (
|
||||
github.com/hanzos3/go-sdk v1.0.2 // indirect
|
||||
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||
github.com/luxfi/age v1.5.0 // indirect
|
||||
github.com/luxfi/bft v0.1.5 // indirect
|
||||
github.com/luxfi/corona v0.10.3 // indirect
|
||||
github.com/luxfi/crypto/ipa v1.2.4 // indirect
|
||||
github.com/luxfi/dkg v0.3.5 // indirect
|
||||
@@ -201,10 +201,9 @@ require (
|
||||
github.com/luxfi/mdns v0.1.1 // indirect
|
||||
github.com/luxfi/mlwe v0.2.1 // indirect
|
||||
github.com/luxfi/pq v1.0.3 // indirect
|
||||
github.com/luxfi/precompile v0.16.0 // indirect
|
||||
github.com/luxfi/precompile v0.19.1 // indirect
|
||||
github.com/luxfi/pulsar v1.9.0 // indirect
|
||||
github.com/luxfi/staking v1.5.1 // indirect
|
||||
github.com/luxfi/threshold v1.12.0 // indirect
|
||||
github.com/luxfi/trace v1.1.0 // indirect
|
||||
github.com/luxfi/zapcodec v1.0.1 // indirect
|
||||
github.com/luxfi/zapdb v1.10.1 // indirect
|
||||
|
||||
@@ -1,24 +1,63 @@
|
||||
bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5 h1:A0NsYy4lDBZAC6QiYeJ4N+XuHIKBpyhAVRMHRQZKTeQ=
|
||||
bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5/go.mod h1:gG3RZAMXCa/OTes6rr9EwusmR1OH1tDDy+cg9c5YliY=
|
||||
c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd h1:ZLsPO6WdZ5zatV4UfVpr7oAwLGRZ+sebTUruuM4Ra3M=
|
||||
c2sp.org/CCTV/age v0.0.0-20251208015420-e9274a7bdbfd/go.mod h1:SrHC2C7r5GkDk8R+NFVzYy/sdj0Ypg9htaPXQq5Cqeo=
|
||||
capnproto.org/go/capnp/v3 v3.0.1-alpha.2 h1:W/cf+XEArUSwcBBE/9wS2NpWDkM5NLQOjmzEiHZpYi0=
|
||||
capnproto.org/go/capnp/v3 v3.0.1-alpha.2/go.mod h1:2vT5D2dtG8sJGEoEKU17e+j7shdaYp1Myl8X03B3hmc=
|
||||
codeberg.org/go-fonts/liberation v0.5.0 h1:SsKoMO1v1OZmzkG2DY+7ZkCL9U+rrWI09niOLfQ5Bo0=
|
||||
codeberg.org/go-fonts/liberation v0.5.0/go.mod h1:zS/2e1354/mJ4pGzIIaEtm/59VFCFnYC7YV6YdGl5GU=
|
||||
codeberg.org/go-latex/latex v0.1.0 h1:hoGO86rIbWVyjtlDLzCqZPjNykpWQ9YuTZqAzPcfL3c=
|
||||
codeberg.org/go-latex/latex v0.1.0/go.mod h1:LA0q/AyWIYrqVd+A9Upkgsb+IqPcmSTKc9Dny04MHMw=
|
||||
codeberg.org/go-pdf/fpdf v0.10.0 h1:u+w669foDDx5Ds43mpiiayp40Ov6sZalgcPMDBcZRd4=
|
||||
codeberg.org/go-pdf/fpdf v0.10.0/go.mod h1:Y0DGRAdZ0OmnZPvjbMp/1bYxmIPxm0ws4tfoPOc4LjU=
|
||||
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
|
||||
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
|
||||
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
|
||||
filippo.io/hpke v0.4.0 h1:p575VVQ6ted4pL+it6M00V/f2qTZITO0zgmdKCkd5+A=
|
||||
filippo.io/hpke v0.4.0/go.mod h1:EmAN849/P3qdeK+PCMkDpDm83vRHM5cDipBJ8xbQLVY=
|
||||
filippo.io/nistec v0.0.4 h1:F14ZHT5htWlMnQVPndX9ro9arf56cBhQxq4LnDI491s=
|
||||
filippo.io/nistec v0.0.4/go.mod h1:PK/lw8I1gQT4hUML4QGaqljwdDaFcMyFKSXN7kjrtKI=
|
||||
git.sr.ht/~sbinet/gg v0.6.0 h1:RIzgkizAk+9r7uPzf/VfbJHBMKUr0F5hRFxTUGMnt38=
|
||||
git.sr.ht/~sbinet/gg v0.6.0/go.mod h1:uucygbfC9wVPQIfrmwM2et0imr8L7KQWywX0xpFMm94=
|
||||
github.com/ALTree/bigfloat v0.2.0 h1:AwNzawrpFuw55/YDVlcPw0F0cmmXrmngBHhVrvdXPvM=
|
||||
github.com/ALTree/bigfloat v0.2.0/go.mod h1:+NaH2gLeY6RPBPPQf4aRotPPStg+eXc8f9ZaE4vRfD4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 h1:jWQK1GI+LeGGUKBADtcH2rRqPxYB1Ljwms5gFA2LqrM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4/go.mod h1:8mwH4klAm9DUgR2EEHyEEAQlRDvLPyg5fQry3y+cDew=
|
||||
github.com/ChainSafe/go-schnorrkel v1.1.0 h1:rZ6EU+CZFCjB4sHUE1jIu8VDoB/wRKZxoe1tkcO71Wk=
|
||||
github.com/ChainSafe/go-schnorrkel v1.1.0/go.mod h1:ABkENxiP+cvjFiByMIZ9LYbRoNNLeBLiakC1XeTFxfE=
|
||||
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
|
||||
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d h1:EA+kZ8mxGb1W/ewiIBMzb/1gg5BiW1Fvr3r4qCUBJEg=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/StephenButtolph/canoto v0.17.3 h1:lvsnYD4b96vD1knnmp1xCmZqfYpY/jSeRozGdOfdvGI=
|
||||
github.com/StephenButtolph/canoto v0.17.3/go.mod h1:IcnAHC6nJUfQFVR9y60ko2ecUqqHHSB6UwI9NnBFZnE=
|
||||
github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 h1:xlwdaKcTNVW4PtpQb8aKA4Pjy0CdJHEqvFbAnvR5m2g=
|
||||
github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY=
|
||||
github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw=
|
||||
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
|
||||
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0=
|
||||
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.5 h1:zWFmPmgw4sveAYi1mRqG+E/g0461cJ5M4bJ8/nc6d3Q=
|
||||
@@ -45,6 +84,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3x
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18 h1:/A/xDuZAVD2BpsS2fftFRo/NoEKQJ8YTnJDEHBy2Gtg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.18/go.mod h1:hWe9b4f+djUQGmyiGEeOnZv69dtMSgpDRIvNMvuvzvY=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.5 h1:Z+/OLsb85Kpq7TVLCspskqePaf68Tdv6GfmJP4kH6i0=
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.5/go.mod h1:TmxGowuBYwjmHFOsEDxaZdsQE62JJzOmtiWafTi/czg=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2 h1:M1A9AjcFwlxTLuf0Faj88L8Iqw0n/AJHjpZTQzMMsSc=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.96.2/go.mod h1:KsdTV6Q9WKUZm2mNJnUFmIoXfZux91M3sr/a4REX8e0=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg=
|
||||
@@ -64,11 +105,12 @@ github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
|
||||
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
|
||||
github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY=
|
||||
github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.6 h1:IzlsEr9olcSRKB/n7c4351F3xHKxS2lma+1UFGCYd4E=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.6/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.5.0 h1:KioMXOWa76b86sTZZOmbzv/ldaQCmB8KFAyn5PbB8E8=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.5.0/go.mod h1:+K/MYXcLBtHEQjRbjHuJChuybk4LCgjdjgRwil+e+Kk=
|
||||
github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00=
|
||||
@@ -78,24 +120,54 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtyd
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chainhash/v2 v2.0.0 h1:PMLlSloHJuEeB80XG9EjpXWNEKAZAMLl6YHZ6YsEuoA=
|
||||
github.com/btcsuite/btcd/chainhash/v2 v2.0.0/go.mod h1:mKxcZ7oGTXE7IRV+sS9hP4EVBwc/SzfNR+52IsOP9j8=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
|
||||
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4=
|
||||
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE=
|
||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
|
||||
github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
|
||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chelnak/ysmrr v0.6.0 h1:kMhO0oI02tl/9szvxrOE0yeImtrK4KQhER0oXu1K/iM=
|
||||
github.com/chelnak/ysmrr v0.6.0/go.mod h1:56JSrmQgb7/7xoMvuD87h3PE/qW6K1+BQcrgWtVLTUo=
|
||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/cloudflare/cloudflare-go v0.116.0 h1:iRPMnTtnswRpELO65NTwMX4+RTdxZl+Xf/zi+HPE95s=
|
||||
github.com/cloudflare/cloudflare-go v0.116.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
|
||||
github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo=
|
||||
@@ -110,8 +182,12 @@ github.com/cockroachdb/redact v1.1.8 h1:8eVLLj6juKxiKrAEw2b8cJvNqWq++U8WOfQFuL7K
|
||||
github.com/cockroachdb/redact v1.1.8/go.mod h1:GceHHpJ0rMDpYARL5In88Alq/xMBUtVlz7Qxix6ZVkw=
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb h1:3bCgBvB8PbJVMX1ouCcSIxvsqKPYM7gs72o0zC76n9g=
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
|
||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
||||
github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381 h1:d5EKgQfRQvO97jnISfR89AiCCCJMwMFoSxUiU0OGCRU=
|
||||
github.com/colega/zeropool v0.0.0-20230505084239-6fb4a4f75381/go.mod h1:OU76gHeRo8xrzGJU3F3I1CqX1ekM8dfJw0+wPeMwnp0=
|
||||
github.com/consensys/bavard v0.2.2-0.20260118153501-cba9f5475432 h1:4ACburMEVC+uaqG54jGgAwYTQmKHixtNej9j1Xs0H0o=
|
||||
github.com/consensys/bavard v0.2.2-0.20260118153501-cba9f5475432/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
|
||||
github.com/consensys/gnark-crypto v0.20.1 h1:PXDUBvk8AzhvWowHLWBEAfUQcV1/aZgWIqD6eMpXmDg=
|
||||
github.com/consensys/gnark-crypto v0.20.1/go.mod h1:RBWrSgy+IDbGR69RRV313th3M/aZU1ubk2om+qHuTSc=
|
||||
github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY=
|
||||
@@ -121,9 +197,14 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/crate-crypto/go-eth-kzg v1.5.0 h1:FYRiJMJG2iv+2Dy3fi14SVGjcPteZ5HAAUe4YWlJygc=
|
||||
github.com/crate-crypto/go-eth-kzg v1.5.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
|
||||
github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4=
|
||||
github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cronokirby/saferith v0.33.0 h1:TgoQlfsD4LIwx71+ChfRcIpjkw+RPOapDEVxa+LhwLo=
|
||||
github.com/cronokirby/saferith v0.33.0/go.mod h1:QKJhjoqUtBsXCAVEjw38mFqoi7DebT7kthcD7UzbnoA=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -140,23 +221,40 @@ github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPc
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/dgraph-io/badger/v4 v4.9.0 h1:tpqWb0NewSrCYqTvywbcXOhQdWcqephkVkbBmaaqHzc=
|
||||
github.com/dgraph-io/badger/v4 v4.9.0/go.mod h1:5/MEx97uzdPUHR4KtkNt8asfI2T4JiEiQlV7kWUo8c0=
|
||||
github.com/dgraph-io/ristretto/v2 v2.4.0 h1:I/w09yLjhdcVD2QV192UJcq8dPBaAJb9pOuMyNy0XlU=
|
||||
github.com/dgraph-io/ristretto/v2 v2.4.0/go.mod h1:0KsrXtXvnv0EqnzyowllbVJB8yBonswa2lTCK2gGo9E=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 h1:C7t6eeMaEQVy6e8CarIhscYQlNmw5e3G36y7l7Y21Ao=
|
||||
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0/go.mod h1:56wL82FO0bfMU5RvfXoIwSOP2ggqqxT+tAfNEIyxuHw=
|
||||
github.com/dop251/goja v0.0.0-20260311135729-065cd970411c h1:OcLmPfx1T1RmZVHHFwWMPaZDdRf0DBMZOFMVWJa7Pdk=
|
||||
github.com/dop251/goja v0.0.0-20260311135729-065cd970411c/go.mod h1:MxLav0peU43GgvwVgNbLAj1s/bSGboKkhuULvq/7hx4=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emicklei/dot v1.11.0 h1:zsrhCuFHAJge/aZIC4N4LdHy5tqYu4tWEaUzIwdYj4Y=
|
||||
github.com/emicklei/dot v1.11.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.7 h1:aat3CuITdDbPC6pmEGRT0zJ5eOxzrZj8TJT5z7Xk//M=
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.7/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw=
|
||||
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
|
||||
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
|
||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/ferranbt/fastssz v1.0.0 h1:9EXXYsracSqQRBQiHeaVsG/KQeYblPf40hsQPb9Dzk8=
|
||||
github.com/ferranbt/fastssz v1.0.0/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
|
||||
github.com/fjl/gencodec v0.1.1 h1:DhQY29Q6JLXB/GgMqE86NbOEuvckiYcJCbXFu02toms=
|
||||
github.com/fjl/gencodec v0.1.1/go.mod h1:chDHL3wKXuBgauP8x3XNZkl5EIAR5SoCTmmmDTZRzmw=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
@@ -166,8 +264,14 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.9.1 h1:2rWm8B193Ll4VdjsJY28jxs70IdDsHRWgQYAI80+rMQ=
|
||||
github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61 h1:IZqZOB2fydHte3kUgxrzK5E1fW7RQGeDwE8F/ZZnUYc=
|
||||
github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILDlzrGEckF6HKjXe48EgsY/l7K7vhY4MW8=
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20250918194357-1ec6f2e601c6 h1:ko+DlyhLqUHpgrvwqs5ybydoGAqjpJQTXpAS7vUqVlM=
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20250918194357-1ec6f2e601c6/go.mod h1:3IVE7v4II2gS2V5amIH7F7NeYQtbbORtQtjdflgS1vk=
|
||||
github.com/getsentry/sentry-go v0.44.1 h1:/cPtrA5qB7uMRrhgSn9TYtcEF36auGP3Y6+ThvD/yaI=
|
||||
github.com/getsentry/sentry-go v0.44.1/go.mod h1:XDotiNZbgf5U8bPDUAfvcFmOnMQQceESxyKaObSssW0=
|
||||
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9 h1:r5GgOLGbza2wVHRzK7aAj6lWZjfbAwiu/RDCVOKjRyM=
|
||||
github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=
|
||||
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
|
||||
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
|
||||
@@ -176,8 +280,16 @@ github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01
|
||||
github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-json-experiment/json v0.0.0-20260601182631-00ed12fed2a6 h1:nxP4pPoyqOAgX8lYDFCfl3DyKeXErCvSvhcyzwGV9CE=
|
||||
github.com/go-json-experiment/json v0.0.0-20260601182631-00ed12fed2a6/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -188,19 +300,42 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccmack/gocc v1.0.2 h1:PHv20lcM1Erz+kovS+c07DnDFp6X5cvghndtTXuEyfE=
|
||||
github.com/goccmack/gocc v1.0.2/go.mod h1:LXX2tFVUggS/Zgx/ICPOr3MLyusuM7EcbfkPvNsjdO8=
|
||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
|
||||
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
|
||||
github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0=
|
||||
github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/gogo/status v1.1.0 h1:+eIkrewn5q6b30y+g/BJINVVdi2xH7je5MPJ3ZPK3JA=
|
||||
github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -212,6 +347,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
@@ -219,12 +356,16 @@ github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0=
|
||||
github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
@@ -243,10 +384,16 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
|
||||
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
|
||||
github.com/graph-gophers/graphql-go v1.9.0 h1:yu0ucKHLc5qGpRwLYKIWtr9bOoxovkWasuBrPQwlHls=
|
||||
github.com/graph-gophers/graphql-go v1.9.0/go.mod h1:23olKZ7duEvHlF/2ELEoSZaY1aNPfShjP782SOoNTyM=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
||||
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
||||
github.com/gtank/ristretto255 v0.2.0 h1:LeOuWr6giplWkkMizx2emfG03SRPJqKt1nfIHLVHQ/0=
|
||||
github.com/gtank/ristretto255 v0.2.0/go.mod h1:OJ1ox/dWcp7sJ5grYDcZ+kkHYuj5nelW5aaL7ESVXBw=
|
||||
github.com/guptarohit/asciigraph v0.5.5 h1:ccFnUF8xYIOUPPY3tmdvRyHqmn1MYI9iv1pLKX+/ZkQ=
|
||||
github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag=
|
||||
github.com/hanzoai/vfs v0.4.3 h1:QN9SemEQBq9x1l/toi51/TZWctbt3i3mgUJfz5RPALY=
|
||||
github.com/hanzoai/vfs v0.4.3/go.mod h1:wTHfTpJ/165yz0qfPBNFcYRg+tGw8YDwPu+xgps88zU=
|
||||
github.com/hanzos3/go-sdk v1.0.2 h1:EOJQGVnwclkzIyRJyWqtqmA2muyaSsF4y+7KYC4Vhdw=
|
||||
@@ -261,24 +408,64 @@ github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZ
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
|
||||
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
|
||||
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
||||
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||
github.com/hydrogen18/memlistener v1.0.0 h1:JR7eDj8HD6eXrc5fWLbSUnfcQFL06PYvCc0DKQnWfaU=
|
||||
github.com/hydrogen18/memlistener v1.0.0/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b h1:ogbOPx86mIhFy764gGkqnkFC8m5PJA7sPzlk9ppLVQA=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.14.0 h1:AjbBfJuq+QoaXNcrova8smSjwJdUHnwvfjMF71M1iI4=
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.14.0/go.mod h1:Ahpm3QXKMJslpXl3IftVLVezreAUtBOTZssDrjZEFHI=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSHzRbhzK8RdXOsAdfDgO49TtqC1oZ+acxPrkfTxcCs=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU=
|
||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
|
||||
github.com/jackpal/gateway v1.1.1 h1:UXXXkJGIHFsStms9ZBgGpoaFEJP7oJtFn5vplIT68E8=
|
||||
github.com/jackpal/gateway v1.1.1/go.mod h1:Tl1vZVtUaXx5j6P5HFmv45alhEi4yHHLfT4PRbB7eyw=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jacobsa/fuse v0.0.0-20260302145937-f1ba38d60fdf h1:1FpPcJSf6jjJGvIltaLwJCpbFCMI9bVUCAAxUSxqWnY=
|
||||
github.com/jacobsa/fuse v0.0.0-20260302145937-f1ba38d60fdf/go.mod h1:fcpw1yk/suvFhB8rT9P+pst+NLboWsBLky9csooKjPc=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jedisct1/go-minisign v0.0.0-20241212093149-d2f9f49435c7 h1:FWpSWRD8FbVkKQu8M1DM9jF5oXFLyE+XpisIYfdzbic=
|
||||
github.com/jedisct1/go-minisign v0.0.0-20241212093149-d2f9f49435c7/go.mod h1:BMxO138bOokdgt4UaxZiEfypcSHX0t6SIFimVP1oRfk=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE=
|
||||
github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b h1:FQ7+9fxhyp82ks9vAuyPzG0/vVbWwMwLJ+P6yJI5FN8=
|
||||
github.com/juju/fslock v0.0.0-20160525022230-4d5c94c67b4b/go.mod h1:HMcgvsgd0Fjj4XXDkbjdmlbI505rUPBs6WBMYg2pXks=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4=
|
||||
github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig=
|
||||
github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4=
|
||||
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
@@ -286,6 +473,8 @@ github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzh
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM=
|
||||
github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -298,8 +487,10 @@ github.com/luxfi/accel v1.2.4 h1:5VbIHyEvvfobn2zBiTFODxDw1CeqxCepZOLlvkuf9yQ=
|
||||
github.com/luxfi/accel v1.2.4/go.mod h1:ISIwAX+ZfsL/S5nsP2JvfldXN6Nc+QzoWf6Jtaq+xsQ=
|
||||
github.com/luxfi/address v1.0.1 h1:Sc4keyuVzBIvHr7uVeYZf2/WY9YDGUgDi/iiWenj49g=
|
||||
github.com/luxfi/address v1.0.1/go.mod h1:5j3Eh66v9zvv1GbNdZwt+23krV8JlSDaRzmWZU8ZRM0=
|
||||
github.com/luxfi/age v1.5.0 h1:zC/Fw/ptZwAXr9nqrxmrcf8752EIl1Lq9RECp9OmCO0=
|
||||
github.com/luxfi/age v1.5.0 h1:G69HbSV4R3vKEH9B0CulnRaMdSdf4RalMgP8xKmxHeI=
|
||||
github.com/luxfi/age v1.5.0/go.mod h1:iAYAxgvrXxcy746+Ovh/eWWDuF9teJLNcCSSOX9RYW0=
|
||||
github.com/luxfi/ai v0.1.0 h1:PwTGob0GJivbdqNpUs82bvwcE8/qxyTokxfY7BZVPoo=
|
||||
github.com/luxfi/ai v0.1.0/go.mod h1:lTuY32dGjEJUgVheYuanlt1O0jHj0AZ09sUSpRsmH6M=
|
||||
github.com/luxfi/api v1.0.15 h1:Q5ox3Ompw/AZNMfB9wpHZosrj9C+ZldAEHKtHEotFdY=
|
||||
github.com/luxfi/api v1.0.15/go.mod h1:Eer59msIXMnOlFncG0XjEGH3TZML0Dd1bUu4GtB7f4Q=
|
||||
github.com/luxfi/atomic v1.0.0 h1:xUV60MuzRvXngaQ1sM0yVC2v4TRoLlUGkkH7M9PS4yw=
|
||||
@@ -308,20 +499,22 @@ github.com/luxfi/bft v0.1.5 h1:5xVLPkog4e5LTgaVlb9pgxA0EWE6tkrKwHPZVRz+RZw=
|
||||
github.com/luxfi/bft v0.1.5/go.mod h1:5I8Ft8yA69xZlDe3RB0i4MgbqFKLZe65o/sha8JuKvU=
|
||||
github.com/luxfi/cache v1.2.1 h1:kAzOS55/hmYeNKR+0HAKv4ma48Y6JjkI8UQeqdZ8bfI=
|
||||
github.com/luxfi/cache v1.2.1/go.mod h1:co7JTxZZHpKT31Yh01LFp5aZOxmoUg157FhBLQdQHVU=
|
||||
github.com/luxfi/chains v1.4.8 h1:i5QxDfGR922oPGYrBbUo2Qn3tFMpJD9BilNTLFaQscE=
|
||||
github.com/luxfi/chains v1.4.8/go.mod h1:F/jT9YbC8/yD4WxJu4AvRFbi4NyKY+FHBWrE8M08dgE=
|
||||
github.com/luxfi/chains v1.7.3 h1:XkWnRPLzRQdY0kG7+gDldcSBezGdGuSN1OjXUYM4Jlg=
|
||||
github.com/luxfi/chains v1.7.3/go.mod h1:0HTPP22YJgTVPWWeHwoWrqa1xzeI5/f387WnRhCikhY=
|
||||
github.com/luxfi/codec v1.1.5 h1:KBq8uvYm5Dy+E1heG8WBmqbqu8kstlFyE5ASBBB+C8I=
|
||||
github.com/luxfi/codec v1.1.5/go.mod h1:/ugIv5iEgI+VAuPIetzxNT0eJaEjOID/mrIsgIjJh8g=
|
||||
github.com/luxfi/compress v0.0.5 h1:4tEUHw5MK1bu5UOjfYCt4OKMiH7yykIgmGPRA/BfJTM=
|
||||
github.com/luxfi/compress v0.0.5/go.mod h1:Cc1yxD2pfzrvpO32W2GDwLKff+CylHEvzZh2Ko8RSIU=
|
||||
github.com/luxfi/concurrent v0.0.3 h1:eJyv1fhaC0jMLMw6+QS774cUmp7GK+ouMgvLCqnC7cc=
|
||||
github.com/luxfi/concurrent v0.0.3/go.mod h1:Aj/FR5NpM0cB2P4Nt3+tz9+dV6V+LUW4HuMgSjwq5hw=
|
||||
github.com/luxfi/consensus v1.33.3 h1:gUfmxb+KSLJnixFkk7CvKHeO1B9CNBt/Zp6EJEwgVXE=
|
||||
github.com/luxfi/consensus v1.33.3/go.mod h1:2ON+tN8hsAqd13DmsGk1Epfz6EQ0s4lv/rmOUsh7Gpk=
|
||||
github.com/luxfi/consensus v1.35.2 h1:Vy0yrLkCqRHhijYu3qNNwEf7e79HvSAbkRqbBLAYZnM=
|
||||
github.com/luxfi/consensus v1.35.2/go.mod h1:2ON+tN8hsAqd13DmsGk1Epfz6EQ0s4lv/rmOUsh7Gpk=
|
||||
github.com/luxfi/constants v1.5.8 h1:iNP9AWNUcM4Tps7jYnx49CwtCWAC9mYRxJfGou2za0g=
|
||||
github.com/luxfi/constants v1.5.8/go.mod h1:Pu5jWHdnUtQRbWC43yTUjU/pbIIKMDOd2a2yroSfo48=
|
||||
github.com/luxfi/config v1.1.2 h1:iCUewwm7oT7ckRNyQkrVHZ5Ge+l+FadciGX/zyaPo/k=
|
||||
github.com/luxfi/config v1.1.2/go.mod h1:z6t0a5pGpQz2uDW2qJPLX5fZ/eWbpiNa51gBc63ebFk=
|
||||
github.com/luxfi/consensus v1.35.29 h1:vZfj/T7dlDpbAKzWCd/qVBN7/1Nr1RDJm6QW1Q4DhxU=
|
||||
github.com/luxfi/consensus v1.35.29/go.mod h1:hmsGz3CcTrKxvw7/YSmfu8qAtzgyL5zQ3ajpUNcub/4=
|
||||
github.com/luxfi/const v1.4.0 h1:8bHt5I8g76TkeuFIY478aYtfcLgbdkGNo/e/Sy3+6p0=
|
||||
github.com/luxfi/const v1.4.0/go.mod h1:qWfecjdImPY3kXsy3UAe4ZVpMcsp2/iUlHwUMPVREoI=
|
||||
github.com/luxfi/constants v1.6.1 h1:4AfBh1YxDgnQjWPLqLpjiBaLAjPBw5naTTzRWWM19ms=
|
||||
github.com/luxfi/constants v1.6.1/go.mod h1:Pu5jWHdnUtQRbWC43yTUjU/pbIIKMDOd2a2yroSfo48=
|
||||
github.com/luxfi/container v0.0.4 h1:BXhF82WyfqVP5mjlNcr7tP0Fcnvl0Ap1rkiu+rq5XuM=
|
||||
github.com/luxfi/container v0.0.4/go.mod h1:Z3SpmMF5d4t77MM0nHYXURpn+EMVaeu1fhbd/3BGaek=
|
||||
github.com/luxfi/corona v0.10.3 h1:Yi1oAkW0HEsf5fvst/tUN0AjRVg6DoNHB/IC0qrFWZE=
|
||||
@@ -332,14 +525,22 @@ github.com/luxfi/crypto/ipa v1.2.4 h1:6xfwhI9/HrcDkF3Ti5/NxsNQIWbwYDJmRSNIHRQ/xf
|
||||
github.com/luxfi/crypto/ipa v1.2.4/go.mod h1:43J6f6rcfUMrZt4cQectMOZb6Ps+fAEj8ZTPC3Kk+gE=
|
||||
github.com/luxfi/database v1.20.4 h1:WOt2GIGJxf8AFpg49odMz8DZ8RFSLDrozGhZtmorN70=
|
||||
github.com/luxfi/database v1.20.4/go.mod h1:S/LvmfzNYWVNslcEcZwDrntqUO2ksaL8ql1nRmLUA/Q=
|
||||
github.com/luxfi/dex v1.14.0 h1:4PtorVbRmbD73S6YWPvgp5GRCb9jeuaedl7mo1CbzCI=
|
||||
github.com/luxfi/dex v1.14.0/go.mod h1:wYWQmwospvdKBWQ6OJMXb8I+kfgEtE46R1rD8H7vHCQ=
|
||||
github.com/luxfi/dkg v0.3.5 h1:s2L2mMQaz+n9m0b0ghvoV5VZNxiwb2z4WrGugvK0udY=
|
||||
github.com/luxfi/dkg v0.3.5/go.mod h1:M+WH7GFRN+YUD851Rlnumdp0Md98kplNN8pVx65U8I8=
|
||||
github.com/luxfi/evm v1.104.4 h1:Y2OxnhMZBOG4O5CdYcBkNhO3O/Y8PW+LafTuy1lpJME=
|
||||
github.com/luxfi/evm v1.104.4/go.mod h1:OjKvdO53g3LrNutLgx6N0ZXair6glO+MtueD4MHCKaE=
|
||||
github.com/luxfi/fhe v1.8.2 h1:QllnObNFbi6D4mvFI6uQkepW8HgLtdy4RMR1TKYAInA=
|
||||
github.com/luxfi/fhe v1.8.2/go.mod h1:16yxwhcnCez/rNcd/C9JjH9IjbEz73X+0tvlsONyLeA=
|
||||
github.com/luxfi/filesystem v0.0.1 h1:VZ6xMFKaAPBW/ddlMsDnI2G0VU1lV5rYaVcW5d+KwEY=
|
||||
github.com/luxfi/filesystem v0.0.1/go.mod h1:OQVSU6XNwqrr1AI+MqkID2taHUclx7NYmmr3svgttec=
|
||||
github.com/luxfi/formatting v1.0.1 h1:ZnE1rAdEUds9yAegdVdGDOBGN6hLMPOv6E03Fp8IEYo=
|
||||
github.com/luxfi/formatting v1.0.1/go.mod h1:mYzNf5DJOiqSSKUPzNj5dKy4tstFbN3pZlkI5716eKc=
|
||||
github.com/luxfi/genesis v1.13.16 h1:suwWPwUu2nv1fxvx9vwHgcgJCzkCpiVMxBrJIh4S3BQ=
|
||||
github.com/luxfi/genesis v1.13.16/go.mod h1:qUa+AcTWwxv0x+CJochBsRNOMbmEBjw07HJKZLhs5c0=
|
||||
github.com/luxfi/genesis v1.16.1 h1:t8zFIeFg9hwl39HpYKshpB2hlHjVVBpd8va4d+FZ8T0=
|
||||
github.com/luxfi/genesis v1.16.1/go.mod h1:vpnyQ/YcGINhUekrCiZWFryvP3qgYzTgFkfWoExlUdE=
|
||||
github.com/luxfi/genesis/builder v0.0.0-20260607050918-bf8751181b9c h1:JvD76e3juXlpBUN9hgRdF5M3l0tl/KWnE0fjPY+P8qY=
|
||||
github.com/luxfi/genesis/builder v0.0.0-20260607050918-bf8751181b9c/go.mod h1:BGDsFPIYLplOoBax/cpnUprd+yzLKjYtvSRt/eSP4sk=
|
||||
github.com/luxfi/genesis/pkg/genesis/security v1.13.8 h1:9ier0p55ErSpoXHRJpZ04WV5HwwMB1uDrU7PHGBKG2U=
|
||||
github.com/luxfi/genesis/pkg/genesis/security v1.13.8/go.mod h1:DzU+GYUFv12ja4Vc46bWKNBBmNYbcow3u/DASx4wpfI=
|
||||
github.com/luxfi/geth v1.17.12 h1:UP/fhpcfbGPTrkOCwX3d88Oc3jVm5gTOgfjgq+lek6s=
|
||||
@@ -348,8 +549,12 @@ github.com/luxfi/go-bip32 v1.0.2 h1:7vFbb+Wr4Z499q2tuCLdd7wWjtn8sH+HWBlx76mhH9Y=
|
||||
github.com/luxfi/go-bip32 v1.0.2/go.mod h1:bc7/LXDKAJQZ/F0Xjf5yXaTZxY9/ssLb4FC+Hxn/cDk=
|
||||
github.com/luxfi/go-bip39 v1.1.2 h1:p+wLMPGs6MLQh7q0YIsmy2EhHL7LHiELEGTJko6t/Jg=
|
||||
github.com/luxfi/go-bip39 v1.1.2/go.mod h1:96de9VkR2kY/ASAnhMtvt3TSh+PZkAFAngNj0GjRGDo=
|
||||
github.com/luxfi/ids v1.3.0 h1:11xnwRDm6zQzbqcRnkFujOYkvhK4Fs/+g+sKRlRUNsU=
|
||||
github.com/luxfi/ids v1.3.0/go.mod h1:6vpdcdZW0qxeade+3xby8aLTutbcJ7O0r8+fNQrksGI=
|
||||
github.com/luxfi/gpu v1.0.1 h1:7cDJTwTZ8Wk5KmyzsiGPe1nekyTgsSG6LJ+4rgKim8E=
|
||||
github.com/luxfi/gpu v1.0.1/go.mod h1:8r1ReBPsLx1zmcA3GksKD4J7azP52FSTU/LpT9TyYHE=
|
||||
github.com/luxfi/hid v0.9.3 h1:pMrOQQ4014IcZPt/MNul7eokXH4ypF6NkQs29/A3h/Y=
|
||||
github.com/luxfi/hid v0.9.3/go.mod h1:XJ/7DZAHf5dggm3zWNbitKuFGB7J96b4iX+8NO3BsnY=
|
||||
github.com/luxfi/ids v1.3.1 h1:CGE3QvYzdwfDpfODAVNjMygSaueVPWXSB9yaeyCEd+k=
|
||||
github.com/luxfi/ids v1.3.1/go.mod h1:6vpdcdZW0qxeade+3xby8aLTutbcJ7O0r8+fNQrksGI=
|
||||
github.com/luxfi/keychain v1.0.2 h1:uQgmjs37/VBIALEiYrrszTpxvtqr07/YvS9TnmxGafs=
|
||||
github.com/luxfi/keychain v1.0.2/go.mod h1:q/4ULgZBlstKkwzOzG/0T6y73BDPgnkrcibbJyTvmbU=
|
||||
github.com/luxfi/keys v1.2.0 h1:3TAcr4twyMpwQp7J29ZRtIa5vzAoDrnXnLcPKVHJWmw=
|
||||
@@ -358,10 +563,14 @@ github.com/luxfi/kms v1.11.7 h1:E25z8SCNTGOVvzzg5tj6pwJQ2K3FrE/nuy0KAfF+0zs=
|
||||
github.com/luxfi/kms v1.11.7/go.mod h1:XhLUVqN4RBv6j4Bj3MNgTZmHCnm74jH7RqqK0b9xbzw=
|
||||
github.com/luxfi/lattice/v7 v7.1.4 h1:hQR02M6cHTAV5+joOPi9gb9Gm+z/hKJnhJF4IlciIJs=
|
||||
github.com/luxfi/lattice/v7 v7.1.4/go.mod h1:DmIQFi3mJiehVsR235l1NKYEU0JhU649OX5p7gMEW2c=
|
||||
github.com/luxfi/ledger v1.1.6 h1:GqzUVNa9aoCBantBSvVm4js0O8uNKOwBKc5xzvBRlaA=
|
||||
github.com/luxfi/ledger v1.1.6/go.mod h1:hyNV+4a6nI3yfwhyJcmXICwJeWfnxA1PbMZEOZ1VFnw=
|
||||
github.com/luxfi/lens v0.1.4 h1:goGjGDXx2BNdjzXDunL5QT8elK2ZyCcc0z8TAbtWYrg=
|
||||
github.com/luxfi/lens v0.1.4/go.mod h1:mL+G8IK+9L41d78/2FYRgfhEzAjcr5+VEXB8SGuHbus=
|
||||
github.com/luxfi/log v1.4.3 h1:xkUKRWvQ4ZwvlUC2e0/RTtHYZOYSMvSQ9W9lbjwBmiI=
|
||||
github.com/luxfi/log v1.4.3/go.mod h1:myIkufyiQomSQH34K981kbz6cG4WUoerRUh7F4XhlQI=
|
||||
github.com/luxfi/lpm v1.9.4 h1:DWQTDlmKmW+ylWUl1a/0HmVxXs65zuOQEbY616uyhkA=
|
||||
github.com/luxfi/lpm v1.9.4/go.mod h1:K9NTVtLpFZ/ojG4fs963108I8My58gyO6DlnULoxag0=
|
||||
github.com/luxfi/magnetar v1.2.3 h1:n4UrJZLK+mhDDZr1HLl2H/KgA6o6v62r5oiC61R7awE=
|
||||
github.com/luxfi/magnetar v1.2.3/go.mod h1:z9PLkqzzYiaFGT/qFBQSnNoHmZrg8y7JlYGiNnHAAdk=
|
||||
github.com/luxfi/math v1.4.1 h1:1t9bCCsEqnl9yIKrShlbs80DBKyYTWdnzkVfBqEeO7Q=
|
||||
@@ -380,16 +589,26 @@ github.com/luxfi/mock v0.1.1 h1:0HEtIjg1J6CWz+IUyP6rsGqNWTcmxjFnSQIhaDuARwY=
|
||||
github.com/luxfi/mock v0.1.1/go.mod h1:jo35akl3Vtd8LbzDts8VJ0jmSVycrd1/eBi6g6t5hKU=
|
||||
github.com/luxfi/net v0.0.5 h1:F1lD3NsIioV0wr2V5jWc4TtMyiE/Ffo1LoeblFv3TrI=
|
||||
github.com/luxfi/net v0.0.5/go.mod h1:BEQR1HEVmkjii/F1R6vJrNUVE7wr55b4eMq9Iz5wjUw=
|
||||
github.com/luxfi/netrunner v1.19.2 h1:f0bUsW8vN4Zw6gzQV7q+8ED+XkNeJZ0hSNY8Mj87c8A=
|
||||
github.com/luxfi/netrunner v1.19.2/go.mod h1:e9oiUxS1B49UNbps275fHrKOnv98/dQi67bIkjM1NhI=
|
||||
github.com/luxfi/oracle v1.0.0 h1:Jcr1jbiVCFuaeer2Vcrabc0A8/u2CNCiAEoauftlxfE=
|
||||
github.com/luxfi/oracle v1.0.0/go.mod h1:7xmiYMZwKiIazVr56lMHDxC0kMF0/Y/9OLSdOBBQ7/M=
|
||||
github.com/luxfi/ordering v0.0.1 h1:pZkMD5AC1Q/5RnZsTKK0KWd1tHXiJw7WPU0oebT0wog=
|
||||
github.com/luxfi/ordering v0.0.1/go.mod h1:Ld5UAayScKxicsd9BYuQ/D7vzEUndOC7cGCT+fj++jM=
|
||||
github.com/luxfi/p2p v1.21.1 h1:gmz1JMDhzHIL3dQlhwIDvR4OlFuhNVfnWUl/ipYhAIo=
|
||||
github.com/luxfi/p2p v1.21.1/go.mod h1:SsNPR5fPGWWNem9plGWhSmRqyDoysJ3kPAN0zG0g3iw=
|
||||
github.com/luxfi/password v0.0.1 h1:K/MDOeLzNfWKizqitT8Juwz7BWGwubJIjOxR5JY52U8=
|
||||
github.com/luxfi/password v0.0.1/go.mod h1:xKhi28+V3xqPc4+kIZHRp7oZ1W7nUKze6EauvMVq19s=
|
||||
github.com/luxfi/pq v1.0.3 h1:ksw1dmfTR0dqqNMRS7BjGcprCO2Fhc+3Iiq2/NMuONw=
|
||||
github.com/luxfi/pq v1.0.3/go.mod h1:8bppZcRElfrVt0n3nYCZW3iX1TvhvzNbdjNdK1irgIE=
|
||||
github.com/luxfi/precompile v0.16.0 h1:lMdKapbApcbehtAc0mkRqkHFdTTITRTo3e3ivdI63RY=
|
||||
github.com/luxfi/precompile v0.16.0/go.mod h1:nIO7c4arFTqCl3nR0BoumPn1etYY32EYExJxqwu23VA=
|
||||
github.com/luxfi/precompile v0.19.1 h1:nTfhwrubwQKED5SAOFqIbFO1o6J49IZNdSYbOEaiPpA=
|
||||
github.com/luxfi/precompile v0.19.1/go.mod h1:AOMGWGFXHtnGVYjel/mP/7Dt60e2u7ef0SswY4j8F+k=
|
||||
github.com/luxfi/proto v1.3.5 h1:AW11rnu5xyvB7beyowoiY9uIffLOF3+eMR/a3EkK2c8=
|
||||
github.com/luxfi/proto v1.3.5/go.mod h1:ixTofGpdW1rTYr+wgTuBhAsgBv8GnWYHMLWbPNEdm7M=
|
||||
github.com/luxfi/pulsar v1.9.0 h1:c0JnatYF79aN87aof9VlYjIoCzmixxrgNPeUUuh8ScU=
|
||||
github.com/luxfi/pulsar v1.9.0/go.mod h1:1+/atAiiiOm9RnXM3c66eHF3garjAa3C+sn4rAU7JUU=
|
||||
github.com/luxfi/relay v1.0.0 h1:AHE8JtNmicYcwu7hCrUUMJ8WfCtjL4UlkzVaHwIrBpI=
|
||||
github.com/luxfi/relay v1.0.0/go.mod h1:srhFAQ3dS+WBTRwPqzinZiw+QbWdRhajT+W7k9ShjrE=
|
||||
github.com/luxfi/resource v0.0.1 h1:mTh+ICWSy548GTUSSyx7V/X5dV18oEwxZeQEYGJQhD4=
|
||||
github.com/luxfi/resource v0.0.1/go.mod h1:wWpZktciYwIi6RNqA+fHwzmPrUJa7PRX7urfwT+spRE=
|
||||
github.com/luxfi/rpc v1.1.0 h1:B/PJbK399th1mHRDSufhCpVbAciZqId3LsaWhIGNWH4=
|
||||
@@ -404,8 +623,8 @@ github.com/luxfi/staking v1.5.1 h1:f9MaGnRm0xc02crDm5Qs1T2r88d3KzNkHZypAvsmAlU=
|
||||
github.com/luxfi/staking v1.5.1/go.mod h1:lT7KLaiTpdq3lg78H0gp2qSEfX9LaK1vs7w73XV/9nw=
|
||||
github.com/luxfi/sys v0.1.0 h1:M7RYOt8W4Wws7cxxsyOHe50UKMYTzIu7HYknqW4xt0Y=
|
||||
github.com/luxfi/sys v0.1.0/go.mod h1:GT8vGdYTfoqRy9/11blmRuqPPypzwrudCTHZXT+ru9M=
|
||||
github.com/luxfi/threshold v1.12.0 h1:JJ369xC/YyDvrqXj+xFoK98nP2rUM099qFs03hBvq/M=
|
||||
github.com/luxfi/threshold v1.12.0/go.mod h1:iuRQGDAy8ZKjQhZjkSKg7NtbP75/8Up9zj52y7IuyZo=
|
||||
github.com/luxfi/threshold v1.12.1 h1:pA6ZB8Qv6BStprSemfoCY3fD7P5PEod36Nj6FmJR1jQ=
|
||||
github.com/luxfi/threshold v1.12.1/go.mod h1:iuRQGDAy8ZKjQhZjkSKg7NtbP75/8Up9zj52y7IuyZo=
|
||||
github.com/luxfi/timer v1.0.2 h1:g/odi0VQJIsrzdklJUG1thHZ/sGNnbIiVGcU6LctJm0=
|
||||
github.com/luxfi/timer v1.0.2/go.mod h1:SoaZwntYigUE3H6z1GV32YwP8QaSiAT0UiEv7iPugXg=
|
||||
github.com/luxfi/tls v1.0.3 h1:rK3nxSAxrUOOSHOZnKChwV4f6UJ+cfOl8KWJXAQx/SI=
|
||||
@@ -424,11 +643,11 @@ github.com/luxfi/validators v1.2.0 h1:VygpiBqBAdGrfkb7xzE2yrVmnXaqE+hm8FLWdGXO7G
|
||||
github.com/luxfi/validators v1.2.0/go.mod h1:GYLulrNXAan23ZlX7sgWVbVnLpUexeB/m2qr2ymsXok=
|
||||
github.com/luxfi/version v1.0.1 h1:T/1KYWEMmsrNQk7pN7PFPAwh/7XbeX7cFAKLBqI37Sk=
|
||||
github.com/luxfi/version v1.0.1/go.mod h1:Y5fPkQ2DB0XRBCxgSPXp4ISzL1/jptKnmFknShRJCyg=
|
||||
github.com/luxfi/vm v1.2.5 h1:L1etY/gh68f9tns1BtyDUpZcBVqc3Ng1mqU3n38GyLo=
|
||||
github.com/luxfi/vm v1.2.5/go.mod h1:TCCg4lDcQFCjxaxfXnxPIrpRSVAyyf2ucT4A4w654Hg=
|
||||
github.com/luxfi/vm v1.2.6 h1:nJuy62YEBxfX2X65ujmqyeFaFoVj8A2ZQ4i4qpyD5dQ=
|
||||
github.com/luxfi/vm v1.2.6/go.mod h1:TCCg4lDcQFCjxaxfXnxPIrpRSVAyyf2ucT4A4w654Hg=
|
||||
github.com/luxfi/warp v1.24.0 h1:jrcJNlbOiZsAEopJMy9bSaCwI5NDZ8qgp/6sNoXqepg=
|
||||
github.com/luxfi/warp v1.24.0/go.mod h1:bKvTi24JHlANsl7qkWZAVr/DsMfvwy42f+Cc9x4+Sq8=
|
||||
github.com/luxfi/zap v0.8.11 h1:jT+ol9rj557MRdmnzxrVUCR3CDFaE+8OpzUsLIn92og=
|
||||
github.com/luxfi/zap v0.8.11 h1:NIiZp/YyS1TQHfU/PHnMXynzPOKCfZOQssn5ytCdYXg=
|
||||
github.com/luxfi/zap v0.8.11/go.mod h1:JfqII8VtVQYLLTX6obU1DP9sjGqf9L24vfug5ifh0b8=
|
||||
github.com/luxfi/zapcodec v1.0.1 h1:pRxLxCOi6uihQMg8A8riDjNjefU2cXZxfRVZ+obeuL8=
|
||||
github.com/luxfi/zapcodec v1.0.1/go.mod h1:txrRt2JK4O76ssTxlXIwoNVsgzyZVL0ES4mlXqGNogs=
|
||||
@@ -436,12 +655,22 @@ github.com/luxfi/zapdb v1.10.1 h1:XV3k4UTTKKxUMgbfC7woPXgUEIJd3P5nj2lGTQ88xeE=
|
||||
github.com/luxfi/zapdb v1.10.1/go.mod h1:3Y0hH2A9kvjR+Bp9N2yEbtHnhXGHhqCQOLvBRkHrrM0=
|
||||
github.com/luxfi/zwing v0.5.2 h1:2+nDKHVIdT8GvaKO79GuO3x/3DgJvX0gI32h4P+P6RU=
|
||||
github.com/luxfi/zwing v0.5.2/go.mod h1:8nixkEL3bhO2LrqVqhJ8WgT+QGUtnCPIQIhFQh9gQio=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
|
||||
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
|
||||
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
|
||||
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
|
||||
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/melbahja/goph v1.4.0 h1:z0PgDbBFe66lRYl3v5dGb9aFgPy0kotuQ37QOwSQFqs=
|
||||
github.com/melbahja/goph v1.4.0/go.mod h1:uG+VfK2Dlhk+O32zFrRlc3kYKTlV6+BtvPWd/kK7U68=
|
||||
github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE=
|
||||
github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A=
|
||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
@@ -454,23 +683,49 @@ github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI
|
||||
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.100 h1:ShkWi8Tyj9RtU57OQB2HIXKz4bFgtVib0bbT1sbtLI8=
|
||||
github.com/minio/minio-go/v7 v7.0.100/go.mod h1:EtGNKtlX20iL2yaYnxEigaIvj0G0GwSDnifnG8ClIdw=
|
||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw=
|
||||
github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
|
||||
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
|
||||
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/montanaflynn/stats v0.9.0 h1:tsBJ0RXwph9BmAuFoCmqGv6e8xa0MENQ8m0ptKq29mQ=
|
||||
github.com/montanaflynn/stats v0.9.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/mr-tron/base58 v1.3.0 h1:K6Y13R2h+dku0wOqKtecgRnBUBPrZzLZy5aIj8lCcJI=
|
||||
github.com/mr-tron/base58 v1.3.0/go.mod h1:2BuubE67DCSWwVfx37JWNG8emOC0sHEU4/HpcYgCLX8=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/naoina/go-stringutil v0.1.0 h1:rCUeRUHjBjGTSHl0VC00jUPLz8/F9dDzYI70Hzifhks=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 h1:shk/vn9oCoOTmwcouEdwIeOtOGA/ELRUw/GwvxwfT+0=
|
||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oapi-codegen/runtime v1.3.1 h1:RgDY6J4OGQLbRXhG/Xpt3vSVqYpHQS7hN4m85+5xB9g=
|
||||
github.com/oapi-codegen/runtime v1.3.1/go.mod h1:kOdeacKy7t40Rclb1je37ZLFboFxh+YLy0zaPCMibPY=
|
||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
|
||||
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
|
||||
github.com/olekukonko/tablewriter v1.0.9 h1:XGwRsYLC2bY7bNd93Dk51bcPZksWZmLYuaTHR0FqfL8=
|
||||
github.com/olekukonko/tablewriter v1.0.9/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
@@ -491,6 +746,8 @@ github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
|
||||
github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
|
||||
github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=
|
||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 h1:+FZIDR/D97YOPik4N4lPDaUcLDF/EQPogxtlHB2ZZRM=
|
||||
@@ -507,9 +764,14 @@ github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkY
|
||||
github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ=
|
||||
github.com/pires/go-proxyproto v0.11.0 h1:gUQpS85X/VJMdUsYyEgyn59uLJvGqPhJV5YvG68wXH4=
|
||||
github.com/pires/go-proxyproto v0.11.0/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
|
||||
github.com/pkg/sftp v1.13.5/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
@@ -522,8 +784,18 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
|
||||
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||
github.com/protolambda/bls12-381-util v0.1.0 h1:05DU2wJN7DTU7z28+Q+zejXkIsA/MF8JZQGhtBZZiWk=
|
||||
github.com/protolambda/bls12-381-util v0.1.0/go.mod h1:cdkysJTRpeFeuUVx/TXGDQNMTiRAalk1vQw3TYTHcE4=
|
||||
github.com/protolambda/zrnt v0.34.1 h1:qW55rnhZJDnOb3TwFiFRJZi3yTXFrJdGOFQM7vCwYGg=
|
||||
github.com/protolambda/zrnt v0.34.1/go.mod h1:A0fezkp9Tt3GBLATSPIbuY4ywYESyAuc/FFmPKg8Lqs=
|
||||
github.com/protolambda/ztyp v0.2.2 h1:rVcL3vBu9W/aV646zF6caLS/dyn9BN8NYiuJzicLNyY=
|
||||
github.com/protolambda/ztyp v0.2.2/go.mod h1:9bYgKGqg3wJqT9ac1gI2hnVb0STQq7p/1lapqrqY1dU=
|
||||
github.com/quic-go/quic-go v0.59.1 h1:0Gmua0HW1Tv7ANR7hUYwRyD0MG5OJfgvYSZasGZzBic=
|
||||
github.com/quic-go/quic-go v0.59.1/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
@@ -537,8 +809,22 @@ github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88ee
|
||||
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA=
|
||||
github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
@@ -550,6 +836,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/status-im/keycard-go v0.3.3 h1:qk/JHSkT9sMka+lVXrTOIVSgHIY7lDm46wrUqTsNa4s=
|
||||
github.com/status-im/keycard-go v0.3.3/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -596,11 +884,25 @@ github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
|
||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg=
|
||||
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zap-proto/http v0.0.0-20260506200741-fd6047874433 h1:7WsCr/pZvWozimdYNffL3B9K6gLr8w0Z7WAi1+eZWtc=
|
||||
@@ -615,8 +917,16 @@ go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5
|
||||
go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/zpages v0.62.0 h1:9fUYTLmrK0x/lweM2uM+BOx069jLx8PxVqWhegGJ9Bo=
|
||||
go.opentelemetry.io/contrib/zpages v0.62.0/go.mod h1:C8kXoiC1Ytvereztus2R+kqdSa6W/MZ8FfS8Zwj+LiM=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0/go.mod h1:2qXPNBX1OVRC0IwOnfo1ljoid+RD0QK3443EaqVlsOU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
@@ -625,8 +935,12 @@ go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfC
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
@@ -647,6 +961,8 @@ golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9 h1:4d4PbuBNwaxMXkXI8yiIYjydtMU+04RHeuSxJdgKftM=
|
||||
golang.org/x/exp v0.0.0-20260529124908-c761662dc8c9/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw=
|
||||
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
|
||||
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -669,6 +985,10 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/perf v0.0.0-20230113213139-801c7ef9e5c5 h1:ObuXPmIgI4ZMyQLIz48cJYgSyWdjUXc2SZAdyJMwEAU=
|
||||
golang.org/x/perf v0.0.0-20230113213139-801c7ef9e5c5/go.mod h1:UBKtEnL8aqnd+0JHqZ+2qoMDwtuy6cYhhKNoHLBiTQc=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -700,8 +1020,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE=
|
||||
golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -720,13 +1044,30 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
||||
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=
|
||||
golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
|
||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
gonum.org/v1/plot v0.15.2 h1:Tlfh/jBk2tqjLZ4/P8ZIwGrLEWQSPDLRm/SNWKNXiGI=
|
||||
gonum.org/v1/plot v0.15.2/go.mod h1:DX+x+DWso3LTha+AdkJEv5Txvi+Tql3KAGkehP0/Ubg=
|
||||
gonum.org/v1/tools v0.0.0-20200318103217-c168b003ce8c h1:cJWOvXtcaFSGXz2F4z2AMM0VV7edDDGrxb5GLQH7ayQ=
|
||||
gonum.org/v1/tools v0.0.0-20200318103217-c168b003ce8c/go.mod h1:fy6Otjqbk477ELp8IXTpw1cObQtLbRCBVonY+bTTfcM=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
|
||||
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -740,11 +1081,20 @@ google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -754,3 +1104,35 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE=
|
||||
k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug=
|
||||
k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0=
|
||||
k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
k8s.io/client-go v0.34.0 h1:YoWv5r7bsBfb0Hs2jh8SOvFbKzzxyNo0nSb0zC19KZo=
|
||||
k8s.io/client-go v0.34.0/go.mod h1:ozgMnEKXkRjeMvBZdV1AijMHLTh3pbACPvK7zFR+QQY=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
modernc.org/libc v1.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c=
|
||||
modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/sqlite v1.50.0 h1:eMowQSWLK0MeiQTdmz3lqoF5dqclujdlIKeJA11+7oM=
|
||||
modernc.org/sqlite v1.50.0/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew=
|
||||
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
|
||||
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
+1
-1
@@ -70,4 +70,4 @@ esac
|
||||
|
||||
echo ""
|
||||
echo "Import complete! Verify with:"
|
||||
echo " curl -s -X POST --data '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_blockNumber\",\"params\":[]}' -H 'content-type:application/json' \$RPC_URL/ext/bc/<blockchain-id>/rpc"
|
||||
echo " curl -s -X POST --data '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_blockNumber\",\"params\":[]}' -H 'content-type:application/json' \$RPC_URL/v1/bc/<blockchain-id>/rpc"
|
||||
|
||||
+2
-2
@@ -21,8 +21,8 @@ type Client struct {
|
||||
// calls.
|
||||
// [uri] is the path to make API calls to.
|
||||
// For example:
|
||||
// - http://1.2.3.4:9650/ext/index/C/block
|
||||
// - http://1.2.3.4:9650/ext/index/X/tx
|
||||
// - http://1.2.3.4:9650/v1/index/C/block
|
||||
// - http://1.2.3.4:9650/v1/index/X/tx
|
||||
func NewClient(uri string) *Client {
|
||||
return &Client{
|
||||
Requester: rpc.NewEndpointRequester(uri),
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
// and prints the ID of the block and its transactions.
|
||||
func main() {
|
||||
var (
|
||||
uri = primary.LocalAPIURI + "/ext/index/P/block"
|
||||
uri = primary.LocalAPIURI + "/v1/index/P/block"
|
||||
client = indexer.NewClient(uri)
|
||||
ctx = context.Background()
|
||||
nextIndex uint64
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
// and prints the ID of the block and its transactions.
|
||||
func main() {
|
||||
var (
|
||||
uri = primary.LocalAPIURI + "/ext/index/X/block"
|
||||
uri = primary.LocalAPIURI + "/v1/index/X/block"
|
||||
xChainID = ids.FromStringOrPanic("2eNy1mUFdmaxXNj1eQHUe7Np4gju9sJsEtWQ4MX3ToiNKuADed")
|
||||
client = indexer.NewClient(uri)
|
||||
ctx = context.Background()
|
||||
|
||||
+13
-13
@@ -25,29 +25,29 @@ Each chain has one or more index. To see if a C-Chain block is accepted, for exa
|
||||
### C-Chain Blocks
|
||||
|
||||
```
|
||||
/ext/index/C/block
|
||||
/v1/index/C/block
|
||||
```
|
||||
|
||||
### P-Chain Blocks
|
||||
|
||||
```
|
||||
/ext/index/P/block
|
||||
/v1/index/P/block
|
||||
```
|
||||
|
||||
### X-Chain Transactions
|
||||
|
||||
```
|
||||
/ext/index/X/tx
|
||||
/v1/index/X/tx
|
||||
```
|
||||
|
||||
### X-Chain Blocks
|
||||
|
||||
```
|
||||
/ext/index/X/block
|
||||
/v1/index/X/block
|
||||
```
|
||||
|
||||
<Callout type="warn">
|
||||
To ensure historical data can be accessed, the `/ext/index/X/vtx` is still accessible, even though it is no longer populated with chain data since the X-Chain block linearization landed. If you are using `V1.10.0` or higher, you need to migrate to using the `/ext/index/X/block` endpoint.
|
||||
To ensure historical data can be accessed, the `/v1/index/X/vtx` is still accessible, even though it is no longer populated with chain data since the X-Chain block linearization landed. If you are using `V1.10.0` or higher, you need to migrate to using the `/v1/index/X/block` endpoint.
|
||||
</Callout>
|
||||
|
||||
## Methods
|
||||
@@ -87,7 +87,7 @@ index.getContainerByID({
|
||||
**Example Call**:
|
||||
|
||||
```sh
|
||||
curl --location --request POST 'localhost:9630/ext/index/X/tx' \
|
||||
curl --location --request POST 'localhost:9630/v1/index/X/tx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -151,7 +151,7 @@ index.getContainerByIndex({
|
||||
**Example Call**:
|
||||
|
||||
```sh
|
||||
curl --location --request POST 'localhost:9630/ext/index/X/tx' \
|
||||
curl --location --request POST 'localhost:9630/v1/index/X/tx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -223,7 +223,7 @@ index.getContainerRange({
|
||||
**Example Call**:
|
||||
|
||||
```sh
|
||||
curl --location --request POST 'localhost:9630/ext/index/X/tx' \
|
||||
curl --location --request POST 'localhost:9630/v1/index/X/tx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -282,7 +282,7 @@ index.getIndex({
|
||||
**Example Call**:
|
||||
|
||||
```sh
|
||||
curl --location --request POST 'localhost:9630/ext/index/X/tx' \
|
||||
curl --location --request POST 'localhost:9630/v1/index/X/tx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -339,7 +339,7 @@ index.getLastAccepted({
|
||||
**Example Call**:
|
||||
|
||||
```sh
|
||||
curl --location --request POST 'localhost:9630/ext/index/X/tx' \
|
||||
curl --location --request POST 'localhost:9630/v1/index/X/tx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -394,7 +394,7 @@ index.isAccepted({
|
||||
**Example Call**:
|
||||
|
||||
```sh
|
||||
curl --location --request POST 'localhost:9630/ext/index/X/tx' \
|
||||
curl --location --request POST 'localhost:9630/v1/index/X/tx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -430,7 +430,7 @@ To get an X-Chain transaction by its index (the order it was accepted in), use I
|
||||
For example, to get the second transaction (note that `"index":1`) accepted on the X-Chain, do:
|
||||
|
||||
```sh
|
||||
curl --location --request POST 'https://indexer-demo.lux.network/ext/index/X/tx' \
|
||||
curl --location --request POST 'https://indexer-demo.lux.network/v1/index/X/tx' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"jsonrpc": "2.0",
|
||||
@@ -474,7 +474,7 @@ curl -X POST --data '{
|
||||
"txID":"ZGYTSU8w3zUP6VFseGC798vA2Vnxnfj6fz1QPfA9N93bhjJvo",
|
||||
"encoding": "json"
|
||||
}
|
||||
}' -H 'content-type:application/json;' https://api.lux.network/ext/bc/X
|
||||
}' -H 'content-type:application/json;' https://api.lux.network/v1/bc/X
|
||||
```
|
||||
|
||||
**Response**:
|
||||
|
||||
@@ -79,7 +79,7 @@ spec:
|
||||
cpu: "4"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /ext/health
|
||||
path: /v1/health
|
||||
port: 9630
|
||||
initialDelaySeconds: 120
|
||||
periodSeconds: 30
|
||||
@@ -87,7 +87,7 @@ spec:
|
||||
timeoutSeconds: 10
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ext/health
|
||||
path: /v1/health
|
||||
port: 9630
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
@@ -95,7 +95,7 @@ spec:
|
||||
timeoutSeconds: 5
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /ext/health
|
||||
path: /v1/health
|
||||
port: 9630
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
|
||||
+24
@@ -34,6 +34,15 @@ type Net interface {
|
||||
// IsBootstrapped returns true if the chains in this chain are done bootstrapping
|
||||
IsBootstrapped() bool
|
||||
|
||||
// IsChainBootstrapped reports whether a SPECIFIC chain in this net has finished
|
||||
// initial sync — i.e. Bootstrapped(chainID) was called for it (the chain reached
|
||||
// the network frontier and its VM went to normal operation). This is the per-chain
|
||||
// truth that info.isBootstrapped keys on, distinct from IsBootstrapped() which is
|
||||
// the net-wide "no chain still bootstrapping" aggregate. A chain that is merely
|
||||
// tracked (added, sync goroutine launched) but has NOT converged is still in the
|
||||
// bootstrapping set and reads false here — closing the premature-true masking bug.
|
||||
IsChainBootstrapped(chainID ids.ID) bool
|
||||
|
||||
// Bootstrapped marks the chain as done bootstrapping
|
||||
Bootstrapped(chainID ids.ID)
|
||||
|
||||
@@ -66,6 +75,21 @@ func (s *chain) IsBootstrapped() bool {
|
||||
return s.bootstrapping.Len() == 0
|
||||
}
|
||||
|
||||
// IsChainBootstrapped assumes MONOTONIC per-process bootstrapped state: a chain
|
||||
// only ever moves bootstrapping→bootstrapped (Bootstrapped is forward-only and
|
||||
// AddChain refuses to re-add a chain already in either set), so this signal — and
|
||||
// the Bootstrapping() health check that shares the set — never report stale-true.
|
||||
// INVARIANT: if a future path ever moves a live chain BACK to bootstrapping (e.g.
|
||||
// SetState(Bootstrapping) on a running chain) it MUST remove the chainID from
|
||||
// bootstrapped, or both this signal and the readiness health check will report a
|
||||
// re-syncing chain as still bootstrapped.
|
||||
func (s *chain) IsChainBootstrapped(chainID ids.ID) bool {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
return s.bootstrapped.Contains(chainID)
|
||||
}
|
||||
|
||||
func (s *chain) Bootstrapped(chainID ids.ID) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
+5
-5
@@ -1825,18 +1825,18 @@ func (n *Node) initInfoAPI() error {
|
||||
// initSecurityAPI exposes the chain-wide ChainSecurityProfile as a
|
||||
// read-only API surface. Three endpoints share one handler:
|
||||
//
|
||||
// - JSON-RPC: POST /ext/security with methods securityProfile and
|
||||
// - JSON-RPC: POST /v1/security with methods securityProfile and
|
||||
// blockSecurity (dispatched on the wire as security_securityProfile
|
||||
// / security_blockSecurity per gorilla/rpc namespace convention)
|
||||
// - REST: GET /ext/security/profile
|
||||
// - REST: GET /ext/security/block/{n}
|
||||
// - REST: GET /v1/security/profile
|
||||
// - REST: GET /v1/security/block/{n}
|
||||
//
|
||||
// All three share the same Service receiver; the shape returned is
|
||||
// the SCREAMING_SNAKE canonical profile JSON consumed by audit tooling,
|
||||
// wallet posture banners, and block explorers.
|
||||
//
|
||||
// Prometheus gauges for the active profile are stamped onto the
|
||||
// node-wide metrics gatherer here so /ext/metrics carries the profile
|
||||
// node-wide metrics gatherer here so /v1/metrics carries the profile
|
||||
// posture immediately after boot.
|
||||
//
|
||||
// Closes F102 follow-ups (securityProfile RPC + profile metrics).
|
||||
@@ -1844,7 +1844,7 @@ func (n *Node) initSecurityAPI() error {
|
||||
n.Log.Info("initializing security API")
|
||||
|
||||
// Register profile metrics under the "security" namespace on the
|
||||
// node-wide gatherer so /ext/metrics carries them alongside the
|
||||
// node-wide gatherer so /v1/metrics carries them alongside the
|
||||
// existing process / api / chain metric families.
|
||||
securityMetricsReg, err := metric.MakeAndRegister(
|
||||
n.MetricsGatherer,
|
||||
|
||||
+2
-1
@@ -124,7 +124,8 @@ var OptionalVMs = map[ids.ID]PluginSpec{
|
||||
constants.KeyVMID: {Name: "keyvm"},
|
||||
constants.OracleVMID: {Name: "oraclevm"},
|
||||
constants.RelayVMID: {Name: "relayvm"},
|
||||
constants.ThresholdVMID: {Name: "thresholdvm"},
|
||||
constants.MPCVMID: {Name: "mpcvm"},
|
||||
constants.FHEVMID: {Name: "fhevm"},
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -58,7 +58,7 @@ func TestOptionalVMsNotLinkedInProcess(t *testing.T) {
|
||||
"github.com/luxfi/chains/keyvm",
|
||||
"github.com/luxfi/chains/oraclevm",
|
||||
"github.com/luxfi/chains/relayvm",
|
||||
"github.com/luxfi/chains/thresholdvm",
|
||||
"github.com/luxfi/chains/mpcvm",
|
||||
}
|
||||
for _, pkg := range []string{"./node/", "./main"} {
|
||||
deps := goListDeps(t, pkg)
|
||||
|
||||
@@ -72,7 +72,7 @@ echo ""
|
||||
# Wait for RPC to be ready
|
||||
echo -n "Waiting for RPC..."
|
||||
for _ in {1..30}; do
|
||||
if curl -s "http://127.0.0.1:$HTTP_PORT/ext/info" >/dev/null 2>&1; then
|
||||
if curl -s "http://127.0.0.1:$HTTP_PORT/v1/info" >/dev/null 2>&1; then
|
||||
echo " ready!"
|
||||
break
|
||||
fi
|
||||
@@ -83,10 +83,10 @@ done
|
||||
# Show status
|
||||
echo ""
|
||||
echo "=== Instance Ready ==="
|
||||
echo " C-Chain RPC: http://127.0.0.1:$HTTP_PORT/ext/bc/C/rpc"
|
||||
echo " X-Chain RPC: http://127.0.0.1:$HTTP_PORT/ext/bc/X"
|
||||
echo " P-Chain RPC: http://127.0.0.1:$HTTP_PORT/ext/bc/P"
|
||||
echo " Info API: http://127.0.0.1:$HTTP_PORT/ext/info"
|
||||
echo " C-Chain RPC: http://127.0.0.1:$HTTP_PORT/v1/bc/C/rpc"
|
||||
echo " X-Chain RPC: http://127.0.0.1:$HTTP_PORT/v1/bc/X"
|
||||
echo " P-Chain RPC: http://127.0.0.1:$HTTP_PORT/v1/bc/P"
|
||||
echo " Info API: http://127.0.0.1:$HTTP_PORT/v1/info"
|
||||
echo ""
|
||||
echo " Logs: tail -f $LOG_FILE"
|
||||
echo " Stop: kill $(cat "$PID_FILE")"
|
||||
|
||||
@@ -47,8 +47,11 @@ if [[ -z "${IMAGE_REF}" || -z "${S3_DEST}" ]]; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Canonical plugin set: the 12 VM-ID filenames the Dockerfile writes to
|
||||
# Canonical plugin set: the 13 VM-ID filenames the Dockerfile writes to
|
||||
# /luxd/build/plugins/. Kept in lockstep with the Dockerfile plugin stages.
|
||||
# mpcvm (M-Chain) and fhevm (F-Chain) are the SAME byte-identical ThresholdVM
|
||||
# binary installed under two chain VM IDs (mode split by genesis); they share a
|
||||
# sha256 in the manifest but ship as two objects.
|
||||
PLUGINS=(
|
||||
mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6 # evm (C-Chain EVM, 0x9999)
|
||||
mDVT5EWMumBp3LCqvKwuyZQeY1VXr1jvjGNAt8nL4UFiXvqXr # dexvm (native D-Chain DEX)
|
||||
@@ -60,7 +63,8 @@ PLUGINS=(
|
||||
r5m1ujrmXxVcQetG3CQfuDLHp2RHKh6vCDaFgBRQfUcTZh7eS # oraclevm
|
||||
ry9Sg8rZdT26iEKvJDmC2wkESs4SDKgZEhk5BgLSwg1EpcNug # quantumvm
|
||||
sP6dLqrrBR9w3soP18fbJ3YzZecZdD7DDdfH2cFhhLq7Hy9bz # relayvm
|
||||
tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t # thresholdvm
|
||||
qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS # mpcvm (M-Chain, ThresholdVM mode=mpc)
|
||||
n6sSsSfbpQBrU9sY4R29U6z8VrmnTo2CntW6da4rRS7qmnGdv # fhevm (F-Chain, ThresholdVM mode=fhe; byte-identical binary)
|
||||
vv3qPfyTVXZ5ArRZA9Jh4hbYDTBe43f7sgQg4CHfNg1rnnvX9 # zkvm
|
||||
)
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ global:
|
||||
|
||||
scrape_configs:
|
||||
- job_name: "node"
|
||||
metrics_path: "/ext/metrics"
|
||||
metrics_path: "/v1/metrics"
|
||||
file_sd_configs:
|
||||
- files:
|
||||
- '${FILE_SD_PATH}/*.json'
|
||||
|
||||
+11
-11
@@ -147,9 +147,9 @@ func (r *router) handleRootGET(w http.ResponseWriter, _ *http.Request) {
|
||||
P string `json:"p"`
|
||||
X string `json:"x"`
|
||||
}{
|
||||
C: "/ext/bc/C/rpc",
|
||||
P: "/ext/bc/P",
|
||||
X: "/ext/bc/X",
|
||||
C: baseURL + "/bc/C/rpc",
|
||||
P: baseURL + "/bc/P",
|
||||
X: baseURL + "/bc/X",
|
||||
},
|
||||
Endpoints: struct {
|
||||
RPC string `json:"rpc"`
|
||||
@@ -157,10 +157,10 @@ func (r *router) handleRootGET(w http.ResponseWriter, _ *http.Request) {
|
||||
Info string `json:"info"`
|
||||
Health string `json:"health"`
|
||||
}{
|
||||
RPC: "/ext/bc/C/rpc",
|
||||
Websocket: "/ext/bc/C/ws",
|
||||
Info: "/ext/info",
|
||||
Health: "/ext/health",
|
||||
RPC: baseURL + "/bc/C/rpc",
|
||||
Websocket: baseURL + "/bc/C/ws",
|
||||
Info: baseURL + "/info",
|
||||
Health: baseURL + "/health",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -173,10 +173,10 @@ func (r *router) handleRootGET(w http.ResponseWriter, _ *http.Request) {
|
||||
// handleRootPOST proxies JSON-RPC requests to the C-chain
|
||||
func (r *router) handleRootPOST(w http.ResponseWriter, req *http.Request) {
|
||||
// Look up the C-chain RPC handler
|
||||
handler, err := r.GetHandler("/ext/bc/C", "/rpc")
|
||||
handler, err := r.GetHandler(baseURL+"/bc/C", "/rpc")
|
||||
if err != nil {
|
||||
// Try alternate path formats
|
||||
handler, err = r.GetHandler("/ext/bc/C/rpc", "")
|
||||
handler, err = r.GetHandler(baseURL+"/bc/C/rpc", "")
|
||||
if err != nil {
|
||||
// Return proper JSON-RPC error
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
@@ -198,10 +198,10 @@ func (r *router) SetRootInfoProvider(provider RootInfoProvider) {
|
||||
}
|
||||
|
||||
// handleHealthz returns a minimal health response for K8s probes.
|
||||
// This delegates to the full /ext/health handler when available,
|
||||
// This delegates to the full /v1/health handler when available,
|
||||
// falling back to a static 200 response during early startup.
|
||||
func (r *router) handleHealthz(w http.ResponseWriter, req *http.Request) {
|
||||
if handler, err := r.GetHandler("/ext/health", "/health"); err == nil {
|
||||
if handler, err := r.GetHandler(baseURL+"/health", "/health"); err == nil {
|
||||
handler.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,7 +25,12 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
baseURL = "/ext"
|
||||
// baseURL is the canonical — and only — prefix for every luxd HTTP route
|
||||
// (/v1/bc/C/rpc, /v1/info, /v1/health, ...). Single source of truth:
|
||||
// AddRoute/AddAliases and the root/health helpers in router.go all derive
|
||||
// their paths from it. The legacy Avalanche-heritage /ext prefix is gone;
|
||||
// one way, no backward compatibility (activation Dec 25 2025).
|
||||
baseURL = "/v1"
|
||||
maxConcurrentStreams = 64
|
||||
)
|
||||
|
||||
@@ -95,7 +100,7 @@ type server struct {
|
||||
listener net.Listener
|
||||
|
||||
// handler is the fully-wrapped API handler chain (CORS + host-filter +
|
||||
// /ext/* router). Held here so the optional ZAP-RPC listener serves the
|
||||
// /v1/* router). Held here so the optional ZAP-RPC listener serves the
|
||||
// exact same handler as the HTTP listener.
|
||||
handler http.Handler
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// ZAP-RPC listener — serves the EXACT same fully-wrapped API handler chain
|
||||
// (CORS + host-filter + /ext/* router) as the HTTP listener, but over the
|
||||
// (CORS + host-filter + /v1/* router) as the HTTP listener, but over the
|
||||
// github.com/zap-proto/http binary protocol. This is what makes luxd a
|
||||
// first-class citizen of the ZAP service mesh: the api.<brand> gateway can
|
||||
// proxy to luxd over native ZAP instead of HTTP/1.1.
|
||||
|
||||
@@ -60,7 +60,7 @@ func TestStartZapRPCListener_RoundTrip(t *testing.T) {
|
||||
const body = `{"jsonrpc":"2.0","result":"0x2a","id":1}`
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/ext/bc/C/rpc", func(w http.ResponseWriter, r *http.Request) {
|
||||
mux.HandleFunc("/v1/bc/C/rpc", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = io.WriteString(w, body)
|
||||
})
|
||||
@@ -75,7 +75,7 @@ func TestStartZapRPCListener_RoundTrip(t *testing.T) {
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
|
||||
client := &http.Client{Transport: zaphttp.NewTransport(addr)}
|
||||
resp, err := client.Post("http://"+addr+"/ext/bc/C/rpc", "application/json", nil)
|
||||
resp, err := client.Post("http://"+addr+"/v1/bc/C/rpc", "application/json", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("ZAP round-trip POST failed: %v", err)
|
||||
}
|
||||
|
||||
+12
-12
@@ -143,7 +143,7 @@ func TestRevokeToken(t *testing.T) {
|
||||
auth := NewFromHash(log.NewNoOpLogger(), "auth", hashedPassword).(*auth)
|
||||
|
||||
// Make a token
|
||||
endpoints := []string{"/ext/info", "/ext/bc/X", "/ext/metrics"}
|
||||
endpoints := []string{"/v1/info", "/v1/bc/X", "/v1/metrics"}
|
||||
tokenStr, err := auth.NewToken(testPassword, defaultTokenLifespan, endpoints)
|
||||
require.NoError(err)
|
||||
|
||||
@@ -157,7 +157,7 @@ func TestWrapHandlerHappyPath(t *testing.T) {
|
||||
auth := NewFromHash(log.NewNoOpLogger(), "auth", hashedPassword)
|
||||
|
||||
// Make a token
|
||||
endpoints := []string{"/ext/info", "/ext/bc/X", "/ext/metrics"}
|
||||
endpoints := []string{"/v1/info", "/v1/bc/X", "/v1/metrics"}
|
||||
tokenStr, err := auth.NewToken(testPassword, defaultTokenLifespan, endpoints)
|
||||
require.NoError(err)
|
||||
|
||||
@@ -178,7 +178,7 @@ func TestWrapHandlerRevokedToken(t *testing.T) {
|
||||
auth := NewFromHash(log.NewNoOpLogger(), "auth", hashedPassword)
|
||||
|
||||
// Make a token
|
||||
endpoints := []string{"/ext/info", "/ext/bc/X", "/ext/metrics"}
|
||||
endpoints := []string{"/v1/info", "/v1/bc/X", "/v1/metrics"}
|
||||
tokenStr, err := auth.NewToken(testPassword, defaultTokenLifespan, endpoints)
|
||||
require.NoError(err)
|
||||
|
||||
@@ -205,7 +205,7 @@ func TestWrapHandlerExpiredToken(t *testing.T) {
|
||||
auth.clock.Set(time.Now().Add(-2 * defaultTokenLifespan))
|
||||
|
||||
// Make a token that expired well in the past
|
||||
endpoints := []string{"/ext/info", "/ext/bc/X", "/ext/metrics"}
|
||||
endpoints := []string{"/v1/info", "/v1/bc/X", "/v1/metrics"}
|
||||
tokenStr, err := auth.NewToken(testPassword, defaultTokenLifespan, endpoints)
|
||||
require.NoError(err)
|
||||
|
||||
@@ -227,7 +227,7 @@ func TestWrapHandlerNoAuthToken(t *testing.T) {
|
||||
|
||||
auth := NewFromHash(log.NewNoOpLogger(), "auth", hashedPassword)
|
||||
|
||||
endpoints := []string{"/ext/info", "/ext/bc/X", "/ext/metrics"}
|
||||
endpoints := []string{"/v1/info", "/v1/bc/X", "/v1/metrics"}
|
||||
wrappedHandler := auth.WrapHandler(dummyHandler)
|
||||
for _, endpoint := range endpoints {
|
||||
req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://127.0.0.1:9630%s", endpoint), strings.NewReader(""))
|
||||
@@ -245,11 +245,11 @@ func TestWrapHandlerUnauthorizedEndpoint(t *testing.T) {
|
||||
auth := NewFromHash(log.NewNoOpLogger(), "auth", hashedPassword)
|
||||
|
||||
// Make a token
|
||||
endpoints := []string{"/ext/info"}
|
||||
endpoints := []string{"/v1/info"}
|
||||
tokenStr, err := auth.NewToken(testPassword, defaultTokenLifespan, endpoints)
|
||||
require.NoError(err)
|
||||
|
||||
unauthorizedEndpoints := []string{"/ext/bc/X", "/ext/metrics", "", "/foo", "/ext/info/foo"}
|
||||
unauthorizedEndpoints := []string{"/v1/bc/X", "/v1/metrics", "", "/foo", "/v1/info/foo"}
|
||||
|
||||
wrappedHandler := auth.WrapHandler(dummyHandler)
|
||||
for _, endpoint := range unauthorizedEndpoints {
|
||||
@@ -269,12 +269,12 @@ func TestWrapHandlerAuthEndpoint(t *testing.T) {
|
||||
auth := NewFromHash(log.NewNoOpLogger(), "auth", hashedPassword)
|
||||
|
||||
// Make a token
|
||||
endpoints := []string{"/ext/info", "/ext/bc/X", "/ext/metrics", "", "/foo", "/ext/info/foo"}
|
||||
endpoints := []string{"/v1/info", "/v1/bc/X", "/v1/metrics", "", "/foo", "/v1/info/foo"}
|
||||
tokenStr, err := auth.NewToken(testPassword, defaultTokenLifespan, endpoints)
|
||||
require.NoError(err)
|
||||
|
||||
wrappedHandler := auth.WrapHandler(dummyHandler)
|
||||
req := httptest.NewRequest(http.MethodPost, "http://127.0.0.1:9630/ext/auth", strings.NewReader(""))
|
||||
req := httptest.NewRequest(http.MethodPost, "http://127.0.0.1:9630/v1/auth", strings.NewReader(""))
|
||||
req.Header.Add("Authorization", headerValStart+tokenStr)
|
||||
rr := httptest.NewRecorder()
|
||||
wrappedHandler.ServeHTTP(rr, req)
|
||||
@@ -287,7 +287,7 @@ func TestWrapHandlerAccessAll(t *testing.T) {
|
||||
auth := NewFromHash(log.NewNoOpLogger(), "auth", hashedPassword)
|
||||
|
||||
// Make a token that allows access to all endpoints
|
||||
endpoints := []string{"/ext/info", "/ext/bc/X", "/ext/metrics", "", "/foo", "/ext/foo/info"}
|
||||
endpoints := []string{"/v1/info", "/v1/bc/X", "/v1/metrics", "", "/foo", "/v1/foo/info"}
|
||||
tokenStr, err := auth.NewToken(testPassword, defaultTokenLifespan, []string{"*"})
|
||||
require.NoError(err)
|
||||
|
||||
@@ -316,7 +316,7 @@ func TestWrapHandlerMutatedRevokedToken(t *testing.T) {
|
||||
auth := NewFromHash(log.NewNoOpLogger(), "auth", hashedPassword)
|
||||
|
||||
// Make a token
|
||||
endpoints := []string{"/ext/info", "/ext/bc/X", "/ext/metrics"}
|
||||
endpoints := []string{"/v1/info", "/v1/bc/X", "/v1/metrics"}
|
||||
tokenStr, err := auth.NewToken(testPassword, defaultTokenLifespan, endpoints)
|
||||
require.NoError(err)
|
||||
|
||||
@@ -339,7 +339,7 @@ func TestWrapHandlerInvalidSigningMethod(t *testing.T) {
|
||||
auth := NewFromHash(log.NewNoOpLogger(), "auth", hashedPassword).(*auth)
|
||||
|
||||
// Make a token
|
||||
endpoints := []string{"/ext/info", "/ext/bc/X", "/ext/metrics"}
|
||||
endpoints := []string{"/v1/info", "/v1/bc/X", "/v1/metrics"}
|
||||
idBytes := [tokenIDByteLen]byte{}
|
||||
_, err := rand.Read(idBytes[:])
|
||||
require.NoError(err)
|
||||
|
||||
@@ -22,7 +22,7 @@ type Password struct {
|
||||
type NewTokenArgs struct {
|
||||
Password
|
||||
// Endpoints that may be accessed with this token e.g. if endpoints is
|
||||
// ["/ext/bc/X", "/ext/admin"] then the token holder can hit the X-Chain API
|
||||
// ["/v1/bc/X", "/v1/admin"] then the token holder can hit the X-Chain API
|
||||
// and the admin API. If [Endpoints] contains an element "*" then the token
|
||||
// allows access to all API endpoints. [Endpoints] must have between 1 and
|
||||
// [maxEndpoints] elements
|
||||
|
||||
@@ -23,7 +23,7 @@ To get an HTTP status code response that indicates the node's health, make a `GE
|
||||
To filter GET health checks, add a `tag` query parameter to the request. The `tag` parameter is a string. For example, to filter health results by netID `29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL`, use the following query:
|
||||
|
||||
```sh
|
||||
curl 'http://localhost:9630/ext/health?tag=29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL'
|
||||
curl 'http://localhost:9630/v1/health?tag=29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL'
|
||||
```
|
||||
|
||||
In this example returned results will contain global health checks and health checks that are related to netID `29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL`.
|
||||
@@ -33,7 +33,7 @@ In this example returned results will contain global health checks and health ch
|
||||
In order to filter results by multiple tags, use multiple `tag` query parameters. For example, to filter health results by netID `29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL` and `28nrH5T2BMvNrWecFcV3mfccjs6axM1TVyqe79MCv2Mhs8kxiY` use the following query:
|
||||
|
||||
```sh
|
||||
curl 'http://localhost:9630/ext/health?tag=29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL&tag=28nrH5T2BMvNrWecFcV3mfccjs6axM1TVyqe79MCv2Mhs8kxiY'
|
||||
curl 'http://localhost:9630/v1/health?tag=29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL&tag=28nrH5T2BMvNrWecFcV3mfccjs6axM1TVyqe79MCv2Mhs8kxiY'
|
||||
```
|
||||
|
||||
The returned results will include health checks for both netIDs as well as global health checks.
|
||||
@@ -42,10 +42,10 @@ The returned results will include health checks for both netIDs as well as globa
|
||||
|
||||
The available endpoints for GET requests are:
|
||||
|
||||
- `/ext/health` returns a holistic report of the status of the node. **Most operators should monitor this status.**
|
||||
- `/ext/health/health` is the same as `/ext/health`.
|
||||
- `/ext/health/readiness` returns healthy once the node has finished initializing.
|
||||
- `/ext/health/liveness` returns healthy once the endpoint is available.
|
||||
- `/v1/health` returns a holistic report of the status of the node. **Most operators should monitor this status.**
|
||||
- `/v1/health/health` is the same as `/v1/health`.
|
||||
- `/v1/health/readiness` returns healthy once the node has finished initializing.
|
||||
- `/v1/health/liveness` returns healthy once the endpoint is available.
|
||||
|
||||
## JSON RPC Request
|
||||
|
||||
@@ -71,7 +71,7 @@ curl -H 'Content-Type: application/json' --data '{
|
||||
"params": {
|
||||
"tags": ["11111111111111111111111111111111LpoYY", "29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL"]
|
||||
}
|
||||
}' 'http://localhost:9630/ext/health'
|
||||
}' 'http://localhost:9630/v1/health'
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -203,7 +203,7 @@ curl -H 'Content-Type: application/json' --data '{
|
||||
"params": {
|
||||
"tags": ["11111111111111111111111111111111LpoYY", "29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL"]
|
||||
}
|
||||
}' 'http://localhost:9630/ext/health'
|
||||
}' 'http://localhost:9630/v1/health'
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -249,7 +249,7 @@ curl -H 'Content-Type: application/json' --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id" :1,
|
||||
"method" :"health.liveness"
|
||||
}' 'http://localhost:9630/ext/health'
|
||||
}' 'http://localhost:9630/v1/health'
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
+15
-15
@@ -7,7 +7,7 @@ This API uses the `json 2.0` RPC format. For more information on making JSON RPC
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
/ext/info
|
||||
/v1/info
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -38,7 +38,7 @@ curl -sX POST --data '{
|
||||
"id" :1,
|
||||
"method" :"info.lps",
|
||||
"params" :{}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -125,7 +125,7 @@ curl -X POST --data '{
|
||||
"params": {
|
||||
"chain":"X"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -160,7 +160,7 @@ curl -X POST --data '{
|
||||
"params": {
|
||||
"alias":"X"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -192,7 +192,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id" :1,
|
||||
"method" :"info.getNetworkID"
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -226,7 +226,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id" :1,
|
||||
"method" :"info.getNetworkName"
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -273,7 +273,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id" :1,
|
||||
"method" :"info.getNodeID"
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -313,7 +313,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id" :1,
|
||||
"method" :"info.getNodeIP"
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -359,7 +359,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id" :1,
|
||||
"method" :"info.getNodeVersion"
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -426,7 +426,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id" :1,
|
||||
"method" :"info.getTxFee"
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -473,7 +473,7 @@ curl -X POST --data '{
|
||||
"id" :1,
|
||||
"method" :"info.getVMs",
|
||||
"params" :{}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -540,7 +540,7 @@ curl -X POST --data '{
|
||||
"params": {
|
||||
"nodeIDs": []
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -619,7 +619,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id" :1,
|
||||
"method" :"info.uptime"
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
@@ -645,7 +645,7 @@ curl -X POST --data '{
|
||||
"params" :{
|
||||
"netID":"29uVeLPJB1eQJkzRemU8g8wZDw5uJRqpab5U2mX9euieVwiEbL"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/info
|
||||
```
|
||||
|
||||
#### Example Lux L1 Response
|
||||
@@ -672,7 +672,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id" :1,
|
||||
"method" :"info.upgrades"
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/info
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/v1/info
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
@@ -64,7 +64,7 @@ func TestGetNodeVersionConsensusRoundtrip(t *testing.T) {
|
||||
log: log.NewNoOpLogger(),
|
||||
}
|
||||
|
||||
req := httptest.NewRequest("POST", "/ext/info", nil)
|
||||
req := httptest.NewRequest("POST", "/v1/info", nil)
|
||||
reply := apiinfo.GetNodeVersionReply{}
|
||||
require.NoError(info.GetNodeVersion(req, nil, &reply))
|
||||
require.NotNil(reply.Consensus)
|
||||
@@ -114,7 +114,7 @@ func TestGetVMsSuccess(t *testing.T) {
|
||||
id2: []string{alias2},
|
||||
}
|
||||
|
||||
req := httptest.NewRequest("POST", "/ext/info", nil)
|
||||
req := httptest.NewRequest("POST", "/v1/info", nil)
|
||||
resources.mockVMManager.EXPECT().ListFactories(req.Context()).Times(1).Return(vmIDs, nil)
|
||||
resources.mockVMManager.EXPECT().PrimaryAlias(req.Context(), id1).Times(1).Return(alias1, nil)
|
||||
resources.mockVMManager.EXPECT().PrimaryAlias(req.Context(), id2).Times(1).Return(alias2, nil)
|
||||
@@ -128,7 +128,7 @@ func TestGetVMsSuccess(t *testing.T) {
|
||||
func TestGetVMsVMsListFactoriesFails(t *testing.T) {
|
||||
resources := initGetVMsTest(t)
|
||||
|
||||
req := httptest.NewRequest("POST", "/ext/info", nil)
|
||||
req := httptest.NewRequest("POST", "/v1/info", nil)
|
||||
resources.mockVMManager.EXPECT().ListFactories(req.Context()).Times(1).Return(nil, errTest)
|
||||
|
||||
reply := apiinfo.GetVMsReply{}
|
||||
@@ -146,7 +146,7 @@ func TestGetVMsGetAliasesFails(t *testing.T) {
|
||||
vmIDs := []ids.ID{id1, id2}
|
||||
alias1 := "vm1-alias-1"
|
||||
|
||||
req := httptest.NewRequest("POST", "/ext/info", nil)
|
||||
req := httptest.NewRequest("POST", "/v1/info", nil)
|
||||
resources.mockVMManager.EXPECT().ListFactories(req.Context()).Times(1).Return(vmIDs, nil)
|
||||
resources.mockVMManager.EXPECT().PrimaryAlias(req.Context(), id1).Times(1).Return(alias1, nil)
|
||||
resources.mockVMManager.EXPECT().PrimaryAlias(req.Context(), id2).Times(1).Return("", errTest)
|
||||
|
||||
@@ -38,7 +38,7 @@ type client struct {
|
||||
// instead.
|
||||
func NewClient(uri string) Client {
|
||||
return &client{requester: rpc.NewEndpointRequester(
|
||||
uri + "/ext/keystore",
|
||||
uri + "/v1/keystore",
|
||||
)}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ This API uses the `json 2.0` API format. For more information on making JSON RPC
|
||||
## Endpoint
|
||||
|
||||
```text
|
||||
/ext/keystore
|
||||
/v1/keystore
|
||||
```
|
||||
|
||||
## Methods
|
||||
@@ -89,7 +89,7 @@ curl -X POST --data '{
|
||||
"username":"myUsername",
|
||||
"password":"myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/keystore
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/keystore
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -129,7 +129,7 @@ curl -X POST --data '{
|
||||
"username":"myUsername",
|
||||
"password":"myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/keystore
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/keystore
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -183,7 +183,7 @@ curl -X POST --data '{
|
||||
"username":"myUsername",
|
||||
"password":"myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/keystore
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/keystore
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -238,7 +238,7 @@ curl -X POST --data '{
|
||||
"password":"myPassword",
|
||||
"user" :"0x7655a29df6fc2747b0874e1148b423b954a25fcdb1f170d0ec8eb196430f7001942ce55b02a83b1faf50a674b1e55bfc000000008cf2d869"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/keystore
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/keystore
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -274,7 +274,7 @@ curl -X POST --data '{
|
||||
"jsonrpc":"2.0",
|
||||
"id" :1,
|
||||
"method" :"keystore.listUsers"
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/keystore
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/keystore
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
|
||||
@@ -32,7 +32,7 @@ type Client struct {
|
||||
// NewClient returns a new Metrics API Client
|
||||
func NewClient(uri string) *Client {
|
||||
return &Client{
|
||||
uri: uri + "/ext/metrics",
|
||||
uri: uri + "/v1/metrics",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ This API set is for a specific node, it is unavailable on the [public server](ht
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
/ext/metrics
|
||||
/v1/metrics
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -15,7 +15,7 @@ This API set is for a specific node, it is unavailable on the [public server](ht
|
||||
To get the node metrics:
|
||||
|
||||
```sh
|
||||
curl -X POST 127.0.0.1:9630/ext/metrics
|
||||
curl -X POST 127.0.0.1:9630/v1/metrics
|
||||
```
|
||||
|
||||
## Format
|
||||
|
||||
@@ -27,7 +27,7 @@ var ErrNoProfile = errors.New(
|
||||
"(genesis carries no SecurityProfile{} block); RPC unavailable")
|
||||
|
||||
// Service is the JSON-RPC handler set registered under the security
|
||||
// namespace at /ext/security. Methods are read-only; the underlying
|
||||
// namespace at /v1/security. Methods are read-only; the underlying
|
||||
// profile pointer is set once at construction and never mutated.
|
||||
//
|
||||
// Exposed methods:
|
||||
@@ -37,7 +37,7 @@ var ErrNoProfile = errors.New(
|
||||
//
|
||||
// On the wire, gorilla/rpc dispatches these as security_securityProfile
|
||||
// and security_blockSecurity (namespace_method). Callers using the REST
|
||||
// sidecars hit /ext/security/profile and /ext/security/block/{n}
|
||||
// sidecars hit /v1/security/profile and /v1/security/block/{n}
|
||||
// directly. One namespace, two transports, one shape.
|
||||
//
|
||||
// The "block" method returns the chain-wide envelope; per-block
|
||||
@@ -53,13 +53,13 @@ type Service struct {
|
||||
// namespace. Profile may be nil — see ErrNoProfile.
|
||||
//
|
||||
// The returned handler is suitable for APIServer.AddRoute(handler,
|
||||
// "security", "") so it lands at /ext/security on the node's HTTP
|
||||
// "security", "") so it lands at /v1/security on the node's HTTP
|
||||
// listener. REST sidecars are mounted at /profile and /block/{n} on
|
||||
// the same handler so the full external surface is:
|
||||
//
|
||||
// POST /ext/security (JSON-RPC, methods above)
|
||||
// GET /ext/security/profile (REST sidecar)
|
||||
// GET /ext/security/block/{n} (REST sidecar)
|
||||
// POST /v1/security (JSON-RPC, methods above)
|
||||
// GET /v1/security/profile (REST sidecar)
|
||||
// GET /v1/security/block/{n} (REST sidecar)
|
||||
func NewHandler(logger log.Logger, profile *consensusconfig.ChainSecurityProfile) (http.Handler, error) {
|
||||
server := rpc.NewServer()
|
||||
codec := avajson.NewCodec()
|
||||
@@ -71,7 +71,7 @@ func NewHandler(logger log.Logger, profile *consensusconfig.ChainSecurityProfile
|
||||
}
|
||||
// REST sidecars share the Service receiver so the two transports
|
||||
// stay byte-identical in semantics (one and only one way to
|
||||
// compute the shape). Paths relative to /ext/security:
|
||||
// compute the shape). Paths relative to /v1/security:
|
||||
// /profile → restProfile
|
||||
// /block/{n} → restBlockSecurity
|
||||
mux := http.NewServeMux()
|
||||
@@ -133,7 +133,7 @@ func (s *Service) BlockSecurity(_ *http.Request, _ *BlockSecurityArgs, reply *Bl
|
||||
return nil
|
||||
}
|
||||
|
||||
// restProfile is the GET /profile sidecar (full path /ext/security/profile).
|
||||
// restProfile is the GET /profile sidecar (full path /v1/security/profile).
|
||||
// Same body as the securityProfile JSON-RPC method; useful for
|
||||
// explorers that don't speak JSON-RPC. Refuses every non-GET method so
|
||||
// callers can't smuggle state through the read-only endpoint.
|
||||
@@ -151,7 +151,7 @@ func (s *Service) restProfile(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// restBlockSecurity is the GET /block/{n} sidecar (full path
|
||||
// /ext/security/block/{n}). The path suffix is taken as the block
|
||||
// /v1/security/block/{n}). The path suffix is taken as the block
|
||||
// number; chain alias defaults to the platform chain (callers can
|
||||
// re-route per-chain at the chain-manager layer if needed).
|
||||
func (s *Service) restBlockSecurity(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -203,7 +203,7 @@ func TestRPC_blockSecurity_StrictPQ(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestREST_securityProfile_GET proves the /profile sidecar (full path
|
||||
// /ext/security/profile when mounted on APIServer) returns the same
|
||||
// /v1/security/profile when mounted on APIServer) returns the same
|
||||
// JSON shape as the JSON-RPC handler. One shape, two transports — no
|
||||
// per-transport drift.
|
||||
func TestREST_securityProfile_GET(t *testing.T) {
|
||||
@@ -262,7 +262,7 @@ func TestREST_securityProfile_MethodNotAllowed(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestREST_blockSecurity_GET proves the /block/{n} sidecar (full path
|
||||
// /ext/security/block/{n} when mounted on APIServer) returns the same
|
||||
// /v1/security/block/{n} when mounted on APIServer) returns the same
|
||||
// JSON shape as the JSON-RPC handler. One shape, two transports — no
|
||||
// per-transport drift.
|
||||
func TestREST_blockSecurity_GET(t *testing.T) {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
// Package security exposes the chain-wide ChainSecurityProfile to operators,
|
||||
// dApps, and auditors through the security JSON-RPC namespace at
|
||||
// /ext/security and two REST sidecars at /ext/security/profile and
|
||||
// /ext/security/block/{n}.
|
||||
// /v1/security and two REST sidecars at /v1/security/profile and
|
||||
// /v1/security/block/{n}.
|
||||
//
|
||||
// The handlers in this package are read-only: every shape is derived from
|
||||
// the immutable *consensusconfig.ChainSecurityProfile resolved at node
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
// ProfileReply is the JSON body returned by the securityProfile RPC
|
||||
// (POST /ext/security) and the REST sidecar (GET /ext/security/profile).
|
||||
// (POST /v1/security) and the REST sidecar (GET /v1/security/profile).
|
||||
//
|
||||
// Stable shape: every field has a fixed JSON tag, no embedded structs.
|
||||
// Adding a new field requires bumping the major version of the security
|
||||
@@ -99,8 +99,8 @@ type ProfileReply struct {
|
||||
}
|
||||
|
||||
// BlockSecurityReply is the JSON body returned by the blockSecurity
|
||||
// RPC (POST /ext/security) and the REST endpoint
|
||||
// /ext/security/block/{n}. It enriches a block lookup with the
|
||||
// RPC (POST /v1/security) and the REST endpoint
|
||||
// /v1/security/block/{n}. It enriches a block lookup with the
|
||||
// chain-wide security envelope so explorers can show "this block was
|
||||
// finalised under profile X with backend Y" without reimplementing
|
||||
// profile lookup.
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ type SimpleNodesMetrics map[string]SimpleNodeMetrics
|
||||
|
||||
// GetSimpleNodeMetrics retrieves the specified metrics the provided node URI.
|
||||
func GetSimpleNodeMetrics(nodeURI string, metricNames ...string) (SimpleNodeMetrics, error) {
|
||||
uri := nodeURI + "/ext/metrics"
|
||||
uri := nodeURI + "/v1/metrics"
|
||||
return GetMetricsValue(uri, metricNames...)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.22.79
|
||||
1.32.11
|
||||
|
||||
@@ -76,8 +76,8 @@ var (
|
||||
// These should match the latest git tag
|
||||
const (
|
||||
defaultMajor = 1
|
||||
defaultMinor = 30
|
||||
defaultPatch = 6
|
||||
defaultMinor = 32
|
||||
defaultPatch = 11
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -149,7 +149,7 @@ type Client interface {
|
||||
For example:
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://34.235.54.228:9630/ext/bc/28iioW2fYMBnKv24VG5nw9ifY2PsFuwuhxhyzxZB5MmxDd3rnT' \
|
||||
curl --location --request POST 'http://34.235.54.228:9630/v1/bc/28iioW2fYMBnKv24VG5nw9ifY2PsFuwuhxhyzxZB5MmxDd3rnT' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"jsonrpc": "2.0",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// Package thresholdvm — backend selection re-export.
|
||||
// Package mpcvm — backend selection re-export.
|
||||
//
|
||||
// The canonical dlopen probe lives in github.com/luxfi/chains/thresholdvm.
|
||||
// The canonical dlopen probe lives in github.com/luxfi/chains/mpcvm.
|
||||
// Its init() runs once at package load time and pins a process-wide
|
||||
// GPUBackend handle (cuda → hip → metal → vulkan → webgpu probe order).
|
||||
// This file is the node-side entry point for diagnostics and ops tooling.
|
||||
@@ -13,20 +13,20 @@
|
||||
// NOT yet registered in running luxd"). This file provides the bridge
|
||||
// surface only — flipping the manager hook-up is a separate ops PR.
|
||||
|
||||
package thresholdvm
|
||||
package mpcvm
|
||||
|
||||
// SelectGPUBackend returns the resolved GPU plugin (or nil) and a single
|
||||
// human-readable diagnostic string. luxd startup logs use this to surface
|
||||
// "thresholdvm-gpu backend=<name>" lines alongside the cevm backend
|
||||
// "mpcvm-gpu backend=<name>" lines alongside the cevm backend
|
||||
// selection, matching the cevm.go pattern at
|
||||
// ~/work/lux/chains/evm/backend_cgo.go.
|
||||
//
|
||||
// Calling this multiple times is cheap — the underlying probe runs once
|
||||
// at package init via sync.Once in chains/thresholdvm/backend.go.
|
||||
// at package init via sync.Once in chains/mpcvm/backend.go.
|
||||
func SelectGPUBackend() (*GPUBackend, string) {
|
||||
g := GPUBackendInstance()
|
||||
if g == nil || !g.IsAvailable() {
|
||||
return nil, "thresholdvm-gpu: no plugin resolved (CPU-only)"
|
||||
return nil, "mpcvm-gpu: no plugin resolved (CPU-only)"
|
||||
}
|
||||
return g, "thresholdvm-gpu: backend=" + g.Kind.String() + " path=" + g.Path
|
||||
return g, "mpcvm-gpu: backend=" + g.Kind.String() + " path=" + g.Path
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// Package mpcvm re-exports the canonical Threshold (FHE / MPC)
|
||||
// VM from github.com/luxfi/chains/mpcvm so existing callers
|
||||
// that imported github.com/luxfi/node/vms/mpcvm pre-extraction
|
||||
// keep working without source changes.
|
||||
//
|
||||
// New code should import the canonical path:
|
||||
// "github.com/luxfi/chains/mpcvm"
|
||||
//
|
||||
// This file is a thin alias wrapper kept for backward compatibility.
|
||||
package mpcvm
|
||||
|
||||
import "github.com/luxfi/chains/mpcvm"
|
||||
|
||||
type (
|
||||
Block = mpcvm.Block
|
||||
BlockError = mpcvm.BlockError
|
||||
Client = mpcvm.Client
|
||||
Operation = mpcvm.Operation
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidOperation = mpcvm.ErrInvalidOperation
|
||||
NewClient = mpcvm.NewClient
|
||||
)
|
||||
@@ -3,20 +3,20 @@
|
||||
|
||||
//go:build cgo
|
||||
|
||||
// Package thresholdvm re-exports the GPU bridge from
|
||||
// github.com/luxfi/chains/thresholdvm so callers that import the legacy
|
||||
// Package mpcvm re-exports the GPU bridge from
|
||||
// github.com/luxfi/chains/mpcvm so callers that import the legacy
|
||||
// node path keep working without source changes.
|
||||
//
|
||||
// One and only one way to dlopen: the canonical bridge lives in
|
||||
// chains/thresholdvm. This package is a typed alias layer — both the
|
||||
// chains/mpcvm. This package is a typed alias layer — both the
|
||||
// types and the GPUBackend singleton come straight from the upstream.
|
||||
// There is exactly one dlopen handle, one symbol table, one init()
|
||||
// probe across the entire luxd process. Consumers of either import path
|
||||
// share the same plugin instance.
|
||||
|
||||
package thresholdvm
|
||||
package mpcvm
|
||||
|
||||
import "github.com/luxfi/chains/thresholdvm"
|
||||
import "github.com/luxfi/chains/mpcvm"
|
||||
|
||||
// =============================================================================
|
||||
// Type re-exports — Go aliases preserve the public API so external callers
|
||||
@@ -26,37 +26,37 @@ import "github.com/luxfi/chains/thresholdvm"
|
||||
// =============================================================================
|
||||
|
||||
type (
|
||||
GPUBackend = thresholdvm.GPUBackend
|
||||
GPUBackendKind = thresholdvm.GPUBackendKind
|
||||
GPUBackend = mpcvm.GPUBackend
|
||||
GPUBackendKind = mpcvm.GPUBackendKind
|
||||
|
||||
GPUCeremony = thresholdvm.GPUCeremony
|
||||
GPUKeyShare = thresholdvm.GPUKeyShare
|
||||
GPUContribution = thresholdvm.GPUContribution
|
||||
GPUMPCVMState = thresholdvm.GPUMPCVMState
|
||||
GPUMPCVMRoundDescriptor = thresholdvm.GPUMPCVMRoundDescriptor
|
||||
GPUCeremonyOp = thresholdvm.GPUCeremonyOp
|
||||
GPUContributionOp = thresholdvm.GPUContributionOp
|
||||
GPUMPCVMTransitionResult = thresholdvm.GPUMPCVMTransitionResult
|
||||
GPUCeremony = mpcvm.GPUCeremony
|
||||
GPUKeyShare = mpcvm.GPUKeyShare
|
||||
GPUContribution = mpcvm.GPUContribution
|
||||
GPUMPCVMState = mpcvm.GPUMPCVMState
|
||||
GPUMPCVMRoundDescriptor = mpcvm.GPUMPCVMRoundDescriptor
|
||||
GPUCeremonyOp = mpcvm.GPUCeremonyOp
|
||||
GPUContributionOp = mpcvm.GPUContributionOp
|
||||
GPUMPCVMTransitionResult = mpcvm.GPUMPCVMTransitionResult
|
||||
)
|
||||
|
||||
// Backend constants re-exported. Matches the chains/thresholdvm dlopen
|
||||
// Backend constants re-exported. Matches the chains/mpcvm dlopen
|
||||
// probe order: cuda → hip → metal → vulkan → webgpu.
|
||||
const (
|
||||
GPUBackendNone = thresholdvm.GPUBackendNone
|
||||
GPUBackendCUDA = thresholdvm.GPUBackendCUDA
|
||||
GPUBackendHIP = thresholdvm.GPUBackendHIP
|
||||
GPUBackendMetal = thresholdvm.GPUBackendMetal
|
||||
GPUBackendVulkan = thresholdvm.GPUBackendVulkan
|
||||
GPUBackendWebGPU = thresholdvm.GPUBackendWebGPU
|
||||
GPUBackendNone = mpcvm.GPUBackendNone
|
||||
GPUBackendCUDA = mpcvm.GPUBackendCUDA
|
||||
GPUBackendHIP = mpcvm.GPUBackendHIP
|
||||
GPUBackendMetal = mpcvm.GPUBackendMetal
|
||||
GPUBackendVulkan = mpcvm.GPUBackendVulkan
|
||||
GPUBackendWebGPU = mpcvm.GPUBackendWebGPU
|
||||
)
|
||||
|
||||
// ErrGPUNotAvailable is the canonical "no plugin loaded" error. Callers
|
||||
// `errors.Is(err, thresholdvm.ErrGPUNotAvailable)` to distinguish a
|
||||
// `errors.Is(err, mpcvm.ErrGPUNotAvailable)` to distinguish a
|
||||
// fallback-to-CPU condition from a hard launcher failure.
|
||||
var ErrGPUNotAvailable = thresholdvm.ErrGPUNotAvailable
|
||||
var ErrGPUNotAvailable = mpcvm.ErrGPUNotAvailable
|
||||
|
||||
// GPUBackendInstance returns the dlopen'd GPU plugin handle resolved at
|
||||
// chains/thresholdvm package init. nil means no plugin was loaded
|
||||
// chains/mpcvm package init. nil means no plugin was loaded
|
||||
// (CPU-only mode). The handle is shared across the whole process — both
|
||||
// the node and chains paths see the same instance.
|
||||
//
|
||||
@@ -65,7 +65,7 @@ var ErrGPUNotAvailable = thresholdvm.ErrGPUNotAvailable
|
||||
// package, and `Backend` is already a domain term in the threshold
|
||||
// state machine. Callers write:
|
||||
//
|
||||
// if g := thresholdvm.GPUBackendInstance(); g != nil && g.IsAvailable() {
|
||||
// if g := mpcvm.GPUBackendInstance(); g != nil && g.IsAvailable() {
|
||||
// _, err := g.CeremonyApply(desc, ops, ceremonies)
|
||||
// ...
|
||||
// }
|
||||
@@ -74,5 +74,5 @@ var ErrGPUNotAvailable = thresholdvm.ErrGPUNotAvailable
|
||||
// `cevm.LibraryABIVersion()` — discovery via package-scope function,
|
||||
// not via a global variable.
|
||||
func GPUBackendInstance() *GPUBackend {
|
||||
return thresholdvm.Backend()
|
||||
return mpcvm.Backend()
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
// nocgo re-export of the chains/mpcvm GPU bridge. Under !cgo the
|
||||
// upstream GPUBackend methods all return ErrGPUNotAvailable and
|
||||
// Backend() returns nil; this layer transparently passes that through
|
||||
// so callers see identical behaviour regardless of build mode.
|
||||
//
|
||||
// One and only one nocgo stub for the entire process: it lives in
|
||||
// chains/mpcvm. This package just re-exports the types and the
|
||||
// sentinel error.
|
||||
|
||||
package mpcvm
|
||||
|
||||
import "github.com/luxfi/chains/mpcvm"
|
||||
|
||||
type (
|
||||
GPUBackend = mpcvm.GPUBackend
|
||||
GPUBackendKind = mpcvm.GPUBackendKind
|
||||
|
||||
GPUCeremony = mpcvm.GPUCeremony
|
||||
GPUKeyShare = mpcvm.GPUKeyShare
|
||||
GPUContribution = mpcvm.GPUContribution
|
||||
GPUMPCVMState = mpcvm.GPUMPCVMState
|
||||
GPUMPCVMRoundDescriptor = mpcvm.GPUMPCVMRoundDescriptor
|
||||
GPUCeremonyOp = mpcvm.GPUCeremonyOp
|
||||
GPUContributionOp = mpcvm.GPUContributionOp
|
||||
GPUMPCVMTransitionResult = mpcvm.GPUMPCVMTransitionResult
|
||||
)
|
||||
|
||||
const (
|
||||
GPUBackendNone = mpcvm.GPUBackendNone
|
||||
GPUBackendCUDA = mpcvm.GPUBackendCUDA
|
||||
GPUBackendHIP = mpcvm.GPUBackendHIP
|
||||
GPUBackendMetal = mpcvm.GPUBackendMetal
|
||||
GPUBackendVulkan = mpcvm.GPUBackendVulkan
|
||||
GPUBackendWebGPU = mpcvm.GPUBackendWebGPU
|
||||
)
|
||||
|
||||
var ErrGPUNotAvailable = mpcvm.ErrGPUNotAvailable
|
||||
|
||||
// GPUBackendInstance returns nil under !cgo — the upstream Backend()
|
||||
// returns nil because no dlopen ever happens. Callers branch on the
|
||||
// IsAvailable() check (or `g == nil`) and route to the CPU reference.
|
||||
func GPUBackendInstance() *GPUBackend {
|
||||
return mpcvm.Backend()
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package thresholdvm
|
||||
package mpcvm
|
||||
|
||||
// TestGPUBridgeCgoNocgoParity is the node-side mirror of the parity
|
||||
// test in chains/thresholdvm — proves that the re-exported GPUBackend
|
||||
// test in chains/mpcvm — proves that the re-exported GPUBackend
|
||||
// method set produces byte-identical MPC ceremony state transitions
|
||||
// regardless of build flavor (cgo / nocgo) and plugin presence.
|
||||
//
|
||||
// The node package is a thin alias layer over chains/thresholdvm —
|
||||
// The node package is a thin alias layer over chains/mpcvm —
|
||||
// type aliases on the wire structs and a re-exported GPUBackendInstance
|
||||
// accessor — so the parity properties of the canonical bridge transfer
|
||||
// directly. We re-run the four-op pipeline through the node-side
|
||||
@@ -19,13 +19,13 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
chainsthreshold "github.com/luxfi/chains/thresholdvm"
|
||||
chainsthreshold "github.com/luxfi/chains/mpcvm"
|
||||
)
|
||||
|
||||
// TestGPUBridgeCgoNocgoParity runs a 3-of-5 FROST keygen ceremony
|
||||
// through GPUBackendInstance() (the node-side accessor) and compares
|
||||
// the resulting arena byte-for-byte to a fresh run via the canonical
|
||||
// bridge in chains/thresholdvm. Under cgo, the comparison is the
|
||||
// bridge in chains/mpcvm. Under cgo, the comparison is the
|
||||
// upstream cgo bridge vs itself (both runs hit the same dispatcher).
|
||||
// Under !cgo, the comparison is the upstream nocgo bridge vs itself.
|
||||
// Either path proves the alias layer is transparent and the substrate
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package thresholdvm
|
||||
package mpcvm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// device-side __align__(16) values declared in
|
||||
// the GPU plugin install tree ops/mpcvm/cuda/mpcvm_kernels_common.cuh.
|
||||
//
|
||||
// Even though the structs are type aliases to chains/thresholdvm, this
|
||||
// Even though the structs are type aliases to chains/mpcvm, this
|
||||
// test sits at the node import boundary — if upstream sizes drift the
|
||||
// node-side surface MUST also fail loud rather than miscompile.
|
||||
func TestNodeGPULayoutSizes(t *testing.T) {
|
||||
@@ -5,7 +5,7 @@ The `GetAllValidatorsAt` endpoint returns the validator sets of all networks (in
|
||||
|
||||
## Endpoint
|
||||
```
|
||||
POST /ext/bc/P
|
||||
POST /v1/bc/P
|
||||
```
|
||||
|
||||
## Request Format
|
||||
@@ -71,7 +71,7 @@ curl -X POST --data '{
|
||||
"params": {
|
||||
"height": 1000
|
||||
}
|
||||
}' -H 'content-type:application/json;' http://127.0.0.1:9650/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/P
|
||||
```
|
||||
|
||||
### Get validators at proposed height
|
||||
@@ -83,7 +83,7 @@ curl -X POST --data '{
|
||||
"params": {
|
||||
"height": "proposed"
|
||||
}
|
||||
}' -H 'content-type:application/json;' http://127.0.0.1:9650/ext/bc/P
|
||||
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/P
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
@@ -29,7 +29,7 @@ type staticClient struct {
|
||||
// NewClient returns a platformvm client for interacting with the platformvm static api
|
||||
func NewStaticClient(uri string) StaticClient {
|
||||
return &staticClient{requester: rpc.NewEndpointRequester(
|
||||
uri + "/ext/vm/platform",
|
||||
uri + "/v1/vm/platform",
|
||||
)}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ type Client struct {
|
||||
|
||||
func NewClient(uri string) *Client {
|
||||
return &Client{Requester: rpc.NewEndpointRequester(
|
||||
uri + "/ext/bc/P",
|
||||
uri + "/v1/bc/P",
|
||||
)}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func NewClient(uri string) *Client {
|
||||
// for proper bech32 address formatting
|
||||
func NewClientWithNetworkID(uri string, networkID uint32) *Client {
|
||||
return &Client{
|
||||
Requester: rpc.NewEndpointRequester(uri + "/ext/bc/P"),
|
||||
Requester: rpc.NewEndpointRequester(uri + "/v1/bc/P"),
|
||||
networkID: networkID,
|
||||
}
|
||||
}
|
||||
|
||||
+36
-36
@@ -3,7 +3,7 @@ The P-Chain API allows clients to interact with the [P-Chain](https://build.lux.
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
/ext/bc/P
|
||||
/v1/bc/P
|
||||
```
|
||||
|
||||
## Format
|
||||
@@ -56,7 +56,7 @@ curl -X POST --data '{
|
||||
"params" :{
|
||||
"addresses":["P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"]
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -133,7 +133,7 @@ curl -X POST --data '{
|
||||
"encoding": "hex"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -162,7 +162,7 @@ curl -X POST --data '{
|
||||
"encoding": "json"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -266,7 +266,7 @@ curl -X POST --data '{
|
||||
"encoding": "hex"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -295,7 +295,7 @@ curl -X POST --data '{
|
||||
"encoding": "json"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -398,7 +398,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getBlockchains",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -490,7 +490,7 @@ curl -X POST --data '{
|
||||
"blockchainID":"2NbS4dwGaf2p1MaXb65PrkZdXRwmSX4ZzGnUu7jm3aykgThuZE"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -530,7 +530,7 @@ curl -X POST --data '{
|
||||
"netID": "11111111111111111111111111111111LpoYY"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -671,7 +671,7 @@ curl -X POST --data '{
|
||||
"nodeIDs": ["NodeID-5mb46qkSBj81k9g9e4VFjGGSbaaSLFRzD"]
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response (Primary Network):**
|
||||
@@ -793,7 +793,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getFeeConfig",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -836,7 +836,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getFeeState",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -875,7 +875,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getHeight",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -944,7 +944,7 @@ curl -X POST --data '{
|
||||
"validationID": ["9FAftNgNBrzHUMMApsSyV6RcFiL9UmCbvsCu28xdLV2mQ7CMo"]
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -998,7 +998,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getProposedHeight",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1040,7 +1040,7 @@ curl -X POST --data '{
|
||||
"params": {
|
||||
"netID":"11111111111111111111111111111111LpoYY"
|
||||
},
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1096,7 +1096,7 @@ curl -X POST --data '{
|
||||
"txID": "2nmH8LithVbdjaXsxVQCQfXtzN9hBbmebrsaEYnLM9T32Uy2Y5"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1161,7 +1161,7 @@ curl -X POST --data '{
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1210,7 +1210,7 @@ curl -X POST --data '{
|
||||
"netID": "11111111111111111111111111111111LpoYY"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1275,7 +1275,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getNet",
|
||||
"params": {"netID":"Vz2ArUpigHt7fyE79uF3gAXvTPLJi2LGgZoMpgNPHowUZJxBb"},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1340,7 +1340,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getNets",
|
||||
"params": {"ids":["hW8Ma7dLMA7o4xmJf3AXBbo17bXzE7xnThUd3ypM4VAWo1sNJ"]},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1383,7 +1383,7 @@ curl -X POST --data '{
|
||||
"params": {},
|
||||
"id": 1
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1426,7 +1426,7 @@ curl -X POST --data '{
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1455,7 +1455,7 @@ curl -X POST --data '{
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1500,7 +1500,7 @@ curl -X POST --data '{
|
||||
"encoding": "json"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1612,7 +1612,7 @@ curl -X POST --data '{
|
||||
"txID":"TAG9Ns1sa723mZy1GSoGqWipK6Mvpaj7CAswVJGM6MkVJDF9Q"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1685,7 +1685,7 @@ curl -X POST --data '{
|
||||
"limit":5,
|
||||
"encoding": "hex"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
This gives response:
|
||||
@@ -1729,7 +1729,7 @@ curl -X POST --data '{
|
||||
},
|
||||
"encoding": "hex"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
This gives response:
|
||||
@@ -1772,7 +1772,7 @@ curl -X POST --data '{
|
||||
"sourceChain": "X",
|
||||
"encoding": "hex"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
This gives response:
|
||||
@@ -1825,7 +1825,7 @@ curl -X POST --data '{
|
||||
"height":1
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1874,7 +1874,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getValidatorFeeConfig",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1915,7 +1915,7 @@ curl -X POST --data '{
|
||||
"method": "platform.getValidatorFeeState",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1961,7 +1961,7 @@ curl -X POST --data '{
|
||||
"encoding": "hex"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -2008,7 +2008,7 @@ curl -X POST --data '{
|
||||
"params" :{
|
||||
"size":2
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -2053,7 +2053,7 @@ curl -X POST --data '{
|
||||
"blockchainID": "KDYHHKjM4yTJTT8H8qPs5KXzE6gQH5TZrmP1qVr1P6qECj3XN"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -2095,7 +2095,7 @@ curl -X POST --data '{
|
||||
"netID":"2bRCr6B4MiEfSjidDwxDpdCyviwnfUVqB2HGwhm947w9YYqb7r"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/P
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/P
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
|
||||
@@ -74,7 +74,7 @@ ground-truth numbers, drop captured bytes into `testdata/`:
|
||||
|
||||
```bash
|
||||
POD=$(kubectl -n lux get pods -l app=luxd -o jsonpath='{.items[0].metadata.name}')
|
||||
kubectl -n lux exec "$POD" -- curl -s http://localhost:9650/ext/bc/P \
|
||||
kubectl -n lux exec "$POD" -- curl -s http://localhost:9650/v1/bc/P \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"jsonrpc":"2.0","id":1,"method":"platform.getMempool"}' \
|
||||
> /tmp/mempool.json
|
||||
|
||||
@@ -8,12 +8,14 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
validators "github.com/luxfi/validators"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/math/set"
|
||||
"github.com/luxfi/node/cache"
|
||||
"github.com/luxfi/node/cache/lru"
|
||||
"github.com/luxfi/node/vms/platformvm/block"
|
||||
@@ -113,7 +115,12 @@ func NewManager(
|
||||
state: state,
|
||||
metrics: metrics,
|
||||
clk: clk,
|
||||
caches: make(map[ids.ID]cache.Cacher[uint64, map[ids.NodeID]*validators.GetValidatorOutput]),
|
||||
// Snapshot the tracked-chain set. cfg.TrackedChains aliases the network's
|
||||
// runtime-mutable set, and the P-chain VM (this constructor) is initialized
|
||||
// synchronously before the API server serves setTrackedChains, so this
|
||||
// one-time copy happens-before any concurrent TrackChain writer.
|
||||
trackedChains: set.Of(cfg.TrackedChains.List()...),
|
||||
caches: make(map[ids.ID]cache.Cacher[uint64, map[ids.NodeID]*validators.GetValidatorOutput]),
|
||||
recentlyAccepted: window.New[ids.ID](
|
||||
window.Config{
|
||||
Clock: clk,
|
||||
@@ -132,6 +139,19 @@ type manager struct {
|
||||
metrics metrics.Metrics
|
||||
clk *mockable.Clock
|
||||
|
||||
// trackedChains is an immutable snapshot of cfg.TrackedChains taken at
|
||||
// construction. getValidatorSetCache reads it lock-free from the consensus
|
||||
// hot path; cfg.TrackedChains itself aliases the network's runtime-mutable
|
||||
// set (mutated by network.TrackChain under peersLock via the admin
|
||||
// setTrackedChains RPC), so the hot path must not read that live map.
|
||||
trackedChains set.Set[ids.ID]
|
||||
|
||||
// cachesLock guards concurrent access to caches. getValidatorSetCache runs a
|
||||
// check-then-insert that is reached concurrently from every chain's
|
||||
// proposervm (windower.ExpectedProposer -> GetValidatorSet), so the map
|
||||
// read, existence check, and insert must be atomic.
|
||||
cachesLock sync.Mutex
|
||||
|
||||
// Maps caches for each net that is currently tracked.
|
||||
// Key: Net ID
|
||||
// Value: cache mapping height -> validator set map
|
||||
@@ -253,11 +273,22 @@ func (m *manager) GetValidatorSetWithContext(
|
||||
}
|
||||
|
||||
func (m *manager) getValidatorSetCache(chainID ids.ID) cache.Cacher[uint64, map[ids.NodeID]*validators.GetValidatorOutput] {
|
||||
// Only cache tracked chains
|
||||
if chainID != constants.PrimaryNetworkID && !m.cfg.TrackedChains.Contains(chainID) {
|
||||
// Only cache the primary network and chains tracked at startup. trackedChains
|
||||
// is an immutable snapshot (see NewManager), so this read is lock-free and
|
||||
// cannot race the network's runtime setTrackedChains writer. A chain tracked
|
||||
// at runtime via the (default-disabled) admin API is not reflected here: it
|
||||
// reads as untracked and its validator set is recomputed per call rather than
|
||||
// cached — correct, just uncached.
|
||||
if chainID != constants.PrimaryNetworkID && !m.trackedChains.Contains(chainID) {
|
||||
return &cache.Empty[uint64, map[ids.NodeID]*validators.GetValidatorOutput]{}
|
||||
}
|
||||
|
||||
// The check-then-insert on caches must be atomic: this method is reached
|
||||
// concurrently across chains, so an unguarded map write here crashes the
|
||||
// node with "concurrent map writes".
|
||||
m.cachesLock.Lock()
|
||||
defer m.cachesLock.Unlock()
|
||||
|
||||
validatorSetsCache, exists := m.caches[chainID]
|
||||
if exists {
|
||||
return validatorSetsCache
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package validators
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
validators "github.com/luxfi/validators"
|
||||
"github.com/luxfi/constants"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/math/set"
|
||||
"github.com/luxfi/node/cache"
|
||||
"github.com/luxfi/node/vms/platformvm/config"
|
||||
"github.com/luxfi/timer/mockable"
|
||||
)
|
||||
|
||||
// emptyCache reports whether c is the no-op cache returned for chains the
|
||||
// manager does not cache (untracked at startup).
|
||||
func emptyCache(c validatorSetCacher) bool {
|
||||
_, ok := c.(*cache.Empty[uint64, map[ids.NodeID]*validators.GetValidatorOutput])
|
||||
return ok
|
||||
}
|
||||
|
||||
// validatorSetCacher is the value type stored in manager.caches.
|
||||
type validatorSetCacher = cache.Cacher[uint64, map[ids.NodeID]*validators.GetValidatorOutput]
|
||||
|
||||
// TestGetValidatorSetCacheConcurrent hammers getValidatorSetCache from many
|
||||
// goroutines across many chainIDs simultaneously. It is the regression guard
|
||||
// for the unsynchronized map write on manager.caches that crashed nodes with
|
||||
// "fatal error: concurrent map writes" during multi-validator restart storms
|
||||
// (proposervm windower.ExpectedProposer -> GetValidatorSet -> getValidatorSetCache
|
||||
// across multiple chains warming their caches at once).
|
||||
//
|
||||
// Run with -race. The test asserts two properties:
|
||||
// 1. Race-freedom: no concurrent map read/write on manager.caches.
|
||||
// 2. Correctness: a given chainID resolves to exactly one cache instance for
|
||||
// every caller (no lost insert — the check-then-create-then-store is
|
||||
// atomic), and that instance is the one persisted in manager.caches.
|
||||
func TestGetValidatorSetCacheConcurrent(t *testing.T) {
|
||||
const (
|
||||
numChains = 16
|
||||
numGoroutines = 64
|
||||
numIters = 256
|
||||
)
|
||||
|
||||
// All generated chains are tracked so they exercise the cached (racy)
|
||||
// branch. PrimaryNetworkID is always cached and is included to cover that
|
||||
// branch too.
|
||||
generated := make([]ids.ID, numChains)
|
||||
tracked := set.NewSet[ids.ID](numChains)
|
||||
for i := range generated {
|
||||
generated[i] = ids.GenerateTestID()
|
||||
tracked.Add(generated[i])
|
||||
}
|
||||
testChains := append([]ids.ID{constants.PrimaryNetworkID}, generated...)
|
||||
|
||||
m := &manager{
|
||||
trackedChains: tracked,
|
||||
caches: make(map[ids.ID]validatorSetCacher),
|
||||
}
|
||||
|
||||
// results[g][c] holds the cache instance goroutine g last observed for
|
||||
// testChains[c]. Each goroutine writes only its own row (a distinct backing
|
||||
// array), so the grid is race-free without a lock; it is read only after all
|
||||
// goroutines join.
|
||||
results := make([][]validatorSetCacher, numGoroutines)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
start := make(chan struct{})
|
||||
for g := 0; g < numGoroutines; g++ {
|
||||
results[g] = make([]validatorSetCacher, len(testChains))
|
||||
wg.Add(1)
|
||||
go func(g int) {
|
||||
defer wg.Done()
|
||||
// Release all goroutines simultaneously to maximize the first-access
|
||||
// collision window on each chain's cache creation.
|
||||
<-start
|
||||
for iter := 0; iter < numIters; iter++ {
|
||||
for c, chainID := range testChains {
|
||||
results[g][c] = m.getValidatorSetCache(chainID)
|
||||
}
|
||||
}
|
||||
}(g)
|
||||
}
|
||||
close(start)
|
||||
wg.Wait()
|
||||
|
||||
// Correctness: for each chain, every goroutine must have observed the exact
|
||||
// same (non-nil) cache instance, and that instance must be the one stored in
|
||||
// manager.caches. A lost insert would surface here as a mismatch.
|
||||
for c, chainID := range testChains {
|
||||
want := results[0][c]
|
||||
if want == nil {
|
||||
t.Fatalf("chain %s (idx %d): got nil cache", chainID, c)
|
||||
}
|
||||
for g := 1; g < numGoroutines; g++ {
|
||||
if results[g][c] != want {
|
||||
t.Fatalf("chain %s (idx %d): goroutine %d observed a different cache instance (lost insert)", chainID, c, g)
|
||||
}
|
||||
}
|
||||
if stored := m.caches[chainID]; stored != want {
|
||||
t.Fatalf("chain %s (idx %d): manager.caches holds a different instance than callers observed", chainID, c)
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.caches) != len(testChains) {
|
||||
t.Fatalf("expected %d cache entries, got %d", len(testChains), len(m.caches))
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetValidatorSetCacheUntrackedReturnsEmpty locks in the behavior that
|
||||
// untracked chains are never cached (they get a fresh cache.Empty), so the
|
||||
// locking change does not alter which cache is returned.
|
||||
func TestGetValidatorSetCacheUntrackedReturnsEmpty(t *testing.T) {
|
||||
m := &manager{
|
||||
trackedChains: set.NewSet[ids.ID](0),
|
||||
caches: make(map[ids.ID]validatorSetCacher),
|
||||
}
|
||||
|
||||
untracked := ids.GenerateTestID()
|
||||
got := m.getValidatorSetCache(untracked)
|
||||
if !emptyCache(got) {
|
||||
t.Fatalf("untracked chain: expected *cache.Empty, got %T", got)
|
||||
}
|
||||
if len(m.caches) != 0 {
|
||||
t.Fatalf("untracked chain must not be cached; caches has %d entries", len(m.caches))
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetValidatorSetCacheTrackedChainsSnapshot is the regression guard for
|
||||
// Vector-4: getValidatorSetCache must not read the network's runtime-mutable
|
||||
// TrackedChains set. In production cfg.TrackedChains aliases the same set.Set
|
||||
// that network.TrackChain mutates (under peersLock) via the admin
|
||||
// setTrackedChains RPC, so a lock-free Contains on it from the consensus hot
|
||||
// path races that writer -> "concurrent map read and map write" -> exit(2).
|
||||
//
|
||||
// NewManager snapshots TrackedChains into a manager-owned immutable copy, so
|
||||
// this test races getValidatorSetCache against Add on the ORIGINAL set (RED's
|
||||
// repro shape) and requires it to be race-clean. It also pins the snapshot
|
||||
// semantic: chains tracked at construction are cached; chains added to the
|
||||
// original set afterwards are not reflected (they read as untracked/uncached).
|
||||
//
|
||||
// Run with -race.
|
||||
func TestGetValidatorSetCacheTrackedChainsSnapshot(t *testing.T) {
|
||||
const (
|
||||
numReaders = 32
|
||||
numIters = 4096
|
||||
)
|
||||
|
||||
// live simulates the network's runtime-mutable TrackedChains instance that
|
||||
// cfg.TrackedChains aliases in production.
|
||||
live := set.NewSet[ids.ID](1)
|
||||
seed := ids.GenerateTestID() // tracked at construction time
|
||||
live.Add(seed)
|
||||
|
||||
m := NewManager(
|
||||
config.Internal{TrackedChains: live},
|
||||
nil, // State: unused by getValidatorSetCache
|
||||
nil, // Metrics: unused by getValidatorSetCache
|
||||
&mockable.Clock{},
|
||||
).(*manager)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
start := make(chan struct{})
|
||||
|
||||
// Writer: continuously mutate the ORIGINAL live set, exactly as
|
||||
// network.TrackChain does. After the fix the manager never reads this set,
|
||||
// so this must not race the readers.
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-start
|
||||
for i := 0; i < numIters; i++ {
|
||||
live.Add(ids.GenerateTestID())
|
||||
}
|
||||
}()
|
||||
|
||||
// Readers: hammer the consensus hot path.
|
||||
for r := 0; r < numReaders; r++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-start
|
||||
for i := 0; i < numIters; i++ {
|
||||
_ = m.getValidatorSetCache(seed)
|
||||
_ = m.getValidatorSetCache(constants.PrimaryNetworkID)
|
||||
_ = m.getValidatorSetCache(ids.GenerateTestID())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
close(start)
|
||||
wg.Wait()
|
||||
|
||||
// Snapshot semantic: the chain tracked at construction is cached.
|
||||
if emptyCache(m.getValidatorSetCache(seed)) {
|
||||
t.Fatal("seed chain tracked at construction must be cached, got cache.Empty")
|
||||
}
|
||||
// A chain added to the original set AFTER construction is not reflected by
|
||||
// the immutable snapshot: it reads as untracked (uncached). This documents
|
||||
// the deliberate trade-off of decoupling from the runtime set.
|
||||
later := ids.GenerateTestID()
|
||||
live.Add(later)
|
||||
if !emptyCache(m.getValidatorSetCache(later)) {
|
||||
t.Fatal("chain added to the original set post-construction must not be tracked by the snapshot")
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/cloudflare/circl/kem/mlkem/mlkem768"
|
||||
"github.com/luxfi/crypto/bls"
|
||||
"github.com/luxfi/crypto/threshold"
|
||||
_ "github.com/luxfi/crypto/threshold/bls" // Register BLS threshold scheme
|
||||
_ "github.com/luxfi/threshold/scheme/bls" // Register BLS threshold scheme
|
||||
"github.com/luxfi/math/set"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ The `GetProposedHeight` API endpoint returns the P-Chain height that would be pr
|
||||
## Endpoint
|
||||
|
||||
```
|
||||
/ext/bc/<CHAIN_ID>/proposervm
|
||||
/v1/bc/<CHAIN_ID>/proposervm
|
||||
```
|
||||
|
||||
## Method
|
||||
@@ -45,7 +45,7 @@ curl -X POST --data '{
|
||||
"id" :1,
|
||||
"method" :"proposervm.getProposedHeight",
|
||||
"params" :{}
|
||||
}' -H 'content-type:application/json;' http://127.0.0.1:9650/ext/bc/C/proposervm
|
||||
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/C/proposervm
|
||||
```
|
||||
|
||||
### Response
|
||||
|
||||
+68
-5
@@ -210,6 +210,42 @@ func (p *postForkCommonComponents) Verify(
|
||||
)
|
||||
}
|
||||
|
||||
// slotSnappedChildTimestamp returns the timestamp a child built at wall-clock `now` off a
|
||||
// parent at `parentTimestamp` should carry. It is the parent-anchored proposer-window grid:
|
||||
// parentTimestamp + slot*WindowDuration, where slot = TimeToSlot(parentTimestamp, now).
|
||||
//
|
||||
// WHY (round-scoped view-change liveness): off a STALE parent — idle past the proposer
|
||||
// window, so "anyone can propose" — buildChild is called repeatedly, and a raw wall-clock
|
||||
// timestamp (now.Truncate(1s)) makes EVERY rebuild a DISTINCT envelope over the same inner
|
||||
// block. That is an unbounded, ever-growing set of siblings at one height; the round-scoped
|
||||
// view-change can never gather α aligned prevotes on any single candidate (no proof-of-lock
|
||||
// forms) → finality stalls fleet-wide (the distributed liveness stall a goroutine dump of the
|
||||
// hung fleet showed: all nodes quiescent, waiting for votes no one will send). Snapping to the
|
||||
// window grid makes all rebuilds WITHIN one slot byte-IDENTICAL — one stable candidate per
|
||||
// slot for the view-change to converge on.
|
||||
//
|
||||
// WHY IT IS SAFE: TimeToSlot(parent, snapped) == slot == TimeToSlot(parent, now), so the
|
||||
// block's SLOT is unchanged; ExpectedProposer (verifyPostDurangoBlockDelay /
|
||||
// shouldBuildSignedBlockPostDurango) returns the IDENTICAL verdict, so the proposer-window and
|
||||
// signed/unsigned checks are byte-for-byte unaffected, and the derived epoch is stabilised
|
||||
// across the slot instead of churned. Only slot>0 is snapped, so:
|
||||
// - normal in-window production (slot 0, child < WindowDuration after parent) keeps its exact
|
||||
// sub-window timestamp — zero behaviour change on a live chain;
|
||||
// - a snapped time is strictly > parent (monotonic, no errTimeNotMonotonic) and ≤ now (never
|
||||
// errTimeTooAdvanced).
|
||||
// It is a pure function of (parentTimestamp, now) so it is idempotent for any two calls in the
|
||||
// same slot — the property the liveness fix relies on.
|
||||
func slotSnappedChildTimestamp(parentTimestamp, now time.Time) time.Time {
|
||||
ts := now.Truncate(time.Second)
|
||||
if ts.Before(parentTimestamp) {
|
||||
return parentTimestamp
|
||||
}
|
||||
if slot := proposer.TimeToSlot(parentTimestamp, ts); slot > 0 {
|
||||
return parentTimestamp.Add(time.Duration(slot) * proposer.WindowDuration)
|
||||
}
|
||||
return ts
|
||||
}
|
||||
|
||||
// Return the child (a *postForkBlock) of this block
|
||||
func (p *postForkCommonComponents) buildChild(
|
||||
ctx context.Context,
|
||||
@@ -218,11 +254,10 @@ func (p *postForkCommonComponents) buildChild(
|
||||
parentPChainHeight uint64,
|
||||
parentEpoch chain.Epoch,
|
||||
) (Block, error) {
|
||||
// Child's timestamp is the later of now and this block's timestamp
|
||||
newTimestamp := p.vm.Time().Truncate(time.Second)
|
||||
if newTimestamp.Before(parentTimestamp) {
|
||||
newTimestamp = parentTimestamp
|
||||
}
|
||||
// Child's timestamp is the later of now and this block's timestamp, SLOT-SNAPPED to the
|
||||
// parent-anchored proposer-window grid (see slotSnappedChildTimestamp) so repeated rebuilds
|
||||
// off a stale parent are byte-identical — the round-scoped view-change liveness fix.
|
||||
newTimestamp := slotSnappedChildTimestamp(parentTimestamp, p.vm.Time())
|
||||
|
||||
// The child's P-Chain height is proposed as the optimal P-Chain height that
|
||||
// is at least the parent's P-Chain height
|
||||
@@ -329,6 +364,34 @@ func (p *postForkCommonComponents) setInnerBlk(innerBlk chain.Block) {
|
||||
p.innerBlk = innerBlk
|
||||
}
|
||||
|
||||
// canonicalCommitter (proposervm side) — expose the INNER execution block's
|
||||
// identity to the consensus engine so it collapses duplicate outer wrappers of ONE
|
||||
// inner block to ONE consensus object.
|
||||
//
|
||||
// WHY THIS EXISTS. Post-Durango, when the chain is stuck the designated proposer
|
||||
// re-wraps the SAME inner block with a fresh proposer signature / block-timestamp
|
||||
// each slot — many DISTINCT outer envelope ids over ONE inner block. The consensus
|
||||
// engine's finality object is the CanonicalID (VotePosition.CanonicalID; the signed
|
||||
// vote binds it and EXCLUDES the outer id). Until this method existed, no block
|
||||
// exposed a CanonicalID, so the engine's canonicalIDOf fell back to the OUTER id and
|
||||
// treated every re-wrap as a distinct fork — votes scattered across the wrappers,
|
||||
// no α-of-K cert ever formed, and the chain froze (the mainnet 1082879 storm). By
|
||||
// returning the inner block's id here, every outer wrapper of one inner block yields
|
||||
// the SAME CanonicalID → they collapse to ONE, votes aggregate, one cert forms.
|
||||
//
|
||||
// SAFETY. CanonicalID is derived from the ACTUAL verified inner block (innerBlk is
|
||||
// execution-verified before the wrapper is tracked), so two wrappers collapse iff
|
||||
// they genuinely carry the same inner block; two wrappers over DIFFERENT inner
|
||||
// blocks (a real execution fork) return DIFFERENT CanonicalIDs and are NOT collapsed.
|
||||
// ParentCanonicalID is the inner PARENT id, so a forked parent (two wrappers of one
|
||||
// inner parent) also collapses for its children. The execution/payload roots are left
|
||||
// Empty (the engine treats Empty as "not exposed"); the inner-id collapse is what the
|
||||
// finality path needs, and Empty roots keep the signed message byte-compatible.
|
||||
func (p *postForkCommonComponents) CanonicalID() ids.ID { return p.innerBlk.ID() }
|
||||
func (p *postForkCommonComponents) ParentCanonicalID() ids.ID { return p.innerBlk.Parent() }
|
||||
func (p *postForkCommonComponents) ExecutionStateRoot() ids.ID { return ids.Empty }
|
||||
func (p *postForkCommonComponents) PayloadRoot() ids.ID { return ids.Empty }
|
||||
|
||||
func verifyIsOracleBlock(ctx context.Context, b chain.Block) error {
|
||||
oracle, ok := b.(OracleBlock)
|
||||
if !ok {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package proposervm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/node/vms/proposervm/proposer"
|
||||
)
|
||||
|
||||
// TestSlotSnappedChildTimestamp proves the view-change liveness fix's properties on the
|
||||
// timestamp buildChild stamps: the same-slot IDEMPOTENCE that makes a stale-parent rebuild
|
||||
// one stable candidate, and the SLOT-INVARIANCE that keeps every proposer-window check
|
||||
// byte-identical to the pre-fix behaviour.
|
||||
func TestSlotSnappedChildTimestamp(t *testing.T) {
|
||||
parent := time.Unix(1_700_000_000, 0) // fixed parent instant
|
||||
|
||||
t.Run("idempotent_within_a_slot_off_a_stale_parent", func(t *testing.T) {
|
||||
// A parent 2h stale: slot = 1440. Two rebuilds 1.9s apart land in the SAME slot and
|
||||
// MUST produce the byte-identical timestamp (→ identical envelope id). This is the
|
||||
// property that collapses the unbounded churn to one candidate.
|
||||
base := parent.Add(2 * time.Hour)
|
||||
a := slotSnappedChildTimestamp(parent, base.Add(200*time.Millisecond))
|
||||
b := slotSnappedChildTimestamp(parent, base.Add(2100*time.Millisecond))
|
||||
if !a.Equal(b) {
|
||||
t.Fatalf("NOT idempotent within a slot: %v != %v (churn not eliminated)", a, b)
|
||||
}
|
||||
// And it is exactly on the window grid.
|
||||
if off := a.Sub(parent) % proposer.WindowDuration; off != 0 {
|
||||
t.Fatalf("snapped timestamp %v is not on the WindowDuration grid (offset %v)", a, off)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("slot_invariant_proposer_window_unchanged", func(t *testing.T) {
|
||||
// For a spread of stale ages, the snapped timestamp MUST map to the SAME slot as the
|
||||
// raw now — this is what guarantees ExpectedProposer returns the identical verdict, so
|
||||
// the fix cannot cause errUnexpectedProposer / errProposerWindowNotStarted.
|
||||
for _, age := range []time.Duration{
|
||||
6 * time.Second, 5*time.Minute + time.Second, time.Hour, 3 * time.Hour,
|
||||
} {
|
||||
now := parent.Add(age)
|
||||
snapped := slotSnappedChildTimestamp(parent, now)
|
||||
if got, want := proposer.TimeToSlot(parent, snapped), proposer.TimeToSlot(parent, now.Truncate(time.Second)); got != want {
|
||||
t.Fatalf("age %v: snapped slot %d != raw slot %d — proposer-window verdict would change", age, got, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("monotonic_and_not_advanced", func(t *testing.T) {
|
||||
for _, age := range []time.Duration{
|
||||
5 * time.Second, 7 * time.Second, time.Hour,
|
||||
} {
|
||||
now := parent.Add(age)
|
||||
snapped := slotSnappedChildTimestamp(parent, now)
|
||||
if !snapped.After(parent) {
|
||||
t.Fatalf("age %v: snapped %v not strictly after parent %v (errTimeNotMonotonic risk)", age, snapped, parent)
|
||||
}
|
||||
if snapped.After(now) {
|
||||
t.Fatalf("age %v: snapped %v runs ahead of now %v (errTimeTooAdvanced risk)", age, snapped, now)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fresh_in_window_child_unchanged", func(t *testing.T) {
|
||||
// Slot 0 (child < WindowDuration after parent) is the NORMAL live case: no snap, exact
|
||||
// wall-clock (truncated) timestamp — zero behaviour change on a healthy chain.
|
||||
now := parent.Add(3 * time.Second)
|
||||
if got, want := slotSnappedChildTimestamp(parent, now), now.Truncate(time.Second); !got.Equal(want) {
|
||||
t.Fatalf("in-window child was altered: got %v want %v", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("clock_before_parent_pins_to_parent", func(t *testing.T) {
|
||||
now := parent.Add(-10 * time.Second)
|
||||
if got := slotSnappedChildTimestamp(parent, now); !got.Equal(parent) {
|
||||
t.Fatalf("now<parent must pin to parent, got %v", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// canonical_commitment_test.go — proves the proposervm block exposes the INNER
|
||||
// execution commitment to the consensus engine (Part A of the mainnet 1082879
|
||||
// duplicate-wrapper-storm fix).
|
||||
//
|
||||
// THE BUG THIS GUARDS AGAINST. The consensus engine's finality object is the
|
||||
// CanonicalID (VotePosition.CanonicalID; the signed accept vote binds it and
|
||||
// EXCLUDES the outer envelope id). The engine reads it by asserting a VM block to a
|
||||
// structural canonicalCommitter interface (engine/chain/engine.go canonicalIDOf);
|
||||
// when the assertion FAILS it falls back to the block's OUTER id. Before Part A NO
|
||||
// block type anywhere implemented that interface, so every one of the 758 outer
|
||||
// wrappers of the single inner EVM block 25Q837Lw got a DISTINCT canonical (its own
|
||||
// outer id), votes scattered across them, no α-of-K cert formed, and the C-Chain
|
||||
// froze at 1082879. These tests fail-closed if the proposervm block ever stops
|
||||
// exposing the inner id.
|
||||
package proposervm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/vm/chain/blocktest"
|
||||
)
|
||||
|
||||
// canonicalCommitter mirrors the exact (unexported) structural interface the
|
||||
// consensus engine's canonicalIDOf asserts. If *postForkBlock / *postForkOption
|
||||
// stop satisfying this shape, the engine silently falls back to the outer id and
|
||||
// the duplicate-wrapper storm can no longer collapse — so pin it at compile time.
|
||||
type canonicalCommitter interface {
|
||||
CanonicalID() ids.ID
|
||||
ParentCanonicalID() ids.ID
|
||||
ExecutionStateRoot() ids.ID
|
||||
PayloadRoot() ids.ID
|
||||
}
|
||||
|
||||
var (
|
||||
_ canonicalCommitter = (*postForkBlock)(nil)
|
||||
_ canonicalCommitter = (*postForkOption)(nil)
|
||||
)
|
||||
|
||||
// TestCanonicalCommitter_ExposesInnerID proves the wrapper's CanonicalID is the
|
||||
// INNER block's id (not the outer envelope id) and ParentCanonicalID is the inner
|
||||
// PARENT's id — the values that let the engine collapse aliases and a forked parent.
|
||||
func TestCanonicalCommitter_ExposesInnerID(t *testing.T) {
|
||||
innerID := ids.GenerateTestID()
|
||||
innerParent := ids.GenerateTestID()
|
||||
inner := &blocktest.Block{IDV: innerID, ParentV: innerParent}
|
||||
pfb := &postForkBlock{postForkCommonComponents: postForkCommonComponents{innerBlk: inner}}
|
||||
|
||||
if got := pfb.CanonicalID(); got != innerID {
|
||||
t.Fatalf("CanonicalID = %s, want inner id %s (engine would fall back to the outer id → storm)", got, innerID)
|
||||
}
|
||||
if got := pfb.ParentCanonicalID(); got != innerParent {
|
||||
t.Fatalf("ParentCanonicalID = %s, want inner parent id %s (forked parent would not collapse)", got, innerParent)
|
||||
}
|
||||
if pfb.ExecutionStateRoot() != ids.Empty || pfb.PayloadRoot() != ids.Empty {
|
||||
t.Fatal("exec/payload roots must be Empty (not exposed) to keep the signed message byte-compatible")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCanonicalCommitter_CollapsesAliasesNotForks proves the core invariant: two
|
||||
// DISTINCT outer wrappers of the SAME inner block share a CanonicalID (they collapse
|
||||
// to one consensus object), while wrappers of DIFFERENT inner blocks keep DISTINCT
|
||||
// CanonicalIDs (a genuine execution fork is never merged — safety).
|
||||
func TestCanonicalCommitter_CollapsesAliasesNotForks(t *testing.T) {
|
||||
innerID := ids.GenerateTestID()
|
||||
|
||||
// Two wrappers of the SAME inner block (the re-wrapped-each-slot storm shape).
|
||||
aliasA := &postForkBlock{postForkCommonComponents: postForkCommonComponents{innerBlk: &blocktest.Block{IDV: innerID}}}
|
||||
aliasB := &postForkOption{postForkCommonComponents: postForkCommonComponents{innerBlk: &blocktest.Block{IDV: innerID}}}
|
||||
if aliasA.CanonicalID() != aliasB.CanonicalID() {
|
||||
t.Fatal("two wrappers of the SAME inner block must share a CanonicalID (alias collapse — the storm fix)")
|
||||
}
|
||||
|
||||
// A wrapper of a DIFFERENT inner block (a real execution fork).
|
||||
fork := &postForkBlock{postForkCommonComponents: postForkCommonComponents{innerBlk: &blocktest.Block{IDV: ids.GenerateTestID()}}}
|
||||
if aliasA.CanonicalID() == fork.CanonicalID() {
|
||||
t.Fatal("wrappers of DIFFERENT inner blocks must NOT collapse (genuine-fork safety)")
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ type GetProposedHeightReply struct {
|
||||
// "id" :1,
|
||||
// "method" :"proposervm.getProposedHeight",
|
||||
// "params" :{}
|
||||
// }' -H 'content-type:application/json;' http://127.0.0.1:9650/ext/bc/C/rpc
|
||||
// }' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/C/rpc
|
||||
func (s *Service) GetProposedHeight(r *http.Request, _ *GetProposedHeightArgs, reply *GetProposedHeightReply) error {
|
||||
ctx := r.Context()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ The ProposerVM API allows clients to fetch information about a chain's Consensus
|
||||
## Endpoint
|
||||
|
||||
```text
|
||||
/ext/bc/{blockchainID}/proposervm
|
||||
/v1/bc/{blockchainID}/proposervm
|
||||
```
|
||||
|
||||
## Format
|
||||
@@ -35,7 +35,7 @@ curl -X POST --data '{
|
||||
"method": "proposervm.getProposedHeight",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P/proposervm
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/v1/bc/P/proposervm
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -73,7 +73,7 @@ curl -X POST --data '{
|
||||
"method": "proposervm.getCurrentEpoch",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/ext/bc/P/proposervm
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9650/v1/bc/P/proposervm
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
|
||||
+36
-10
@@ -370,20 +370,41 @@ func (vm *VM) SetPreference(ctx context.Context, preferred ids.ID) error {
|
||||
return err
|
||||
}
|
||||
|
||||
vm.preferred = preferred
|
||||
|
||||
// Check for context cancellation before expensive operations
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// VALIDATE BEFORE ASSIGN (defect #1 — the luxd-1 rejoin wedge).
|
||||
//
|
||||
// The prior code assigned vm.preferred = preferred BEFORE fetching the block. A
|
||||
// preference for a block this proposervm does not hold therefore POISONED
|
||||
// vm.preferred permanently: BuildBlock reads vm.preferred (getBlock) and, on the
|
||||
// poisoned id, logs "failed to fetch preferred block" and errors on EVERY build
|
||||
// attempt forever. Quasar's cert-finality has no polling re-converge path, so the
|
||||
// node never recovers — it just spams the failure.
|
||||
//
|
||||
// A validator that fell behind is steered by the consensus engine
|
||||
// (consensus/engine/chain/integration.go — defect #2) to a DAG build-tip ABOVE its
|
||||
// own frontier, which is exactly such an unheld id. Ava never hits this because it
|
||||
// upholds the single-store invariant (it only ever calls SetPreference with a block
|
||||
// Consensus already Verified-into-VM); Lux's build-tip steering does not, so the
|
||||
// proposervm must be hardened to never adopt an id it cannot serve builds on.
|
||||
//
|
||||
// getBlock resolves BOTH the post-fork store and the inner (pre-fork) VM, so a miss
|
||||
// here means the id is held in NEITHER namespace. Fetch first; only adopt the
|
||||
// preference once the fetch + inner delegation both succeed.
|
||||
blk, err := vm.getBlock(ctx, preferred)
|
||||
if err != nil {
|
||||
vm.logger.Error("preferred block not found",
|
||||
log.Stringer("blkID", preferred),
|
||||
// KEEP the prior-good preference (guaranteed held: this function only assigns
|
||||
// vm.preferred after a successful fetch). BuildBlock stays live on the last held
|
||||
// tip — producing on lastAccepted while the catch-up path pulls the gap — and a
|
||||
// later SetPreference(held tip) advances us once the gap is closed.
|
||||
//
|
||||
// NOT fatal: an unheld build hint must never wedge or crash the VM. Returning an
|
||||
// error here is what the old code did AND it left vm.preferred poisoned, which is
|
||||
// strictly worse than a no-op. Fail secure: keep building.
|
||||
vm.logger.Warn("SetPreference: preferred block not held by this node; keeping prior preference (no wedge)",
|
||||
log.Stringer("requested", preferred),
|
||||
log.Stringer("keptPreferred", vm.preferred),
|
||||
log.Err(err),
|
||||
)
|
||||
return fmt.Errorf("preferred block %s not found: %w", preferred, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check for context cancellation before delegating to inner VM
|
||||
@@ -400,6 +421,11 @@ func (vm *VM) SetPreference(ctx context.Context, preferred ids.ID) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Adopt the preference ONLY after we confirmed we hold the block AND the inner VM
|
||||
// accepted it — an atomic all-or-nothing update, so a partial failure never leaves
|
||||
// vm.preferred pointing at a block BuildBlock cannot fetch.
|
||||
vm.preferred = preferred
|
||||
|
||||
vm.logger.Debug("set preference",
|
||||
log.Stringer("blkID", preferred),
|
||||
log.Stringer("innerBlkID", innerBlkID),
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2019-2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package proposervm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/database/memdb"
|
||||
"github.com/luxfi/database/versiondb"
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/log"
|
||||
vmchain "github.com/luxfi/vm/chain"
|
||||
|
||||
"github.com/luxfi/node/vms/proposervm/state"
|
||||
)
|
||||
|
||||
// stubInnerVM embeds the ChainVM interface (nil) and overrides ONLY GetBlock to report
|
||||
// "not found", so getBlock (getPostForkBlock miss → getPreForkBlock → ChainVM.GetBlock)
|
||||
// cleanly errors for an unheld id without a panic. No other inner method is invoked on
|
||||
// the miss path under test — the #1 fix returns before any inner delegation.
|
||||
type stubInnerVM struct{ vmchain.ChainVM }
|
||||
|
||||
func (stubInnerVM) GetBlock(context.Context, ids.ID) (vmchain.Block, error) {
|
||||
return nil, errors.New("stub inner VM: block not found")
|
||||
}
|
||||
|
||||
// newWedgeTestVM builds the minimal proposervm needed to exercise SetPreference's
|
||||
// getBlock path: an empty on-disk State (post-fork miss) + a stub inner VM (pre-fork
|
||||
// miss) ⇒ getBlock cleanly errors for any id not pre-seeded, exactly as a validator
|
||||
// that fell behind the frontier sees a build-tip above its own store.
|
||||
func newWedgeTestVM(priorPreferred ids.ID) *VM {
|
||||
vm := &VM{
|
||||
preferred: priorPreferred,
|
||||
verifiedBlocks: map[ids.ID]PostForkBlock{},
|
||||
logger: log.Noop(),
|
||||
}
|
||||
vm.State = state.New(versiondb.New(memdb.New()))
|
||||
vm.ChainVM = stubInnerVM{}
|
||||
return vm
|
||||
}
|
||||
|
||||
// TestSetPreference_UnheldBlock_DoesNotPoison is the deterministic unit repro of the
|
||||
// luxd-1 REJOIN WEDGE (defect #1).
|
||||
//
|
||||
// A validator that fell behind is steered by the consensus engine
|
||||
// (consensus/engine/chain/integration.go — defect #2) to a build tip ABOVE its own
|
||||
// frontier: an id this proposervm does not hold. The OLD SetPreference assigned
|
||||
// vm.preferred = <unheld id> BEFORE validating, so on the (inevitable) getBlock miss it
|
||||
// returned a hard error but LEFT vm.preferred poisoned. BuildBlock then read the poisoned
|
||||
// id (getBlock) and logged "unexpected build block failure / failed to fetch preferred
|
||||
// block" on EVERY build attempt forever — Quasar cert-finality has no re-converge poll, so
|
||||
// the node never recovered.
|
||||
//
|
||||
// The fix validates BEFORE assigning: on an unheld id, vm.preferred is UNCHANGED (kept
|
||||
// prior-good) and SetPreference is a non-fatal no-op, so BuildBlock stays live on the last
|
||||
// held tip while the catch-up path pulls the gap.
|
||||
func TestSetPreference_UnheldBlock_DoesNotPoison(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
priorGood := ids.GenerateTestID()
|
||||
vm := newWedgeTestVM(priorGood)
|
||||
|
||||
unheld := ids.GenerateTestID()
|
||||
err := vm.SetPreference(context.Background(), unheld)
|
||||
|
||||
// An unheld build hint must be a non-fatal no-op — NEVER a hard error that also leaves
|
||||
// the id poisoned (the old behavior that wedged BuildBlock).
|
||||
require.NoError(err, "unheld SetPreference must not be fatal")
|
||||
|
||||
// THE anti-wedge invariant: vm.preferred is NOT poisoned to the unheld id.
|
||||
require.Equal(priorGood, vm.preferred,
|
||||
"vm.preferred must stay the prior-good tip; poisoning it wedges BuildBlock forever")
|
||||
require.NotEqual(unheld, vm.preferred)
|
||||
}
|
||||
|
||||
// TestSetPreference_UnheldBlock_KeepsBuildLive asserts the DOWNSTREAM property the fix
|
||||
// exists to protect: after an unheld SetPreference, the preference the VM will build on is
|
||||
// still the prior-good (held) tip, not the poisoned id — i.e. BuildBlock would fetch a
|
||||
// block it holds, not spam "failed to fetch preferred block". We assert on vm.preferred
|
||||
// (what BuildBlock reads) rather than driving a full inner-VM build, keeping the repro
|
||||
// hermetic.
|
||||
func TestSetPreference_UnheldBlock_KeepsBuildLive(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
priorGood := ids.GenerateTestID()
|
||||
vm := newWedgeTestVM(priorGood)
|
||||
|
||||
// Repeated unheld steers (the engine re-steers on every gossip receipt) must never
|
||||
// accumulate poison: the build preference stays pinned to the held tip.
|
||||
for i := 0; i < 5; i++ {
|
||||
require.NoError(vm.SetPreference(context.Background(), ids.GenerateTestID()))
|
||||
require.Equal(priorGood, vm.preferred, "build tip must remain the held prior-good after steer %d", i)
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
// Package thresholdvm re-exports the canonical Threshold (FHE / MPC)
|
||||
// VM from github.com/luxfi/chains/thresholdvm so existing callers
|
||||
// that imported github.com/luxfi/node/vms/thresholdvm pre-extraction
|
||||
// keep working without source changes.
|
||||
//
|
||||
// New code should import the canonical path:
|
||||
// "github.com/luxfi/chains/thresholdvm"
|
||||
//
|
||||
// This file is a thin alias wrapper kept for backward compatibility.
|
||||
package thresholdvm
|
||||
|
||||
import "github.com/luxfi/chains/thresholdvm"
|
||||
|
||||
type (
|
||||
Block = thresholdvm.Block
|
||||
BlockError = thresholdvm.BlockError
|
||||
Client = thresholdvm.Client
|
||||
Operation = thresholdvm.Operation
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidOperation = thresholdvm.ErrInvalidOperation
|
||||
NewClient = thresholdvm.NewClient
|
||||
)
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
// nocgo re-export of the chains/thresholdvm GPU bridge. Under !cgo the
|
||||
// upstream GPUBackend methods all return ErrGPUNotAvailable and
|
||||
// Backend() returns nil; this layer transparently passes that through
|
||||
// so callers see identical behaviour regardless of build mode.
|
||||
//
|
||||
// One and only one nocgo stub for the entire process: it lives in
|
||||
// chains/thresholdvm. This package just re-exports the types and the
|
||||
// sentinel error.
|
||||
|
||||
package thresholdvm
|
||||
|
||||
import "github.com/luxfi/chains/thresholdvm"
|
||||
|
||||
type (
|
||||
GPUBackend = thresholdvm.GPUBackend
|
||||
GPUBackendKind = thresholdvm.GPUBackendKind
|
||||
|
||||
GPUCeremony = thresholdvm.GPUCeremony
|
||||
GPUKeyShare = thresholdvm.GPUKeyShare
|
||||
GPUContribution = thresholdvm.GPUContribution
|
||||
GPUMPCVMState = thresholdvm.GPUMPCVMState
|
||||
GPUMPCVMRoundDescriptor = thresholdvm.GPUMPCVMRoundDescriptor
|
||||
GPUCeremonyOp = thresholdvm.GPUCeremonyOp
|
||||
GPUContributionOp = thresholdvm.GPUContributionOp
|
||||
GPUMPCVMTransitionResult = thresholdvm.GPUMPCVMTransitionResult
|
||||
)
|
||||
|
||||
const (
|
||||
GPUBackendNone = thresholdvm.GPUBackendNone
|
||||
GPUBackendCUDA = thresholdvm.GPUBackendCUDA
|
||||
GPUBackendHIP = thresholdvm.GPUBackendHIP
|
||||
GPUBackendMetal = thresholdvm.GPUBackendMetal
|
||||
GPUBackendVulkan = thresholdvm.GPUBackendVulkan
|
||||
GPUBackendWebGPU = thresholdvm.GPUBackendWebGPU
|
||||
)
|
||||
|
||||
var ErrGPUNotAvailable = thresholdvm.ErrGPUNotAvailable
|
||||
|
||||
// GPUBackendInstance returns nil under !cgo — the upstream Backend()
|
||||
// returns nil because no dlopen ever happens. Callers branch on the
|
||||
// IsAvailable() check (or `g == nil`) and route to the CPU reference.
|
||||
func GPUBackendInstance() *GPUBackend {
|
||||
return thresholdvm.Backend()
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
// charging 1,000x too little (quantumvm). The root cause was that fee
|
||||
// policy lived as ad-hoc fields on each VM's Config — there was no shared
|
||||
// surface to enforce a non-zero floor, and no sentinel for chains that
|
||||
// legitimately accept no user txs (thresholdvm, oraclevm, relayvm —
|
||||
// legitimately accept no user txs (mpcvm, oraclevm, relayvm —
|
||||
// committee-driven, no user mempool).
|
||||
//
|
||||
// This package provides exactly one way to declare a fee policy:
|
||||
@@ -94,7 +94,7 @@ type Policy interface {
|
||||
|
||||
// FlatPolicy charges a fixed fee per user tx. The canonical
|
||||
// implementation for VMs without dynamic gas pricing — dexvm, bridgevm,
|
||||
// keyvm, zkvm, aivm, quantumvm all use this.
|
||||
// keyvm, zkvm, aivm use this (quantumvm is NoUserTxPolicy per LP-0130 §6).
|
||||
type FlatPolicy struct {
|
||||
// Fee is the per-tx burn amount, in nLUX. MUST be > 0.
|
||||
Fee uint64
|
||||
@@ -122,7 +122,7 @@ func (p FlatPolicy) ValidateFee(paid uint64, asset ids.ID) error {
|
||||
}
|
||||
|
||||
// NoUserTxPolicy is the sentinel policy for chains that accept no
|
||||
// user-submitted txs — committee-driven only (thresholdvm, oraclevm,
|
||||
// user-submitted txs — committee-driven only (mpcvm, oraclevm,
|
||||
// relayvm). Distinguishing this from "policy not set" is what makes
|
||||
// Manager's boot-time Validate able to refuse zero-fee user-facing
|
||||
// chains without false-positive-ing the committee chains.
|
||||
|
||||
+39
-39
@@ -20,9 +20,9 @@ This API uses the `json 2.0` RPC format. For more information on making JSON RPC
|
||||
|
||||
## Endpoints
|
||||
|
||||
`/ext/bc/X` to interact with the X-Chain.
|
||||
`/v1/bc/X` to interact with the X-Chain.
|
||||
|
||||
`/ext/bc/blockchainID` to interact with other XVM instances, where `blockchainID` is the ID of a
|
||||
`/v1/bc/blockchainID` to interact with other XVM instances, where `blockchainID` is the ID of a
|
||||
blockchain running the XVM.
|
||||
|
||||
## Methods
|
||||
@@ -36,7 +36,7 @@ of that state.
|
||||
|
||||
This call is made to the XVM’s static API endpoint:
|
||||
|
||||
`/ext/vm/xvm`
|
||||
`/v1/vm/xvm`
|
||||
|
||||
Note: addresses should not include a chain prefix (that is `X-`) in calls to the static API endpoint
|
||||
because these prefixes refer to a specific chain.
|
||||
@@ -169,7 +169,7 @@ curl -X POST --data '{
|
||||
},
|
||||
"encoding": "hex"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/vm/xvm
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/vm/xvm
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -219,7 +219,7 @@ curl -X POST --data '{
|
||||
"password": "myPassword"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -316,7 +316,7 @@ curl -X POST --data '{
|
||||
"username":"myUsername",
|
||||
"password":"myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -404,7 +404,7 @@ curl -X POST --data '{
|
||||
"username":"myUsername",
|
||||
"password":"myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -505,7 +505,7 @@ curl -X POST --data '{
|
||||
"username":"myUsername",
|
||||
"password":"myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -582,7 +582,7 @@ curl -X POST --data '{
|
||||
"username":"myUsername",
|
||||
"password":"myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -642,7 +642,7 @@ curl -X POST --data '{
|
||||
"password":"myPassword",
|
||||
"address":"X-lux18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -713,7 +713,7 @@ curl -X POST --data '{
|
||||
"assetID":"LUX",
|
||||
"pageSize":20
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -759,7 +759,7 @@ curl -X POST --data '{
|
||||
"params" :{
|
||||
"address":"X-lux1c79e0dd0susp7dc8udq34jgk2yvve7hapvdyht"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -829,7 +829,7 @@ curl -X POST --data '{
|
||||
"params" :{
|
||||
"assetID" :"FvwEAhmxKfeiG8SnEvq42hc6whRyY3EFYAvebMqDNDGCgxN5Z"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -878,7 +878,7 @@ curl -X POST --data '{
|
||||
"address":"X-lux18jma8ppw3nhx5r4ap8clazz0dps7rv5ukulre5",
|
||||
"assetID": "2pYGetDWyKdHxpFxh2LHeoLNCH6H5vxxCxHQtFnnFaYxLsqtHC"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -938,7 +938,7 @@ curl -X POST --data '{
|
||||
"encoding": "hex"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -994,7 +994,7 @@ curl -X POST --data '{
|
||||
"encoding": "hex"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1031,7 +1031,7 @@ curl -X POST --data '{
|
||||
"method": "xvm.getHeight",
|
||||
"params": {},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1074,7 +1074,7 @@ curl -X POST --data '{
|
||||
"txID":"2oJCbb8pfdxEHAf9A8CdN4Afj9VSR3xzyzNkf8tDv7aM1sfNFL",
|
||||
"encoding": "json"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1208,7 +1208,7 @@ curl -X POST --data '{
|
||||
"params" :{
|
||||
"txID":"2QouvFWUbjuySRxeX5xMbNCuAaKWfbk5FeEa2JmoF85RKLk2dD"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1279,7 +1279,7 @@ curl -X POST --data '{
|
||||
"limit":5,
|
||||
"encoding": "hex"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
This gives response:
|
||||
@@ -1323,7 +1323,7 @@ curl -X POST --data '{
|
||||
},
|
||||
"encoding": "hex"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
This gives response:
|
||||
@@ -1367,7 +1367,7 @@ curl -X POST --data '{
|
||||
"sourceChain": "P",
|
||||
"encoding": "hex"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
This gives response:
|
||||
@@ -1435,7 +1435,7 @@ curl -X POST --data '{
|
||||
"username":"myUsername",
|
||||
"password":"myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1493,7 +1493,7 @@ curl -X POST --data '{
|
||||
"password":"myPassword",
|
||||
"privateKey":"PrivateKey-2w4XiXxPfQK4TypYqnohRL8DRNTz9cGiGmwQ1zmgEqD9c9KWLq"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1536,7 +1536,7 @@ curl -X POST --data '{
|
||||
"tx":"0x00000009de31b4d8b22991d51aa6aa1fc733f23a851a8c9400000000000186a0000000005f041280000000005f9ca900000030390000000000000001fceda8f90fcb5d30614b99d79fc4baa29307762668f16eb0259a57c2d3b78c875c86ec2045792d4df2d926c40f829196e0bb97ee697af71f5b0a966dabff749634c8b729855e937715b0e44303fd1014daedc752006011b730",
|
||||
"encoding": "hex"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1585,7 +1585,7 @@ curl -X POST --data '{
|
||||
"password":"myPassword"
|
||||
},
|
||||
"id": 1
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1660,7 +1660,7 @@ curl -X POST --data '{
|
||||
"username":"myUsername",
|
||||
"password":"myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1741,7 +1741,7 @@ curl -X POST --data '{
|
||||
"username":"myUsername",
|
||||
"password":"myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1814,7 +1814,7 @@ curl -X POST --data '{
|
||||
"username" : "userThatControlsAtLeast10000OfThisAsset",
|
||||
"password" : "myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1897,7 +1897,7 @@ curl -X POST --data '{
|
||||
"username" : "username",
|
||||
"password" : "myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1967,7 +1967,7 @@ curl -X POST --data '{
|
||||
"username" : "myUsername",
|
||||
"password" : "myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -1991,7 +1991,7 @@ the format of the signed transaction. Can only be `hex` when a value is provided
|
||||
|
||||
This call is made to the wallet API endpoint:
|
||||
|
||||
`/ext/bc/X/wallet`
|
||||
`/v1/bc/X/wallet`
|
||||
|
||||
:::caution
|
||||
|
||||
@@ -2021,7 +2021,7 @@ curl -X POST --data '{
|
||||
"tx":"0x00000009de31b4d8b22991d51aa6aa1fc733f23a851a8c9400000000000186a0000000005f041280000000005f9ca900000030390000000000000001fceda8f90fcb5d30614b99d79fc4baa29307762668f16eb0259a57c2d3b78c875c86ec2045792d4df2d926c40f829196e0bb97ee697af71f5b0a966dabff749634c8b729855e937715b0e44303fd1014daedc752006011b730",
|
||||
"encoding": "hex"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X/wallet
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X/wallet
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -2047,7 +2047,7 @@ can use the modified UTXO set.
|
||||
|
||||
This call is made to the wallet API endpoint:
|
||||
|
||||
`/ext/bc/X/wallet`
|
||||
`/v1/bc/X/wallet`
|
||||
|
||||
:::caution
|
||||
|
||||
@@ -2098,7 +2098,7 @@ curl -X POST --data '{
|
||||
"username" : "userThatControlsAtLeast10000OfThisAsset",
|
||||
"password" : "myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X/wallet
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X/wallet
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -2125,7 +2125,7 @@ addresses and assume the TX will be accepted so that future calls can use the mo
|
||||
|
||||
This call is made to the wallet API endpoint:
|
||||
|
||||
`/ext/bc/X/wallet`
|
||||
`/v1/bc/X/wallet`
|
||||
|
||||
:::caution
|
||||
|
||||
@@ -2185,7 +2185,7 @@ curl -X POST --data '{
|
||||
"username" : "username",
|
||||
"password" : "myPassword"
|
||||
}
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/ext/bc/X/wallet
|
||||
}' -H 'content-type:application/json;' 127.0.0.1:9630/v1/bc/X/wallet
|
||||
```
|
||||
|
||||
**Example Response:**
|
||||
@@ -2207,7 +2207,7 @@ Listen for transactions on a specified address.
|
||||
|
||||
This call is made to the events API endpoint:
|
||||
|
||||
`/ext/bc/X/events`
|
||||
`/v1/bc/X/events`
|
||||
|
||||
:::caution
|
||||
|
||||
@@ -2240,7 +2240,7 @@ func main() {
|
||||
}
|
||||
|
||||
httpHeader := http.Header{}
|
||||
conn, _, err := dialer.Dial("ws://localhost:9630/ext/bc/X/events", httpHeader)
|
||||
conn, _, err := dialer.Dial("ws://localhost:9630/v1/bc/X/events", httpHeader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user