node/cmd/guardiand: governance template improvements

The template commands now support generating full governance messages
with all required fields. Outputs to stdout instead of a file.

Change-Id: I3837107c3075363a54d31f9dfb4d6dc07c79daa5
This commit is contained in:
Leo 2021-10-29 17:17:11 +02:00 committed by Leopold Schabel
parent 964566c559
commit d5f6540656
2 changed files with 140 additions and 47 deletions

View File

@ -1,10 +1,17 @@
package guardiand package guardiand
import ( import (
"encoding/hex"
"fmt" "fmt"
"github.com/btcsuite/btcutil/bech32"
"github.com/certusone/wormhole/node/pkg/vaa"
"github.com/ethereum/go-ethereum/common"
"github.com/mr-tron/base58"
"github.com/spf13/pflag"
"github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/libs/rand"
"io/ioutil"
"log" "log"
"strconv"
"strings"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -16,14 +23,31 @@ import (
var setUpdateNumGuardians *int var setUpdateNumGuardians *int
var templateGuardianIndex *int var templateGuardianIndex *int
var chainID *string
var address *string
var module *string
func init() { func init() {
governanceFlagSet := pflag.NewFlagSet("governance", pflag.ExitOnError)
chainID = governanceFlagSet.String("chain-id", "", "Chain ID")
address = governanceFlagSet.String("new-address", "", "New address (hex, base58 or bech32)")
moduleFlagSet := pflag.NewFlagSet("module", pflag.ExitOnError)
module = moduleFlagSet.String("module", "", "Module name")
templateGuardianIndex = TemplateCmd.PersistentFlags().Int("idx", 0, "Default current guardian set index") templateGuardianIndex = TemplateCmd.PersistentFlags().Int("idx", 0, "Default current guardian set index")
setUpdateNumGuardians = AdminClientGuardianSetTemplateCmd.Flags().Int("num", 1, "Number of devnet guardians in example file") setUpdateNumGuardians = AdminClientGuardianSetTemplateCmd.Flags().Int("num", 1, "Number of devnet guardians in example file")
TemplateCmd.AddCommand(AdminClientGuardianSetTemplateCmd) AdminClientContractUpgradeTemplateCmd.Flags().AddFlagSet(governanceFlagSet)
TemplateCmd.AddCommand(AdminClientContractUpgradeTemplateCmd) TemplateCmd.AddCommand(AdminClientContractUpgradeTemplateCmd)
AdminClientTokenBridgeRegisterChainCmd.Flags().AddFlagSet(governanceFlagSet)
AdminClientTokenBridgeRegisterChainCmd.Flags().AddFlagSet(moduleFlagSet)
TemplateCmd.AddCommand(AdminClientTokenBridgeRegisterChainCmd) TemplateCmd.AddCommand(AdminClientTokenBridgeRegisterChainCmd)
AdminClientTokenBridgeUpgradeContractCmd.Flags().AddFlagSet(governanceFlagSet)
AdminClientTokenBridgeUpgradeContractCmd.Flags().AddFlagSet(moduleFlagSet)
TemplateCmd.AddCommand(AdminClientTokenBridgeUpgradeContractCmd) TemplateCmd.AddCommand(AdminClientTokenBridgeUpgradeContractCmd)
} }
@ -33,36 +57,30 @@ var TemplateCmd = &cobra.Command{
} }
var AdminClientGuardianSetTemplateCmd = &cobra.Command{ var AdminClientGuardianSetTemplateCmd = &cobra.Command{
Use: "guardian-set-update [FILENAME]", Use: "guardian-set-update",
Short: "Generate an empty guardian set template at specified path (offline)", Short: "Generate an empty guardian set template",
Run: runGuardianSetTemplate, Run: runGuardianSetTemplate,
Args: cobra.ExactArgs(1),
} }
var AdminClientContractUpgradeTemplateCmd = &cobra.Command{ var AdminClientContractUpgradeTemplateCmd = &cobra.Command{
Use: "contract-upgrade [FILENAME]", Use: "contract-upgrade",
Short: "Generate an empty contract upgrade template at specified path (offline)", Short: "Generate an empty contract upgrade template",
Run: runContractUpgradeTemplate, Run: runContractUpgradeTemplate,
Args: cobra.ExactArgs(1),
} }
var AdminClientTokenBridgeRegisterChainCmd = &cobra.Command{ var AdminClientTokenBridgeRegisterChainCmd = &cobra.Command{
Use: "token-bridge-register-chain [FILENAME]", Use: "token-bridge-register-chain",
Short: "Generate an empty token bridge chain registration template at specified path (offline)", Short: "Generate an empty token bridge chain registration template at specified path",
Run: runTokenBridgeRegisterChainTemplate, Run: runTokenBridgeRegisterChainTemplate,
Args: cobra.ExactArgs(1),
} }
var AdminClientTokenBridgeUpgradeContractCmd = &cobra.Command{ var AdminClientTokenBridgeUpgradeContractCmd = &cobra.Command{
Use: "token-bridge-upgrade-contract [FILENAME]", Use: "token-bridge-upgrade-contract",
Short: "Generate an empty token bridge contract upgrade template at specified path (offline)", Short: "Generate an empty token bridge contract upgrade template at specified path",
Run: runTokenBridgeUpgradeContractTemplate, Run: runTokenBridgeUpgradeContractTemplate,
Args: cobra.ExactArgs(1),
} }
func runGuardianSetTemplate(cmd *cobra.Command, args []string) { func runGuardianSetTemplate(cmd *cobra.Command, args []string) {
path := args[0]
// Use deterministic devnet addresses as examples in the template, such that this doubles as a test fixture. // Use deterministic devnet addresses as examples in the template, such that this doubles as a test fixture.
guardians := make([]*nodev1.GuardianSetUpdate_Guardian, *setUpdateNumGuardians) guardians := make([]*nodev1.GuardianSetUpdate_Guardian, *setUpdateNumGuardians)
for i := 0; i < *setUpdateNumGuardians; i++ { for i := 0; i < *setUpdateNumGuardians; i++ {
@ -75,7 +93,7 @@ func runGuardianSetTemplate(cmd *cobra.Command, args []string) {
m := &nodev1.InjectGovernanceVAARequest{ m := &nodev1.InjectGovernanceVAARequest{
CurrentSetIndex: uint32(*templateGuardianIndex), CurrentSetIndex: uint32(*templateGuardianIndex),
Sequence: 1234, Sequence: rand.Uint64(),
Nonce: rand.Uint32(), Nonce: rand.Uint32(),
Payload: &nodev1.InjectGovernanceVAARequest_GuardianSet{ Payload: &nodev1.InjectGovernanceVAARequest_GuardianSet{
GuardianSet: &nodev1.GuardianSetUpdate{Guardians: guardians}, GuardianSet: &nodev1.GuardianSetUpdate{Guardians: guardians},
@ -86,15 +104,18 @@ func runGuardianSetTemplate(cmd *cobra.Command, args []string) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
fmt.Print(string(b))
err = ioutil.WriteFile(path, b, 0640)
if err != nil {
log.Fatal(err)
}
} }
func runContractUpgradeTemplate(cmd *cobra.Command, args []string) { func runContractUpgradeTemplate(cmd *cobra.Command, args []string) {
path := args[0] address, err := parseAddress(*address)
if err != nil {
log.Fatal(err)
}
chainID, err := parseChainID(*chainID)
if err != nil {
log.Fatal(err)
}
m := &nodev1.InjectGovernanceVAARequest{ m := &nodev1.InjectGovernanceVAARequest{
CurrentSetIndex: uint32(*templateGuardianIndex), CurrentSetIndex: uint32(*templateGuardianIndex),
@ -102,8 +123,8 @@ func runContractUpgradeTemplate(cmd *cobra.Command, args []string) {
Nonce: rand.Uint32(), Nonce: rand.Uint32(),
Payload: &nodev1.InjectGovernanceVAARequest_ContractUpgrade{ Payload: &nodev1.InjectGovernanceVAARequest_ContractUpgrade{
ContractUpgrade: &nodev1.ContractUpgrade{ ContractUpgrade: &nodev1.ContractUpgrade{
ChainId: 1, ChainId: uint32(chainID),
NewContract: "0000000000000000000000000290FB167208Af455bB137780163b7B7a9a10C16", NewContract: address,
}, },
}, },
} }
@ -112,14 +133,17 @@ func runContractUpgradeTemplate(cmd *cobra.Command, args []string) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
fmt.Print(string(b))
err = ioutil.WriteFile(path, b, 0640) }
func runTokenBridgeRegisterChainTemplate(cmd *cobra.Command, args []string) {
address, err := parseAddress(*address)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
chainID, err := parseChainID(*chainID)
if err != nil {
log.Fatal(err)
} }
func runTokenBridgeRegisterChainTemplate(cmd *cobra.Command, args []string) {
path := args[0]
m := &nodev1.InjectGovernanceVAARequest{ m := &nodev1.InjectGovernanceVAARequest{
CurrentSetIndex: uint32(*templateGuardianIndex), CurrentSetIndex: uint32(*templateGuardianIndex),
@ -127,9 +151,9 @@ func runTokenBridgeRegisterChainTemplate(cmd *cobra.Command, args []string) {
Nonce: rand.Uint32(), Nonce: rand.Uint32(),
Payload: &nodev1.InjectGovernanceVAARequest_BridgeRegisterChain{ Payload: &nodev1.InjectGovernanceVAARequest_BridgeRegisterChain{
BridgeRegisterChain: &nodev1.BridgeRegisterChain{ BridgeRegisterChain: &nodev1.BridgeRegisterChain{
Module: "TokenBridge", Module: *module,
ChainId: 5, ChainId: uint32(chainID),
EmitterAddress: "0000000000000000000000000290FB167208Af455bB137780163b7B7a9a10C16", EmitterAddress: address,
}, },
}, },
} }
@ -138,15 +162,18 @@ func runTokenBridgeRegisterChainTemplate(cmd *cobra.Command, args []string) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
fmt.Print(string(b))
err = ioutil.WriteFile(path, b, 0640)
if err != nil {
log.Fatal(err)
}
} }
func runTokenBridgeUpgradeContractTemplate(cmd *cobra.Command, args []string) { func runTokenBridgeUpgradeContractTemplate(cmd *cobra.Command, args []string) {
path := args[0] address, err := parseAddress(*address)
if err != nil {
log.Fatal(err)
}
chainID, err := parseChainID(*chainID)
if err != nil {
log.Fatal(err)
}
m := &nodev1.InjectGovernanceVAARequest{ m := &nodev1.InjectGovernanceVAARequest{
CurrentSetIndex: uint32(*templateGuardianIndex), CurrentSetIndex: uint32(*templateGuardianIndex),
@ -154,9 +181,9 @@ func runTokenBridgeUpgradeContractTemplate(cmd *cobra.Command, args []string) {
Nonce: rand.Uint32(), Nonce: rand.Uint32(),
Payload: &nodev1.InjectGovernanceVAARequest_BridgeContractUpgrade{ Payload: &nodev1.InjectGovernanceVAARequest_BridgeContractUpgrade{
BridgeContractUpgrade: &nodev1.BridgeUpgradeContract{ BridgeContractUpgrade: &nodev1.BridgeUpgradeContract{
Module: "TokenBridge", Module: *module,
TargetChainId: 5, TargetChainId: uint32(chainID),
NewContract: "0000000000000000000000000290FB167208Af455bB137780163b7B7a9a10C16", NewContract: address,
}, },
}, },
} }
@ -165,9 +192,55 @@ func runTokenBridgeUpgradeContractTemplate(cmd *cobra.Command, args []string) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
fmt.Print(string(b))
}
err = ioutil.WriteFile(path, b, 0640) // parseAddress parses either a hex-encoded address and returns
// a left-padded 32 byte hex string.
func parseAddress(s string) (string, error) {
// try base58
b, err := base58.Decode(s)
if err == nil {
return leftPadAddress(b)
}
// try bech32
_, b, err = bech32.Decode(s)
if err == nil {
return leftPadAddress(b)
}
// try hex
if len(s) > 2 && strings.ToLower(s[:2]) == "0x" {
s = s[2:]
}
a, err := hex.DecodeString(s)
if err != nil { if err != nil {
log.Fatal(err) return "", fmt.Errorf("invalid hex address: %v", err)
} }
return leftPadAddress(a)
}
func leftPadAddress(a []byte) (string, error) {
if len(a) > 32 {
return "", fmt.Errorf("address longer than 32 bytes")
}
return hex.EncodeToString(common.LeftPadBytes(a, 32)), nil
}
// parseChainID parses a human-readable chain name or a chain ID.
func parseChainID(name string) (vaa.ChainID, error) {
s, err := vaa.ChainIDFromString(name)
if err == nil {
return s, nil
}
// parse as uint32
i, err := strconv.ParseUint(name, 10, 32)
if err != nil {
return 0, fmt.Errorf("failed to parse as name or uint32: %v", err)
}
return vaa.ChainID(i), nil
} }

View File

@ -7,6 +7,7 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io" "io"
"strings"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -98,6 +99,25 @@ func (c ChainID) String() string {
} }
} }
func ChainIDFromString(s string) (ChainID, error) {
s = strings.ToLower(s)
switch s {
case "solana":
return ChainIDSolana, nil
case "ethereum":
return ChainIDEthereum, nil
case "terra":
return ChainIDTerra, nil
case "bsc":
return ChainIDBSC, nil
case "polygon":
return ChainIDPolygon, nil
default:
return ChainIDUnset, fmt.Errorf("unknown chain ID: %s", s)
}
}
const ( const (
ChainIDUnset ChainID = 0 ChainIDUnset ChainID = 0
// ChainIDSolana is the ChainID of Solana // ChainIDSolana is the ChainID of Solana