Files
zeekayandHanzo Dev 64519916bf the corporate name is Lux Industries Inc
Lux Partners Limited is not the entity behind this work. Every place the
old name stood — copyright lines, site footers, brand config, legal and
compliance text — now names the company that actually holds it.

lux.fund is the exception and is not renamed to match: that site belongs
to Lux Assets Management LLC, a different company, and it says so.

Two kinds of line were left for a person rather than rewritten. A dated
news item recording that Lux Partners Limited was established in the Isle
of Man is a historical fact, and renaming it would falsify the record. And
a sentence asserting that the company is domiciled in the Isle of Man is a
claim about a jurisdiction, not a name — an Inc or an LLC is not an Isle
of Man company, so swapping the name there would assert something untrue.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-28 13:01:04 -07:00

166 lines
5.2 KiB
Go

// Copyright (c) 2025 Lux Industries Inc
// SPDX-License-Identifier: MIT
// Package rpcadapter provides the shared JSON-RPC plumbing used by every
// chain-specific DAG adapter (ai, bridge, key, mpc, oracle, quantum,
// threshold, zk, ...). One implementation of the generic Vertex shape,
// one implementation of the request/response dance — each chain adapter
// embeds Adapter and adds only its chain-specific extras.
package rpcadapter
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/luxfi/indexer/dag"
)
// Adapter is the shared JSON-RPC adapter. Every DAG chain's adapter
// package embeds this type and provides its method prefix (e.g. "aivm",
// "bvm", "xvm", "mvm"). The two generic RPCs — getRecentVertices and
// getVertex — dispatch via the prefix.
type Adapter struct {
rpcEndpoint string
client *http.Client
methodPrefix string
}
// New constructs an Adapter for the given RPC endpoint and JSON-RPC
// method prefix (without the dot). For example, methodPrefix="aivm"
// dispatches to "aivm.getRecentVertices" and "aivm.getVertex".
func New(rpcEndpoint, methodPrefix string) *Adapter {
return &Adapter{
rpcEndpoint: rpcEndpoint,
client: &http.Client{Timeout: 30 * time.Second},
methodPrefix: methodPrefix,
}
}
// Endpoint returns the configured RPC endpoint (for logging / tests).
func (a *Adapter) Endpoint() string { return a.rpcEndpoint }
// MethodPrefix returns the JSON-RPC method prefix (e.g. "aivm").
func (a *Adapter) MethodPrefix() string { return a.methodPrefix }
// Client exposes the underlying http.Client so chain-specific adapters
// can reuse it for custom RPCs without opening a second pool.
func (a *Adapter) Client() *http.Client { return a.client }
// ParseVertex parses a generic DAG vertex from the RPC response. Chain-
// specific adapters that need extra metadata should override this by
// wrapping the result and copying fields.
func (a *Adapter) ParseVertex(data json.RawMessage) (*dag.Vertex, error) {
return ParseVertex(data)
}
// GetRecentVertices fetches the N most recent vertices.
func (a *Adapter) GetRecentVertices(ctx context.Context, limit int) ([]json.RawMessage, error) {
var result struct {
Vertices []json.RawMessage `json:"vertices"`
}
if err := a.Call(ctx, a.methodPrefix+".getRecentVertices",
map[string]interface{}{"limit": limit}, &result); err != nil {
return nil, err
}
return result.Vertices, nil
}
// GetVertexByID fetches a specific vertex by ID.
func (a *Adapter) GetVertexByID(ctx context.Context, id string) (json.RawMessage, error) {
var raw json.RawMessage
if err := a.Call(ctx, a.methodPrefix+".getVertex",
map[string]interface{}{"vertexID": id}, &raw); err != nil {
return nil, err
}
return raw, nil
}
// Call is the generic JSON-RPC 2.0 request helper. Chain-specific
// adapters invoke this for their custom methods.
func (a *Adapter) Call(ctx context.Context, method string, params any, out any) error {
body, err := json.Marshal(map[string]interface{}{
"jsonrpc": "2.0",
"id": 1,
"method": method,
"params": params,
})
if err != nil {
return fmt.Errorf("marshal request: %w", err)
}
httpReq, err := http.NewRequestWithContext(ctx, "POST", a.rpcEndpoint, bytes.NewReader(body))
if err != nil {
return fmt.Errorf("create request: %w", err)
}
httpReq.Header.Set("Content-Type", "application/json")
resp, err := a.client.Do(httpReq)
if err != nil {
return fmt.Errorf("rpc call: %w", err)
}
defer resp.Body.Close()
var envelope struct {
Result json.RawMessage `json:"result"`
Error *Error `json:"error"`
}
if err := json.NewDecoder(resp.Body).Decode(&envelope); err != nil {
return fmt.Errorf("decode response: %w", err)
}
if envelope.Error != nil {
return fmt.Errorf("rpc error %d: %s", envelope.Error.Code, envelope.Error.Message)
}
if out == nil {
return nil
}
if err := json.Unmarshal(envelope.Result, out); err != nil {
return fmt.Errorf("unmarshal result: %w", err)
}
return nil
}
// Error is the JSON-RPC 2.0 error object.
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
}
// ParseVertex parses a generic DAG vertex. Exposed as a free function
// so chain adapters can reuse it from their own ParseVertex overrides.
func ParseVertex(data json.RawMessage) (*dag.Vertex, error) {
var raw struct {
ID string `json:"id"`
Type string `json:"type"`
ParentIDs []string `json:"parentIds"`
Height uint64 `json:"height"`
Epoch uint32 `json:"epoch"`
TxIDs []string `json:"txIds"`
Timestamp int64 `json:"timestamp"`
Status string `json:"status"`
Data json.RawMessage `json:"data"`
}
if err := json.Unmarshal(data, &raw); err != nil {
return nil, fmt.Errorf("unmarshal vertex: %w", err)
}
v := &dag.Vertex{
ID: raw.ID,
Type: raw.Type,
ParentIDs: raw.ParentIDs,
Height: raw.Height,
Epoch: raw.Epoch,
TxIDs: raw.TxIDs,
Status: dag.Status(raw.Status),
Data: raw.Data,
Metadata: make(map[string]interface{}),
}
if raw.Timestamp > 0 {
v.Timestamp = time.Unix(raw.Timestamp, 0)
} else {
v.Timestamp = time.Now()
}
return v, nil
}