mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
641 lines
19 KiB
Go
641 lines
19 KiB
Go
// Copyright (C) 2022, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
package subnet
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/luxfi/node/v2/v2/vms/components/lux"
|
|
"github.com/luxfi/node/v2/v2/vms/components/verify"
|
|
|
|
"github.com/luxfi/cli/v2/pkg/application"
|
|
"github.com/luxfi/cli/v2/pkg/constants"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
"github.com/luxfi/cli/v2/pkg/txutils"
|
|
"github.com/luxfi/cli/v2/pkg/ux"
|
|
"github.com/luxfi/netrunner/utils"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/node/v2/v2/utils/crypto/keychain"
|
|
"github.com/luxfi/node/v2/v2/utils/formatting/address"
|
|
"github.com/luxfi/node/v2/v2/utils/set"
|
|
"github.com/luxfi/node/v2/v2/vms/platformvm"
|
|
"github.com/luxfi/node/v2/v2/vms/platformvm/txs"
|
|
"github.com/luxfi/node/v2/v2/vms/secp256k1fx"
|
|
"github.com/luxfi/node/v2/v2/wallet/chain/c"
|
|
"github.com/luxfi/node/v2/v2/wallet/subnet/primary"
|
|
walletoptions "github.com/luxfi/node/v2/v2/wallet"
|
|
)
|
|
|
|
var ErrNoSubnetAuthKeysInWallet = errors.New("auth wallet does not contain subnet auth keys")
|
|
|
|
type PublicDeployer struct {
|
|
LocalDeployer
|
|
usingLedger bool
|
|
kc keychain.Keychain
|
|
network models.Network
|
|
app *application.Lux
|
|
}
|
|
|
|
func NewPublicDeployer(app *application.Lux, usingLedger bool, kc keychain.Keychain, network models.Network) *PublicDeployer {
|
|
return &PublicDeployer{
|
|
LocalDeployer: *NewLocalDeployer(app, "", ""),
|
|
app: app,
|
|
usingLedger: usingLedger,
|
|
kc: kc,
|
|
network: network,
|
|
}
|
|
}
|
|
|
|
// adds a subnet validator to the given [subnetID]
|
|
// - creates an add subnet validator tx
|
|
// - sets the change output owner to be a wallet address (if not, it may go to any other subnet auth address)
|
|
// - signs the tx with the wallet as the owner of fee outputs and a possible subnet auth key
|
|
// - if partially signed, returns the tx so that it can later on be signed by the rest of the subnet auth keys
|
|
// - if fully signed, issues it
|
|
func (d *PublicDeployer) AddValidator(
|
|
controlKeys []string,
|
|
subnetAuthKeysStrs []string,
|
|
subnetID ids.ID,
|
|
nodeID ids.NodeID,
|
|
weight uint64,
|
|
startTime time.Time,
|
|
duration time.Duration,
|
|
) (bool, *txs.Tx, []string, error) {
|
|
wallet, err := d.loadWallet(subnetID)
|
|
if err != nil {
|
|
return false, nil, nil, err
|
|
}
|
|
subnetAuthKeys, err := address.ParseToIDs(subnetAuthKeysStrs)
|
|
if err != nil {
|
|
return false, nil, nil, fmt.Errorf("failure parsing subnet auth keys: %w", err)
|
|
}
|
|
validator := &txs.SubnetValidator{
|
|
Validator: txs.Validator{
|
|
NodeID: nodeID,
|
|
Start: uint64(startTime.Unix()),
|
|
End: uint64(startTime.Add(duration).Unix()),
|
|
Wght: weight,
|
|
},
|
|
Subnet: subnetID,
|
|
}
|
|
if d.usingLedger {
|
|
ux.Logger.PrintToUser("*** Please sign SubnetValidator transaction on the ledger device *** ")
|
|
}
|
|
|
|
tx, err := d.createAddSubnetValidatorTx(subnetAuthKeys, validator, wallet)
|
|
if err != nil {
|
|
return false, nil, nil, err
|
|
}
|
|
|
|
_, remainingSubnetAuthKeys, err := txutils.GetRemainingSigners(tx, controlKeys)
|
|
if err != nil {
|
|
return false, nil, nil, err
|
|
}
|
|
isFullySigned := len(remainingSubnetAuthKeys) == 0
|
|
|
|
if isFullySigned {
|
|
id, err := d.Commit(tx)
|
|
if err != nil {
|
|
return false, nil, nil, err
|
|
}
|
|
ux.Logger.PrintToUser("Transaction successful, transaction ID: %s", id)
|
|
return true, nil, nil, nil
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Partial tx created")
|
|
return false, tx, remainingSubnetAuthKeys, nil
|
|
}
|
|
|
|
func (d *PublicDeployer) CreateAssetTx(
|
|
subnetID ids.ID,
|
|
tokenName string,
|
|
tokenSymbol string,
|
|
denomination byte,
|
|
initialState map[uint32][]verify.State,
|
|
) (ids.ID, error) {
|
|
wallet, err := d.loadWallet(subnetID)
|
|
if err != nil {
|
|
return ids.Empty, err
|
|
}
|
|
|
|
if d.usingLedger {
|
|
ux.Logger.PrintToUser("*** Please sign Create Asset Transaction hash on the ledger device *** ")
|
|
}
|
|
|
|
tx, err := wallet.X().IssueCreateAssetTx(tokenName, tokenSymbol, denomination, initialState)
|
|
if err != nil {
|
|
return ids.Empty, err
|
|
}
|
|
ux.Logger.PrintToUser("Create Asset Transaction successful, transaction ID: %s", tx.ID())
|
|
ux.Logger.PrintToUser("Now exporting asset to P-Chain ...")
|
|
return tx.ID(), err
|
|
}
|
|
|
|
func (d *PublicDeployer) ExportToPChainTx(
|
|
subnetID ids.ID,
|
|
subnetAssetID ids.ID,
|
|
owner *secp256k1fx.OutputOwners,
|
|
assetAmount uint64,
|
|
) (ids.ID, error) {
|
|
wallet, err := d.loadWallet(subnetID)
|
|
if err != nil {
|
|
return ids.Empty, err
|
|
}
|
|
|
|
if d.usingLedger {
|
|
ux.Logger.PrintToUser("*** Please sign X -> P Chain Export Transaction hash on the ledger device *** ")
|
|
}
|
|
|
|
tx, err := wallet.X().IssueExportTx(ids.Empty,
|
|
[]*lux.TransferableOutput{
|
|
{
|
|
Asset: lux.Asset{
|
|
ID: subnetAssetID,
|
|
},
|
|
Out: &secp256k1fx.TransferOutput{
|
|
Amt: assetAmount,
|
|
OutputOwners: *owner,
|
|
},
|
|
},
|
|
})
|
|
if err == nil {
|
|
ux.Logger.PrintToUser("Export to P-Chain Transaction successful, transaction ID: %s", tx.ID())
|
|
ux.Logger.PrintToUser("Now importing asset from X-Chain ...")
|
|
}
|
|
return tx.ID(), err
|
|
}
|
|
|
|
func (d *PublicDeployer) ImportFromXChain(
|
|
subnetID ids.ID,
|
|
owner *secp256k1fx.OutputOwners,
|
|
) (ids.ID, error) {
|
|
wallet, err := d.loadWallet(subnetID)
|
|
if err != nil {
|
|
return ids.Empty, err
|
|
}
|
|
if d.usingLedger {
|
|
ux.Logger.PrintToUser("*** Please sign X -> P Chain Import Transaction hash on the ledger device *** ")
|
|
}
|
|
xChainID := ids.FromStringOrPanic("2oYMBNV4eNHyqk2fjjV5nVQLDbtmNJzq5s3qs3Lo6ftnC6FByM") // X-Chain ID
|
|
|
|
tx, err := wallet.P().IssueImportTx(xChainID, owner)
|
|
if err == nil {
|
|
ux.Logger.PrintToUser("Import from X Chain Transaction successful, transaction ID: %s", tx.ID())
|
|
ux.Logger.PrintToUser("Now transforming subnet into elastic subnet ...")
|
|
}
|
|
if err != nil {
|
|
return ids.Empty, err
|
|
}
|
|
return tx.ID(), err
|
|
}
|
|
|
|
func (d *PublicDeployer) TransformSubnetTx(
|
|
controlKeys []string,
|
|
subnetAuthKeysStrs []string,
|
|
elasticSubnetConfig models.ElasticSubnetConfig,
|
|
subnetID ids.ID,
|
|
subnetAssetID ids.ID,
|
|
) (bool, ids.ID, *txs.Tx, []string, error) {
|
|
wallet, err := d.loadWallet(subnetID)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, err
|
|
}
|
|
subnetAuthKeys, err := address.ParseToIDs(subnetAuthKeysStrs)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, fmt.Errorf("failure parsing subnet auth keys: %w", err)
|
|
}
|
|
|
|
if d.usingLedger {
|
|
ux.Logger.PrintToUser("*** Please sign Transform Subnet hash on the ledger device *** ")
|
|
}
|
|
|
|
tx, err := d.createTransformSubnetTX(subnetAuthKeys, elasticSubnetConfig, wallet, subnetAssetID)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, err
|
|
}
|
|
_, remainingSubnetAuthKeys, err := txutils.GetRemainingSigners(tx, controlKeys)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, err
|
|
}
|
|
isFullySigned := len(remainingSubnetAuthKeys) == 0
|
|
|
|
if isFullySigned {
|
|
txID, err := d.Commit(tx)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, err
|
|
}
|
|
ux.Logger.PrintToUser("Transaction successful, transaction ID: %s", txID)
|
|
return true, txID, nil, nil, nil
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Partial tx created")
|
|
return false, ids.Empty, tx, remainingSubnetAuthKeys, nil
|
|
}
|
|
|
|
// removes a subnet validator from the given [subnet]
|
|
// - verifies that the wallet is one of the subnet auth keys (so as to sign the AddSubnetValidator tx)
|
|
// - if operation is multisig (len(subnetAuthKeysStrs) > 1):
|
|
// - creates a remove subnet validator tx
|
|
// - sets the change output owner to be a wallet address (if not, it may go to any other subnet auth address)
|
|
// - signs the tx with the wallet as the owner of fee outputs and a possible subnet auth key
|
|
// - if partially signed, returns the tx so that it can later on be signed by the rest of the subnet auth keys
|
|
// - if fully signed, issues it
|
|
func (d *PublicDeployer) RemoveValidator(
|
|
controlKeys []string,
|
|
subnetAuthKeysStrs []string,
|
|
subnetID ids.ID,
|
|
nodeID ids.NodeID,
|
|
) (bool, *txs.Tx, []string, error) {
|
|
wallet, err := d.loadWallet(subnetID)
|
|
if err != nil {
|
|
return false, nil, nil, err
|
|
}
|
|
subnetAuthKeys, err := address.ParseToIDs(subnetAuthKeysStrs)
|
|
if err != nil {
|
|
return false, nil, nil, fmt.Errorf("failure parsing subnet auth keys: %w", err)
|
|
}
|
|
|
|
if d.usingLedger {
|
|
ux.Logger.PrintToUser("*** Please sign tx hash on the ledger device *** ")
|
|
}
|
|
|
|
tx, err := d.createRemoveValidatorTX(subnetAuthKeys, nodeID, subnetID, wallet)
|
|
if err != nil {
|
|
return false, nil, nil, err
|
|
}
|
|
|
|
_, remainingSubnetAuthKeys, err := txutils.GetRemainingSigners(tx, controlKeys)
|
|
if err != nil {
|
|
return false, nil, nil, err
|
|
}
|
|
isFullySigned := len(remainingSubnetAuthKeys) == 0
|
|
|
|
if isFullySigned {
|
|
id, err := d.Commit(tx)
|
|
if err != nil {
|
|
return false, nil, nil, err
|
|
}
|
|
ux.Logger.PrintToUser("Transaction successful, transaction ID: %s", id)
|
|
return true, nil, nil, nil
|
|
}
|
|
|
|
ux.Logger.PrintToUser("Partial tx created")
|
|
return false, tx, remainingSubnetAuthKeys, nil
|
|
}
|
|
|
|
// - creates a subnet for [chain] using the given [controlKeys] and [threshold] as subnet authentication parameters
|
|
func (d *PublicDeployer) DeploySubnet(
|
|
controlKeys []string,
|
|
threshold uint32,
|
|
) (ids.ID, error) {
|
|
wallet, err := d.loadWallet()
|
|
if err != nil {
|
|
return ids.Empty, err
|
|
}
|
|
subnetID, err := d.createSubnetTx(controlKeys, threshold, wallet)
|
|
if err != nil {
|
|
return ids.Empty, err
|
|
}
|
|
ux.Logger.PrintToUser("Subnet has been created with ID: %s", subnetID.String())
|
|
time.Sleep(2 * time.Second)
|
|
return subnetID, nil
|
|
}
|
|
|
|
// creates a blockchain for the given [subnetID]
|
|
// - creates a create blockchain tx
|
|
// - sets the change output owner to be a wallet address (if not, it may go to any other subnet auth address)
|
|
// - signs the tx with the wallet as the owner of fee outputs and a possible subnet auth key
|
|
// - if partially signed, returns the tx so that it can later on be signed by the rest of the subnet auth keys
|
|
// - if fully signed, issues it
|
|
func (d *PublicDeployer) DeployBlockchain(
|
|
controlKeys []string,
|
|
subnetAuthKeysStrs []string,
|
|
subnetID ids.ID,
|
|
chain string,
|
|
genesis []byte,
|
|
) (bool, ids.ID, *txs.Tx, []string, error) {
|
|
ux.Logger.PrintToUser("Now creating blockchain...")
|
|
|
|
wallet, err := d.loadWallet(subnetID)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, err
|
|
}
|
|
|
|
vmID, err := utils.VMID(chain)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, fmt.Errorf("failed to create VM ID from %s: %w", chain, err)
|
|
}
|
|
|
|
subnetAuthKeys, err := address.ParseToIDs(subnetAuthKeysStrs)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, fmt.Errorf("failure parsing subnet auth keys: %w", err)
|
|
}
|
|
|
|
if d.usingLedger {
|
|
ux.Logger.PrintToUser("*** Please sign CreateChain transaction on the ledger device *** ")
|
|
}
|
|
|
|
tx, err := d.createBlockchainTx(subnetAuthKeys, chain, vmID, subnetID, genesis, wallet)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, err
|
|
}
|
|
|
|
_, remainingSubnetAuthKeys, err := txutils.GetRemainingSigners(tx, controlKeys)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, err
|
|
}
|
|
isFullySigned := len(remainingSubnetAuthKeys) == 0
|
|
|
|
id := ids.Empty
|
|
if isFullySigned {
|
|
id, err = d.Commit(tx)
|
|
if err != nil {
|
|
return false, ids.Empty, nil, nil, err
|
|
}
|
|
}
|
|
|
|
return isFullySigned, id, tx, remainingSubnetAuthKeys, nil
|
|
}
|
|
|
|
func (d *PublicDeployer) Commit(
|
|
tx *txs.Tx,
|
|
) (ids.ID, error) {
|
|
wallet, err := d.loadWallet()
|
|
if err != nil {
|
|
return ids.Empty, err
|
|
}
|
|
err = wallet.P().IssueTx(tx)
|
|
if err != nil {
|
|
return ids.Empty, err
|
|
}
|
|
return tx.ID(), nil
|
|
}
|
|
|
|
func (d *PublicDeployer) Sign(
|
|
tx *txs.Tx,
|
|
subnetAuthKeysStrs []string,
|
|
subnet ids.ID,
|
|
) error {
|
|
wallet, err := d.loadWallet(subnet)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
subnetAuthKeys, err := address.ParseToIDs(subnetAuthKeysStrs)
|
|
if err != nil {
|
|
return fmt.Errorf("failure parsing subnet auth keys: %w", err)
|
|
}
|
|
if ok := d.checkWalletHasSubnetAuthAddresses(subnetAuthKeys); !ok {
|
|
return ErrNoSubnetAuthKeysInWallet
|
|
}
|
|
if d.usingLedger {
|
|
txName := txutils.GetLedgerDisplayName(tx)
|
|
if len(txName) == 0 {
|
|
ux.Logger.PrintToUser("*** Please sign tx hash on the ledger device *** ")
|
|
} else {
|
|
ux.Logger.PrintToUser("*** Please sign %s transaction on the ledger device *** ", txName)
|
|
}
|
|
}
|
|
if err := d.signTx(tx, wallet); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *PublicDeployer) loadWallet(preloadTxs ...ids.ID) (primary.Wallet, error) {
|
|
ctx := context.Background()
|
|
|
|
var api string
|
|
switch d.network {
|
|
case models.Testnet:
|
|
api = constants.TestnetAPIEndpoint
|
|
case models.Mainnet:
|
|
api = constants.MainnetAPIEndpoint
|
|
case models.Local:
|
|
// used for E2E testing of public related paths
|
|
api = constants.LocalAPIEndpoint
|
|
default:
|
|
return primary.Wallet{}, fmt.Errorf("unsupported public network")
|
|
}
|
|
|
|
// Create empty EthKeychain if kc doesn't implement it
|
|
var ethKc c.EthKeychain
|
|
if ekc, ok := d.kc.(c.EthKeychain); ok {
|
|
ethKc = ekc
|
|
} else {
|
|
// Create a minimal EthKeychain implementation
|
|
ethKc = &emptyEthKeychain{}
|
|
}
|
|
wallet, err := primary.MakeWallet(ctx, api, d.kc, ethKc, primary.WalletConfig{})
|
|
if err != nil {
|
|
return primary.Wallet{}, err
|
|
}
|
|
return *wallet, nil
|
|
}
|
|
|
|
func (d *PublicDeployer) getMultisigTxOptions(subnetAuthKeys []ids.ShortID) []walletoptions.Option {
|
|
options := []walletoptions.Option{}
|
|
walletAddr := d.kc.Addresses().List()[0]
|
|
// addrs to use for signing
|
|
customAddrsSet := set.Set[ids.ShortID]{}
|
|
customAddrsSet.Add(walletAddr)
|
|
customAddrsSet.Add(subnetAuthKeys...)
|
|
options = append(options, walletoptions.WithCustomAddresses(customAddrsSet))
|
|
// set change to go to wallet addr (instead of any other subnet auth key)
|
|
changeOwner := &secp256k1fx.OutputOwners{
|
|
Threshold: 1,
|
|
Addrs: []ids.ShortID{walletAddr},
|
|
}
|
|
options = append(options, walletoptions.WithChangeOwner(changeOwner))
|
|
return options
|
|
}
|
|
|
|
func (d *PublicDeployer) createBlockchainTx(
|
|
subnetAuthKeys []ids.ShortID,
|
|
chainName string,
|
|
vmID,
|
|
subnetID ids.ID,
|
|
genesis []byte,
|
|
wallet primary.Wallet,
|
|
) (*txs.Tx, error) {
|
|
fxIDs := make([]ids.ID, 0)
|
|
options := d.getMultisigTxOptions(subnetAuthKeys)
|
|
// create tx
|
|
unsignedTx, err := wallet.P().Builder().NewCreateChainTx(
|
|
subnetID,
|
|
genesis,
|
|
vmID,
|
|
fxIDs,
|
|
chainName,
|
|
options...,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx := txs.Tx{Unsigned: unsignedTx}
|
|
// sign with current wallet
|
|
if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil {
|
|
return nil, err
|
|
}
|
|
return &tx, nil
|
|
}
|
|
|
|
func (d *PublicDeployer) createAddSubnetValidatorTx(
|
|
subnetAuthKeys []ids.ShortID,
|
|
validator *txs.SubnetValidator,
|
|
wallet primary.Wallet,
|
|
) (*txs.Tx, error) {
|
|
options := d.getMultisigTxOptions(subnetAuthKeys)
|
|
// create tx
|
|
unsignedTx, err := wallet.P().Builder().NewAddSubnetValidatorTx(validator, options...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx := txs.Tx{Unsigned: unsignedTx}
|
|
// sign with current wallet
|
|
if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil {
|
|
return nil, err
|
|
}
|
|
return &tx, nil
|
|
}
|
|
|
|
func (d *PublicDeployer) createRemoveValidatorTX(
|
|
subnetAuthKeys []ids.ShortID,
|
|
nodeID ids.NodeID,
|
|
subnetID ids.ID,
|
|
wallet primary.Wallet,
|
|
) (*txs.Tx, error) {
|
|
options := d.getMultisigTxOptions(subnetAuthKeys)
|
|
// create tx
|
|
unsignedTx, err := wallet.P().Builder().NewRemoveSubnetValidatorTx(nodeID, subnetID, options...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx := txs.Tx{Unsigned: unsignedTx}
|
|
// sign with current wallet
|
|
if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil {
|
|
return nil, err
|
|
}
|
|
return &tx, nil
|
|
}
|
|
|
|
func (d *PublicDeployer) createTransformSubnetTX(
|
|
subnetAuthKeys []ids.ShortID,
|
|
elasticSubnetConfig models.ElasticSubnetConfig,
|
|
wallet primary.Wallet,
|
|
assetID ids.ID,
|
|
) (*txs.Tx, error) {
|
|
options := d.getMultisigTxOptions(subnetAuthKeys)
|
|
// create tx
|
|
unsignedTx, err := wallet.P().Builder().NewTransformSubnetTx(elasticSubnetConfig.SubnetID, assetID,
|
|
elasticSubnetConfig.InitialSupply, elasticSubnetConfig.MaxSupply, elasticSubnetConfig.MinConsumptionRate,
|
|
elasticSubnetConfig.MaxConsumptionRate, elasticSubnetConfig.MinValidatorStake, elasticSubnetConfig.MaxValidatorStake,
|
|
elasticSubnetConfig.MinStakeDuration, elasticSubnetConfig.MaxStakeDuration, elasticSubnetConfig.MinDelegationFee,
|
|
elasticSubnetConfig.MinDelegatorStake, elasticSubnetConfig.MaxValidatorWeightFactor, elasticSubnetConfig.UptimeRequirement, options...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tx := txs.Tx{Unsigned: unsignedTx}
|
|
// sign with current wallet
|
|
if err := wallet.P().Signer().Sign(context.Background(), &tx); err != nil {
|
|
return nil, err
|
|
}
|
|
return &tx, nil
|
|
}
|
|
|
|
func (*PublicDeployer) signTx(
|
|
tx *txs.Tx,
|
|
wallet primary.Wallet,
|
|
) error {
|
|
if err := wallet.P().Signer().Sign(context.Background(), tx); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *PublicDeployer) createSubnetTx(controlKeys []string, threshold uint32, wallet primary.Wallet) (ids.ID, error) {
|
|
addrs, err := address.ParseToIDs(controlKeys)
|
|
if err != nil {
|
|
return ids.Empty, fmt.Errorf("failure parsing control keys: %w", err)
|
|
}
|
|
owners := &secp256k1fx.OutputOwners{
|
|
Addrs: addrs,
|
|
Threshold: threshold,
|
|
Locktime: 0,
|
|
}
|
|
opts := []walletoptions.Option{}
|
|
if d.usingLedger {
|
|
ux.Logger.PrintToUser("*** Please sign CreateSubnet transaction on the ledger device *** ")
|
|
}
|
|
tx, err := wallet.P().IssueCreateSubnetTx(owners, opts...)
|
|
if err != nil {
|
|
return ids.Empty, err
|
|
}
|
|
return tx.ID(), nil
|
|
}
|
|
|
|
func (d *PublicDeployer) getSubnetAuthAddressesInWallet(subnetAuth []ids.ShortID) []ids.ShortID {
|
|
walletAddrs := d.kc.Addresses().List()
|
|
subnetAuthInWallet := []ids.ShortID{}
|
|
for _, walletAddr := range walletAddrs {
|
|
for _, addr := range subnetAuth {
|
|
if addr == walletAddr {
|
|
subnetAuthInWallet = append(subnetAuthInWallet, addr)
|
|
}
|
|
}
|
|
}
|
|
return subnetAuthInWallet
|
|
}
|
|
|
|
// check that the wallet at least contain one subnet auth address
|
|
func (d *PublicDeployer) checkWalletHasSubnetAuthAddresses(subnetAuth []ids.ShortID) bool {
|
|
addrs := d.getSubnetAuthAddressesInWallet(subnetAuth)
|
|
return len(addrs) != 0
|
|
}
|
|
|
|
func IsSubnetValidator(subnetID ids.ID, nodeID ids.NodeID, network models.Network) (bool, error) {
|
|
var apiURL string
|
|
switch network {
|
|
case models.Mainnet:
|
|
apiURL = constants.MainnetAPIEndpoint
|
|
case models.Testnet:
|
|
apiURL = constants.TestnetAPIEndpoint
|
|
default:
|
|
return false, fmt.Errorf("invalid network: %s", network)
|
|
}
|
|
pClient := platformvm.NewClient(apiURL)
|
|
ctx, cancel := context.WithTimeout(context.Background(), constants.E2ERequestTimeout)
|
|
defer cancel()
|
|
|
|
vals, err := pClient.GetCurrentValidators(ctx, subnetID, []ids.NodeID{nodeID})
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to get current validators")
|
|
}
|
|
|
|
return !(len(vals) == 0), nil
|
|
}
|
|
|
|
func GetPublicSubnetValidators(subnetID ids.ID, network models.Network) ([]platformvm.ClientPermissionlessValidator, error) {
|
|
var apiURL string
|
|
switch network {
|
|
case models.Mainnet:
|
|
apiURL = constants.MainnetAPIEndpoint
|
|
case models.Testnet:
|
|
apiURL = constants.TestnetAPIEndpoint
|
|
default:
|
|
return nil, fmt.Errorf("invalid network: %s", network)
|
|
}
|
|
pClient := platformvm.NewClient(apiURL)
|
|
ctx, cancel := context.WithTimeout(context.Background(), constants.E2ERequestTimeout)
|
|
defer cancel()
|
|
|
|
vals, err := pClient.GetCurrentValidators(ctx, subnetID, []ids.NodeID{})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get current validators")
|
|
}
|
|
|
|
return vals, nil
|
|
}
|