code.vegaprotocol.io/vega@v0.79.0/cmd/vega/commands/announce_node.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package commands 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "net" 23 "strconv" 24 "time" 25 26 "code.vegaprotocol.io/vega/core/blockchain" 27 "code.vegaprotocol.io/vega/core/blockchain/abci" 28 "code.vegaprotocol.io/vega/core/config" 29 "code.vegaprotocol.io/vega/core/nodewallets" 30 "code.vegaprotocol.io/vega/core/txn" 31 "code.vegaprotocol.io/vega/core/validators" 32 vgcrypto "code.vegaprotocol.io/vega/libs/crypto" 33 vgjson "code.vegaprotocol.io/vega/libs/json" 34 "code.vegaprotocol.io/vega/logging" 35 "code.vegaprotocol.io/vega/paths" 36 api "code.vegaprotocol.io/vega/protos/vega/api/v1" 37 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 38 39 "github.com/jessevdk/go-flags" 40 "google.golang.org/grpc" 41 "google.golang.org/grpc/credentials/insecure" 42 ) 43 44 type AnnounceNodeCmd struct { 45 config.VegaHomeFlag 46 config.OutputFlag 47 config.Passphrase `long:"passphrase-file"` 48 49 InfoURL string `description:"An url to the website / information about this validator" long:"info-url" required:"true" short:"i"` 50 Country string `description:"The country from which the validator is operating" long:"country" required:"true" short:"c"` 51 Name string `description:"The name of this validator" long:"name" required:"true" short:"n"` 52 AvatarURL string `description:"A link to an avatar picture for this validator" long:"avatar-url" required:"true" short:"a"` 53 FromEpoch uint64 `description:"The epoch from which this validator should be ready to validate blocks" long:"from-epoch" required:"true" short:"f"` 54 SubmitterAddress string `description:"Ethereum address to use as a submitter to contract changes" long:"submitter-address" short:"s"` 55 } 56 57 var announceNodeCmd AnnounceNodeCmd 58 59 func (opts *AnnounceNodeCmd) Execute(_ []string) error { 60 log := logging.NewLoggerFromConfig(logging.NewDefaultConfig()) 61 defer log.AtExit() 62 63 output, err := opts.GetOutput() 64 if err != nil { 65 return err 66 } 67 68 registryPass, err := opts.Get("node wallet", false) 69 if err != nil { 70 return err 71 } 72 73 vegaPaths := paths.New(opts.VegaHome) 74 75 _, conf, err := config.EnsureNodeConfig(vegaPaths) 76 if err != nil { 77 return err 78 } 79 80 if !conf.IsValidator() { 81 return errors.New("node is not a validator") 82 } 83 84 nodeWallets, err := nodewallets.GetNodeWallets(conf.NodeWallet, vegaPaths, registryPass) 85 if err != nil { 86 return fmt.Errorf("couldn't get node wallets: %w", err) 87 } 88 89 // ensure the nodewallet is setup properly, if not we could not complete the command 90 if err := nodeWallets.Verify(); err != nil { 91 return fmt.Errorf("node wallet misconfigured: %w", err) 92 } 93 94 cmd := commandspb.AnnounceNode{ 95 Id: nodeWallets.Vega.ID().Hex(), 96 VegaPubKey: nodeWallets.Vega.PubKey().Hex(), 97 VegaPubKeyIndex: nodeWallets.Vega.Index(), 98 ChainPubKey: nodeWallets.Tendermint.Pubkey, 99 EthereumAddress: vgcrypto.EthereumChecksumAddress(nodeWallets.Ethereum.PubKey().Hex()), 100 FromEpoch: opts.FromEpoch, 101 InfoUrl: opts.InfoURL, 102 Name: opts.Name, 103 AvatarUrl: opts.AvatarURL, 104 Country: opts.Country, 105 SubmitterAddress: opts.SubmitterAddress, 106 } 107 108 if len(cmd.SubmitterAddress) != 0 { 109 cmd.SubmitterAddress = vgcrypto.EthereumChecksumAddress(cmd.SubmitterAddress) 110 } 111 112 if err := validators.SignAnnounceNode( 113 &cmd, nodeWallets.Vega, nodeWallets.Ethereum, 114 ); err != nil { 115 return err 116 } 117 118 // now we are OK, send the command 119 120 commander, blockData, cfunc, err := getNodeWalletCommander(log, registryPass, vegaPaths) 121 if err != nil { 122 return fmt.Errorf("failed to get commander: %w", err) 123 } 124 defer cfunc() 125 126 tid := vgcrypto.RandomHash() 127 powNonce, _, err := vgcrypto.PoW(blockData.Hash, tid, uint(blockData.SpamPowDifficulty), vgcrypto.Sha3) 128 if err != nil { 129 return fmt.Errorf("failed to get commander: %w", err) 130 } 131 132 pow := &commandspb.ProofOfWork{ 133 Tid: tid, 134 Nonce: powNonce, 135 } 136 137 var txHash string 138 ch := make(chan struct{}) 139 commander.CommandWithPoW( 140 context.Background(), 141 txn.AnnounceNodeCommand, 142 &cmd, 143 func(h string, e error) { 144 txHash, err = h, e 145 close(ch) 146 }, nil, pow) 147 148 <-ch 149 if err != nil { 150 return err 151 } 152 153 if output.IsHuman() { 154 fmt.Printf("node successfully announced.\ntxHash: %s\nvega signature: %v\nethereum signature: %v\n", 155 txHash, 156 cmd.VegaSignature.Value, 157 cmd.EthereumSignature.Value, 158 ) 159 } else if output.IsJSON() { 160 return vgjson.Print(struct { 161 TxHash string `json:"txHash"` 162 EthereumSignature string `json:"ethereumSignature"` 163 VegaSignature string `json:"vegaSignature"` 164 }{ 165 TxHash: txHash, 166 EthereumSignature: cmd.EthereumSignature.Value, 167 VegaSignature: cmd.VegaSignature.Value, 168 }) 169 } 170 171 return nil 172 } 173 174 func AnnounceNode(ctx context.Context, parser *flags.Parser) error { 175 announceNodeCmd = AnnounceNodeCmd{} 176 177 var ( 178 short = "Announce a node as a potential validator to the network" 179 long = "Announce a node as a potential validator to the network" 180 ) 181 _, err := parser.AddCommand("announce_node", short, long, &announceNodeCmd) 182 return err 183 } 184 185 func getNodeWalletCommander(log *logging.Logger, registryPass string, vegaPaths paths.Paths) (*nodewallets.Commander, *api.LastBlockHeightResponse, context.CancelFunc, error) { 186 _, cfg, err := config.EnsureNodeConfig(vegaPaths) 187 if err != nil { 188 return nil, nil, nil, err 189 } 190 191 vegaWallet, err := nodewallets.GetVegaWallet(vegaPaths, registryPass) 192 if err != nil { 193 return nil, nil, nil, fmt.Errorf("couldn't get Vega node wallet: %w", err) 194 } 195 196 rpcAddr := cfg.Blockchain.Tendermint.RPCAddr 197 if len(rpcAddr) <= 0 { 198 rpcAddr = "tcp://127.0.0.1:26657" 199 log.Warn("Blockchain.Tendermint.RPCAddr is empty, using default", logging.String("address", rpcAddr)) 200 } 201 202 abciClient, err := abci.NewClient(rpcAddr) 203 if err != nil { 204 return nil, nil, nil, fmt.Errorf("couldn't initialise ABCI client: %w", err) 205 } 206 207 coreClient, err := getCoreClient(net.JoinHostPort(cfg.API.IP, strconv.Itoa(cfg.API.Port))) 208 if err != nil { 209 return nil, nil, nil, fmt.Errorf("couldn't connect to node: %w", err) 210 } 211 212 ctx, cancel := timeoutContext() 213 resp, err := coreClient.LastBlockHeight(ctx, &api.LastBlockHeightRequest{}) 214 if err != nil { 215 return nil, nil, nil, fmt.Errorf("couldn't get last block height: %w", err) 216 } 217 218 commander, err := nodewallets.NewCommander(cfg.NodeWallet, log, blockchain.NewClientWithImpl(abciClient), vegaWallet, heightProvider{height: resp.Height}) 219 if err != nil { 220 return nil, nil, nil, fmt.Errorf("couldn't initialise node wallet commander: %w", err) 221 } 222 223 return commander, resp, cancel, nil 224 } 225 226 type heightProvider struct { 227 height uint64 228 } 229 230 func (h heightProvider) Height() uint64 { 231 return h.height 232 } 233 234 func getCoreClient(address string) (api.CoreServiceClient, error) { 235 tdconn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials())) 236 if err != nil { 237 return nil, err 238 } 239 return api.NewCoreServiceClient(tdconn), nil 240 } 241 242 func timeoutContext() (context.Context, func()) { 243 return context.WithTimeout(context.Background(), 5*time.Second) 244 }