chore: migrate luxd HTTP routes /ext -> /v1

Drop the Avalanche-heritage /ext prefix; /v1 is the single canonical route
surface (one way, no backward compat). The node's baseURL is the source of
truth; clients, SDKs, CLI, indexer, maker, genesis, netrunner, and the
k8s/compose/gateway/explorer configs are updated to match.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
This commit is contained in:
zeekay
2026-07-01 11:40:25 -07:00
co-authored by Hanzo Dev
parent fdc2e24299
commit 13cf8b0d21
8 changed files with 29 additions and 29 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ func TestTransferPChain(t *testing.T) {
fmt.Printf("Target (CLI): %s ShortID: %x\n", dstAddr, targetShortID[:])
// Get UTXOs for source
pURL := "https://api.lux-dev.network/ext/bc/P"
pURL := "https://api.lux-dev.network/v1/bc/P"
utxoResult, err := rpc(pURL, "platform.getUTXOs", map[string]interface{}{
"addresses": []string{srcAddr},
"limit": 10,
+2 -2
View File
@@ -657,12 +657,12 @@ type platformBlockchain struct {
}
// platformGetBlockchains issues a single JSON-RPC POST to
// /ext/bc/P with method="platform.getBlockchains" and parses the
// /v1/bc/P with method="platform.getBlockchains" and parses the
// response. We hand-roll this RPC because info.getBlockchainID(alias)
// would re-introduce the X-Chain coupling we exist to avoid.
func platformGetBlockchains(ctx context.Context, uri string) ([]platformBlockchain, error) {
body := strings.NewReader(`{"jsonrpc":"2.0","id":1,"method":"platform.getBlockchains","params":{}}`)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, strings.TrimRight(uri, "/")+"/ext/bc/P", body)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, strings.TrimRight(uri, "/")+"/v1/bc/P", body)
if err != nil {
return nil, err
}
+1 -1
View File
@@ -90,7 +90,7 @@ func getBalance(rpc, paddr string) (uint64, string, error) {
},
}
body, _ := json.Marshal(req)
httpReq, err := http.NewRequest("POST", rpc+"/ext/bc/P", bytes.NewReader(body))
httpReq, err := http.NewRequest("POST", rpc+"/v1/bc/P", bytes.NewReader(body))
if err != nil {
return 0, "", err
}
+6 -6
View File
@@ -18,8 +18,8 @@
// same PVC as luxd) and if it shows Status=done + matching {first,last,
// firstHash,lastHash} for THIS run's range, exit 0 without calling luxd.
// This is the "operator schedules the same export every hour" optimization.
// 2. Health probe. Confirm luxd is reachable on ${LuxdRPC}/ext/health.
// 3. RPC call. POST admin_exportChain to ${LuxdRPC}/ext/bc/${ChainAlias}/rpc
// 2. Health probe. Confirm luxd is reachable on ${LuxdRPC}/v1/health.
// 3. RPC call. POST admin_exportChain to ${LuxdRPC}/v1/bc/${ChainAlias}/rpc
// with {file, first, last}. Respect KMS_AUTH_TOKEN bearer auth same as
// rlp-import.
// 4. Translate the response status into an exit code:
@@ -248,10 +248,10 @@ func resolveToHeight(s string) (uint64, error) {
return v, nil
}
// waitForLuxd polls ${rpc}/ext/health until 200, capped at timeout.
// waitForLuxd polls ${rpc}/v1/health until 200, capped at timeout.
func waitForLuxd(ctx context.Context, rpc string, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
url := rpc + "/ext/health"
url := rpc + "/v1/health"
for time.Now().Before(deadline) {
select {
case <-ctx.Done():
@@ -311,7 +311,7 @@ type exportResult struct {
Message string `json:"message,omitempty"`
}
// callAdminExportChain POSTs admin_exportChain to the per-alias /ext/bc/
// callAdminExportChain POSTs admin_exportChain to the per-alias /v1/bc/
// endpoint. Returns the parsed result on success, err on luxd-side failure.
func callAdminExportChain(ctx context.Context, rpc, alias, file string, first, last uint64) (*exportResult, error) {
body, err := json.Marshal(jsonRPCRequest{
@@ -325,7 +325,7 @@ func callAdminExportChain(ctx context.Context, rpc, alias, file string, first, l
if err != nil {
return nil, err
}
url := rpc + "/ext/bc/" + alias + "/rpc"
url := rpc + "/v1/bc/" + alias + "/rpc"
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
if err != nil {
return nil, err
+8 -8
View File
@@ -206,9 +206,9 @@ func TestRun_HappyPath(t *testing.T) {
var exportBodyRaw []byte
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.URL.Path == "/ext/health":
case r.URL.Path == "/v1/health":
w.WriteHeader(http.StatusOK)
case strings.HasPrefix(r.URL.Path, "/ext/bc/"):
case strings.HasPrefix(r.URL.Path, "/v1/bc/"):
exportCalled = true
exportBodyRaw, _ = io.ReadAll(r.Body)
w.Header().Set("Content-Type", "application/json")
@@ -266,9 +266,9 @@ func TestRun_HappyPath(t *testing.T) {
func TestRun_NoopFromServer(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.URL.Path == "/ext/health":
case r.URL.Path == "/v1/health":
w.WriteHeader(http.StatusOK)
case strings.HasPrefix(r.URL.Path, "/ext/bc/"):
case strings.HasPrefix(r.URL.Path, "/v1/bc/"):
_, _ = w.Write([]byte(`{"jsonrpc":"2.0","result":{
"success": true,
"status": "noop",
@@ -305,9 +305,9 @@ func TestRun_NoopFromServer(t *testing.T) {
func TestRun_InterruptedFromServer(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.URL.Path == "/ext/health":
case r.URL.Path == "/v1/health":
w.WriteHeader(http.StatusOK)
case strings.HasPrefix(r.URL.Path, "/ext/bc/"):
case strings.HasPrefix(r.URL.Path, "/v1/bc/"):
_, _ = w.Write([]byte(`{"jsonrpc":"2.0","result":{
"success": false,
"status": "interrupted",
@@ -367,9 +367,9 @@ func TestRun_LuxdUnreachable(t *testing.T) {
func TestRun_AdminRPCError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.URL.Path == "/ext/health":
case r.URL.Path == "/v1/health":
w.WriteHeader(http.StatusOK)
case strings.HasPrefix(r.URL.Path, "/ext/bc/"):
case strings.HasPrefix(r.URL.Path, "/v1/bc/"):
_, _ = w.Write([]byte(`{"jsonrpc":"2.0","error":{"code":-32601,"message":"admin API not enabled"},"id":1}`))
default:
http.NotFound(w, r)
+5 -5
View File
@@ -20,7 +20,7 @@
// 3. SHA-256 verify. Compare against ${SHA256}; abort on mismatch.
// 4. Wipe ${DataDir}/chainData/${BlockchainID} so admin.importChain starts
// from a known-empty EVM state.
// 5. POST admin.importChain → ${LuxdRPC}/ext/bc/${ChainAlias}/rpc with the
// 5. POST admin.importChain → ${LuxdRPC}/v1/bc/${ChainAlias}/rpc with the
// RLP path. Wait for an HTTP 200 + JSON-RPC result.
// 6. Touch the sentinel. Only on full success; partial-success leaves
// no sentinel so the K8s Job retry policy re-runs the steps next pod.
@@ -270,11 +270,11 @@ func touchSentinel(path string) error {
return f.Close()
}
// waitForLuxd polls ${rpc}/ext/health until 200, capped at timeout. Returns
// waitForLuxd polls ${rpc}/v1/health until 200, capped at timeout. Returns
// nil when luxd is responsive, err otherwise. Polls every 2s.
func waitForLuxd(rpc string, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
url := rpc + "/ext/health"
url := rpc + "/v1/health"
for time.Now().Before(deadline) {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
@@ -313,7 +313,7 @@ type jsonRPCResponse struct {
ID int `json:"id"`
}
// callAdminImportChain POSTs admin.importChain to the per-alias /ext/bc/
// callAdminImportChain POSTs admin.importChain to the per-alias /v1/bc/
// endpoint. Returns nil on success, err with the JSON-RPC error message
// on luxd-side failure.
func callAdminImportChain(rpc, alias, rlpPath string) error {
@@ -326,7 +326,7 @@ func callAdminImportChain(rpc, alias, rlpPath string) error {
if err != nil {
return err
}
url := rpc + "/ext/bc/" + alias + "/rpc"
url := rpc + "/v1/bc/" + alias + "/rpc"
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
if err != nil {
return err
+5 -5
View File
@@ -120,9 +120,9 @@ func TestRun_HappyPath(t *testing.T) {
case r.URL.Path == "/rlp":
w.Header().Set("Content-Type", "application/octet-stream")
_, _ = w.Write(body)
case r.URL.Path == "/ext/health":
case r.URL.Path == "/v1/health":
w.WriteHeader(http.StatusOK)
case strings.HasPrefix(r.URL.Path, "/ext/bc/"):
case strings.HasPrefix(r.URL.Path, "/v1/bc/"):
importCalled = true
importBodyRaw, _ = io.ReadAll(r.Body)
w.Header().Set("Content-Type", "application/json")
@@ -193,7 +193,7 @@ func TestRun_SHAMismatchExits1(t *testing.T) {
switch r.URL.Path {
case "/rlp":
_, _ = w.Write([]byte("real-rlp"))
case "/ext/health":
case "/v1/health":
w.WriteHeader(http.StatusOK)
default:
http.NotFound(w, r)
@@ -261,9 +261,9 @@ func TestRun_AdminRPCErrorExits3(t *testing.T) {
switch {
case r.URL.Path == "/rlp":
_, _ = w.Write([]byte("x"))
case r.URL.Path == "/ext/health":
case r.URL.Path == "/v1/health":
w.WriteHeader(http.StatusOK)
case strings.HasPrefix(r.URL.Path, "/ext/bc/"):
case strings.HasPrefix(r.URL.Path, "/v1/bc/"):
_, _ = w.Write([]byte(`{"jsonrpc":"2.0","error":{"code":-32601,"message":"admin API not enabled"},"id":1}`))
default:
http.NotFound(w, r)
+1 -1
View File
@@ -244,7 +244,7 @@ func GetBlockchainIDFromAlias(endpoint string, alias string) (ids.ID, error) {
type getBlockchainIDReply struct {
BlockchainID ids.ID `json:"blockchainID"`
}
requester := rpc.NewEndpointRequester(endpoint + "/ext/info")
requester := rpc.NewEndpointRequester(endpoint + "/v1/info")
ctx, cancel := GetAPIContext()
defer cancel()
reply := &getBlockchainIDReply{}