Files

192 lines
5.5 KiB
Go

// Copyright (C) 2022-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// Package blockchain provides helper functions for blockchain operations
// including peer management, URI handling, and blockchain state queries.
package blockchain
import (
"encoding/json"
"fmt"
"time"
"github.com/luxfi/ids"
apiinfo "github.com/luxfi/api/info"
"github.com/luxfi/cli/pkg/application"
"github.com/luxfi/cli/pkg/localnet"
"github.com/luxfi/cli/pkg/utils"
"github.com/luxfi/cli/pkg/ux"
"github.com/luxfi/math/set"
"github.com/luxfi/proto/p/signer"
sdkinfo "github.com/luxfi/sdk/info"
"github.com/luxfi/sdk/models"
"github.com/luxfi/sdk/platformvm"
)
// GetAggregatorExtraPeers returns a list of peers for the aggregator from the cluster.
func GetAggregatorExtraPeers(
app *application.Lux,
clusterName string,
) ([]apiinfo.Peer, error) {
uris, err := GetAggregatorNetworkUris(app, clusterName)
if err != nil {
return nil, err
}
urisSet := set.Of(uris...)
uris = urisSet.List()
return UrisToPeers(uris)
}
// GetAggregatorNetworkUris returns network URIs for the specified cluster.
func GetAggregatorNetworkUris(app *application.Lux, clusterName string) ([]string, error) {
aggregatorExtraPeerEndpointsUris := []string{}
if clusterName != "" {
if localnet.LocalClusterExists(app, clusterName) {
return localnet.GetLocalClusterURIs(app, clusterName)
}
// remote cluster case
clustersConfig, err := app.LoadClustersConfig()
if err != nil {
return nil, err
}
// Type assertions for map[string]interface{}
clustersMap, ok := clustersConfig["clusters"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("invalid clusters config format")
}
clusterData, ok := clustersMap[clusterName].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("cluster %s not found", clusterName)
}
// Parse cluster config to extract node endpoints
parseClusterConfig(clusterData, &aggregatorExtraPeerEndpointsUris)
}
return aggregatorExtraPeerEndpointsUris, nil
}
// parseClusterConfig extracts node endpoints from cluster configuration
func parseClusterConfig(clusterData map[string]interface{}, endpoints *[]string) {
// Extract nodes field
if nodes, ok := clusterData["Nodes"].([]interface{}); ok {
for _, node := range nodes {
if nodeStr, ok := node.(string); ok {
// Construct endpoint URI from node ID
endpoint := fmt.Sprintf("http://%s:9630", nodeStr)
*endpoints = append(*endpoints, endpoint)
}
}
}
// Extract API nodes if available
if apiNodes, ok := clusterData["APINodes"].([]interface{}); ok {
for _, apiNode := range apiNodes {
if apiNodeStr, ok := apiNode.(string); ok {
// API nodes are already endpoints
*endpoints = append(*endpoints, apiNodeStr)
}
}
}
// Extract network data if present
if networkData, ok := clusterData["Network"].(map[string]interface{}); ok {
if endpoint, ok := networkData["Endpoint"].(string); ok && endpoint != "" {
*endpoints = append(*endpoints, endpoint)
}
}
}
// UrisToPeers converts a list of node URIs to peer information.
func UrisToPeers(uris []string) ([]apiinfo.Peer, error) {
peers := []apiinfo.Peer{}
ctx, cancel := utils.GetANRContext()
defer cancel()
for _, uri := range uris {
client := sdkinfo.NewClient(uri)
nodeID, _, err := client.GetNodeID(ctx)
if err != nil {
return nil, err
}
ip, err := client.GetNodeIP(ctx)
if err != nil {
return nil, err
}
peers = append(peers, apiinfo.Peer{
PeerInfo: apiinfo.PeerInfo{
ID: nodeID,
PublicIP: ip,
},
})
}
return peers, nil
}
// ConvertToBLSProofOfPossession converts public key and proof of possession strings to a ProofOfPossession struct.
func ConvertToBLSProofOfPossession(publicKey, proofOfPossesion string) (signer.ProofOfPossession, error) {
type jsonProofOfPossession struct {
PublicKey string
ProofOfPossession string
}
jsonPop := jsonProofOfPossession{
PublicKey: publicKey,
ProofOfPossession: proofOfPossesion,
}
popBytes, err := json.Marshal(jsonPop)
if err != nil {
return signer.ProofOfPossession{}, err
}
pop := &signer.ProofOfPossession{}
err = pop.UnmarshalJSON(popBytes)
if err != nil {
return signer.ProofOfPossession{}, err
}
return *pop, nil
}
// UpdatePChainHeight displays a progress bar while waiting for P-Chain height update.
func UpdatePChainHeight(
title string,
) error {
_, err := ux.TimedProgressBar(
30*time.Second,
title,
0,
)
if err != nil {
return err
}
fmt.Println()
return nil
}
// GetBlockchainTimestamp returns the current timestamp from the blockchain.
func GetBlockchainTimestamp(network models.Network) (time.Time, error) {
ctx, cancel := utils.GetAPIContext()
defer cancel()
platformCli := platformvm.NewClient(network.Endpoint())
return platformCli.GetTimestamp(ctx)
}
// GetChain returns chain validators information
func GetChain(chainID ids.ID, network models.Network) (interface{}, error) {
api := network.Endpoint()
pClient := platformvm.NewClient(api)
ctx, cancel := utils.GetAPIContext()
defer cancel()
// GetChain has been replaced, using GetCurrentValidators instead
validators, err := pClient.GetCurrentValidators(ctx, chainID, nil)
if err != nil {
return nil, err
}
return validators, nil
}
// GetChainIDFromBlockchainID returns the chain ID that validates the given blockchain.
func GetChainIDFromBlockchainID(blockchainID ids.ID, network models.Network) (ids.ID, error) {
api := network.Endpoint()
pClient := platformvm.NewClient(api)
ctx, cancel := utils.GetAPIContext()
defer cancel()
return pClient.ValidatedBy(ctx, blockchainID)
}