Files
zeekay 8f56009cc7 fix: deep audit - remove stubs, fix imports, clean dead code
Deep audit changes:
- Replace stub signature aggregator with real SDK warp implementation
- Remove dead pkg/lpm package (SDK lpmintegration used instead)
- Fix fs imports: use local utils.FileExists, DirExists, ExpandHome
- Fix secp256k1fx imports: use node/vms/secp256k1fx
- Fix platformvm imports: use node/vms/platformvm
- Fix e2e test TODOs: uncomment BootstrapValidators assertions
- Improve localnet stub implementations with real logic
- Fix node.go fs.FileExists reference

Note: Build requires GOWORK=off due to workspace dependency issues
2026-01-10 11:17:00 -08:00

477 lines
13 KiB
Go

// Copyright (C) 2022-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package contract
import (
"fmt"
"strings"
cmdflags "github.com/luxfi/cli/cmd/flags"
"github.com/luxfi/cli/pkg/application"
"github.com/luxfi/cli/pkg/localnet"
"github.com/luxfi/cli/pkg/utils"
"github.com/luxfi/constants"
"github.com/luxfi/ids"
"github.com/luxfi/sdk/models"
"github.com/luxfi/sdk/prompts"
"github.com/spf13/cobra"
)
type ChainSpec struct {
BlockchainName string
blockchainNameFlagEnabled bool
blockchainNameFlagName string
CChain bool
cChainFlagEnabled bool
cChainFlagName string
PChain bool
pChainFlagEnabled bool
pChainFlagName string
XChain bool
xChainFlagEnabled bool
xChainFlagName string
BlockchainID string
blockchainIDFlagEnabled bool
blockchainIDFlagName string
OnlySOV bool
}
const (
defaultBlockchainNameFlagName = "blockchain"
defaultCChainFlagName = "c-chain"
defaultPChainFlagName = "p-chain"
defaultXChainFlagName = "x-chain"
defaultBlockchainIDFlagName = "blockchain-id"
)
func (cs *ChainSpec) CheckMutuallyExclusiveFields() error {
if !cmdflags.EnsureMutuallyExclusive([]bool{
cs.BlockchainName != "",
cs.BlockchainID != "",
cs.CChain,
cs.PChain,
cs.XChain,
}) {
flags := []string{}
if cs.blockchainNameFlagEnabled {
flags = append(flags, cs.blockchainNameFlagName)
}
if cs.cChainFlagEnabled {
flags = append(flags, cs.cChainFlagName)
}
if cs.pChainFlagEnabled {
flags = append(flags, cs.pChainFlagName)
}
if cs.xChainFlagEnabled {
flags = append(flags, cs.xChainFlagName)
}
if cs.blockchainIDFlagEnabled {
flags = append(flags, cs.blockchainIDFlagName)
}
return fmt.Errorf("%s are mutually exclusive flags",
strings.Join(flags, ", "),
)
}
return nil
}
func (cs *ChainSpec) Defined() bool {
if cs.blockchainNameFlagEnabled && cs.BlockchainName != "" {
return true
}
if cs.cChainFlagEnabled && cs.CChain {
return true
}
if cs.pChainFlagEnabled && cs.PChain {
return true
}
if cs.xChainFlagEnabled && cs.XChain {
return true
}
if cs.blockchainIDFlagEnabled && cs.BlockchainID != "" {
return true
}
return false
}
func (cs *ChainSpec) fillDefaults() {
if cs.blockchainNameFlagName == "" {
cs.blockchainIDFlagEnabled = true
cs.blockchainNameFlagName = defaultBlockchainNameFlagName
}
if cs.cChainFlagName == "" {
cs.cChainFlagEnabled = true
cs.cChainFlagName = defaultCChainFlagName
}
if cs.pChainFlagName == "" {
cs.pChainFlagName = defaultPChainFlagName
}
if cs.xChainFlagName == "" {
cs.xChainFlagName = defaultXChainFlagName
}
if cs.blockchainIDFlagName == "" {
cs.blockchainIDFlagName = defaultBlockchainIDFlagName
}
}
func (cs *ChainSpec) SetFlagNames(
blockchainNameFlagName string,
cChainFlagName string,
pChainFlagName string,
xChainFlagName string,
blockchainIDFlagName string,
) {
cs.blockchainNameFlagName = blockchainNameFlagName
cs.cChainFlagName = cChainFlagName
cs.pChainFlagName = pChainFlagName
cs.xChainFlagName = xChainFlagName
cs.blockchainIDFlagName = blockchainIDFlagName
cs.SetEnabled(
cs.blockchainNameFlagName != "",
cs.cChainFlagName != "",
cs.pChainFlagName != "",
cs.xChainFlagName != "",
cs.blockchainIDFlagName != "",
)
}
func (cs *ChainSpec) SetEnabled(
blockchainNameFlagEnabled bool,
cChainFlagEnabled bool,
pChainFlagEnabled bool,
xChainFlagEnabled bool,
blockchainIDFlagEnabled bool,
) {
cs.blockchainNameFlagEnabled = blockchainNameFlagEnabled
cs.cChainFlagEnabled = cChainFlagEnabled
cs.pChainFlagEnabled = pChainFlagEnabled
cs.xChainFlagEnabled = xChainFlagEnabled
cs.blockchainIDFlagEnabled = blockchainIDFlagEnabled
}
func (cs *ChainSpec) AddToCmd(
cmd *cobra.Command,
goalFmt string,
) {
cs.fillDefaults()
if cs.blockchainNameFlagEnabled {
cmd.Flags().StringVar(&cs.BlockchainName, cs.blockchainNameFlagName, "", fmt.Sprintf(goalFmt, "the given CLI blockchain"))
}
if cs.cChainFlagEnabled {
cmd.Flags().BoolVar(&cs.CChain, cs.cChainFlagName, false, fmt.Sprintf(goalFmt, "C-Chain"))
}
if cs.pChainFlagEnabled {
cmd.Flags().BoolVar(&cs.PChain, cs.pChainFlagName, false, fmt.Sprintf(goalFmt, "P-Chain"))
}
if cs.xChainFlagEnabled {
cmd.Flags().BoolVar(&cs.XChain, cs.xChainFlagName, false, fmt.Sprintf(goalFmt, "X-Chain"))
}
if cs.blockchainIDFlagEnabled {
cmd.Flags().StringVar(&cs.BlockchainID, cs.blockchainIDFlagName, "", fmt.Sprintf(goalFmt, "the given blockchain ID/Alias"))
}
}
func GetBlockchainEndpoints(
app *application.Lux,
network models.Network,
chainSpec ChainSpec,
promptForRPCEndpoint bool,
promptForWSEndpoint bool,
) (string, string, error) {
var (
rpcEndpoint string
wsEndpoint string
)
if chainSpec.BlockchainName != "" {
sc, err := app.LoadSidecar(chainSpec.BlockchainName)
if err != nil {
return "", "", fmt.Errorf("failed to load sidecar: %w", err)
}
networkName := network.Name()
if sc.Networks[networkName].BlockchainID == ids.Empty {
// look into the cluster deploys
for k := range sc.Networks {
sidecarNetwork, err := app.GetNetworkFromSidecarNetworkName(k)
if err == nil {
if sidecarNetwork.Equals(network) {
networkName = sidecarNetwork.Name()
}
}
}
}
if len(sc.Networks[networkName].RPCEndpoints) > 0 {
rpcEndpoint = sc.Networks[networkName].RPCEndpoints[0]
}
if len(sc.Networks[networkName].WSEndpoints) > 0 {
wsEndpoint = sc.Networks[networkName].WSEndpoints[0]
}
}
if rpcEndpoint == "" && chainSpec.CChain {
rpcEndpoint = network.CChainEndpoint()
wsEndpoint = network.CChainWSEndpoint()
}
blockchainDesc, err := GetBlockchainDesc(chainSpec)
if err != nil {
return "", "", err
}
if rpcEndpoint == "" && promptForRPCEndpoint {
rpcEndpoint, err = app.Prompt.CaptureURL("What is the RPC endpoint for " + blockchainDesc)
if err != nil {
return "", "", err
}
}
if wsEndpoint == "" && promptForWSEndpoint {
wsEndpoint, err = app.Prompt.CaptureURL("What is the WS endpoint for " + blockchainDesc)
if err != nil {
return "", "", err
}
}
return rpcEndpoint, wsEndpoint, nil
}
func GetBlockchainID(
app *application.Lux,
network models.Network,
chainSpec ChainSpec,
) (ids.ID, error) {
var blockchainID ids.ID
switch {
case chainSpec.BlockchainID != "":
var err error
blockchainID, err = ids.FromString(chainSpec.BlockchainID)
if err != nil {
// it should be an alias at this point
blockchainID, err = utils.GetChainID(network.Endpoint(), chainSpec.BlockchainID)
if err != nil {
return ids.Empty, err
}
}
case chainSpec.CChain:
chainID, err := utils.GetChainID(network.Endpoint(), "C")
if err != nil {
return ids.Empty, err
}
blockchainID = chainID
case chainSpec.BlockchainName != "":
sc, err := app.LoadSidecar(chainSpec.BlockchainName)
if err != nil {
return ids.Empty, fmt.Errorf("failed to load sidecar: %w", err)
}
if sc.Networks[network.Name()].BlockchainID == ids.Empty {
return ids.Empty, fmt.Errorf("blockchain has not been deployed to %s", network.Name())
}
blockchainID = sc.Networks[network.Name()].BlockchainID
default:
return ids.Empty, fmt.Errorf("blockchain is not defined")
}
return blockchainID, nil
}
func GetChainID(
app *application.Lux,
network models.Network,
chainSpec ChainSpec,
) (ids.ID, error) {
var chainID ids.ID
switch {
case chainSpec.CChain:
chainID = ids.Empty
case chainSpec.BlockchainName != "":
sc, err := app.LoadSidecar(chainSpec.BlockchainName)
if err != nil {
return ids.Empty, fmt.Errorf("failed to load sidecar: %w", err)
}
if sc.Networks[network.Name()].BlockchainID == ids.Empty {
return ids.Empty, fmt.Errorf("blockchain has not been deployed to %s", network.Name())
}
chainID = sc.Networks[network.Name()].BlockchainID
case chainSpec.BlockchainID != "":
blockchainID, err := ids.FromString(chainSpec.BlockchainID)
if err != nil {
return ids.Empty, fmt.Errorf("failure parsing %s as id: %w", chainSpec.BlockchainID, err)
}
chainID = blockchainID
default:
return ids.Empty, fmt.Errorf("blockchain is not defined")
}
return chainID, nil
}
func GetBlockchainDesc(
chainSpec ChainSpec,
) (string, error) {
blockchainDesc := ""
switch {
case chainSpec.BlockchainName != "":
blockchainDesc = chainSpec.BlockchainName
case chainSpec.CChain:
blockchainDesc = "C-Chain"
case chainSpec.PChain:
blockchainDesc = "P-Chain"
case chainSpec.XChain:
blockchainDesc = "X-Chain"
case chainSpec.BlockchainID != "":
blockchainDesc = chainSpec.BlockchainID
default:
return "", fmt.Errorf("blockchain is not defined")
}
return blockchainDesc, nil
}
func GetWarpInfo(
app *application.Lux,
network models.Network,
chainSpec ChainSpec,
promptForRegistry bool,
promptForMessenger bool,
defaultToLatestReleasedMessenger bool,
) (string, string, error) {
registryAddress := ""
messengerAddress := ""
switch {
case chainSpec.CChain:
var err error
registryAddress, messengerAddress, err = GetCChainWarpInfo(app, network)
if err != nil {
return "", "", err
}
case chainSpec.BlockchainID != "":
case chainSpec.BlockchainName != "":
sc, err := app.LoadSidecar(chainSpec.BlockchainName)
if err != nil {
return "", "", fmt.Errorf("failed to load sidecar: %w", err)
}
if sc.Networks[network.Name()].BlockchainID == ids.Empty {
return "", "", fmt.Errorf("blockchain has not been deployed to %s", network.Name())
}
registryAddress = sc.Networks[network.Name()].TeleporterRegistryAddress
messengerAddress = sc.Networks[network.Name()].TeleporterMessengerAddress
default:
return "", "", fmt.Errorf("blockchain is not defined")
}
blockchainDesc, err := GetBlockchainDesc(chainSpec)
if err != nil {
return "", "", err
}
if registryAddress == "" && promptForRegistry {
addr, err := app.Prompt.CaptureAddress("Which is the Warp Registry address for " + blockchainDesc)
if err != nil {
return "", "", err
}
registryAddress = addr.Hex()
}
if messengerAddress == "" {
if promptForMessenger {
addr, err := app.Prompt.CaptureAddress("Which is the Warp Messenger address for " + blockchainDesc)
if err != nil {
return "", "", err
}
messengerAddress = addr.Hex()
} else if defaultToLatestReleasedMessenger {
messengerAddress = constants.DefaultWarpMessengerAddress
}
}
return registryAddress, messengerAddress, nil
}
func PromptChain(
app *application.Lux,
network models.Network,
prompt string,
blockchainNameToAvoid string,
chainSpec *ChainSpec,
) (bool, error) {
var (
err error
blockchainNames []string
)
if chainSpec.blockchainNameFlagEnabled {
blockchainNames, err = app.GetBlockchainNamesOnNetwork(network, chainSpec.OnlySOV)
if err != nil {
return false, err
}
}
cancel, pChain, xChain, cChain, blockchainName, blockchainID, err := prompts.PromptChain(
app.Prompt,
prompt,
blockchainNames,
chainSpec.pChainFlagEnabled,
chainSpec.xChainFlagEnabled,
chainSpec.cChainFlagEnabled,
blockchainNameToAvoid,
chainSpec.blockchainIDFlagEnabled,
)
if err != nil || cancel {
return cancel, err
}
if blockchainID != "" {
chainID, err := ids.FromString(blockchainID)
if err != nil {
// map from alias to blockchain ID (or identity)
chainID, err = utils.GetChainID(network.Endpoint(), blockchainID)
if err != nil {
return cancel, err
}
}
blockchainID = chainID.String()
}
chainSpec.BlockchainName = blockchainName
chainSpec.CChain = cChain
chainSpec.PChain = pChain
chainSpec.XChain = xChain
chainSpec.BlockchainID = blockchainID
return false, nil
}
func GetCChainWarpInfo(
app *application.Lux,
network models.Network,
) (string, string, error) {
messengerAddress := ""
registryAddress := ""
switch {
case network.Kind() == models.Local:
hasDataRaw, extraLocalNetworkData, err := localnet.GetExtraLocalNetworkData(app, "")
if err != nil {
return "", "", err
}
// Check if hasData is nil or false (it's interface{} so we need to check)
hasData := hasDataRaw != nil
if b, ok := hasDataRaw.(bool); ok {
hasData = b
}
if !hasData {
return "", "", fmt.Errorf("no extra local network data available")
}
if msg, ok := extraLocalNetworkData["CChainTeleporterMessengerAddress"].(string); ok {
messengerAddress = msg
}
if reg, ok := extraLocalNetworkData["CChainTeleporterRegistryAddress"].(string); ok {
registryAddress = reg
}
case network.ClusterName() != "":
clusterConfig, err := app.GetClusterConfig(network.ClusterName())
if err != nil {
return "", "", err
}
// Type assertion for ExtraNetworkData field
extraData, ok := clusterConfig["extraNetworkData"].(map[string]interface{})
if ok {
if msg, ok := extraData["CChainTeleporterMessengerAddress"].(string); ok {
messengerAddress = msg
}
if reg, ok := extraData["CChainTeleporterRegistryAddress"].(string); ok {
registryAddress = reg
}
}
case network.Kind() == models.Testnet:
messengerAddress = constants.DefaultWarpMessengerAddress
registryAddress = constants.MainnetCChainWarpRegistryAddress // Use same registry address for testnet
case network.Kind() == models.Mainnet:
messengerAddress = constants.DefaultWarpMessengerAddress
registryAddress = constants.MainnetCChainWarpRegistryAddress
}
return registryAddress, messengerAddress, nil
}