Skip to content

Commit

Permalink
One config file to rule them all and in cryptography bind them (#147)
Browse files Browse the repository at this point in the history
* One config file for all signers

* Update docs

* Fix test

* Fix cosigner cmd

* Bump test timeout

* handle feedback on naming

* lint

* fix testdata rsa_keys.json

* fix cosigners from flag test

* consolidate start command. panic if config is v2 format
  • Loading branch information
agouin committed May 10, 2023
1 parent 955f172 commit 69a88ab
Show file tree
Hide file tree
Showing 51 changed files with 1,645 additions and 1,504 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
build
data
private_share_*
*_shard.json
rsa_keys.json
priv_validator_key.json
dist/
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ build-linux:
@GOOS=linux GOARCH=amd64 go build --mod readonly $(BUILD_FLAGS) -o ./build/horcrux ./cmd/horcrux

test:
@go test -race -timeout 20m -mod readonly -v ./...
@go test -race -timeout 30m -mod readonly -v ./...

test-short:
@go test -mod readonly -run TestDownedSigners2of3 -v ./...
Expand Down
2 changes: 1 addition & 1 deletion client/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func SanitizeAddress(address string) (string, error) {
u, err := url.Parse(address)
if err != nil {
return "", fmt.Errorf("error parsing peer URL: %w", err)
return "", fmt.Errorf("error parsing URL: %w", err)
}

return u.Host, nil
Expand Down
109 changes: 109 additions & 0 deletions cmd/horcrux/cmd/address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package cmd

import (
"encoding/hex"
"encoding/json"
"fmt"
"strings"

"github.com/cometbft/cometbft/crypto"
cometprivval "github.com/cometbft/cometbft/privval"
"github.com/cosmos/cosmos-sdk/types/bech32"
"github.com/spf13/cobra"
"github.com/strangelove-ventures/horcrux/signer"
)

type AddressCmdOutput struct {
HexAddress string
PubKey string
ValConsAddress string
ValConsPubAddress string
}

func addressCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "address chain-id [bech32]",
Short: "Get public key hex address and valcons address",
Example: `horcrux cosigner address cosmos`,
SilenceUsage: true,
Args: cobra.RangeArgs(1, 2),
RunE: func(cmd *cobra.Command, args []string) error {

var pubKey crypto.PubKey

switch config.Config.SignMode {
case signer.SignModeThreshold:
err := config.Config.ValidateThresholdModeConfig()
if err != nil {
return err
}

keyFile, err := config.KeyFileExistsCosigner(args[0])
if err != nil {
return err
}

key, err := signer.LoadCosignerEd25519Key(keyFile)
if err != nil {
return fmt.Errorf("error reading cosigner key: %s", err)
}

pubKey = key.PubKey
case signer.SignModeSingle:
err := config.Config.ValidateSingleSignerConfig()
if err != nil {
return err
}
keyFile, err := config.KeyFileExistsSingleSigner(args[0])
if err != nil {
return err
}

filePV := cometprivval.LoadFilePVEmptyState(keyFile, "")
pubKey = filePV.Key.PubKey
default:
panic(fmt.Errorf("unexpected sign mode: %s", config.Config.SignMode))
}

pubKeyAddress := pubKey.Address()

pubKeyJSON, err := signer.PubKey("", pubKey)
if err != nil {
return err
}

output := AddressCmdOutput{
HexAddress: strings.ToUpper(hex.EncodeToString(pubKeyAddress)),
PubKey: pubKeyJSON,
}

if len(args) == 2 {
bech32ValConsAddress, err := bech32.ConvertAndEncode(args[1]+"valcons", pubKeyAddress)
if err != nil {
return err
}
output.ValConsAddress = bech32ValConsAddress
pubKeyBech32, err := signer.PubKey(args[1], pubKey)
if err != nil {
return err
}
output.ValConsPubAddress = pubKeyBech32
} else {
bech32Hint := "Pass bech32 base prefix as argument to generate (e.g. cosmos)"
output.ValConsAddress = bech32Hint
output.ValConsPubAddress = bech32Hint
}

jsonOut, err := json.Marshal(output)
if err != nil {
return err
}

fmt.Println(string(jsonOut))

return nil
},
}

return cmd
}
107 changes: 54 additions & 53 deletions cmd/horcrux/cmd/config.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package cmd

import (
"errors"
"fmt"
"net"
"net/url"
"os"

"github.com/spf13/cobra"
"github.com/strangelove-ventures/horcrux/signer"
)

const (
flagSignMode = "mode"
flagNode = "node"
flagCosigner = "cosigner"
flagDebugAddr = "debug-addr"
flagKeyDir = "key-dir"
flagRaftTimeout = "raft-timeout"
flagGRPCTimeout = "grpc-timeout"
flagOverwrite = "overwrite"
)

func configCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "config",
Expand All @@ -31,18 +39,18 @@ func initCmd() *cobra.Command {
Long: "initialize configuration file, use flags for cosigner configuration.\n\n" +
"[chain-nodes] is a comma separated array of chain node addresses i.e.\n" +
"tcp://chain-node-1:1234,tcp://chain-node-2:1234",
Args: cobra.RangeArgs(0, 1),
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) (err error) {
var cn signer.ChainNodes
if len(args) == 1 {
cn, err = signer.ChainNodesFromArg(args[0])
if err != nil {
return err
}
cmdFlags := cmd.Flags()

nodes, _ := cmdFlags.GetStringSlice(flagNode)

cn, err := signer.ChainNodesFromFlag(nodes)
if err != nil {
return err
}

cmdFlags := cmd.Flags()
overwrite, _ := cmdFlags.GetBool("overwrite")
overwrite, _ := cmdFlags.GetBool(flagOverwrite)

if _, err := os.Stat(config.ConfigFile); !os.IsNotExist(err) && !overwrite {
return fmt.Errorf("%s already exists. Provide the -o flag to overwrite the existing config",
Expand All @@ -51,57 +59,43 @@ func initCmd() *cobra.Command {

var cfg signer.Config

cs, _ := cmdFlags.GetBool("cosigner")
keyDirFlag, _ := cmdFlags.GetString("key-dir")
signMode, _ := cmdFlags.GetString(flagSignMode)
keyDirFlag, _ := cmdFlags.GetString(flagKeyDir)
var keyDir *string
if keyDirFlag != "" {
keyDir = &keyDirFlag
}
debugAddr, _ := cmdFlags.GetString("debug-addr")
if cs {
// Cosigner Config
p, _ := cmdFlags.GetStringSlice("peers")
threshold, _ := cmdFlags.GetInt("threshold")
timeout, _ := cmdFlags.GetString("timeout")
peers, err := signer.PeersFromFlag(p)
if signMode == string(signer.SignModeThreshold) {
// Threshold Mode Config
cosignersFlag, _ := cmdFlags.GetStringSlice(flagCosigner)
threshold, _ := cmdFlags.GetInt(flagThreshold)
raftTimeout, _ := cmdFlags.GetString(flagRaftTimeout)
grpcTimeout, _ := cmdFlags.GetString(flagGRPCTimeout)
cosigners, err := signer.CosignersFromFlag(cosignersFlag)
if err != nil {
return err
}

listen, _ := cmdFlags.GetString("listen")
if listen == "" {
return errors.New("must input at least one node")
}
url, err := url.Parse(listen)
if err != nil {
return fmt.Errorf("error parsing listen address: %s, %v", listen, err)
}
host, _, err := net.SplitHostPort(url.Host)
if err != nil {
return err
}
if host == "0.0.0.0" {
return errors.New("host cannot be 0.0.0.0, must be reachable from other peers")
}

cfg = signer.Config{
SignMode: signer.SignModeThreshold,
PrivValKeyDir: keyDir,
CosignerConfig: &signer.CosignerConfig{
Threshold: threshold,
Shares: len(peers) + 1,
P2PListen: listen,
Peers: peers,
Timeout: timeout,
ThresholdModeConfig: &signer.ThresholdModeConfig{
Threshold: threshold,
Cosigners: cosigners,
GRPCTimeout: grpcTimeout,
RaftTimeout: raftTimeout,
},
ChainNodes: cn,
DebugAddr: debugAddr,
}
if err = cfg.ValidateCosignerConfig(); err != nil {
if err = cfg.ValidateThresholdModeConfig(); err != nil {
return err
}
} else {
// Single Signer Config
cfg = signer.Config{
SignMode: signer.SignModeSingle,
PrivValKeyDir: keyDir,
ChainNodes: cn,
DebugAddr: debugAddr,
Expand All @@ -128,16 +122,23 @@ func initCmd() *cobra.Command {
return nil
},
}
cmd.Flags().BoolP("cosigner", "c", false, "set to initialize a cosigner node, requires --peers and --threshold")
cmd.Flags().StringSliceP("peers", "p", []string{},
"cosigner peer addresses in format tcp://{addr}:{port}|{share-id} \n"+
"(i.e. \"tcp://node-1:2222|2,tcp://node-2:2222|3\")")
cmd.Flags().IntP("threshold", "t", 0, "indicate number of signatures required for threshold signature")
cmd.Flags().StringP("listen", "l", "", "listen address of the signer")
cmd.Flags().StringP("debug-addr", "d", "", "listen address for Debug and Prometheus metrics in format localhost:8543")
cmd.Flags().StringP("key-dir", "k", "", "priv val key directory")
cmd.Flags().String("timeout", "1500ms", "configure cosigner rpc server timeout value, \n"+
cmd.Flags().StringP(flagSignMode, "m", string(signer.SignModeThreshold),
`sign mode, "threshold" (recommended) or "single" (unsupported). threshold mode requires --cosigners and --threshold`,
)
cmd.Flags().StringSliceP(flagNode, "n", []string{}, "chain nodes in format tcp://{p2p-addr}:{port}")
cmd.Flags().StringSliceP(flagCosigner, "c", []string{},
"cosigners in format tcp://{p2p-addr}:{port}|{shard-id} \n"+
"(i.e. \"tcp://node-1:2222|1,tcp://node-2:2222|2,tcp://node-3:2222|3\")")
cmd.Flags().IntP(flagThreshold, "t", 0, "number of shards required for threshold signature")
cmd.Flags().StringP(
flagDebugAddr, "d", "",
"listen address for debug server and prometheus metrics in format localhost:8543",
)
cmd.Flags().StringP(flagKeyDir, "k", "", "key directory if other than home directory")
cmd.Flags().String(flagRaftTimeout, "1500ms", "cosigner raft timeout value, \n"+
"accepts valid duration strings for Go's time.ParseDuration() e.g. 1s, 1000ms, 1.5m")
cmd.Flags().String(flagGRPCTimeout, "1500ms", "cosigner grpc timeout value, \n"+
"accepts valid duration strings for Go's time.ParseDuration() e.g. 1s, 1000ms, 1.5m")
cmd.Flags().BoolP("overwrite", "o", false, "set to overwrite an existing config.yaml")
cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite an existing config.yaml")
return cmd
}
Loading

0 comments on commit 69a88ab

Please sign in to comment.