chain routes: /v1/bc -> /v1/chain

luxd serves chain routes under /v1/chain; the /v1/bc prefix it replaced is
gone, so anything composing it reaches nothing. Lines that record an
observation - a probe, a status code, a past release - keep the prefix they
were written against, as do URLs on hosts that are not ours.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-30 20:55:26 -07:00
co-authored by Hanzo Dev
parent 07d6f6a9bb
commit 0abb4f609b
11 changed files with 40 additions and 40 deletions
+1 -1
View File
@@ -2223,7 +2223,7 @@ a `TxImport`, so there is no retroactive effect.
### D-Chain HTTP surface — and how to read a 404
`CreateHandlers` mounts `/v1/bc/D/dex/<method>` (`ingest.go`). The method names
`CreateHandlers` mounts `/v1/chain/D/dex/<method>` (`ingest.go`). The method names
were **`clob_*` up to dex v1.5.x** and `dex_*` after (commit `28970d8`). So on a
fleet running an older plugin:
+2 -2
View File
@@ -57,13 +57,13 @@ const dexBalanceMethod = "dex_balance"
// httpChainPrefix is the chain route prefix the optional -http surface mounts
// under. It mirrors the EXACT external path luxd gives the in-process plugin
// (/v1/bc/<chainID-or-alias>), using the canonical "D" alias the exchange-api
// (/v1/chain/<chainID-or-alias>), using the canonical "D" alias the exchange-api
// and maker dial (DEX_DCHAIN_URL / DEX_HTTP_URL = http://<host>/v1/dex). So a
// standalone single-operator venue is a byte-identical drop-in for the in-luxd
// D-Chain read+write surface — no luxd required to serve the markets pipeline.
// httpChainPrefix is what the venue mounts its chain surface under. The keys
// httpHandlers returns already carry the chain's own namespace ("/dex/<method>"
// — the same segment a node serves under /v1/bc/<D>/), so the prefix supplies
// — the same segment a node serves under /v1/chain/<D>/), so the prefix supplies
// only the version. Spelling "dex" here too produced /v1/dex/dex/<method>: a
// path no client written against a node would try, and not the one this flag
// promises.
+3 -3
View File
@@ -16,7 +16,7 @@ import (
// the VM runs as a luxd plugin (cmd/dchain via github.com/luxfi/vm/rpc.Serve),
// luxd asks the VM for HTTP handlers (CreateHandlers); the plugin transport stands
// up a local http.Server per handler inside the plugin process and the node
// reverse-proxies to it. luxd mounts each handler at /v1/bc/<chainID-or-alias>
// reverse-proxies to it. luxd mounts each handler at /v1/chain/<chainID-or-alias>
// + <endpoint-key> as an EXACT gorilla/mux route (node/server/http/router.go:
// `r.router.Handle(base+endpoint, handler)` — exact path, no subtree). So we
// return ONE handler per method, each keyed by its full sub-path:
@@ -38,7 +38,7 @@ import (
// NEVER mutates the book synchronously: the chain is the matcher.
// ingestNamespace is the path segment grouping the DEX methods under the chain
// route. The full external path of a method m is /v1/bc/<chainID>/dex/<m>.
// route. The full external path of a method m is /v1/chain/<chainID>/dex/<m>.
const ingestNamespace = "dex"
// maxIngestBody bounds a request body. The largest frozen frame is the 96-byte
@@ -50,7 +50,7 @@ const maxIngestBody = 4 << 10
// full endpoint sub-path ("/dex/<method>"). It folds the SAME vm.dexMethods table
// the ZAP socket transport uses — one method->handler table, two transports — so a
// method added to dexMethods is reachable over HTTP automatically. luxd registers
// each key as an exact route under /v1/bc/<chainID> (see file doc).
// each key as an exact route under /v1/chain/<chainID> (see file doc).
//
// It ALSO folds the read surface (vm.readMethods, read.go): the dex_get_* query
// endpoints that return committed state as JSON. Reads are mounted on the same
+2 -2
View File
@@ -22,7 +22,7 @@ import (
// ingest_test.go proves the IN-LUXD ingestion seam (ingest.go / CreateHandlers):
// an order submitted over the VM's HTTP handler — the EXACT handler luxd mounts at
// /v1/bc/<D>/dex/<method> — lands in the mempool and is matched into a block by
// /v1/chain/<D>/dex/<method> — lands in the mempool and is matched into a block by
// the VM's own consensus lifecycle (BuildBlock -> Verify -> Accept), with the fill
// committed as D-Chain state (a trade:<height><seq> row). The matcher is the
// chain; there is no external venue and no relay.
@@ -53,7 +53,7 @@ func startIngestServer(t *testing.T) (*VM, string, func()) {
// Model the node EXACTLY: CreateHandlers returns one handler per method keyed
// by its full sub-path ("/dex/<method>"); luxd registers each as an exact route
// under /v1/bc/<chainID>. We register each key as an exact route here, so the
// under /v1/chain/<chainID>. We register each key as an exact route here, so the
// request paths and matching semantics match production (exact path, not a
// subtree — node/server/http/router.go uses gorilla Handle, not PathPrefix).
handlers, err := vm.CreateHandlers(context.Background())
+2 -2
View File
@@ -47,7 +47,7 @@ import (
// read method names. They live in the dex_ namespace beside the write methods
// but are pure reads. The dex_ prefix means the node mounts them under the same
// /v1/bc/<D>/dex/ route group as the writes (one chain surface).
// /v1/chain/<D>/dex/ route group as the writes (one chain surface).
const (
MethodGetTrades = "dex_get_trades"
MethodGetOrders = "dex_get_orders"
@@ -71,7 +71,7 @@ type readMethod struct {
}
// readMethods is the authoritative read method->handler table. A method added
// here is mounted under /v1/bc/<D>/dex/<method> automatically (httpHandlers).
// here is mounted under /v1/chain/<D>/dex/<method> automatically (httpHandlers).
func (vm *VM) readMethods() []readMethod {
return []readMethod{
{MethodGetTrades, vm.handleGetTrades},
+1 -1
View File
@@ -25,7 +25,7 @@ import (
// read_test.go proves the D-Chain READ surface (read.go / dex_get_*):
//
// 1. The read endpoints are reachable over the EXACT in-luxd HTTP transport
// (CreateHandlers, mounted at /v1/bc/<D>/dex/<method>) — the same routes a
// (CreateHandlers, mounted at /v1/chain/<D>/dex/<method>) — the same routes a
// deployed validator exposes.
// 2. After a crossing order is matched into a committed block (via the write
// path + the auto-sealer playing consensus), dex_get_trades returns the
+1 -1
View File
@@ -633,7 +633,7 @@ func (vm *VM) Version(ctx context.Context) (string, error) { return Version, nil
func (vm *VM) NewHTTPHandler(ctx context.Context) (http.Handler, error) { return nil, nil }
// CreateHandlers returns the VM's named HTTP handlers for luxd to mount under
// /v1/bc/<DCHAIN_ID>/ (and the "D" alias). It returns one handler per DEX method
// /v1/chain/<DCHAIN_ID>/ (and the "D" alias). It returns one handler per DEX method
// keyed by its full sub-path ("/dex/<method>"), so an order POSTed to
// /v1/dex/dex/dex_submit reaches the matcher through the node's own router —
// the in-luxd ingestion seam (ingest.go). This is how an order enters the native
+1 -1
View File
@@ -19,7 +19,7 @@ import (
//
// ZAP socket (this file) : RegisterDEX on rpc.Listen — the HFT native path,
// binary wire, co-located on the pod. CANONICAL.
// HTTP router (ingest.go) : the same dexMethods over the node's /v1/bc route.
// HTTP router (ingest.go) : the same dexMethods over the node's /v1/chain route.
// COMPAT (web / exchange-api / debug).
//
// WHY BOTH, AND WHY THIS IS NOT DUPLICATION: transport is orthogonal to consensus.
+9 -9
View File
@@ -1062,7 +1062,7 @@ func TestOracleSetVerifier(t *testing.T) {
}
// Create and attach verifier
verifier := NewQChainVerifier("http://localhost:9650/v1/bc/Q", "ws://localhost:9650/v1/bc/Q/ws")
verifier := NewQChainVerifier("http://localhost:9650/v1/chain/Q", "ws://localhost:9650/v1/chain/Q/ws")
oracle.SetVerifier(verifier)
// Verify it's attached
@@ -1116,7 +1116,7 @@ func TestOracleVerifiedPriceWithVerifier(t *testing.T) {
oracle.AddSource("x-chain", src)
// Add verifier
verifier := NewQChainVerifier("http://localhost:9650/v1/bc/Q", "ws://localhost:9650/v1/bc/Q/ws")
verifier := NewQChainVerifier("http://localhost:9650/v1/chain/Q", "ws://localhost:9650/v1/chain/Q/ws")
verifier.Start()
oracle.SetVerifier(verifier)
@@ -1159,7 +1159,7 @@ func TestOracleStopWithVerifier(t *testing.T) {
oracle := NewOracle()
// Add verifier
verifier := NewQChainVerifier("http://localhost:9650/v1/bc/Q", "ws://localhost:9650/v1/bc/Q/ws")
verifier := NewQChainVerifier("http://localhost:9650/v1/chain/Q", "ws://localhost:9650/v1/chain/Q/ws")
verifier.Start()
oracle.SetVerifier(verifier)
@@ -1228,7 +1228,7 @@ func TestVerifiedDataFields(t *testing.T) {
}
func TestQChainVerifierBasic(t *testing.T) {
v := NewQChainVerifier("http://localhost:9650/v1/bc/Q", "ws://localhost:9650/v1/bc/Q/ws")
v := NewQChainVerifier("http://localhost:9650/v1/chain/Q", "ws://localhost:9650/v1/chain/Q/ws")
// Start verifier
if err := v.Start(); err != nil {
@@ -1258,7 +1258,7 @@ func TestQChainVerifierBasic(t *testing.T) {
}
func TestQChainVerifierFinality(t *testing.T) {
v := NewQChainVerifier("http://localhost:9650/v1/bc/Q", "ws://localhost:9650/v1/bc/Q/ws")
v := NewQChainVerifier("http://localhost:9650/v1/chain/Q", "ws://localhost:9650/v1/chain/Q/ws")
v.Start()
defer v.Close()
@@ -1284,7 +1284,7 @@ func TestQChainVerifierFinality(t *testing.T) {
}
func TestQChainVerifierCrossChain(t *testing.T) {
v := NewQChainVerifier("http://localhost:9650/v1/bc/Q", "ws://localhost:9650/v1/bc/Q/ws")
v := NewQChainVerifier("http://localhost:9650/v1/chain/Q", "ws://localhost:9650/v1/chain/Q/ws")
v.Start()
defer v.Close()
@@ -1304,7 +1304,7 @@ func TestQChainVerifierCrossChain(t *testing.T) {
}
func TestQChainVerifierSetQuorum(t *testing.T) {
v := NewQChainVerifier("http://localhost:9650/v1/bc/Q", "ws://localhost:9650/v1/bc/Q/ws")
v := NewQChainVerifier("http://localhost:9650/v1/chain/Q", "ws://localhost:9650/v1/chain/Q/ws")
// Default quorum
v.SetQuorum(5)
@@ -1314,7 +1314,7 @@ func TestQChainVerifierSetQuorum(t *testing.T) {
}
func TestQChainVerifierFinalityLatency(t *testing.T) {
v := NewQChainVerifier("http://localhost:9650/v1/bc/Q", "ws://localhost:9650/v1/bc/Q/ws")
v := NewQChainVerifier("http://localhost:9650/v1/chain/Q", "ws://localhost:9650/v1/chain/Q/ws")
v.Start()
defer v.Close()
@@ -1329,5 +1329,5 @@ func TestQChainVerifierFinalityLatency(t *testing.T) {
func TestQChainBackwardsCompatibility(t *testing.T) {
// Test that QChainSource alias works
var _ *QChainSource = NewQChainSource("http://localhost:9650/v1/bc/Q", "ws://localhost:9650/v1/bc/Q/ws")
var _ *QChainSource = NewQChainSource("http://localhost:9650/v1/chain/Q", "ws://localhost:9650/v1/chain/Q/ws")
}
+8 -8
View File
@@ -158,7 +158,7 @@ if curl -s -X POST --data '{
"address":"X-lux18jma8ppw3nhx5r4ap8clazz0dps7rv5u6wdp",
"assetID":"LUX"
}
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/X 2>/dev/null | grep -q "balance"; then
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/chain/X 2>/dev/null | grep -q "balance"; then
echo -e "${GREEN}✅ Working${NC}"
else
echo -e "${RED}❌ Failed${NC}"
@@ -171,7 +171,7 @@ if curl -s -X POST --data '{
"method":"eth_chainId",
"params":[],
"id":1
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/C/rpc 2>/dev/null | grep -q "result"; then
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/chain/C/rpc 2>/dev/null | grep -q "result"; then
echo -e "${GREEN}✅ Working${NC}"
else
echo -e "${RED}❌ Failed${NC}"
@@ -184,7 +184,7 @@ BLOCK_HEIGHT=$(curl -s -X POST --data '{
"method":"eth_blockNumber",
"params":[],
"id":1
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/C/rpc 2>/dev/null | grep -o '"result":"[^"]*"' | sed 's/"result":"//;s/"//')
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/chain/C/rpc 2>/dev/null | grep -o '"result":"[^"]*"' | sed 's/"result":"//;s/"//')
if [ ! -z "$BLOCK_HEIGHT" ]; then
echo -e "${GREEN}✅ Current block: $BLOCK_HEIGHT${NC}"
@@ -199,12 +199,12 @@ echo ""
echo "RPC Endpoints:"
echo " Info API: http://localhost:9650/v1/info"
echo " Health: http://localhost:9650/v1/health"
echo " X-Chain: http://localhost:9650/v1/bc/X"
echo " C-Chain: http://localhost:9650/v1/bc/C/rpc"
echo " P-Chain: http://localhost:9650/v1/bc/P"
echo " X-Chain: http://localhost:9650/v1/chain/X"
echo " C-Chain: http://localhost:9650/v1/chain/C/rpc"
echo " P-Chain: http://localhost:9650/v1/chain/P"
echo ""
echo "WebSocket:"
echo " C-Chain WS: ws://localhost:9650/v1/bc/C/ws"
echo " C-Chain WS: ws://localhost:9650/v1/chain/C/ws"
echo ""
echo "Node PID: $NODE_PID"
echo "To stop: kill $NODE_PID"
@@ -220,7 +220,7 @@ while true; do
"method":"eth_blockNumber",
"params":[],
"id":1
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/C/rpc 2>/dev/null | grep -o '"result":"[^"]*"' | sed 's/"result":"//;s/"//')
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/chain/C/rpc 2>/dev/null | grep -o '"result":"[^"]*"' | sed 's/"result":"//;s/"//')
if [ ! -z "$BLOCK" ]; then
BLOCK_DEC=$((16#${BLOCK#0x}))
+10 -10
View File
@@ -136,17 +136,17 @@ test_endpoint "2. Keystore API" \
# Test X-Chain (AVM - Asset Virtual Machine)
test_endpoint "3. X-Chain API" \
"http://127.0.0.1:9650/v1/bc/X" \
"http://127.0.0.1:9650/v1/chain/X" \
'{"jsonrpc":"2.0","id":1,"method":"avm.getAssetDescription","params":{"assetID":"LUX"}}'
# Test P-Chain (Platform Chain)
test_endpoint "4. P-Chain API" \
"http://127.0.0.1:9650/v1/bc/P" \
"http://127.0.0.1:9650/v1/chain/P" \
'{"jsonrpc":"2.0","id":1,"method":"platform.getHeight"}'
# Test C-Chain (Contract Chain - EVM)
test_endpoint "5. C-Chain API" \
"http://127.0.0.1:9650/v1/bc/C/rpc" \
"http://127.0.0.1:9650/v1/chain/C/rpc" \
'{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
# Get blockchain info
@@ -172,7 +172,7 @@ curl -s -X POST --data '{
"jsonrpc":"2.0",
"id":1,
"method":"avm.getHeight"
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/X 2>/dev/null | grep -o '"height":[0-9]*' || echo "Height: 0"
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/chain/X 2>/dev/null | grep -o '"height":[0-9]*' || echo "Height: 0"
# Create test wallet
echo ""
@@ -199,7 +199,7 @@ X_ADDR=$(curl -s -X POST --data '{
"username":"dexuser",
"password":"dexpass123"
}
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/X 2>/dev/null | grep -o '"address":"[^"]*"' | cut -d'"' -f4)
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/chain/X 2>/dev/null | grep -o '"address":"[^"]*"' | cut -d'"' -f4)
if [ ! -z "$X_ADDR" ]; then
echo -e "${GREEN}✅ X-Chain Address: $X_ADDR${NC}"
@@ -212,9 +212,9 @@ echo ""
echo "RPC Endpoints:"
echo " Info API: http://localhost:9650/v1/info"
echo " Health: http://localhost:9650/v1/health"
echo " X-Chain: http://localhost:9650/v1/bc/X"
echo " P-Chain: http://localhost:9650/v1/bc/P"
echo " C-Chain: http://localhost:9650/v1/bc/C/rpc"
echo " X-Chain: http://localhost:9650/v1/chain/X"
echo " P-Chain: http://localhost:9650/v1/chain/P"
echo " C-Chain: http://localhost:9650/v1/chain/C/rpc"
echo ""
echo "Node PID: $NODE_PID"
echo "Logs: tail -f $DATA_DIR/node.log"
@@ -231,7 +231,7 @@ while true; do
"jsonrpc":"2.0",
"id":1,
"method":"avm.getHeight"
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/X 2>/dev/null | grep -o '"height":[0-9]*' | cut -d':' -f2)
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/chain/X 2>/dev/null | grep -o '"height":[0-9]*' | cut -d':' -f2)
if [ ! -z "$HEIGHT" ]; then
echo "X-Chain Height: $HEIGHT at $(date '+%H:%M:%S')"
@@ -242,7 +242,7 @@ while true; do
"jsonrpc":"2.0",
"id":1,
"method":"platform.getHeight"
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/bc/P 2>/dev/null | grep -o '"height":[0-9]*' | cut -d':' -f2)
}' -H 'content-type:application/json;' http://127.0.0.1:9650/v1/chain/P 2>/dev/null | grep -o '"height":[0-9]*' | cut -d':' -f2)
if [ ! -z "$P_HEIGHT" ]; then
echo "P-Chain Height: $P_HEIGHT"