mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
Route registration, client URL builders, and log lines all move to /v1/ (wallet primary api, chains/rpc handler_manager+chain_integration, xvm client+wallet_client, xsvm api client, multi-network example, manager logs). Zero /ext/ literals remain. One endpoint namespace, no transition alias. Co-authored-by: Hanzo Dev <dev@hanzo.ai>
169 lines
4.9 KiB
Go
169 lines
4.9 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/log"
|
|
"github.com/luxfi/node/server/http"
|
|
"github.com/luxfi/node/vms"
|
|
)
|
|
|
|
// ChainHandlerRegistrar provides a clean interface for chain manager to register handlers.
|
|
// This replaces the inline registration logic with a more robust, testable solution.
|
|
type ChainHandlerRegistrar struct {
|
|
manager *HandlerManager
|
|
server server.Server
|
|
log log.Logger
|
|
cChainID ids.ID // Special handling for C-Chain
|
|
pChainID ids.ID // Platform chain ID for validation
|
|
}
|
|
|
|
// NewChainHandlerRegistrar creates a registrar for chain handler registration.
|
|
// Encapsulates all the registration logic in one place.
|
|
func NewChainHandlerRegistrar(
|
|
server server.Server,
|
|
logger log.Logger,
|
|
cChainID ids.ID,
|
|
pChainID ids.ID,
|
|
) *ChainHandlerRegistrar {
|
|
return &ChainHandlerRegistrar{
|
|
manager: NewHandlerManager(server, logger),
|
|
server: server,
|
|
log: logger,
|
|
cChainID: cChainID,
|
|
pChainID: pChainID,
|
|
}
|
|
}
|
|
|
|
// RegisterChainHandlers is the main entry point from chain manager.
|
|
// Handles all the complexity of VM type checking and handler extraction.
|
|
func (r *ChainHandlerRegistrar) RegisterChainHandlers(
|
|
ctx context.Context,
|
|
chainID ids.ID,
|
|
vm interface{},
|
|
) error {
|
|
r.log.Info("Attempting to register chain handlers",
|
|
log.Stringer("chainID", chainID),
|
|
log.String("vmType", fmt.Sprintf("%T", vm)))
|
|
|
|
// Don't register handlers for Platform VM
|
|
if chainID == r.pChainID {
|
|
r.log.Debug("Skipping handler registration for Platform VM")
|
|
return nil
|
|
}
|
|
|
|
// Extract handlers from VM
|
|
handlers, err := r.extractHandlers(ctx, vm)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to extract handlers: %w", err)
|
|
}
|
|
|
|
if len(handlers) == 0 {
|
|
r.log.Info("VM does not provide any handlers",
|
|
log.Stringer("chainID", chainID))
|
|
return nil
|
|
}
|
|
|
|
// Determine chain alias (special case for C-Chain)
|
|
alias := r.getChainAlias(chainID)
|
|
|
|
// Register with robust handler manager
|
|
return r.manager.RegisterChainHandlers(ctx, chainID, alias, handlers)
|
|
}
|
|
|
|
// extractHandlers attempts to get handlers from the VM using multiple strategies.
|
|
// Handles different VM wrapper types gracefully.
|
|
func (r *ChainHandlerRegistrar) extractHandlers(
|
|
ctx context.Context,
|
|
vm interface{},
|
|
) (map[string]http.Handler, error) {
|
|
// First try direct interface check
|
|
if provider, ok := vm.(vms.HandlerProvider); ok {
|
|
r.log.Debug("VM directly implements HandlerProvider")
|
|
return provider.CreateHandlers(ctx)
|
|
}
|
|
|
|
// Try using the delegate helper (handles wrapped VMs)
|
|
handlers, err := vms.DelegateHandlers(ctx, vm)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("handler delegation failed: %w", err)
|
|
}
|
|
|
|
if len(handlers) > 0 {
|
|
r.log.Debug("Successfully extracted handlers via delegation",
|
|
log.Int("count", len(handlers)))
|
|
}
|
|
|
|
return handlers, nil
|
|
}
|
|
|
|
// getChainAlias returns the appropriate alias for a chain.
|
|
// C-Chain gets special treatment, others use their ID.
|
|
func (r *ChainHandlerRegistrar) getChainAlias(chainID ids.ID) string {
|
|
if chainID == r.cChainID {
|
|
return "C"
|
|
}
|
|
// Could extend this for X-Chain and P-Chain if needed
|
|
return ""
|
|
}
|
|
|
|
// GetRouteInfo returns information about a specific chain's registered routes.
|
|
// Useful for debugging and operational visibility.
|
|
func (r *ChainHandlerRegistrar) GetRouteInfo(chainID ids.ID) (*RouteInfo, bool) {
|
|
return r.manager.GetRouteInfo(chainID)
|
|
}
|
|
|
|
// GetAllRoutes returns all registered routes across all chains.
|
|
// Complete visibility for monitoring and debugging.
|
|
func (r *ChainHandlerRegistrar) GetAllRoutes() map[string]*RouteInfo {
|
|
return r.manager.GetAllRoutes()
|
|
}
|
|
|
|
// HealthCheckAll performs health checks on all registered routes.
|
|
// Returns a map of chainID -> healthy status.
|
|
func (r *ChainHandlerRegistrar) HealthCheckAll() map[string]bool {
|
|
return r.manager.HealthCheckAll()
|
|
}
|
|
|
|
// SetRetryConfig allows tuning of retry behavior for different environments.
|
|
// Production might want more retries, dev might want faster failures.
|
|
func (r *ChainHandlerRegistrar) SetRetryConfig(maxRetries int, initialWait time.Duration) {
|
|
r.manager.SetRetryConfig(maxRetries, initialWait)
|
|
}
|
|
|
|
// ValidateEndpoint performs a test request against a specific endpoint.
|
|
// Useful for debugging specific handler issues.
|
|
func (r *ChainHandlerRegistrar) ValidateEndpoint(
|
|
chainID ids.ID,
|
|
endpoint string,
|
|
) error {
|
|
info, exists := r.manager.GetRouteInfo(chainID)
|
|
if !exists {
|
|
return fmt.Errorf("no routes registered for chain %s", chainID)
|
|
}
|
|
|
|
// Build the full URL
|
|
fullURL := fmt.Sprintf("/v1/%s%s", info.Base, endpoint)
|
|
|
|
r.log.Info("Validating endpoint",
|
|
log.Stringer("chainID", chainID),
|
|
log.String("url", fullURL))
|
|
|
|
// Validates endpoint registration
|
|
for _, registered := range info.Endpoints {
|
|
if registered == endpoint {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("endpoint %s not found in registered endpoints: %v",
|
|
endpoint, info.Endpoints)
|
|
}
|