445 lines
12 KiB
Go
445 lines
12 KiB
Go
package cli
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
|
// "github.com/cosmos/cosmos-sdk/client/flags"
|
|
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/wormhole-foundation/wormchain/x/wormhole/types"
|
|
wormholesdk "github.com/wormhole-foundation/wormhole/sdk"
|
|
"github.com/wormhole-foundation/wormhole/sdk/vaa"
|
|
)
|
|
|
|
// normal vaa flags
|
|
const FLAG_KEY = "key"
|
|
const FLAG_EMITTER_CHAIN = "emitter-chain"
|
|
const FLAG_INDEX = "index"
|
|
const FLAG_SEQUENCE = "sequence"
|
|
const FLAG_NONCE = "nonce"
|
|
const FLAG_PAYLOAD = "payload"
|
|
|
|
// governance vaa flags
|
|
const FLAG_MODULE = "module"
|
|
const FLAG_ACTION = "action"
|
|
const FLAG_CHAIN = "chain"
|
|
const FLAG_PUBLIC_KEY = "public-key"
|
|
const FLAG_NEXT_INDEX = "next-index"
|
|
|
|
// GetGenesisCmd returns the genesis related commands for this module
|
|
func GetGenesisCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "genesis",
|
|
Short: fmt.Sprintf("%s genesis subcommands", types.ModuleName),
|
|
DisableFlagParsing: true,
|
|
SuggestionsMinimumDistance: 2,
|
|
RunE: client.ValidateCmd,
|
|
}
|
|
|
|
cmd.AddCommand(CmdGenerateTestGuardianKey())
|
|
cmd.AddCommand(CmdDecodeAddress())
|
|
cmd.AddCommand(CmdGenerateVaa())
|
|
cmd.AddCommand(CmdGenerateGovernanceVaa())
|
|
cmd.AddCommand(CmdGenerateGuardianSetUpdatea())
|
|
cmd.AddCommand(CmdTestSignAddress())
|
|
|
|
return cmd
|
|
}
|
|
|
|
func CmdGenerateTestGuardianKey() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "generate-test-guardian-keypair [output-private-key.hex] [address.hex]",
|
|
Short: "Generate a guardian keypair for testing use",
|
|
Args: cobra.ExactArgs(2),
|
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
|
outPrivatePath := args[0]
|
|
outPublicPath := args[1]
|
|
|
|
// https://ethereum.org/en/developers/docs/accounts/#account-creation)
|
|
|
|
key, err := crypto.GenerateKey()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
|
private_key := [32]byte{}
|
|
key.D.FillBytes(private_key[:])
|
|
|
|
err = ioutil.WriteFile(outPrivatePath, []byte(hex.EncodeToString(private_key[:])), 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ioutil.WriteFile(outPublicPath, []byte(hex.EncodeToString(addr.Bytes())), 0644)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
return cmd
|
|
}
|
|
|
|
func CmdDecodeAddress() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "decode-address [address]",
|
|
Short: "Decode an address from either account, validator, or evm format",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
|
addrString := args[0]
|
|
if strings.HasPrefix(addrString, sdk.GetConfig().GetBech32AccountAddrPrefix()) {
|
|
addr, err := sdk.AccAddressFromBech32(addrString)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
fmt.Println(base64.StdEncoding.EncodeToString(addr))
|
|
} else if strings.HasPrefix(addrString, sdk.GetConfig().GetBech32ValidatorAddrPrefix()) {
|
|
addr, err := sdk.AccAddressFromBech32(addrString)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
fmt.Println(base64.StdEncoding.EncodeToString(addr))
|
|
} else {
|
|
// treat as hex
|
|
addr, err := hex.DecodeString(strings.TrimPrefix(addrString, "0x"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(base64.StdEncoding.EncodeToString(addr))
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
return cmd
|
|
}
|
|
|
|
func ImportKeyFromFile(filePath string) (*ecdsa.PrivateKey, error) {
|
|
priv := ecdsa.PrivateKey{}
|
|
bz, err := ioutil.ReadFile(filePath)
|
|
if err != nil {
|
|
return &priv, err
|
|
}
|
|
return ImportKeyFromHex(string(bz))
|
|
}
|
|
|
|
func ImportPublicKeyFromFile(filePath string) ([]byte, error) {
|
|
hexBz, err := ioutil.ReadFile(filePath)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
hexStr := string(hexBz)
|
|
bz, err := hex.DecodeString(hexStr)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
return bz, nil
|
|
}
|
|
|
|
func ImportKeyFromHex(privHex string) (*ecdsa.PrivateKey, error) {
|
|
priv := ecdsa.PrivateKey{}
|
|
priv_bz, err := hex.DecodeString(privHex)
|
|
if err != nil {
|
|
return &priv, err
|
|
}
|
|
k := big.NewInt(0)
|
|
k.SetBytes(priv_bz)
|
|
|
|
priv.PublicKey.Curve = crypto.S256()
|
|
priv.D = k
|
|
priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(k.Bytes())
|
|
return &priv, nil
|
|
}
|
|
|
|
func parseVaaFromFlags(cmd *cobra.Command) (vaa.VAA, error) {
|
|
var GOVERNANCE_EMITTER = [32]byte{00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 04}
|
|
|
|
emitterChain, err := cmd.Flags().GetUint16(FLAG_EMITTER_CHAIN)
|
|
if err != nil {
|
|
return vaa.VAA{}, err
|
|
}
|
|
index, err := cmd.Flags().GetUint32(FLAG_INDEX)
|
|
if err != nil {
|
|
return vaa.VAA{}, err
|
|
}
|
|
nonce, err := cmd.Flags().GetUint32(FLAG_NONCE)
|
|
if err != nil {
|
|
return vaa.VAA{}, err
|
|
}
|
|
seq, err := cmd.Flags().GetUint64(FLAG_SEQUENCE)
|
|
if err != nil {
|
|
return vaa.VAA{}, err
|
|
}
|
|
payloadHex, err := cmd.Flags().GetString(FLAG_PAYLOAD)
|
|
if err != nil {
|
|
return vaa.VAA{}, err
|
|
}
|
|
payload, err := hex.DecodeString(payloadHex)
|
|
if err != nil {
|
|
return vaa.VAA{}, err
|
|
}
|
|
|
|
v := vaa.VAA{
|
|
Version: uint8(1),
|
|
GuardianSetIndex: index,
|
|
Signatures: nil,
|
|
Timestamp: time.Now(),
|
|
Nonce: nonce,
|
|
Sequence: seq,
|
|
ConsistencyLevel: uint8(32),
|
|
EmitterChain: vaa.ChainID(emitterChain),
|
|
EmitterAddress: vaa.Address(GOVERNANCE_EMITTER),
|
|
Payload: payload,
|
|
}
|
|
return v, nil
|
|
|
|
}
|
|
|
|
func addVaaFlags(cmd *cobra.Command) {
|
|
cmd.Flags().StringArray(FLAG_KEY, []string{}, "guardian private key file(s) to sign with (hex format) in order.")
|
|
cmd.Flags().Uint16(FLAG_EMITTER_CHAIN, 0, "emitter chain")
|
|
cmd.Flags().Uint32(FLAG_INDEX, 0, "guardian set index")
|
|
cmd.Flags().Uint64(FLAG_SEQUENCE, 0, "sequence number")
|
|
cmd.Flags().Uint32(FLAG_NONCE, 0, "nonce")
|
|
cmd.Flags().String(FLAG_PAYLOAD, "", "payload (hex format)")
|
|
}
|
|
func addGovVaaFlags(cmd *cobra.Command) {
|
|
cmd.Flags().String(FLAG_MODULE, "", "module (ascii string)")
|
|
cmd.Flags().Uint8(FLAG_ACTION, 0, "action")
|
|
cmd.Flags().Uint16(FLAG_CHAIN, 0, "chain")
|
|
}
|
|
|
|
func CmdGenerateVaa() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "generate-vaa",
|
|
Short: "generate and sign a vaa with any payload",
|
|
Args: cobra.ExactArgs(0),
|
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
|
|
|
privateKeys := []*ecdsa.PrivateKey{}
|
|
privateKeysFiles, err := cmd.Flags().GetStringArray(FLAG_KEY)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, privFile := range privateKeysFiles {
|
|
priv, err := ImportKeyFromFile(privFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
privateKeys = append(privateKeys, priv)
|
|
}
|
|
v, err := parseVaaFromFlags(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i, key := range privateKeys {
|
|
v.AddSignature(key, uint8(i))
|
|
}
|
|
|
|
v_bz, err := v.Marshal()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(hex.EncodeToString(v_bz))
|
|
|
|
return nil
|
|
},
|
|
}
|
|
addVaaFlags(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
func CmdGenerateGovernanceVaa() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "generate-gov-vaa",
|
|
Short: "generate and sign a governance vaa with any payload",
|
|
Args: cobra.ExactArgs(0),
|
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
|
|
|
privateKeys := []*ecdsa.PrivateKey{}
|
|
privateKeysFiles, err := cmd.Flags().GetStringArray(FLAG_KEY)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, privFile := range privateKeysFiles {
|
|
priv, err := ImportKeyFromFile(privFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
privateKeys = append(privateKeys, priv)
|
|
}
|
|
v, err := parseVaaFromFlags(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
gov_payload := v.Payload
|
|
moduleString, err := cmd.Flags().GetString(FLAG_MODULE)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
action, err := cmd.Flags().GetUint8(FLAG_ACTION)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
chain, err := cmd.Flags().GetUint16(FLAG_CHAIN)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
module := [32]byte{}
|
|
copy(module[len(module)-len(moduleString):], []byte(moduleString))
|
|
msg := types.NewGovernanceMessage(module, action, chain, gov_payload)
|
|
v.Payload = msg.MarshalBinary()
|
|
v.EmitterChain = 1
|
|
|
|
for i, key := range privateKeys {
|
|
v.AddSignature(key, uint8(i))
|
|
// address := crypto.PubkeyToAddress(privateKeys[0].PublicKey)
|
|
// fmt.Println("signed using ", hex.EncodeToString(address[:]))
|
|
}
|
|
|
|
v_bz, err := v.Marshal()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(hex.EncodeToString(v_bz))
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
addVaaFlags(cmd)
|
|
addGovVaaFlags(cmd)
|
|
|
|
return cmd
|
|
}
|
|
|
|
func CmdGenerateGuardianSetUpdatea() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "generate-guardian-update-vaa",
|
|
Short: "generate and sign a governance vaa with any payload",
|
|
Args: cobra.ExactArgs(0),
|
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
|
|
|
privateKeys := []*ecdsa.PrivateKey{}
|
|
privateKeysFiles, err := cmd.Flags().GetStringArray(FLAG_KEY)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, privFile := range privateKeysFiles {
|
|
priv, err := ImportKeyFromFile(privFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
privateKeys = append(privateKeys, priv)
|
|
}
|
|
v, err := parseVaaFromFlags(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
next_index, err := cmd.Flags().GetUint32(FLAG_NEXT_INDEX)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
publicKeys := [][]byte{}
|
|
pubKeysFiles, err := cmd.Flags().GetStringArray(FLAG_PUBLIC_KEY)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, pubFile := range pubKeysFiles {
|
|
pubBz, err := ImportPublicKeyFromFile(pubFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
publicKeys = append(publicKeys, pubBz)
|
|
}
|
|
set_update := make([]byte, 4)
|
|
binary.BigEndian.PutUint32(set_update, next_index)
|
|
set_update = append(set_update, uint8(len(pubKeysFiles)))
|
|
// Add keys to set_update
|
|
for _, pubkey := range publicKeys {
|
|
set_update = append(set_update, pubkey...)
|
|
}
|
|
|
|
action := vaa.ActionGuardianSetUpdate
|
|
chain := 3104
|
|
module := [32]byte{}
|
|
copy(module[:], vaa.CoreModule)
|
|
msg := types.NewGovernanceMessage(module, byte(action), uint16(chain), set_update)
|
|
v.Payload = msg.MarshalBinary()
|
|
v.EmitterChain = 1
|
|
|
|
for i, key := range privateKeys {
|
|
v.AddSignature(key, uint8(i))
|
|
}
|
|
|
|
v_bz, err := v.Marshal()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(hex.EncodeToString(v_bz))
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
addVaaFlags(cmd)
|
|
cmd.Flags().StringArray(FLAG_PUBLIC_KEY, []string{}, "guardian public key file(s) to include in new set (hex/evm format) in order.")
|
|
cmd.Flags().Uint32(FLAG_NEXT_INDEX, 0, "next guardian set index")
|
|
|
|
return cmd
|
|
}
|
|
|
|
func CmdTestSignAddress() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "test-sign-address",
|
|
Short: "Test method sign the validator address to use for registering as a guardian. Use guardiand for production, not this method. Read guardian key as hex in $GUARDIAN_KEY env variable. use --from to indicate address to sign.",
|
|
Args: cobra.ExactArgs(0),
|
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
|
clientCtx, err := client.GetClientTxContext(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
info, err := clientCtx.Keyring.Key(clientCtx.From)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
keyHex := os.Getenv("GUARDIAN_KEY")
|
|
key, err := ImportKeyFromHex(keyHex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
addr := info.GetAddress()
|
|
addrHash := crypto.Keccak256Hash(wormholesdk.SignedWormchainAddressPrefix, addr)
|
|
sig, err := crypto.Sign(addrHash[:], key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Println(hex.EncodeToString(sig))
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
flags.AddTxFlagsToCmd(cmd)
|
|
|
|
return cmd
|
|
}
|