diff --git a/.golangci.yml b/.golangci.yml index c04197ec..98c32bee 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -91,7 +91,7 @@ linters-settings: alias: gmprometheus - pkg: github.com/mitchellh/go-homedir alias: homedir - - pkg: gitlab.com/unit410/threshold-ed25519/pkg + - pkg: gitlab.com/unit410/threshold-ed25519/src alias: tsed25519 - pkg: github.com/Jille/raft-grpc-transport alias: raftgrpctransport @@ -99,8 +99,10 @@ linters-settings: alias: boltdb - pkg: math/rand alias: mrand - - pkg: github.com/strangelove-ventures/horcrux/pkg/types + - pkg: github.com/strangelove-ventures/horcrux/src/types alias: htypes + - pkg: github.com/strangelove-ventures/horcrux/src/config + alias: cconfig maligned: suggest-new: true govet: diff --git a/client/address.go b/client/address.go index 3a611021..c30fe44b 100644 --- a/client/address.go +++ b/client/address.go @@ -6,6 +6,10 @@ import ( "strings" ) +type clientAdresses struct { + id []string +} + func SanitizeAddress(address string) (string, error) { u, err := url.Parse(address) if err != nil { diff --git a/cmd/horcrux/cmd/address.go b/cmd/horcrux/cmd/address.go index dad3f031..982ac520 100644 --- a/cmd/horcrux/cmd/address.go +++ b/cmd/horcrux/cmd/address.go @@ -10,7 +10,8 @@ import ( cometprivval "github.com/cometbft/cometbft/privval" "github.com/cosmos/cosmos-sdk/types/bech32" "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" + cconfig "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/tss" ) type AddressCmdOutput struct { @@ -29,12 +30,12 @@ func addressCmd() *cobra.Command { Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { - var pubKey crypto.PubKey + var pubKey tss.PubKey chainID := args[0] switch config.Config.SignMode { - case signer.SignModeThreshold: + case cconfig.SignModeThreshold: err := config.Config.ValidateThresholdModeConfig() if err != nil { return err @@ -45,13 +46,13 @@ func addressCmd() *cobra.Command { return err } - key, err := signer.LoadThresholdSignerEd25519Key(keyFile) + key, err := tss.LoadVaultKeyFromFile(keyFile) if err != nil { return fmt.Errorf("error reading threshold key: %w, check that key is present for chain id: %s", err, chainID) } - pubKey = key.PubKey - case signer.SignModeSingle: + pubKey = key.PubKey.(crypto.PubKey) + case cconfig.SignModeSingle: err := config.Config.ValidateSingleSignerConfig() if err != nil { return err @@ -67,10 +68,10 @@ func addressCmd() *cobra.Command { default: panic(fmt.Errorf("unexpected sign mode: %s", config.Config.SignMode)) } + pubKeyComet := pubKey.(crypto.PubKey) + pubKeyAddress := pubKeyComet.Address() - pubKeyAddress := pubKey.Address() - - pubKeyJSON, err := signer.PubKey("", pubKey) + pubKeyJSON, err := cconfig.PubKey("", pubKeyComet) if err != nil { return err } @@ -86,7 +87,7 @@ func addressCmd() *cobra.Command { return err } output.ValConsAddress = bech32ValConsAddress - pubKeyBech32, err := signer.PubKey(args[1], pubKey) + pubKeyBech32, err := cconfig.PubKey(args[1], pubKeyComet) if err != nil { return err } diff --git a/cmd/horcrux/cmd/config.go b/cmd/horcrux/cmd/config.go index 047c04c5..59fc8fe2 100644 --- a/cmd/horcrux/cmd/config.go +++ b/cmd/horcrux/cmd/config.go @@ -5,7 +5,7 @@ import ( "os" "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" + cconfig "github.com/strangelove-ventures/horcrux/src/config" ) const ( @@ -48,7 +48,7 @@ for threshold signer mode, --cosigner flags and --threshold flag are required. bare, _ := cmdFlags.GetBool(flagBare) nodes, _ := cmdFlags.GetStringSlice(flagNode) - cn, err := signer.ChainNodesFromFlag(nodes) + cn, err := cconfig.ChainNodesFromFlag(nodes) if err != nil { return err } @@ -60,7 +60,7 @@ for threshold signer mode, --cosigner flags and --threshold flag are required. config.ConfigFile) } - var cfg signer.Config + var cfg cconfig.Config signMode, _ := cmdFlags.GetString(flagSignMode) keyDirFlag, _ := cmdFlags.GetString(flagKeyDir) @@ -70,21 +70,21 @@ for threshold signer mode, --cosigner flags and --threshold flag are required. } debugAddr, _ := cmdFlags.GetString(flagDebugAddr) grpcAddr, _ := cmdFlags.GetString(flagGRPCAddress) - if signMode == string(signer.SignModeThreshold) { + if signMode == string(cconfig.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) + cosigners, err := cconfig.CosignersFromFlag(cosignersFlag) if err != nil { return err } - cfg = signer.Config{ - SignMode: signer.SignModeThreshold, + cfg = cconfig.Config{ + SignMode: cconfig.SignModeThreshold, PrivValKeyDir: keyDir, - ThresholdModeConfig: &signer.ThresholdModeConfig{ + ThresholdModeConfig: &cconfig.ThresholdModeConfig{ Threshold: threshold, Cosigners: cosigners, GRPCTimeout: grpcTimeout, @@ -102,8 +102,8 @@ for threshold signer mode, --cosigner flags and --threshold flag are required. } } else { // Single Signer Config - cfg = signer.Config{ - SignMode: signer.SignModeSingle, + cfg = cconfig.Config{ + SignMode: cconfig.SignModeSingle, PrivValKeyDir: keyDir, ChainNodes: cn, DebugAddr: debugAddr, @@ -134,10 +134,10 @@ for threshold signer mode, --cosigner flags and --threshold flag are required. } f := cmd.Flags() - f.StringP(flagSignMode, "m", string(signer.SignModeThreshold), + f.StringP(flagSignMode, "m", string(cconfig.SignModeThreshold), `sign mode, "threshold" (recommended) or "single" (unsupported). threshold mode requires --cosigner (multiple) and --threshold`, //nolint ) - f.StringSliceP(flagNode, "n", []string{}, "chain nodes in format tcp://{node-addr}:{privval-port} \n"+ + f.StringSliceP(flagNode, "n", []string{}, "chain cosigner in format tcp://{node-addr}:{privval-port} \n"+ "(e.g. --node tcp://sentry-1:1234 --node tcp://sentry-2:1234 --node tcp://sentry-3:1234 )") f.StringSliceP(flagCosigner, "c", []string{}, diff --git a/cmd/horcrux/cmd/leader_election.go b/cmd/horcrux/cmd/leader_election.go index 8dba6858..05796257 100644 --- a/cmd/horcrux/cmd/leader_election.go +++ b/cmd/horcrux/cmd/leader_election.go @@ -5,12 +5,15 @@ import ( "fmt" "time" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + grpcretry "github.com/grpc-ecosystem/go-grpc-middleware/retry" "github.com/spf13/cobra" "github.com/strangelove-ventures/horcrux/client" - "github.com/strangelove-ventures/horcrux/signer" - "github.com/strangelove-ventures/horcrux/signer/multiresolver" - "github.com/strangelove-ventures/horcrux/signer/proto" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + "github.com/strangelove-ventures/horcrux/src/multiresolver" + + // "github.com/strangelove-ventures/horcrux/src/proto" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) @@ -69,7 +72,7 @@ horcrux elect 2 # elect specific leader`, ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) defer cancelFunc() - grpcClient := proto.NewCosignerClient(conn) + grpcClient := proto.NewNodeServiceClient(conn) _, err = grpcClient.TransferLeadership( ctx, &proto.TransferLeadershipRequest{LeaderID: leaderID}, @@ -116,14 +119,14 @@ func getLeaderCmd() *cobra.Command { return fmt.Errorf("cosigner encryption keys not found (%s) - (%s): %w", keyFileECIES, keyFileRSA, err) } - key, err := signer.LoadCosignerRSAKey(keyFileRSA) + key, err := nodesecurity.LoadCosignerRSAKey(keyFileRSA) if err != nil { return fmt.Errorf("error reading cosigner key (%s): %w", keyFileRSA, err) } id = key.ID } else { - key, err := signer.LoadCosignerECIESKey(keyFileECIES) + key, err := nodesecurity.LoadCosignerECIESKey(keyFileECIES) if err != nil { return fmt.Errorf("error reading cosigner key (%s): %w", keyFileECIES, err) } @@ -166,7 +169,7 @@ func getLeaderCmd() *cobra.Command { ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) defer cancelFunc() - grpcClient := proto.NewCosignerClient(conn) + grpcClient := proto.NewNodeServiceClient(conn) res, err := grpcClient.GetLeader(ctx, &proto.GetLeaderRequest{}) if err != nil { diff --git a/cmd/horcrux/cmd/migrate.go b/cmd/horcrux/cmd/migrate.go index af083e3a..d4948ff0 100644 --- a/cmd/horcrux/cmd/migrate.go +++ b/cmd/horcrux/cmd/migrate.go @@ -8,12 +8,15 @@ import ( "os" "path/filepath" + cconfig "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + "github.com/strangelove-ventures/horcrux/src/tss" + cometcrypto "github.com/cometbft/cometbft/crypto" cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" cometcryptoencoding "github.com/cometbft/cometbft/crypto/encoding" cometprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" amino "github.com/tendermint/go-amino" "gopkg.in/yaml.v2" ) @@ -42,7 +45,7 @@ type ( ChainID string `json:"chain-id" yaml:"chain-id"` PrivValKeyFile *string `json:"key-file,omitempty" yaml:"key-file,omitempty"` Cosigner *v2CosignerConfig `json:"cosigner" yaml:"cosigner"` - ChainNodes []v2ChainNodeConfig `json:"chain-nodes,omitempty" yaml:"chain-nodes,omitempty"` + ChainNodes []v2ChainNodeConfig `json:"chain-cosigner,omitempty" yaml:"chain-cosigner,omitempty"` DebugAddr string `json:"debug-addr,omitempty" yaml:"debug-addr,omitempty"` } @@ -103,7 +106,7 @@ func (key *v2CosignerKey) UnmarshalJSON(data []byte) error { // Prior to the tendermint protobuf migration, the public key bytes in key files // were encoded using the go-amino libraries via - // cdc.MarshalBinaryBare(CosignerEd25519Key.PubKey) + // cdc.MarshalBinaryBare(Ed25519Key.PubKey) // // To support reading the public key bytes from these key files, we fallback to // amino unmarshalling if the protobuf unmarshalling fails @@ -218,7 +221,7 @@ func migrateCmd() *cobra.Command { return err } - newEd25519Key := signer.CosignerEd25519Key{ + newEd25519Key := tss.Ed25519Key{ PubKey: legacyCosignerKey.PubKey, PrivateShard: legacyCosignerKey.ShareKey, ID: legacyCosignerKey.ID, @@ -234,7 +237,7 @@ func migrateCmd() *cobra.Command { return fmt.Errorf("failed to write new Ed25519 key to %s: %w", newEd25519Path, err) } - newRSAKey := signer.CosignerRSAKey{ + newRSAKey := nodesecurity.CosignerRSAKey{ RSAKey: legacyCosignerKey.RSAKey, ID: legacyCosignerKey.ID, RSAPubs: legacyCosignerKey.RSAPubs, @@ -252,10 +255,10 @@ func migrateCmd() *cobra.Command { // only attempt config migration if legacy config exists if legacyCfgErr == nil { - var migratedNodes signer.ChainNodes + var migratedNodes cconfig.ChainNodes for _, n := range legacyCfg.ChainNodes { - migratedNodes = append(migratedNodes, signer.ChainNode{ + migratedNodes = append(migratedNodes, cconfig.ChainNode{ PrivValAddr: n.PrivValAddr, }) } @@ -263,17 +266,17 @@ func migrateCmd() *cobra.Command { config.Config.ChainNodes = migratedNodes config.Config.DebugAddr = legacyCfg.DebugAddr - signMode := signer.SignModeSingle + signMode := cconfig.SignModeSingle if legacyCfg.Cosigner != nil { - signMode = signer.SignModeThreshold + signMode = cconfig.SignModeThreshold - var migratedCosigners signer.CosignersConfig + var migratedCosigners cconfig.CosignersConfig if legacyCfg.Cosigner.P2PListen != "" { migratedCosigners = append( migratedCosigners, - signer.CosignerConfig{ + cconfig.CosignerConfig{ ShardID: legacyCosignerKey.ID, P2PAddr: legacyCfg.Cosigner.P2PListen, }, @@ -281,13 +284,13 @@ func migrateCmd() *cobra.Command { } for _, c := range legacyCfg.Cosigner.Peers { - migratedCosigners = append(migratedCosigners, signer.CosignerConfig{ + migratedCosigners = append(migratedCosigners, cconfig.CosignerConfig{ ShardID: c.ShareID, P2PAddr: c.P2PAddr, }) } - config.Config.ThresholdModeConfig = &signer.ThresholdModeConfig{ + config.Config.ThresholdModeConfig = &cconfig.ThresholdModeConfig{ Threshold: legacyCfg.Cosigner.Threshold, Cosigners: migratedCosigners, GRPCTimeout: legacyCfg.Cosigner.Timeout, diff --git a/cmd/horcrux/cmd/root.go b/cmd/horcrux/cmd/root.go index ff506a7e..626573a0 100644 --- a/cmd/horcrux/cmd/root.go +++ b/cmd/horcrux/cmd/root.go @@ -8,11 +8,11 @@ import ( homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/strangelove-ventures/horcrux/signer" + cconfig "github.com/strangelove-ventures/horcrux/src/config" "gopkg.in/yaml.v2" ) -var config signer.RuntimeConfig +var config cconfig.RuntimeConfig func rootCmd() *cobra.Command { cmd := &cobra.Command{ @@ -74,7 +74,7 @@ func initConfig() { } else { home = config.HomeDir } - config = signer.RuntimeConfig{ + config = cconfig.RuntimeConfig{ HomeDir: home, ConfigFile: filepath.Join(home, "config.yaml"), StateDir: filepath.Join(home, "state"), diff --git a/signer/services.go b/cmd/horcrux/cmd/services.go similarity index 95% rename from signer/services.go rename to cmd/horcrux/cmd/services.go index f9ca8e9e..b2c93e73 100644 --- a/signer/services.go +++ b/cmd/horcrux/cmd/services.go @@ -1,4 +1,4 @@ -package signer +package cmd import ( "errors" @@ -13,6 +13,7 @@ import ( cometservice "github.com/cometbft/cometbft/libs/service" ) +// TODO: Move RequireNotRunning outside of this package. func RequireNotRunning(log cometlog.Logger, pidFilePath string) error { if _, err := os.Stat(pidFilePath); err != nil { if os.IsNotExist(err) { @@ -61,7 +62,8 @@ func RequireNotRunning(log cometlog.Logger, pidFilePath string) error { return nil } - errno, ok := err.(syscall.Errno) + var errno syscall.Errno + ok := errors.As(err, &errno) if !ok { return fmt.Errorf("unexpected error type from signaling horcrux PID: %d", pid) } diff --git a/signer/services_test.go b/cmd/horcrux/cmd/services_test.go similarity index 88% rename from signer/services_test.go rename to cmd/horcrux/cmd/services_test.go index 66d1e732..1cb20091 100644 --- a/signer/services_test.go +++ b/cmd/horcrux/cmd/services_test.go @@ -1,4 +1,4 @@ -package signer_test +package cmd_test import ( "errors" @@ -12,9 +12,10 @@ import ( "testing" "time" + "github.com/strangelove-ventures/horcrux/cmd/horcrux/cmd" + cometlog "github.com/cometbft/cometbft/libs/log" cometservice "github.com/cometbft/cometbft/libs/service" - "github.com/strangelove-ventures/horcrux/signer" fork "github.com/kraken-hpc/go-fork" "github.com/stretchr/testify/require" @@ -33,6 +34,7 @@ func mockHorcruxChildProcess(pidFilePath string) { ) } +// waitForFileToExist waits for a file to exist, returning an error if it does not exist after timeout. func waitForFileToExist(file string, timeout time.Duration) error { exp := time.After(timeout) tick := time.Tick(20 * time.Millisecond) @@ -71,7 +73,7 @@ func TestIsRunning(t *testing.T) { pidBz, err := os.ReadFile(pidFilePath) require.NoError(t, err) - err = signer.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) + err = cmd.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) expectedErrorMsg := fmt.Sprintf("horcrux is already running on PID: %s", strings.TrimSpace(string(pidBz))) require.EqualError(t, err, expectedErrorMsg) } @@ -80,7 +82,7 @@ func TestIsNotRunning(t *testing.T) { homeDir := t.TempDir() pidFilePath := filepath.Join(homeDir, "horcrux.pid") - err := signer.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) + err := cmd.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) require.NoError(t, err) } @@ -129,7 +131,7 @@ func TestIsRunningNonExistentPid(t *testing.T) { ) require.NoError(t, err, "error writing pid file") - err = signer.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) + err = cmd.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) require.Nil(t, err) _, err = os.Stat(pidFilePath) @@ -167,7 +169,7 @@ func TestConcurrentStart(t *testing.T) { for i := 0; i < concurrentAttempts; i++ { go func() { defer recoverFromPanic() - signer.WaitAndTerminate(logger, services, pidFilePath) + cmd.WaitAndTerminate(logger, services, pidFilePath) doneCount++ wg.Done() }() @@ -186,7 +188,7 @@ func TestIsRunningAndWaitForService(t *testing.T) { var logger cometlog.Logger var services []cometservice.Service - go func() { signer.WaitAndTerminate(logger, services, pidFilePath) }() + go func() { cmd.WaitAndTerminate(logger, services, pidFilePath) }() // Wait for signer.WaitAndTerminate to create pidFile var err error @@ -211,7 +213,7 @@ func TestIsRunningAndWaitForService(t *testing.T) { } panicFunction := func() { defer recoverFromPanic() - err = signer.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) + err = cmd.RequireNotRunning(cometlog.NewNopLogger(), pidFilePath) } go panicFunction() wg.Wait() diff --git a/cmd/horcrux/cmd/shards.go b/cmd/horcrux/cmd/shards.go index 57906db8..3798c8a9 100644 --- a/cmd/horcrux/cmd/shards.go +++ b/cmd/horcrux/cmd/shards.go @@ -20,8 +20,10 @@ import ( "os" "path/filepath" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + "github.com/strangelove-ventures/horcrux/src/tss" + "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" ) func createCosignerDirectoryIfNecessary(out string, id int) (string, error) { @@ -81,7 +83,8 @@ func createCosignerEd25519ShardsCmd() *cobra.Command { } if chainID == "" { - return fmt.Errorf("chain-id flag must not be empty") + return fmt.Errorf( + "chain-id flag must not be empty") } if threshold == 0 { @@ -112,9 +115,9 @@ func createCosignerEd25519ShardsCmd() *cobra.Command { return nil } - csKeys, err := signer.CreateEd25519ThresholdSignShardsFromFile(keyFile, threshold, shards) + csKeys, err := tss.CreatePersistentEd25519ThresholdSignShardsFromFile(keyFile, threshold, shards) if err != nil { - return err + return fmt.Errorf("error creating CreatePersistentEd25519ThresholdSignShardsFromFile (%s): %d, %d, %w", keyFile, threshold, shards, err) } out, _ := cmd.Flags().GetString(flagOutputDir) @@ -133,7 +136,7 @@ func createCosignerEd25519ShardsCmd() *cobra.Command { return err } filename := filepath.Join(dir, fmt.Sprintf("%s_shard.json", chainID)) - if err = signer.WriteCosignerEd25519ShardFile(c, filename); err != nil { + if err = tss.WriteToFile(c, filename); err != nil { return err } fmt.Fprintf(cmd.OutOrStdout(), "Created Ed25519 Shard %s\n", filename) @@ -170,7 +173,7 @@ func createCosignerECIESShardsCmd() *cobra.Command { return fmt.Errorf("shards must be greater than zero (%d): %w", shards, err) } - csKeys, err := signer.CreateCosignerECIESShards(int(shards)) + csKeys, err := nodesecurity.CreateCosignerECIESShards(int(shards)) if err != nil { return err } @@ -191,7 +194,7 @@ func createCosignerECIESShardsCmd() *cobra.Command { return err } filename := filepath.Join(dir, "ecies_keys.json") - if err = signer.WriteCosignerECIESShardFile(c, filename); err != nil { + if err = nodesecurity.WriteCosignerECIESShardFile(c, filename); err != nil { return err } fmt.Fprintf(cmd.OutOrStdout(), "Created ECIES Shard %s\n", filename) @@ -218,7 +221,7 @@ func createCosignerRSAShardsCmd() *cobra.Command { return fmt.Errorf("shards must be greater than zero (%d): %w", shards, err) } - csKeys, err := signer.CreateCosignerRSAShards(int(shards)) + csKeys, err := nodesecurity.CreateCosignerRSAShards(int(shards)) if err != nil { return err } @@ -239,7 +242,7 @@ func createCosignerRSAShardsCmd() *cobra.Command { return err } filename := filepath.Join(dir, "rsa_keys.json") - if err = signer.WriteCosignerRSAShardFile(c, filename); err != nil { + if err = nodesecurity.WriteCosignerRSAShardFile(c, filename); err != nil { return err } fmt.Fprintf(cmd.OutOrStdout(), "Created RSA Shard %s\n", filename) diff --git a/cmd/horcrux/cmd/single_signer.go b/cmd/horcrux/cmd/single_signer.go index 18b4ba1e..a0e05714 100644 --- a/cmd/horcrux/cmd/single_signer.go +++ b/cmd/horcrux/cmd/single_signer.go @@ -4,7 +4,7 @@ import ( "fmt" "io" - "github.com/strangelove-ventures/horcrux/signer" + "github.com/strangelove-ventures/horcrux/src/node" ) const ( @@ -23,7 +23,7 @@ as it is not officially supported by Strangelove.` func NewSingleSignerValidator( out io.Writer, acceptRisk bool, -) (*signer.SingleSignerValidator, error) { +) (*node.SingleSignerValidator, error) { fmt.Fprintln(out, singleSignerWarning) if !acceptRisk { @@ -34,5 +34,5 @@ func NewSingleSignerValidator( return nil, err } - return signer.NewSingleSignerValidator(&config), nil + return node.NewSingleSignerValidator(&config), nil } diff --git a/cmd/horcrux/cmd/start.go b/cmd/horcrux/cmd/start.go index ef4fde1d..d333227a 100644 --- a/cmd/horcrux/cmd/start.go +++ b/cmd/horcrux/cmd/start.go @@ -4,10 +4,12 @@ import ( "fmt" "os" + cconfig "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/connector" + cometlog "github.com/cometbft/cometbft/libs/log" "github.com/cometbft/cometbft/libs/service" "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" ) func startCmd() *cobra.Command { @@ -20,7 +22,7 @@ func startCmd() *cobra.Command { out := cmd.OutOrStdout() logger := cometlog.NewTMLogger(cometlog.NewSyncWriter(out)) - err := signer.RequireNotRunning(logger, config.PidFile) + err := RequireNotRunning(logger, config.PidFile) if err != nil { return err } @@ -42,16 +44,16 @@ func startCmd() *cobra.Command { acceptRisk, _ := cmd.Flags().GetBool(flagAcceptRisk) - var val signer.PrivValidator - var services []service.Service + var val connector.IPrivValidator + var services []service.Service // A list of all services that are running switch config.Config.SignMode { - case signer.SignModeThreshold: + case cconfig.SignModeThreshold: services, val, err = NewThresholdValidator(cmd.Context(), logger) if err != nil { return err } - case signer.SignModeSingle: + case cconfig.SignModeSingle: val, err = NewSingleSignerValidator(out, acceptRisk) if err != nil { return err @@ -60,8 +62,9 @@ func startCmd() *cobra.Command { panic(fmt.Errorf("unexpected sign mode: %s", config.Config.SignMode)) } + // Start the service so the Sentry can connecto to our GRPC server if config.Config.GRPCAddr != "" { - grpcServer := signer.NewRemoteSignerGRPCServer(logger, val, config.Config.GRPCAddr) + grpcServer := connector.NewSentrySignerGRPCServer(logger, val, config.Config.GRPCAddr) services = append(services, grpcServer) if err := grpcServer.Start(); err != nil { @@ -71,12 +74,13 @@ func startCmd() *cobra.Command { go EnableDebugAndMetrics(cmd.Context(), out) - services, err = signer.StartRemoteSigners(services, logger, val, config.Config.Nodes()) + // "Entrypoint" to start remote signers + services, err = connector.StartRemoteSigners(services, logger, val, config.Config.Nodes()) if err != nil { return fmt.Errorf("failed to start remote signer(s): %w", err) } - signer.WaitAndTerminate(logger, services, config.PidFile) + WaitAndTerminate(logger, services, config.PidFile) return nil }, diff --git a/cmd/horcrux/cmd/state.go b/cmd/horcrux/cmd/state.go index 99da0861..471d51df 100644 --- a/cmd/horcrux/cmd/state.go +++ b/cmd/horcrux/cmd/state.go @@ -10,10 +10,9 @@ import ( "strings" "time" - "github.com/strangelove-ventures/horcrux/pkg/types" + "github.com/strangelove-ventures/horcrux/src/types" "github.com/spf13/cobra" - "github.com/strangelove-ventures/horcrux/signer" cometjson "github.com/cometbft/cometbft/libs/json" cometlog "github.com/cometbft/cometbft/libs/log" @@ -95,7 +94,7 @@ func setStateCmd() *cobra.Command { // Resetting the priv_validator_state.json should only be allowed if the // signer is not running. - if err := signer.RequireNotRunning(logger, config.PidFile); err != nil { + if err := RequireNotRunning(logger, config.PidFile); err != nil { return err } @@ -161,7 +160,7 @@ func importStateCmd() *cobra.Command { // Resetting the priv_validator_state.json should only be allowed if the // signer is not running. - if err := signer.RequireNotRunning(logger, config.PidFile); err != nil { + if err := RequireNotRunning(logger, config.PidFile); err != nil { return err } diff --git a/cmd/horcrux/cmd/state_test.go b/cmd/horcrux/cmd/state_test.go index fdb8c769..328cbf75 100644 --- a/cmd/horcrux/cmd/state_test.go +++ b/cmd/horcrux/cmd/state_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/strangelove-ventures/horcrux/pkg/types" + "github.com/strangelove-ventures/horcrux/src/types" "github.com/stretchr/testify/require" ) diff --git a/cmd/horcrux/cmd/threshold.go b/cmd/horcrux/cmd/threshold.go index ac2df73f..052681fd 100644 --- a/cmd/horcrux/cmd/threshold.go +++ b/cmd/horcrux/cmd/threshold.go @@ -7,33 +7,67 @@ import ( "path/filepath" "time" + cconfig "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/cosigner" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + "github.com/strangelove-ventures/horcrux/src/node" + cometlog "github.com/cometbft/cometbft/libs/log" cometservice "github.com/cometbft/cometbft/libs/service" - "github.com/strangelove-ventures/horcrux/signer" ) const maxWaitForSameBlockAttempts = 3 +func CosignerSecurityECIES(c cconfig.RuntimeConfig) (*nodesecurity.CosignerSecurityECIES, error) { + keyFile, err := c.KeyFileExistsCosignerECIES() + if err != nil { + return nil, err + } + + key, err := nodesecurity.LoadCosignerECIESKey(keyFile) + if err != nil { + return nil, fmt.Errorf("error reading cosigner key (%s): %w", keyFile, err) + } + + return nodesecurity.NewCosignerSecurityECIES(key), nil +} + +func CosignerSecurityRSA(c cconfig.RuntimeConfig) (*nodesecurity.CosignerSecurityRSA, error) { + keyFile, err := c.KeyFileExistsCosignerRSA() + if err != nil { + return nil, err + } + + key, err := nodesecurity.LoadCosignerRSAKey(keyFile) + if err != nil { + return nil, fmt.Errorf("error reading cosigner key (%s): %w", keyFile, err) + } + + return nodesecurity.NewCosignerSecurityRSA(key), nil +} + +// TODO: Single Responsibility Principle :( func NewThresholdValidator( ctx context.Context, logger cometlog.Logger, -) ([]cometservice.Service, *signer.ThresholdValidator, error) { +) ([]cometservice.Service, *node.ThresholdValidator, error) { if err := config.Config.ValidateThresholdModeConfig(); err != nil { return nil, nil, err } thresholdCfg := config.Config.ThresholdModeConfig - remoteCosigners := make([]signer.Cosigner, 0, len(thresholdCfg.Cosigners)-1) + remoteCosigners := make([]node.ICosigner, 0, len(thresholdCfg.Cosigners)-1) var p2pListen string - var security signer.CosignerSecurity + var security cosigner.ICosignerSecurity var eciesErr error - security, eciesErr = config.CosignerSecurityECIES() + // TODO: This is really ugly and should be refactored + security, eciesErr = CosignerSecurityECIES(config) if eciesErr != nil { var rsaErr error - security, rsaErr = config.CosignerSecurityRSA() + security, rsaErr = CosignerSecurityRSA(config) if rsaErr != nil { return nil, nil, fmt.Errorf("failed to initialize cosigner ECIES / RSA security : %w / %w", eciesErr, rsaErr) } @@ -41,7 +75,7 @@ func NewThresholdValidator( for _, c := range thresholdCfg.Cosigners { if c.ShardID != security.GetID() { - rc, err := signer.NewRemoteCosigner(c.ShardID, c.P2PAddr) + rc, err := cosigner.NewCosignerClient(c.ShardID, c.P2PAddr) if err != nil { return nil, nil, fmt.Errorf("failed to initialize remote cosigner: %w", err) } @@ -58,7 +92,7 @@ func NewThresholdValidator( return nil, nil, fmt.Errorf("cosigner config does not exist for our shard Index %d", security.GetID()) } - localCosigner := signer.NewLocalCosigner( + localCosigner := cosigner.NewLocalCosigner( logger, &config, security, @@ -74,18 +108,18 @@ func NewThresholdValidator( return nil, nil, fmt.Errorf("error creating raft directory: %w", err) } - // RAFT node Index is the cosigner Index + // RAFT node ID is the cosigner id nodeID := fmt.Sprint(security.GetID()) // Start RAFT store listener - raftStore := signer.NewRaftStore(nodeID, + raftStore := node.NewRaftStore(nodeID, raftDir, p2pListen, raftTimeout, logger, localCosigner, remoteCosigners) if err := raftStore.Start(); err != nil { return nil, nil, fmt.Errorf("error starting raft store: %w", err) } services := []cometservice.Service{raftStore} - val := signer.NewThresholdValidator( + val := node.NewThresholdValidator( logger, &config, thresholdCfg.Threshold, @@ -96,7 +130,7 @@ func NewThresholdValidator( raftStore, ) - raftStore.SetThresholdValidator(val) + raftStore.SetThresholdValidator(val, localCosigner) if err := val.Start(ctx); err != nil { return nil, nil, fmt.Errorf("failed to start threshold validator: %w", err) diff --git a/docs/migrating.md b/docs/migrating.md index b3046a41..93ee7b2e 100644 --- a/docs/migrating.md +++ b/docs/migrating.md @@ -40,9 +40,9 @@ sentry-1: 10.168.0.1 sentry-2: 10.168.0.2 sentry-3: 10.168.0.3 -signer-1: 10.168.1.1 -signer-2: 10.168.1.2 -signer-3: 10.168.1.3 +node-1: 10.168.1.1 +node-2: 10.168.1.2 +node-3: 10.168.1.3 ``` When installing `horcrux` we recommend using either the [container image](https://github.com/strangelove-ventures/horcrux/pkgs/container/horcrux) or the [prebuilt binary](https://github.com/strangelove-ventures/horcrux/releases) for the latest stable release. diff --git a/go.mod b/go.mod index 23d9d530..f62d1a6e 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( gitlab.com/unit410/threshold-ed25519 v0.0.0-20220812172601-56783212c4cc golang.org/x/sync v0.5.0 google.golang.org/grpc v1.59.0 + google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -131,7 +132,6 @@ require ( google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect - google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/pkg/types/block.go b/pkg/types/block.go deleted file mode 100644 index 04169402..00000000 --- a/pkg/types/block.go +++ /dev/null @@ -1,42 +0,0 @@ -package types - -import ( - "time" - - "github.com/strangelove-ventures/horcrux/signer/proto" -) - -type Block struct { - Height int64 - Round int64 - Step int8 - SignBytes []byte - Timestamp time.Time -} - -func (block Block) HRSKey() HRSKey { - return HRSKey{ - Height: block.Height, - Round: block.Round, - Step: block.Step, - } -} - -func (block Block) HRSTKey() HRSTKey { - return HRSTKey{ - Height: block.Height, - Round: block.Round, - Step: block.Step, - Timestamp: block.Timestamp.UnixNano(), - } -} - -func (block Block) ToProto() *proto.Block { - return &proto.Block{ - Height: block.Height, - Round: block.Round, - Step: int32(block.Step), - SignBytes: block.SignBytes, - Timestamp: block.Timestamp.UnixNano(), - } -} diff --git a/pkg/types/hrs.go b/pkg/types/hrs.go deleted file mode 100644 index 65f5b9db..00000000 --- a/pkg/types/hrs.go +++ /dev/null @@ -1,70 +0,0 @@ -package types - -import ( - "github.com/strangelove-ventures/horcrux/signer/proto" -) - -// HRSKey represents the key for the HRS metadata map. -type HRSKey struct { - Height int64 - Round int64 - Step int8 -} - -// GreaterThan returns true if the HRSKey is greater than the other HRSKey. -func (hrs HRSKey) GreaterThan(other HRSKey) bool { - if hrs.Height > other.Height { - return true - } - if hrs.Height < other.Height { - return false - } - if hrs.Round > other.Round { - return true - } - if hrs.Round < other.Round { - return false - } - return hrs.Step > other.Step -} - -// LessThan returns true if the HRSKey is less than the other HRSKey. -func (hrs HRSKey) LessThan(other HRSKey) bool { - return hrs != other && !hrs.GreaterThan(other) -} - -// HRSTKey represents the HRS metadata key with a timestamp. -type HRSTKey struct { - Height int64 - Round int64 - Step int8 - Timestamp int64 -} - -// HRSKey returns the HRSKey portion of the HRSTKey. -func (hrst HRSTKey) HRSKey() HRSKey { - return HRSKey{ - Height: hrst.Height, - Round: hrst.Round, - Step: hrst.Step, - } -} - -// HRSTKeyFromProto returns a HRSTKey from a proto.HRST. -func HRSTKeyFromProto(hrs *proto.HRST) HRSTKey { - return HRSTKey{ - Height: hrs.GetHeight(), - Round: hrs.GetRound(), - Step: int8(hrs.GetStep()), - Timestamp: hrs.GetTimestamp(), - } -} - -func (hrst HRSTKey) ToProto() *proto.HRST { - return &proto.HRST{ - Height: hrst.Height, - Round: hrst.Round, - Step: int32(hrst.Step), - Timestamp: hrst.Timestamp, - } -} diff --git a/proto/protoc_local_.sh b/proto/protoc_local_.sh new file mode 100644 index 00000000..02663e0a --- /dev/null +++ b/proto/protoc_local_.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -eox pipefail + +echo "Generating proto code" + +# OUT_DIR=$(pwd)/proto +# echo $OUT_DIR +# protoc --go_out=$(pwd) strangelove/horcrux/cosigner.proto +protoc -I=. --go_out=./ --go-grpc_out=./ --go-grpc_opt=paths=source_relative --go_opt=paths=source_relative strangelove/proto/cosigner.proto +protoc -I=. --go_out=./ --go-grpc_out=./ --go-grpc_opt=paths=source_relative --go_opt=paths=source_relative strangelove/proto/node.proto +protoc -I=. --go_out=./ --go-grpc_out=./ --go-grpc_opt=paths=source_relative --go_opt=paths=source_relative strangelove/proto/connector.proto + +#protoc -I=. --go-grpc_out=./ --go-grpc_opt=paths=source_relative strangelove/horcrux/connector.proto +# protoc -I=. --go_out=strangelove/proto --go_opt=paths=source_relative strangelove/horcrux/node.proto diff --git a/proto/strangelove/horcrux/remote_signer.proto b/proto/strangelove/horcrux/connector.proto similarity index 62% rename from proto/strangelove/horcrux/remote_signer.proto rename to proto/strangelove/horcrux/connector.proto index 5753b281..7137a813 100644 --- a/proto/strangelove/horcrux/remote_signer.proto +++ b/proto/strangelove/horcrux/connector.proto @@ -1,11 +1,12 @@ syntax = "proto3"; package strangelove.horcrux; -import "strangelove/horcrux/cosigner.proto"; +import "strangelove/horcrux/node.proto"; -option go_package = "github.com/strangelove-ventures/horcrux/signer/proto"; +option go_package = "github.com/strangelove-ventures/horcrux/src/proto"; -service RemoteSigner { +// Connection to the remote signer (E.g the Sentry) +service Connector { rpc PubKey (PubKeyRequest) returns (PubKeyResponse) {} rpc Sign(strangelove.horcrux.SignBlockRequest) returns (strangelove.horcrux.SignBlockResponse) {} } diff --git a/proto/strangelove/horcrux/cosigner.proto b/proto/strangelove/horcrux/cosigner.proto index 52475b85..89636820 100644 --- a/proto/strangelove/horcrux/cosigner.proto +++ b/proto/strangelove/horcrux/cosigner.proto @@ -1,34 +1,14 @@ syntax = "proto3"; package strangelove.horcrux; -option go_package = "github.com/strangelove-ventures/horcrux/signer/proto"; +option go_package = "strangelove/horcrux/proto"; service Cosigner { - rpc SignBlock (SignBlockRequest) returns (SignBlockResponse) {} rpc SetNoncesAndSign (SetNoncesAndSignRequest) returns (SetNoncesAndSignResponse) {} rpc GetNonces (GetNoncesRequest) returns (GetNoncesResponse) {} - rpc TransferLeadership (TransferLeadershipRequest) returns (TransferLeadershipResponse) {} - rpc GetLeader (GetLeaderRequest) returns (GetLeaderResponse) {} rpc Ping(PingRequest) returns (PingResponse) {} } -message Block { - int64 height = 1; - int64 round = 2; - int32 step = 3; - bytes signBytes = 4; - int64 timestamp = 5; -} - -message SignBlockRequest { - string chainID = 1; - Block block = 2; -} - -message SignBlockResponse { - bytes signature = 1; - int64 timestamp = 2; -} message Nonce { int32 sourceID = 1; @@ -72,6 +52,16 @@ message GetNoncesResponse { repeated UUIDNonce nonces = 1; } +message PingRequest {} +message PingResponse {} +/* +message GetLeaderRequest {} + +message GetLeaderResponse { + int32 leader = 1; +} + + message TransferLeadershipRequest { string leaderID = 1; } @@ -81,11 +71,22 @@ message TransferLeadershipResponse { string leaderAddress = 2; } -message GetLeaderRequest {} -message GetLeaderResponse { - int32 leader = 1; +message Block { + int64 height = 1; + int64 round = 2; + int32 step = 3; + bytes signBytes = 4; + int64 timestamp = 5; } -message PingRequest {} -message PingResponse {} \ No newline at end of file +message SignBlockRequest { + string chainID = 1; + Block block = 2; +} + +message SignBlockResponse { + bytes signature = 1; + int64 timestamp = 2; +} +*/ \ No newline at end of file diff --git a/proto/strangelove/horcrux/node.proto b/proto/strangelove/horcrux/node.proto new file mode 100644 index 00000000..a7baa206 --- /dev/null +++ b/proto/strangelove/horcrux/node.proto @@ -0,0 +1,45 @@ +syntax = "proto3"; +package strangelove.horcrux; + +option go_package = "github.com/strangelove-ventures/horcrux/src/proto"; + +service TresholdValidator{ + // TODO: ADD rpc Validate(ValidateRequest) returns (ValidateResponse); + + rpc SignBlock (SignBlockRequest) returns (SignBlockResponse) {} + rpc TransferLeadership (TransferLeadershipRequest) returns (TransferLeadershipResponse) {} + rpc GetLeader (GetLeaderRequest) returns (GetLeaderResponse) {} +} + +message SignBlockRequest { + string chainID = 1; + Block block = 2; +} + +message SignBlockResponse { + bytes signature = 1; + int64 timestamp = 2; +} + +message TransferLeadershipRequest { + string leaderID = 1; +} + +message TransferLeadershipResponse { + string leaderID = 1; + string leaderAddress = 2; +} + +message GetLeaderRequest {} + +message GetLeaderResponse { + int32 leader = 1; +} + +message Block { + int64 height = 1; + int64 round = 2; + int32 step = 3; + bytes signBytes = 4; + int64 timestamp = 5; +} diff --git a/proto/strangelove/proto/connector.pb.go b/proto/strangelove/proto/connector.pb.go new file mode 100644 index 00000000..584a80f6 --- /dev/null +++ b/proto/strangelove/proto/connector.pb.go @@ -0,0 +1,230 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc v3.13.0 +// source: strangelove/proto/connector.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type PubKeyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` +} + +func (x *PubKeyRequest) Reset() { + *x = PubKeyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_connector_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PubKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PubKeyRequest) ProtoMessage() {} + +func (x *PubKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_connector_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PubKeyRequest.ProtoReflect.Descriptor instead. +func (*PubKeyRequest) Descriptor() ([]byte, []int) { + return file_strangelove_proto_connector_proto_rawDescGZIP(), []int{0} +} + +func (x *PubKeyRequest) GetChainId() string { + if x != nil { + return x.ChainId + } + return "" +} + +type PubKeyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PubKey []byte `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` +} + +func (x *PubKeyResponse) Reset() { + *x = PubKeyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_connector_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PubKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PubKeyResponse) ProtoMessage() {} + +func (x *PubKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_connector_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PubKeyResponse.ProtoReflect.Descriptor instead. +func (*PubKeyResponse) Descriptor() ([]byte, []int) { + return file_strangelove_proto_connector_proto_rawDescGZIP(), []int{1} +} + +func (x *PubKeyResponse) GetPubKey() []byte { + if x != nil { + return x.PubKey + } + return nil +} + +var File_strangelove_proto_connector_proto protoreflect.FileDescriptor + +var file_strangelove_proto_connector_proto_rawDesc = []byte{ + 0x0a, 0x21, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, + 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2a, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, + 0x22, 0x29, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x32, 0xb1, 0x01, 0x0a, 0x09, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x4f, 0x0a, 0x06, 0x50, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x12, 0x20, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, + 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x04, 0x53, 0x69, + 0x67, 0x6e, 0x12, 0x23, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2d, 0x76, 0x65, 0x6e, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x2f, 0x68, 0x6f, 0x72, 0x63, 0x72, 0x75, 0x78, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_strangelove_proto_connector_proto_rawDescOnce sync.Once + file_strangelove_proto_connector_proto_rawDescData = file_strangelove_proto_connector_proto_rawDesc +) + +func file_strangelove_proto_connector_proto_rawDescGZIP() []byte { + file_strangelove_proto_connector_proto_rawDescOnce.Do(func() { + file_strangelove_proto_connector_proto_rawDescData = protoimpl.X.CompressGZIP(file_strangelove_proto_connector_proto_rawDescData) + }) + return file_strangelove_proto_connector_proto_rawDescData +} + +var file_strangelove_proto_connector_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_strangelove_proto_connector_proto_goTypes = []interface{}{ + (*PubKeyRequest)(nil), // 0: strangelove.proto.PubKeyRequest + (*PubKeyResponse)(nil), // 1: strangelove.proto.PubKeyResponse + (*SignBlockRequest)(nil), // 2: strangelove.proto.SignBlockRequest + (*SignBlockResponse)(nil), // 3: strangelove.proto.SignBlockResponse +} +var file_strangelove_proto_connector_proto_depIdxs = []int32{ + 0, // 0: strangelove.proto.Connector.PubKey:input_type -> strangelove.proto.PubKeyRequest + 2, // 1: strangelove.proto.Connector.Sign:input_type -> strangelove.proto.SignBlockRequest + 1, // 2: strangelove.proto.Connector.PubKey:output_type -> strangelove.proto.PubKeyResponse + 3, // 3: strangelove.proto.Connector.Sign:output_type -> strangelove.proto.SignBlockResponse + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_strangelove_proto_connector_proto_init() } +func file_strangelove_proto_connector_proto_init() { + if File_strangelove_proto_connector_proto != nil { + return + } + file_strangelove_proto_node_proto_init() + if !protoimpl.UnsafeEnabled { + file_strangelove_proto_connector_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PubKeyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_connector_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PubKeyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_strangelove_proto_connector_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_strangelove_proto_connector_proto_goTypes, + DependencyIndexes: file_strangelove_proto_connector_proto_depIdxs, + MessageInfos: file_strangelove_proto_connector_proto_msgTypes, + }.Build() + File_strangelove_proto_connector_proto = out.File + file_strangelove_proto_connector_proto_rawDesc = nil + file_strangelove_proto_connector_proto_goTypes = nil + file_strangelove_proto_connector_proto_depIdxs = nil +} diff --git a/proto/strangelove/proto/connector.proto b/proto/strangelove/proto/connector.proto new file mode 100644 index 00000000..7bb6311a --- /dev/null +++ b/proto/strangelove/proto/connector.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package strangelove.proto; + +import "strangelove/proto/node.proto"; + +option go_package = "github.com/strangelove-ventures/horcrux/proto/strangelove/proto"; + +// Connection to the remote signer (E.g the Sentry) +service Connector { + rpc PubKey (PubKeyRequest) returns (PubKeyResponse) {} + rpc Sign(strangelove.proto.SignBlockRequest) returns (strangelove.proto.SignBlockResponse) {} +} + +message PubKeyRequest { + string chain_id = 1; +} + +message PubKeyResponse { + bytes pub_key = 1; +} diff --git a/proto/strangelove/proto/connector_grpc.pb.go b/proto/strangelove/proto/connector_grpc.pb.go new file mode 100644 index 00000000..6112a7da --- /dev/null +++ b/proto/strangelove/proto/connector_grpc.pb.go @@ -0,0 +1,146 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.13.0 +// source: strangelove/proto/connector.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Connector_PubKey_FullMethodName = "/strangelove.proto.Connector/PubKey" + Connector_Sign_FullMethodName = "/strangelove.proto.Connector/Sign" +) + +// ConnectorClient is the client API for Connector service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ConnectorClient interface { + PubKey(ctx context.Context, in *PubKeyRequest, opts ...grpc.CallOption) (*PubKeyResponse, error) + Sign(ctx context.Context, in *SignBlockRequest, opts ...grpc.CallOption) (*SignBlockResponse, error) +} + +type connectorClient struct { + cc grpc.ClientConnInterface +} + +func NewConnectorClient(cc grpc.ClientConnInterface) ConnectorClient { + return &connectorClient{cc} +} + +func (c *connectorClient) PubKey(ctx context.Context, in *PubKeyRequest, opts ...grpc.CallOption) (*PubKeyResponse, error) { + out := new(PubKeyResponse) + err := c.cc.Invoke(ctx, Connector_PubKey_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *connectorClient) Sign(ctx context.Context, in *SignBlockRequest, opts ...grpc.CallOption) (*SignBlockResponse, error) { + out := new(SignBlockResponse) + err := c.cc.Invoke(ctx, Connector_Sign_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ConnectorServer is the server API for Connector service. +// All implementations must embed UnimplementedConnectorServer +// for forward compatibility +type ConnectorServer interface { + PubKey(context.Context, *PubKeyRequest) (*PubKeyResponse, error) + Sign(context.Context, *SignBlockRequest) (*SignBlockResponse, error) + mustEmbedUnimplementedConnectorServer() +} + +// UnimplementedConnectorServer must be embedded to have forward compatible implementations. +type UnimplementedConnectorServer struct { +} + +func (UnimplementedConnectorServer) PubKey(context.Context, *PubKeyRequest) (*PubKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PubKey not implemented") +} +func (UnimplementedConnectorServer) Sign(context.Context, *SignBlockRequest) (*SignBlockResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Sign not implemented") +} +func (UnimplementedConnectorServer) mustEmbedUnimplementedConnectorServer() {} + +// UnsafeConnectorServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ConnectorServer will +// result in compilation errors. +type UnsafeConnectorServer interface { + mustEmbedUnimplementedConnectorServer() +} + +func RegisterConnectorServer(s grpc.ServiceRegistrar, srv ConnectorServer) { + s.RegisterService(&Connector_ServiceDesc, srv) +} + +func _Connector_PubKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PubKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ConnectorServer).PubKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Connector_PubKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ConnectorServer).PubKey(ctx, req.(*PubKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Connector_Sign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignBlockRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ConnectorServer).Sign(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Connector_Sign_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ConnectorServer).Sign(ctx, req.(*SignBlockRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Connector_ServiceDesc is the grpc.ServiceDesc for Connector service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Connector_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "strangelove.proto.Connector", + HandlerType: (*ConnectorServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "PubKey", + Handler: _Connector_PubKey_Handler, + }, + { + MethodName: "Sign", + Handler: _Connector_Sign_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "strangelove/proto/connector.proto", +} diff --git a/proto/strangelove/proto/cosigner.pb.go b/proto/strangelove/proto/cosigner.pb.go new file mode 100644 index 00000000..e6cc973e --- /dev/null +++ b/proto/strangelove/proto/cosigner.pb.go @@ -0,0 +1,797 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc v3.13.0 +// source: strangelove/proto/cosigner.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Nonce struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SourceID int32 `protobuf:"varint,1,opt,name=sourceID,proto3" json:"sourceID,omitempty"` + DestinationID int32 `protobuf:"varint,2,opt,name=destinationID,proto3" json:"destinationID,omitempty"` + PubKey []byte `protobuf:"bytes,3,opt,name=pubKey,proto3" json:"pubKey,omitempty"` + Share []byte `protobuf:"bytes,4,opt,name=share,proto3" json:"share,omitempty"` + Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (x *Nonce) Reset() { + *x = Nonce{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Nonce) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Nonce) ProtoMessage() {} + +func (x *Nonce) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Nonce.ProtoReflect.Descriptor instead. +func (*Nonce) Descriptor() ([]byte, []int) { + return file_strangelove_proto_cosigner_proto_rawDescGZIP(), []int{0} +} + +func (x *Nonce) GetSourceID() int32 { + if x != nil { + return x.SourceID + } + return 0 +} + +func (x *Nonce) GetDestinationID() int32 { + if x != nil { + return x.DestinationID + } + return 0 +} + +func (x *Nonce) GetPubKey() []byte { + if x != nil { + return x.PubKey + } + return nil +} + +func (x *Nonce) GetShare() []byte { + if x != nil { + return x.Share + } + return nil +} + +func (x *Nonce) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +type UUIDNonce struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uuid []byte `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` + Nonces []*Nonce `protobuf:"bytes,2,rep,name=nonces,proto3" json:"nonces,omitempty"` +} + +func (x *UUIDNonce) Reset() { + *x = UUIDNonce{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UUIDNonce) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UUIDNonce) ProtoMessage() {} + +func (x *UUIDNonce) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UUIDNonce.ProtoReflect.Descriptor instead. +func (*UUIDNonce) Descriptor() ([]byte, []int) { + return file_strangelove_proto_cosigner_proto_rawDescGZIP(), []int{1} +} + +func (x *UUIDNonce) GetUuid() []byte { + if x != nil { + return x.Uuid + } + return nil +} + +func (x *UUIDNonce) GetNonces() []*Nonce { + if x != nil { + return x.Nonces + } + return nil +} + +type HRST struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Round int64 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` + Step int32 `protobuf:"varint,3,opt,name=step,proto3" json:"step,omitempty"` + Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *HRST) Reset() { + *x = HRST{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HRST) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HRST) ProtoMessage() {} + +func (x *HRST) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HRST.ProtoReflect.Descriptor instead. +func (*HRST) Descriptor() ([]byte, []int) { + return file_strangelove_proto_cosigner_proto_rawDescGZIP(), []int{2} +} + +func (x *HRST) GetHeight() int64 { + if x != nil { + return x.Height + } + return 0 +} + +func (x *HRST) GetRound() int64 { + if x != nil { + return x.Round + } + return 0 +} + +func (x *HRST) GetStep() int32 { + if x != nil { + return x.Step + } + return 0 +} + +func (x *HRST) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +type SetNoncesAndSignRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uuid []byte `protobuf:"bytes,1,opt,name=uuid,proto3" json:"uuid,omitempty"` + Nonces []*Nonce `protobuf:"bytes,2,rep,name=nonces,proto3" json:"nonces,omitempty"` + Hrst *HRST `protobuf:"bytes,3,opt,name=hrst,proto3" json:"hrst,omitempty"` + SignBytes []byte `protobuf:"bytes,4,opt,name=signBytes,proto3" json:"signBytes,omitempty"` + ChainID string `protobuf:"bytes,5,opt,name=chainID,proto3" json:"chainID,omitempty"` +} + +func (x *SetNoncesAndSignRequest) Reset() { + *x = SetNoncesAndSignRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetNoncesAndSignRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetNoncesAndSignRequest) ProtoMessage() {} + +func (x *SetNoncesAndSignRequest) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetNoncesAndSignRequest.ProtoReflect.Descriptor instead. +func (*SetNoncesAndSignRequest) Descriptor() ([]byte, []int) { + return file_strangelove_proto_cosigner_proto_rawDescGZIP(), []int{3} +} + +func (x *SetNoncesAndSignRequest) GetUuid() []byte { + if x != nil { + return x.Uuid + } + return nil +} + +func (x *SetNoncesAndSignRequest) GetNonces() []*Nonce { + if x != nil { + return x.Nonces + } + return nil +} + +func (x *SetNoncesAndSignRequest) GetHrst() *HRST { + if x != nil { + return x.Hrst + } + return nil +} + +func (x *SetNoncesAndSignRequest) GetSignBytes() []byte { + if x != nil { + return x.SignBytes + } + return nil +} + +func (x *SetNoncesAndSignRequest) GetChainID() string { + if x != nil { + return x.ChainID + } + return "" +} + +type SetNoncesAndSignResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NoncePublic []byte `protobuf:"bytes,1,opt,name=noncePublic,proto3" json:"noncePublic,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` +} + +func (x *SetNoncesAndSignResponse) Reset() { + *x = SetNoncesAndSignResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SetNoncesAndSignResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetNoncesAndSignResponse) ProtoMessage() {} + +func (x *SetNoncesAndSignResponse) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetNoncesAndSignResponse.ProtoReflect.Descriptor instead. +func (*SetNoncesAndSignResponse) Descriptor() ([]byte, []int) { + return file_strangelove_proto_cosigner_proto_rawDescGZIP(), []int{4} +} + +func (x *SetNoncesAndSignResponse) GetNoncePublic() []byte { + if x != nil { + return x.NoncePublic + } + return nil +} + +func (x *SetNoncesAndSignResponse) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *SetNoncesAndSignResponse) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +type GetNoncesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uuids [][]byte `protobuf:"bytes,1,rep,name=uuids,proto3" json:"uuids,omitempty"` +} + +func (x *GetNoncesRequest) Reset() { + *x = GetNoncesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNoncesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNoncesRequest) ProtoMessage() {} + +func (x *GetNoncesRequest) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetNoncesRequest.ProtoReflect.Descriptor instead. +func (*GetNoncesRequest) Descriptor() ([]byte, []int) { + return file_strangelove_proto_cosigner_proto_rawDescGZIP(), []int{5} +} + +func (x *GetNoncesRequest) GetUuids() [][]byte { + if x != nil { + return x.Uuids + } + return nil +} + +type GetNoncesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Nonces []*UUIDNonce `protobuf:"bytes,1,rep,name=nonces,proto3" json:"nonces,omitempty"` +} + +func (x *GetNoncesResponse) Reset() { + *x = GetNoncesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetNoncesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetNoncesResponse) ProtoMessage() {} + +func (x *GetNoncesResponse) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetNoncesResponse.ProtoReflect.Descriptor instead. +func (*GetNoncesResponse) Descriptor() ([]byte, []int) { + return file_strangelove_proto_cosigner_proto_rawDescGZIP(), []int{6} +} + +func (x *GetNoncesResponse) GetNonces() []*UUIDNonce { + if x != nil { + return x.Nonces + } + return nil +} + +type PingRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PingRequest) Reset() { + *x = PingRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PingRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingRequest) ProtoMessage() {} + +func (x *PingRequest) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingRequest.ProtoReflect.Descriptor instead. +func (*PingRequest) Descriptor() ([]byte, []int) { + return file_strangelove_proto_cosigner_proto_rawDescGZIP(), []int{7} +} + +type PingResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *PingResponse) Reset() { + *x = PingResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PingResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingResponse) ProtoMessage() {} + +func (x *PingResponse) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_cosigner_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingResponse.ProtoReflect.Descriptor instead. +func (*PingResponse) Descriptor() ([]byte, []int) { + return file_strangelove_proto_cosigner_proto_rawDescGZIP(), []int{8} +} + +var File_strangelove_proto_cosigner_proto protoreflect.FileDescriptor + +var file_strangelove_proto_cosigner_proto_rawDesc = []byte{ + 0x0a, 0x20, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x11, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x95, 0x01, 0x0a, 0x05, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x64, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0d, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x68, 0x61, + 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x68, 0x61, 0x72, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x51, 0x0a, + 0x09, 0x55, 0x55, 0x49, 0x44, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x30, + 0x0a, 0x06, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x52, 0x06, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, + 0x22, 0x66, 0x0a, 0x04, 0x48, 0x52, 0x53, 0x54, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x05, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x65, 0x70, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xc4, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x74, + 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x30, 0x0a, 0x06, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, + 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x6f, 0x6e, + 0x63, 0x65, 0x52, 0x06, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x2b, 0x0a, 0x04, 0x68, 0x72, + 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, + 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x52, 0x53, + 0x54, 0x52, 0x04, 0x68, 0x72, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x42, + 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, 0x22, + 0x78, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x53, + 0x69, 0x67, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x6e, + 0x6f, 0x6e, 0x63, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x28, 0x0a, 0x10, 0x47, 0x65, 0x74, + 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x75, 0x75, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x75, 0x75, + 0x69, 0x64, 0x73, 0x22, 0x49, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x06, 0x6e, 0x6f, 0x6e, 0x63, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, + 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x55, 0x49, + 0x44, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x52, 0x06, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x22, 0x0d, + 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, + 0x0c, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x9e, 0x02, + 0x0a, 0x08, 0x43, 0x6f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x12, 0x6d, 0x0a, 0x10, 0x53, 0x65, + 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x2a, + 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x53, + 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x73, 0x74, 0x72, + 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, + 0x65, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x41, 0x6e, 0x64, 0x53, 0x69, 0x67, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x09, 0x47, 0x65, 0x74, + 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, + 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, + 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x74, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x49, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1e, 0x2e, 0x73, 0x74, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x73, 0x74, + 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x41, + 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x72, + 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2d, 0x76, 0x65, 0x6e, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x2f, 0x68, 0x6f, 0x72, 0x63, 0x72, 0x75, 0x78, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_strangelove_proto_cosigner_proto_rawDescOnce sync.Once + file_strangelove_proto_cosigner_proto_rawDescData = file_strangelove_proto_cosigner_proto_rawDesc +) + +func file_strangelove_proto_cosigner_proto_rawDescGZIP() []byte { + file_strangelove_proto_cosigner_proto_rawDescOnce.Do(func() { + file_strangelove_proto_cosigner_proto_rawDescData = protoimpl.X.CompressGZIP(file_strangelove_proto_cosigner_proto_rawDescData) + }) + return file_strangelove_proto_cosigner_proto_rawDescData +} + +var file_strangelove_proto_cosigner_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_strangelove_proto_cosigner_proto_goTypes = []interface{}{ + (*Nonce)(nil), // 0: strangelove.proto.Nonce + (*UUIDNonce)(nil), // 1: strangelove.proto.UUIDNonce + (*HRST)(nil), // 2: strangelove.proto.HRST + (*SetNoncesAndSignRequest)(nil), // 3: strangelove.proto.SetNoncesAndSignRequest + (*SetNoncesAndSignResponse)(nil), // 4: strangelove.proto.SetNoncesAndSignResponse + (*GetNoncesRequest)(nil), // 5: strangelove.proto.GetNoncesRequest + (*GetNoncesResponse)(nil), // 6: strangelove.proto.GetNoncesResponse + (*PingRequest)(nil), // 7: strangelove.proto.PingRequest + (*PingResponse)(nil), // 8: strangelove.proto.PingResponse +} +var file_strangelove_proto_cosigner_proto_depIdxs = []int32{ + 0, // 0: strangelove.proto.UUIDNonce.nonces:type_name -> strangelove.proto.Nonce + 0, // 1: strangelove.proto.SetNoncesAndSignRequest.nonces:type_name -> strangelove.proto.Nonce + 2, // 2: strangelove.proto.SetNoncesAndSignRequest.hrst:type_name -> strangelove.proto.HRST + 1, // 3: strangelove.proto.GetNoncesResponse.nonces:type_name -> strangelove.proto.UUIDNonce + 3, // 4: strangelove.proto.Cosigner.SetNoncesAndSign:input_type -> strangelove.proto.SetNoncesAndSignRequest + 5, // 5: strangelove.proto.Cosigner.GetNonces:input_type -> strangelove.proto.GetNoncesRequest + 7, // 6: strangelove.proto.Cosigner.Ping:input_type -> strangelove.proto.PingRequest + 4, // 7: strangelove.proto.Cosigner.SetNoncesAndSign:output_type -> strangelove.proto.SetNoncesAndSignResponse + 6, // 8: strangelove.proto.Cosigner.GetNonces:output_type -> strangelove.proto.GetNoncesResponse + 8, // 9: strangelove.proto.Cosigner.Ping:output_type -> strangelove.proto.PingResponse + 7, // [7:10] is the sub-list for method output_type + 4, // [4:7] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_strangelove_proto_cosigner_proto_init() } +func file_strangelove_proto_cosigner_proto_init() { + if File_strangelove_proto_cosigner_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_strangelove_proto_cosigner_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Nonce); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_cosigner_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UUIDNonce); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_cosigner_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HRST); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_cosigner_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetNoncesAndSignRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_cosigner_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SetNoncesAndSignResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_cosigner_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNoncesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_cosigner_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetNoncesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_cosigner_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PingRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_cosigner_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PingResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_strangelove_proto_cosigner_proto_rawDesc, + NumEnums: 0, + NumMessages: 9, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_strangelove_proto_cosigner_proto_goTypes, + DependencyIndexes: file_strangelove_proto_cosigner_proto_depIdxs, + MessageInfos: file_strangelove_proto_cosigner_proto_msgTypes, + }.Build() + File_strangelove_proto_cosigner_proto = out.File + file_strangelove_proto_cosigner_proto_rawDesc = nil + file_strangelove_proto_cosigner_proto_goTypes = nil + file_strangelove_proto_cosigner_proto_depIdxs = nil +} diff --git a/proto/strangelove/proto/cosigner.proto b/proto/strangelove/proto/cosigner.proto new file mode 100644 index 00000000..a5e88c0b --- /dev/null +++ b/proto/strangelove/proto/cosigner.proto @@ -0,0 +1,93 @@ +syntax = "proto3"; +package strangelove.proto; + +//option go_package = "strangelove/horcrux/proto"; +option go_package = "github.com/strangelove-ventures/horcrux/proto/strangelove/proto"; + +service Cosigner { + rpc SetNoncesAndSign (SetNoncesAndSignRequest) returns (SetNoncesAndSignResponse) {} + rpc GetNonces (GetNoncesRequest) returns (GetNoncesResponse) {} + rpc Ping(PingRequest) returns (PingResponse) {} +} + + +message Nonce { + int32 sourceID = 1; + int32 destinationID = 2; + bytes pubKey = 3; + bytes share = 4; + bytes signature = 5; +} + +message UUIDNonce { + bytes uuid = 1; + repeated Nonce nonces = 2; +} + +message HRST { + int64 height = 1; + int64 round = 2; + int32 step = 3; + int64 timestamp = 4; +} + +message SetNoncesAndSignRequest { + bytes uuid = 1; + repeated Nonce nonces = 2; + HRST hrst = 3; + bytes signBytes = 4; + string chainID = 5; +} + +message SetNoncesAndSignResponse { + bytes noncePublic = 1; + int64 timestamp = 2; + bytes signature = 3; +} + +message GetNoncesRequest { + repeated bytes uuids = 1; +} + +message GetNoncesResponse { + repeated UUIDNonce nonces = 1; +} + +message PingRequest {} +message PingResponse {} +/* +message GetLeaderRequest {} + +message GetLeaderResponse { + int32 leader = 1; +} + + +message TransferLeadershipRequest { + string leaderID = 1; +} + +message TransferLeadershipResponse { + string leaderID = 1; + string leaderAddress = 2; +} + + +message Block { + int64 height = 1; + int64 round = 2; + int32 step = 3; + bytes signBytes = 4; + int64 timestamp = 5; +} + +message SignBlockRequest { + string chainID = 1; + Block block = 2; +} + +message SignBlockResponse { + bytes signature = 1; + int64 timestamp = 2; +} +*/ \ No newline at end of file diff --git a/proto/strangelove/proto/cosigner_grpc.pb.go b/proto/strangelove/proto/cosigner_grpc.pb.go new file mode 100644 index 00000000..c2a3f11d --- /dev/null +++ b/proto/strangelove/proto/cosigner_grpc.pb.go @@ -0,0 +1,183 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.13.0 +// source: strangelove/proto/cosigner.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + Cosigner_SetNoncesAndSign_FullMethodName = "/strangelove.proto.Cosigner/SetNoncesAndSign" + Cosigner_GetNonces_FullMethodName = "/strangelove.proto.Cosigner/GetNonces" + Cosigner_Ping_FullMethodName = "/strangelove.proto.Cosigner/Ping" +) + +// CosignerClient is the client API for Cosigner service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type CosignerClient interface { + SetNoncesAndSign(ctx context.Context, in *SetNoncesAndSignRequest, opts ...grpc.CallOption) (*SetNoncesAndSignResponse, error) + GetNonces(ctx context.Context, in *GetNoncesRequest, opts ...grpc.CallOption) (*GetNoncesResponse, error) + Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) +} + +type cosignerClient struct { + cc grpc.ClientConnInterface +} + +func NewCosignerClient(cc grpc.ClientConnInterface) CosignerClient { + return &cosignerClient{cc} +} + +func (c *cosignerClient) SetNoncesAndSign(ctx context.Context, in *SetNoncesAndSignRequest, opts ...grpc.CallOption) (*SetNoncesAndSignResponse, error) { + out := new(SetNoncesAndSignResponse) + err := c.cc.Invoke(ctx, Cosigner_SetNoncesAndSign_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cosignerClient) GetNonces(ctx context.Context, in *GetNoncesRequest, opts ...grpc.CallOption) (*GetNoncesResponse, error) { + out := new(GetNoncesResponse) + err := c.cc.Invoke(ctx, Cosigner_GetNonces_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *cosignerClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) { + out := new(PingResponse) + err := c.cc.Invoke(ctx, Cosigner_Ping_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CosignerServer is the server API for Cosigner service. +// All implementations must embed UnimplementedCosignerServer +// for forward compatibility +type CosignerServer interface { + SetNoncesAndSign(context.Context, *SetNoncesAndSignRequest) (*SetNoncesAndSignResponse, error) + GetNonces(context.Context, *GetNoncesRequest) (*GetNoncesResponse, error) + Ping(context.Context, *PingRequest) (*PingResponse, error) + mustEmbedUnimplementedCosignerServer() +} + +// UnimplementedCosignerServer must be embedded to have forward compatible implementations. +type UnimplementedCosignerServer struct { +} + +func (UnimplementedCosignerServer) SetNoncesAndSign(context.Context, *SetNoncesAndSignRequest) (*SetNoncesAndSignResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetNoncesAndSign not implemented") +} +func (UnimplementedCosignerServer) GetNonces(context.Context, *GetNoncesRequest) (*GetNoncesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetNonces not implemented") +} +func (UnimplementedCosignerServer) Ping(context.Context, *PingRequest) (*PingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") +} +func (UnimplementedCosignerServer) mustEmbedUnimplementedCosignerServer() {} + +// UnsafeCosignerServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CosignerServer will +// result in compilation errors. +type UnsafeCosignerServer interface { + mustEmbedUnimplementedCosignerServer() +} + +func RegisterCosignerServer(s grpc.ServiceRegistrar, srv CosignerServer) { + s.RegisterService(&Cosigner_ServiceDesc, srv) +} + +func _Cosigner_SetNoncesAndSign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetNoncesAndSignRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CosignerServer).SetNoncesAndSign(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cosigner_SetNoncesAndSign_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CosignerServer).SetNoncesAndSign(ctx, req.(*SetNoncesAndSignRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cosigner_GetNonces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetNoncesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CosignerServer).GetNonces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cosigner_GetNonces_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CosignerServer).GetNonces(ctx, req.(*GetNoncesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cosigner_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CosignerServer).Ping(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cosigner_Ping_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CosignerServer).Ping(ctx, req.(*PingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Cosigner_ServiceDesc is the grpc.ServiceDesc for Cosigner service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Cosigner_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "strangelove.proto.Cosigner", + HandlerType: (*CosignerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SetNoncesAndSign", + Handler: _Cosigner_SetNoncesAndSign_Handler, + }, + { + MethodName: "GetNonces", + Handler: _Cosigner_GetNonces_Handler, + }, + { + MethodName: "Ping", + Handler: _Cosigner_Ping_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "strangelove/proto/cosigner.proto", +} diff --git a/proto/strangelove/proto/node.pb.go b/proto/strangelove/proto/node.pb.go new file mode 100644 index 00000000..39924799 --- /dev/null +++ b/proto/strangelove/proto/node.pb.go @@ -0,0 +1,611 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc v3.13.0 +// source: strangelove/proto/node.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type SignBlockRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainID string `protobuf:"bytes,1,opt,name=chainID,proto3" json:"chainID,omitempty"` + Block *Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` +} + +func (x *SignBlockRequest) Reset() { + *x = SignBlockRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_node_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SignBlockRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignBlockRequest) ProtoMessage() {} + +func (x *SignBlockRequest) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_node_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignBlockRequest.ProtoReflect.Descriptor instead. +func (*SignBlockRequest) Descriptor() ([]byte, []int) { + return file_strangelove_proto_node_proto_rawDescGZIP(), []int{0} +} + +func (x *SignBlockRequest) GetChainID() string { + if x != nil { + return x.ChainID + } + return "" +} + +func (x *SignBlockRequest) GetBlock() *Block { + if x != nil { + return x.Block + } + return nil +} + +type SignBlockResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *SignBlockResponse) Reset() { + *x = SignBlockResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_node_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SignBlockResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignBlockResponse) ProtoMessage() {} + +func (x *SignBlockResponse) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_node_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignBlockResponse.ProtoReflect.Descriptor instead. +func (*SignBlockResponse) Descriptor() ([]byte, []int) { + return file_strangelove_proto_node_proto_rawDescGZIP(), []int{1} +} + +func (x *SignBlockResponse) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +func (x *SignBlockResponse) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +type TransferLeadershipRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LeaderID string `protobuf:"bytes,1,opt,name=leaderID,proto3" json:"leaderID,omitempty"` +} + +func (x *TransferLeadershipRequest) Reset() { + *x = TransferLeadershipRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_node_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransferLeadershipRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransferLeadershipRequest) ProtoMessage() {} + +func (x *TransferLeadershipRequest) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_node_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransferLeadershipRequest.ProtoReflect.Descriptor instead. +func (*TransferLeadershipRequest) Descriptor() ([]byte, []int) { + return file_strangelove_proto_node_proto_rawDescGZIP(), []int{2} +} + +func (x *TransferLeadershipRequest) GetLeaderID() string { + if x != nil { + return x.LeaderID + } + return "" +} + +type TransferLeadershipResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LeaderID string `protobuf:"bytes,1,opt,name=leaderID,proto3" json:"leaderID,omitempty"` + LeaderAddress string `protobuf:"bytes,2,opt,name=leaderAddress,proto3" json:"leaderAddress,omitempty"` +} + +func (x *TransferLeadershipResponse) Reset() { + *x = TransferLeadershipResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_node_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransferLeadershipResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransferLeadershipResponse) ProtoMessage() {} + +func (x *TransferLeadershipResponse) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_node_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransferLeadershipResponse.ProtoReflect.Descriptor instead. +func (*TransferLeadershipResponse) Descriptor() ([]byte, []int) { + return file_strangelove_proto_node_proto_rawDescGZIP(), []int{3} +} + +func (x *TransferLeadershipResponse) GetLeaderID() string { + if x != nil { + return x.LeaderID + } + return "" +} + +func (x *TransferLeadershipResponse) GetLeaderAddress() string { + if x != nil { + return x.LeaderAddress + } + return "" +} + +type GetLeaderRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetLeaderRequest) Reset() { + *x = GetLeaderRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_node_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLeaderRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLeaderRequest) ProtoMessage() {} + +func (x *GetLeaderRequest) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_node_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLeaderRequest.ProtoReflect.Descriptor instead. +func (*GetLeaderRequest) Descriptor() ([]byte, []int) { + return file_strangelove_proto_node_proto_rawDescGZIP(), []int{4} +} + +type GetLeaderResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Leader int32 `protobuf:"varint,1,opt,name=leader,proto3" json:"leader,omitempty"` +} + +func (x *GetLeaderResponse) Reset() { + *x = GetLeaderResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_node_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetLeaderResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetLeaderResponse) ProtoMessage() {} + +func (x *GetLeaderResponse) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_node_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetLeaderResponse.ProtoReflect.Descriptor instead. +func (*GetLeaderResponse) Descriptor() ([]byte, []int) { + return file_strangelove_proto_node_proto_rawDescGZIP(), []int{5} +} + +func (x *GetLeaderResponse) GetLeader() int32 { + if x != nil { + return x.Leader + } + return 0 +} + +type Block struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Round int64 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` + Step int32 `protobuf:"varint,3,opt,name=step,proto3" json:"step,omitempty"` + SignBytes []byte `protobuf:"bytes,4,opt,name=signBytes,proto3" json:"signBytes,omitempty"` + Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *Block) Reset() { + *x = Block{} + if protoimpl.UnsafeEnabled { + mi := &file_strangelove_proto_node_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Block) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Block) ProtoMessage() {} + +func (x *Block) ProtoReflect() protoreflect.Message { + mi := &file_strangelove_proto_node_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Block.ProtoReflect.Descriptor instead. +func (*Block) Descriptor() ([]byte, []int) { + return file_strangelove_proto_node_proto_rawDescGZIP(), []int{6} +} + +func (x *Block) GetHeight() int64 { + if x != nil { + return x.Height + } + return 0 +} + +func (x *Block) GetRound() int64 { + if x != nil { + return x.Round + } + return 0 +} + +func (x *Block) GetStep() int32 { + if x != nil { + return x.Step + } + return 0 +} + +func (x *Block) GetSignBytes() []byte { + if x != nil { + return x.SignBytes + } + return nil +} + +func (x *Block) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +var File_strangelove_proto_node_proto protoreflect.FileDescriptor + +var file_strangelove_proto_node_proto_rawDesc = []byte{ + 0x0a, 0x1c, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, + 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x5c, 0x0a, 0x10, 0x53, 0x69, 0x67, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x44, 0x12, + 0x2e, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, + 0x4f, 0x0a, 0x11, 0x53, 0x69, 0x67, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x22, 0x37, 0x0a, 0x19, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x49, 0x44, 0x22, 0x5e, 0x0a, 0x1a, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, + 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2b, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x85, 0x01, 0x0a, 0x05, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, + 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x65, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x42, + 0x79, 0x74, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x32, 0xb6, 0x02, 0x0a, 0x0b, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x58, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, + 0x23, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, + 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x12, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x68, + 0x69, 0x70, 0x12, 0x2c, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4c, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2d, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4c, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x58, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x23, + 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x41, 0x5a, 0x3f, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x72, 0x61, 0x6e, 0x67, + 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2d, 0x76, 0x65, 0x6e, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2f, 0x68, + 0x6f, 0x72, 0x63, 0x72, 0x75, 0x78, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x74, 0x72, + 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x76, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_strangelove_proto_node_proto_rawDescOnce sync.Once + file_strangelove_proto_node_proto_rawDescData = file_strangelove_proto_node_proto_rawDesc +) + +func file_strangelove_proto_node_proto_rawDescGZIP() []byte { + file_strangelove_proto_node_proto_rawDescOnce.Do(func() { + file_strangelove_proto_node_proto_rawDescData = protoimpl.X.CompressGZIP(file_strangelove_proto_node_proto_rawDescData) + }) + return file_strangelove_proto_node_proto_rawDescData +} + +var file_strangelove_proto_node_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_strangelove_proto_node_proto_goTypes = []interface{}{ + (*SignBlockRequest)(nil), // 0: strangelove.proto.SignBlockRequest + (*SignBlockResponse)(nil), // 1: strangelove.proto.SignBlockResponse + (*TransferLeadershipRequest)(nil), // 2: strangelove.proto.TransferLeadershipRequest + (*TransferLeadershipResponse)(nil), // 3: strangelove.proto.TransferLeadershipResponse + (*GetLeaderRequest)(nil), // 4: strangelove.proto.GetLeaderRequest + (*GetLeaderResponse)(nil), // 5: strangelove.proto.GetLeaderResponse + (*Block)(nil), // 6: strangelove.proto.Block +} +var file_strangelove_proto_node_proto_depIdxs = []int32{ + 6, // 0: strangelove.proto.SignBlockRequest.block:type_name -> strangelove.proto.Block + 0, // 1: strangelove.proto.NodeService.SignBlock:input_type -> strangelove.proto.SignBlockRequest + 2, // 2: strangelove.proto.NodeService.TransferLeadership:input_type -> strangelove.proto.TransferLeadershipRequest + 4, // 3: strangelove.proto.NodeService.GetLeader:input_type -> strangelove.proto.GetLeaderRequest + 1, // 4: strangelove.proto.NodeService.SignBlock:output_type -> strangelove.proto.SignBlockResponse + 3, // 5: strangelove.proto.NodeService.TransferLeadership:output_type -> strangelove.proto.TransferLeadershipResponse + 5, // 6: strangelove.proto.NodeService.GetLeader:output_type -> strangelove.proto.GetLeaderResponse + 4, // [4:7] is the sub-list for method output_type + 1, // [1:4] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_strangelove_proto_node_proto_init() } +func file_strangelove_proto_node_proto_init() { + if File_strangelove_proto_node_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_strangelove_proto_node_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SignBlockRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_node_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SignBlockResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_node_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransferLeadershipRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_node_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransferLeadershipResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_node_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLeaderRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_node_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetLeaderResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_strangelove_proto_node_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Block); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_strangelove_proto_node_proto_rawDesc, + NumEnums: 0, + NumMessages: 7, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_strangelove_proto_node_proto_goTypes, + DependencyIndexes: file_strangelove_proto_node_proto_depIdxs, + MessageInfos: file_strangelove_proto_node_proto_msgTypes, + }.Build() + File_strangelove_proto_node_proto = out.File + file_strangelove_proto_node_proto_rawDesc = nil + file_strangelove_proto_node_proto_goTypes = nil + file_strangelove_proto_node_proto_depIdxs = nil +} diff --git a/proto/strangelove/proto/node.proto b/proto/strangelove/proto/node.proto new file mode 100644 index 00000000..874dd00d --- /dev/null +++ b/proto/strangelove/proto/node.proto @@ -0,0 +1,45 @@ +syntax = "proto3"; +package strangelove.proto; + +option go_package = "github.com/strangelove-ventures/horcrux/proto/strangelove/proto"; + +service NodeService { + // TODO: ADD rpc Validate(ValidateRequest) returns (ValidateResponse); + + rpc SignBlock (SignBlockRequest) returns (SignBlockResponse) {} + rpc TransferLeadership (TransferLeadershipRequest) returns (TransferLeadershipResponse) {} + rpc GetLeader (GetLeaderRequest) returns (GetLeaderResponse) {} +} + +message SignBlockRequest { + string chainID = 1; + Block block = 2; +} + +message SignBlockResponse { + bytes signature = 1; + int64 timestamp = 2; +} + +message TransferLeadershipRequest { + string leaderID = 1; +} + +message TransferLeadershipResponse { + string leaderID = 1; + string leaderAddress = 2; +} + +message GetLeaderRequest {} + +message GetLeaderResponse { + int32 leader = 1; +} + +message Block { + int64 height = 1; + int64 round = 2; + int32 step = 3; + bytes signBytes = 4; + int64 timestamp = 5; +} diff --git a/proto/strangelove/proto/node_grpc.pb.go b/proto/strangelove/proto/node_grpc.pb.go new file mode 100644 index 00000000..2a09ed64 --- /dev/null +++ b/proto/strangelove/proto/node_grpc.pb.go @@ -0,0 +1,183 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.13.0 +// source: strangelove/proto/node.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + NodeService_SignBlock_FullMethodName = "/strangelove.proto.NodeService/SignBlock" + NodeService_TransferLeadership_FullMethodName = "/strangelove.proto.NodeService/TransferLeadership" + NodeService_GetLeader_FullMethodName = "/strangelove.proto.NodeService/GetLeader" +) + +// NodeServiceClient is the client API for NodeService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type NodeServiceClient interface { + SignBlock(ctx context.Context, in *SignBlockRequest, opts ...grpc.CallOption) (*SignBlockResponse, error) + TransferLeadership(ctx context.Context, in *TransferLeadershipRequest, opts ...grpc.CallOption) (*TransferLeadershipResponse, error) + GetLeader(ctx context.Context, in *GetLeaderRequest, opts ...grpc.CallOption) (*GetLeaderResponse, error) +} + +type nodeServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewNodeServiceClient(cc grpc.ClientConnInterface) NodeServiceClient { + return &nodeServiceClient{cc} +} + +func (c *nodeServiceClient) SignBlock(ctx context.Context, in *SignBlockRequest, opts ...grpc.CallOption) (*SignBlockResponse, error) { + out := new(SignBlockResponse) + err := c.cc.Invoke(ctx, NodeService_SignBlock_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nodeServiceClient) TransferLeadership(ctx context.Context, in *TransferLeadershipRequest, opts ...grpc.CallOption) (*TransferLeadershipResponse, error) { + out := new(TransferLeadershipResponse) + err := c.cc.Invoke(ctx, NodeService_TransferLeadership_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nodeServiceClient) GetLeader(ctx context.Context, in *GetLeaderRequest, opts ...grpc.CallOption) (*GetLeaderResponse, error) { + out := new(GetLeaderResponse) + err := c.cc.Invoke(ctx, NodeService_GetLeader_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// NodeServiceServer is the server API for NodeService service. +// All implementations must embed UnimplementedNodeServiceServer +// for forward compatibility +type NodeServiceServer interface { + SignBlock(context.Context, *SignBlockRequest) (*SignBlockResponse, error) + TransferLeadership(context.Context, *TransferLeadershipRequest) (*TransferLeadershipResponse, error) + GetLeader(context.Context, *GetLeaderRequest) (*GetLeaderResponse, error) + mustEmbedUnimplementedNodeServiceServer() +} + +// UnimplementedNodeServiceServer must be embedded to have forward compatible implementations. +type UnimplementedNodeServiceServer struct { +} + +func (UnimplementedNodeServiceServer) SignBlock(context.Context, *SignBlockRequest) (*SignBlockResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignBlock not implemented") +} +func (UnimplementedNodeServiceServer) TransferLeadership(context.Context, *TransferLeadershipRequest) (*TransferLeadershipResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TransferLeadership not implemented") +} +func (UnimplementedNodeServiceServer) GetLeader(context.Context, *GetLeaderRequest) (*GetLeaderResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetLeader not implemented") +} +func (UnimplementedNodeServiceServer) mustEmbedUnimplementedNodeServiceServer() {} + +// UnsafeNodeServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to NodeServiceServer will +// result in compilation errors. +type UnsafeNodeServiceServer interface { + mustEmbedUnimplementedNodeServiceServer() +} + +func RegisterNodeServiceServer(s grpc.ServiceRegistrar, srv NodeServiceServer) { + s.RegisterService(&NodeService_ServiceDesc, srv) +} + +func _NodeService_SignBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignBlockRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NodeServiceServer).SignBlock(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NodeService_SignBlock_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NodeServiceServer).SignBlock(ctx, req.(*SignBlockRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NodeService_TransferLeadership_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransferLeadershipRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NodeServiceServer).TransferLeadership(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NodeService_TransferLeadership_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NodeServiceServer).TransferLeadership(ctx, req.(*TransferLeadershipRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _NodeService_GetLeader_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetLeaderRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(NodeServiceServer).GetLeader(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: NodeService_GetLeader_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(NodeServiceServer).GetLeader(ctx, req.(*GetLeaderRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// NodeService_ServiceDesc is the grpc.ServiceDesc for NodeService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var NodeService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "strangelove.proto.NodeService", + HandlerType: (*NodeServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SignBlock", + Handler: _NodeService_SignBlock_Handler, + }, + { + MethodName: "TransferLeadership", + Handler: _NodeService_TransferLeadership_Handler, + }, + { + MethodName: "GetLeader", + Handler: _NodeService_GetLeader_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "strangelove/proto/node.proto", +} diff --git a/signer/cosigner.go b/signer/cosigner.go deleted file mode 100644 index 1be4cc77..00000000 --- a/signer/cosigner.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Package signer: Cosinger is responsible for the network communication between the cosigners -*/ -package signer - -import ( - "context" - "time" - - "github.com/strangelove-ventures/horcrux/pkg/types" - - cometcrypto "github.com/cometbft/cometbft/crypto" - "github.com/google/uuid" - "github.com/strangelove-ventures/horcrux/signer/proto" -) - -type Localcosigner interface { - // TODO - add methods -} -type Remotecosigner interface { - // TODO - add methods -} - -// Cosigner interface is a set of methods for an m-of-n threshold signature. -// This interface abstracts the underlying key storage and management -type Cosigner interface { - // GetIndex gets the index of the cosigner - // The index is the shamir index: 1, 2, etc... - GetIndex() int - - // Get the P2P URL (GRPC and Raft) - GetAddress() string - - // Get the combined public key - GetPubKey(chainID string) (cometcrypto.PubKey, error) - - VerifySignature(chainID string, payload, signature []byte) bool - - // Get nonces for all cosigner shards - GetNonces(ctx context.Context, uuids []uuid.UUID) (CosignerUUIDNoncesMultiple, error) - - // Sign the requested bytes - SetNoncesAndSign(ctx context.Context, req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) -} - -type Cosigners []Cosigner - -func (cosigners Cosigners) GetByID(id int) Cosigner { - for _, cosigner := range cosigners { - if cosigner.GetIndex() == id { - return cosigner - } - } - return nil -} - -// CosignerSignRequest is sent to a co-signer to obtain their signature for the SignBytes -// The SignBytes should be a serialized block -type CosignerSignRequest struct { - ChainID string - SignBytes []byte - UUID uuid.UUID -} - -type CosignerSignResponse struct { - NoncePublic []byte - Timestamp time.Time - Signature []byte -} - -type CosignerNonce struct { - SourceID int - DestinationID int - PubKey []byte - Share []byte - Signature []byte -} - -func (secretPart *CosignerNonce) toProto() *proto.Nonce { - return &proto.Nonce{ - SourceID: int32(secretPart.SourceID), - DestinationID: int32(secretPart.DestinationID), - PubKey: secretPart.PubKey, - Share: secretPart.Share, - Signature: secretPart.Signature, - } -} - -type CosignerNonces []CosignerNonce - -func (secretParts CosignerNonces) toProto() (out []*proto.Nonce) { - for _, secretPart := range secretParts { - out = append(out, secretPart.toProto()) - } - return -} - -func CosignerNonceFromProto(secretPart *proto.Nonce) CosignerNonce { - return CosignerNonce{ - SourceID: int(secretPart.SourceID), - DestinationID: int(secretPart.DestinationID), - PubKey: secretPart.PubKey, - Share: secretPart.Share, - Signature: secretPart.Signature, - } -} - -func CosignerNoncesFromProto(secretParts []*proto.Nonce) []CosignerNonce { - out := make([]CosignerNonce, len(secretParts)) - for i, secretPart := range secretParts { - out[i] = CosignerNonceFromProto(secretPart) - } - return out -} - -type CosignerSignBlockRequest struct { - ChainID string - Block *types.Block -} - -type CosignerSignBlockResponse struct { - Signature []byte -} -type CosignerUUIDNonces struct { - UUID uuid.UUID - Nonces CosignerNonces -} - -func (n *CosignerUUIDNonces) For(id int) *CosignerUUIDNonces { - res := &CosignerUUIDNonces{UUID: n.UUID} - for _, nonce := range n.Nonces { - if nonce.DestinationID == id { - res.Nonces = append(res.Nonces, nonce) - } - } - return res -} - -type CosignerUUIDNoncesMultiple []*CosignerUUIDNonces - -func (n CosignerUUIDNoncesMultiple) toProto() []*proto.UUIDNonce { - out := make([]*proto.UUIDNonce, len(n)) - for i, nonces := range n { - out[i] = &proto.UUIDNonce{ - Uuid: nonces.UUID[:], - Nonces: nonces.Nonces.toProto(), - } - } - return out -} - -type CosignerSetNoncesAndSignRequest struct { - ChainID string - Nonces *CosignerUUIDNonces - HRST types.HRSTKey - SignBytes []byte -} diff --git a/signer/cosigner_grpc_server.go b/signer/cosigner_grpc_server.go deleted file mode 100644 index b3e72178..00000000 --- a/signer/cosigner_grpc_server.go +++ /dev/null @@ -1,143 +0,0 @@ -package signer - -import ( - "context" - "fmt" - - "github.com/strangelove-ventures/horcrux/pkg/types" - - "github.com/google/uuid" - "github.com/hashicorp/raft" - "github.com/strangelove-ventures/horcrux/signer/proto" -) - -var _ proto.CosignerServer = &CosignerGRPCServer{} - -type CosignerGRPCServer struct { - cosigner *LocalCosigner - thresholdValidator *ThresholdValidator - raftStore *RaftStore - // TODO: add logger and not rely on raftStore.logger - proto.UnimplementedCosignerServer -} - -func NewCosignerGRPCServer( - cosigner *LocalCosigner, - thresholdValidator *ThresholdValidator, - raftStore *RaftStore, -) *CosignerGRPCServer { - return &CosignerGRPCServer{ - cosigner: cosigner, - thresholdValidator: thresholdValidator, - raftStore: raftStore, - } -} - -func (rpc *CosignerGRPCServer) SignBlock( - ctx context.Context, - req *proto.SignBlockRequest, -) (*proto.SignBlockResponse, error) { - res, _, err := rpc.thresholdValidator.Sign(ctx, req.ChainID, BlockFromProto(req.Block)) - if err != nil { - return nil, err - } - return &proto.SignBlockResponse{ - Signature: res, - }, nil -} - -func (rpc *CosignerGRPCServer) SetNoncesAndSign( - ctx context.Context, - req *proto.SetNoncesAndSignRequest, -) (*proto.SetNoncesAndSignResponse, error) { - res, err := rpc.cosigner.SetNoncesAndSign(ctx, CosignerSetNoncesAndSignRequest{ - ChainID: req.ChainID, - Nonces: &CosignerUUIDNonces{ - UUID: uuid.UUID(req.Uuid), - Nonces: CosignerNoncesFromProto(req.GetNonces()), - }, - HRST: types.HRSTKeyFromProto(req.GetHrst()), - SignBytes: req.GetSignBytes(), - }) - if err != nil { - rpc.raftStore.logger.Error( - "Failed to sign with shard", - "chain_id", req.ChainID, - "height", req.Hrst.Height, - "round", req.Hrst.Round, - "step", req.Hrst.Step, - "error", err, - ) - return nil, err - } - rpc.raftStore.logger.Info( - "Signed with shard", - "chain_id", req.ChainID, - "height", req.Hrst.Height, - "round", req.Hrst.Round, - "step", req.Hrst.Step, - ) - return &proto.SetNoncesAndSignResponse{ - NoncePublic: res.NoncePublic, - Timestamp: res.Timestamp.UnixNano(), - Signature: res.Signature, - }, nil -} - -func (rpc *CosignerGRPCServer) GetNonces( - ctx context.Context, - req *proto.GetNoncesRequest, -) (*proto.GetNoncesResponse, error) { - uuids := make([]uuid.UUID, len(req.Uuids)) - for i, uuidBytes := range req.Uuids { - uuids[i] = uuid.UUID(uuidBytes) - } - res, err := rpc.cosigner.GetNonces( - ctx, - uuids, - ) - if err != nil { - return nil, err - } - - return &proto.GetNoncesResponse{ - Nonces: res.toProto(), - }, nil -} - -// TODO: // TransferLeadership should not be a CosignerGRPCServer method? -func (rpc *CosignerGRPCServer) TransferLeadership( - _ context.Context, - req *proto.TransferLeadershipRequest, -) (*proto.TransferLeadershipResponse, error) { - if rpc.raftStore.raft.State() != raft.Leader { - return &proto.TransferLeadershipResponse{}, nil - } - leaderID := req.GetLeaderID() - if leaderID != "" { - for _, c := range rpc.raftStore.Cosigners { - shardID := fmt.Sprint(c.GetIndex()) - if shardID == leaderID { - raftAddress := p2pURLToRaftAddress(c.GetAddress()) - fmt.Printf("Transferring leadership to Index: %s - Address: %s\n", shardID, raftAddress) - rpc.raftStore.raft.LeadershipTransferToServer(raft.ServerID(shardID), raft.ServerAddress(raftAddress)) - return &proto.TransferLeadershipResponse{LeaderID: shardID, LeaderAddress: raftAddress}, nil - } - } - } - fmt.Printf("Transferring leadership to next candidate\n") - rpc.raftStore.raft.LeadershipTransfer() - return &proto.TransferLeadershipResponse{}, nil -} - -func (rpc *CosignerGRPCServer) GetLeader( - context.Context, - *proto.GetLeaderRequest, -) (*proto.GetLeaderResponse, error) { - leader := rpc.raftStore.GetLeader() - return &proto.GetLeaderResponse{Leader: int32(leader)}, nil -} - -func (rpc *CosignerGRPCServer) Ping(context.Context, *proto.PingRequest) (*proto.PingResponse, error) { - return &proto.PingResponse{}, nil -} diff --git a/signer/cosigner_health_test.go b/signer/cosigner_health_test.go deleted file mode 100644 index 67dad1f1..00000000 --- a/signer/cosigner_health_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package signer - -import ( - "os" - "testing" - - cometlog "github.com/cometbft/cometbft/libs/log" - "github.com/stretchr/testify/require" -) - -func TestCosignerHealth(t *testing.T) { - ch := NewCosignerHealth( - cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), - []Cosigner{ - &RemoteCosigner{id: 2}, - &RemoteCosigner{id: 3}, - &RemoteCosigner{id: 4}, - &RemoteCosigner{id: 5}, - }, - &MockLeader{id: 1}, - ) - - ch.rtt = map[int]int64{ - 2: 200, - 3: -1, - 4: 100, - 5: 300, - } - - fastest := ch.GetFastest() - - require.Len(t, fastest, 4) - - require.Equal(t, 4, fastest[0].GetIndex()) - require.Equal(t, 2, fastest[1].GetIndex()) -} diff --git a/signer/cosigner_key.go b/signer/cosigner_key.go deleted file mode 100644 index 32639a81..00000000 --- a/signer/cosigner_key.go +++ /dev/null @@ -1,84 +0,0 @@ -package signer - -import ( - "encoding/json" - cometcrypto "github.com/cometbft/cometbft/crypto" - cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" - cometcryptoencoding "github.com/cometbft/cometbft/crypto/encoding" - cometprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" - amino "github.com/tendermint/go-amino" -) - -// CosignerEd25519Key is a single Ed255219 key shard for an m-of-n threshold signer. -// TODO: CosignerEd25519Key is almost a duplicate of ThresholdSignerEd25519Key -type CosignerEd25519Key struct { - PubKey cometcrypto.PubKey `json:"pubKey"` - PrivateShard []byte `json:"privateShard"` - ID int `json:"id"` -} - -func (key *CosignerEd25519Key) MarshalJSON() ([]byte, error) { - type Alias CosignerEd25519Key - - protoPubkey, err := cometcryptoencoding.PubKeyToProto(key.PubKey) - if err != nil { - return nil, err - } - - protoBytes, err := protoPubkey.Marshal() - if err != nil { - return nil, err - } - - return json.Marshal(&struct { - PubKey []byte `json:"pubKey"` - *Alias - }{ - PubKey: protoBytes, - Alias: (*Alias)(key), - }) -} - -func (key *CosignerEd25519Key) UnmarshalJSON(data []byte) error { - type Alias CosignerEd25519Key - - aux := &struct { - PubkeyBytes []byte `json:"pubKey"` - *Alias - }{ - Alias: (*Alias)(key), - } - if err := json.Unmarshal(data, &aux); err != nil { - return err - } - - var pubkey cometcrypto.PubKey - var protoPubkey cometprotocrypto.PublicKey - err := protoPubkey.Unmarshal(aux.PubkeyBytes) - - // Prior to the tendermint protobuf migration, the public key bytes in key files - // were encoded using the go-amino libraries via - // cdc.MarshalBinaryBare(key.PubKey) - // - // To support reading the public key bytes from these key files, we fallback to - // amino unmarshalling if the protobuf unmarshalling fails - if err != nil { - var pub cometcryptoed25519.PubKey - codec := amino.NewCodec() - codec.RegisterInterface((*cometcrypto.PubKey)(nil), nil) - codec.RegisterConcrete(cometcryptoed25519.PubKey{}, "tendermint/PubKeyEd25519", nil) - errInner := codec.UnmarshalBinaryBare(aux.PubkeyBytes, &pub) - if errInner != nil { - return err - } - pubkey = pub - } else { - pubkey, err = cometcryptoencoding.PubKeyFromProto(protoPubkey) - if err != nil { - return err - } - } - - key.PubKey = pubkey - return nil -} diff --git a/signer/cosigner_nonce_cache_test.go b/signer/cosigner_nonce_cache_test.go deleted file mode 100644 index 756f5960..00000000 --- a/signer/cosigner_nonce_cache_test.go +++ /dev/null @@ -1,291 +0,0 @@ -package signer - -import ( - "context" - "os" - "sync" - "testing" - "time" - - cometlog "github.com/cometbft/cometbft/libs/log" - "github.com/google/uuid" - "github.com/stretchr/testify/require" -) - -func TestNonceCache(_ *testing.T) { - nc := NonceCache{} - for i := 0; i < 10; i++ { - nc.Add(&CachedNonce{UUID: uuid.New(), Expiration: time.Now().Add(1 * time.Second)}) - } - - nc.Delete(nc.Size() - 1) - nc.Delete(0) -} - -func TestMovingAverage(t *testing.T) { - ma := newMovingAverage(12 * time.Second) - - ma.add(3*time.Second, 500) - require.Len(t, ma.items, 1) - require.Equal(t, float64(500), ma.average()) - - ma.add(3*time.Second, 100) - require.Len(t, ma.items, 2) - require.Equal(t, float64(300), ma.average()) - - ma.add(6*time.Second, 600) - require.Len(t, ma.items, 3) - require.Equal(t, float64(450), ma.average()) - - // should kick out the first one - ma.add(3*time.Second, 500) - require.Len(t, ma.items, 3) - require.Equal(t, float64(450), ma.average()) - - // should kick out the second one - ma.add(6*time.Second, 500) - require.Len(t, ma.items, 3) - require.Equal(t, float64(540), ma.average()) - - for i := 0; i < 5; i++ { - ma.add(2500*time.Millisecond, 1000) - } - - require.Len(t, ma.items, 5) - require.Equal(t, float64(1000), ma.average()) -} - -func TestClearNonces(t *testing.T) { - lcs, _ := getTestLocalCosigners(t, 2, 3) - cosigners := make([]Cosigner, len(lcs)) - for i, lc := range lcs { - cosigners[i] = lc - } - - cnc := CosignerNonceCache{ - threshold: 2, - } - - for i := 0; i < 10; i++ { - // When deleting nonce for cosigner 1 ([0]), - // these nonce will drop below threshold and be deleted. - cnc.cache.Add(&CachedNonce{ - UUID: uuid.New(), - Expiration: time.Now().Add(1 * time.Second), - Nonces: []CosignerNoncesRel{ - {Cosigner: cosigners[0]}, - {Cosigner: cosigners[1]}, - }, - }) - // When deleting nonce for cosigner 1 ([0]), these nonces will still be above threshold, - // so they will remain without cosigner 1. - cnc.cache.Add(&CachedNonce{ - UUID: uuid.New(), - Expiration: time.Now().Add(1 * time.Second), - Nonces: []CosignerNoncesRel{ - {Cosigner: cosigners[0]}, - {Cosigner: cosigners[1]}, - {Cosigner: cosigners[2]}, - }, - }) - } - - require.Equal(t, 20, cnc.cache.Size()) - - cnc.ClearNonces(cosigners[0]) - - require.Equal(t, 10, cnc.cache.Size()) - - for _, n := range cnc.cache.cache { - require.Len(t, n.Nonces, 2) - } -} - -type mockPruner struct { - cnc *CosignerNonceCache - count int - pruned int - mu sync.Mutex -} - -func (mp *mockPruner) PruneNonces() int { - pruned := mp.cnc.PruneNonces() - mp.mu.Lock() - defer mp.mu.Unlock() - mp.count++ - mp.pruned += pruned - return pruned -} - -func (mp *mockPruner) Result() (int, int) { - mp.mu.Lock() - defer mp.mu.Unlock() - return mp.count, mp.pruned -} - -func TestNonceCacheDemand(t *testing.T) { - lcs, _ := getTestLocalCosigners(t, 2, 3) - cosigners := make([]Cosigner, len(lcs)) - for i, lc := range lcs { - cosigners[i] = lc - } - - mp := &mockPruner{} - - nonceCache := NewCosignerNonceCache( - cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), - cosigners, - &MockLeader{id: 1, leader: &ThresholdValidator{myCosigner: lcs[0]}}, - 500*time.Millisecond, - 100*time.Millisecond, - defaultNonceExpiration, - 2, - mp, - ) - - mp.cnc = nonceCache - - ctx, cancel := context.WithCancel(context.Background()) - - nonceCache.LoadN(ctx, 500) - - go nonceCache.Start(ctx) - - for i := 0; i < 3000; i++ { - _, err := nonceCache.GetNonces([]Cosigner{cosigners[0], cosigners[1]}) - require.NoError(t, err) - time.Sleep(10 * time.Millisecond) - require.Greater(t, nonceCache.cache.Size(), 0) - } - - size := nonceCache.cache.Size() - - require.Greater(t, size, 0) - - cancel() - - require.LessOrEqual(t, size, nonceCache.target(nonceCache.movingAverage.average())) - - count, pruned := mp.Result() - - require.Greater(t, count, 0) - require.Equal(t, 0, pruned) -} - -func TestNonceCacheExpiration(t *testing.T) { - lcs, _ := getTestLocalCosigners(t, 2, 3) - cosigners := make([]Cosigner, len(lcs)) - for i, lc := range lcs { - cosigners[i] = lc - } - - mp := &mockPruner{} - - nonceCache := NewCosignerNonceCache( - cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), - cosigners, - &MockLeader{id: 1, leader: &ThresholdValidator{myCosigner: lcs[0]}}, - 250*time.Millisecond, - 10*time.Millisecond, - 500*time.Millisecond, - 2, - mp, - ) - - mp.cnc = nonceCache - - ctx, cancel := context.WithCancel(context.Background()) - - const loadN = 500 - - nonceCache.LoadN(ctx, loadN) - - go nonceCache.Start(ctx) - - time.Sleep(1 * time.Second) - - count, pruned := mp.Result() - - // we should have pruned at least three times after - // waiting for a second with a reconcile interval of 250ms - require.GreaterOrEqual(t, count, 3) - - // we should have pruned at least the number of nonces we loaded and knew would expire - require.GreaterOrEqual(t, pruned, loadN) - - cancel() - - // the cache should be empty or 1 since no nonces are being consumed. - require.LessOrEqual(t, nonceCache.cache.Size(), 1) -} - -func TestNonceCacheDemandSlow(t *testing.T) { - lcs, _ := getTestLocalCosigners(t, 2, 3) - cosigners := make([]Cosigner, len(lcs)) - for i, lc := range lcs { - cosigners[i] = lc - } - - nonceCache := NewCosignerNonceCache( - cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), - cosigners, - &MockLeader{id: 1, leader: &ThresholdValidator{myCosigner: lcs[0]}}, - 90*time.Millisecond, - 100*time.Millisecond, - 500*time.Millisecond, - 2, - nil, - ) - - ctx, cancel := context.WithCancel(context.Background()) - - go nonceCache.Start(ctx) - - for i := 0; i < 10; i++ { - time.Sleep(200 * time.Millisecond) - require.Greater(t, nonceCache.cache.Size(), 0) - _, err := nonceCache.GetNonces([]Cosigner{cosigners[0], cosigners[1]}) - require.NoError(t, err) - } - - cancel() - - require.LessOrEqual(t, nonceCache.cache.Size(), nonceCache.target(300)) -} - -func TestNonceCacheDemandSlowDefault(t *testing.T) { - if testing.Short() { - t.Skip() - } - lcs, _ := getTestLocalCosigners(t, 2, 3) - cosigners := make([]Cosigner, len(lcs)) - for i, lc := range lcs { - cosigners[i] = lc - } - - nonceCache := NewCosignerNonceCache( - cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), - cosigners, - &MockLeader{id: 1, leader: &ThresholdValidator{myCosigner: lcs[0]}}, - defaultGetNoncesInterval, - defaultGetNoncesTimeout, - defaultNonceExpiration, - 2, - nil, - ) - - ctx, cancel := context.WithCancel(context.Background()) - - go nonceCache.Start(ctx) - - for i := 0; i < 10; i++ { - time.Sleep(7 * time.Second) - require.Greater(t, nonceCache.cache.Size(), 0) - _, err := nonceCache.GetNonces([]Cosigner{cosigners[0], cosigners[1]}) - require.NoError(t, err) - } - - cancel() - - require.LessOrEqual(t, nonceCache.cache.Size(), nonceCache.target(60/7)) -} diff --git a/signer/leader.go b/signer/leader.go deleted file mode 100644 index 12cc37c6..00000000 --- a/signer/leader.go +++ /dev/null @@ -1,15 +0,0 @@ -package signer - -import "github.com/strangelove-ventures/horcrux/pkg/types" - -// Leader is an interface for the detecting if the current cosigner is the leader and performing leader actions. -type Leader interface { - // IsLeader returns true if the cosigner is the leader. - IsLeader() bool - - // ShareSigned shares the last signed state with the other cosigners. - ShareSigned(lss types.ChainSignStateConsensus) error - - // Get current leader - GetLeader() int -} diff --git a/signer/leader_mock.go b/signer/leader_mock.go deleted file mode 100644 index daef26d9..00000000 --- a/signer/leader_mock.go +++ /dev/null @@ -1,36 +0,0 @@ -package signer - -import ( - "sync" - - "github.com/strangelove-ventures/horcrux/pkg/types" -) - -var _ Leader = (*MockLeader)(nil) - -type MockLeader struct { - id int - - mu sync.Mutex - leader *ThresholdValidator -} - -func (m *MockLeader) IsLeader() bool { - m.mu.Lock() - defer m.mu.Unlock() - return m.leader != nil && m.leader.myCosigner.GetIndex() == m.id -} - -func (m *MockLeader) SetLeader(tv *ThresholdValidator) { - m.mu.Lock() - defer m.mu.Unlock() - m.leader = tv -} - -func (m *MockLeader) GetLeader() int { - return m.id -} - -func (m *MockLeader) ShareSigned(_ types.ChainSignStateConsensus) error { - return nil -} diff --git a/signer/remote_signer_grpc_server.go b/signer/remote_signer_grpc_server.go deleted file mode 100644 index 04aa4ef8..00000000 --- a/signer/remote_signer_grpc_server.go +++ /dev/null @@ -1,188 +0,0 @@ -package signer - -import ( - "context" - "net" - "time" - - "github.com/strangelove-ventures/horcrux/pkg/types" - - cometlog "github.com/cometbft/cometbft/libs/log" - cometservice "github.com/cometbft/cometbft/libs/service" - - "github.com/strangelove-ventures/horcrux/signer/proto" - "google.golang.org/grpc" - "google.golang.org/grpc/reflection" -) - -var _ proto.RemoteSignerServer = &RemoteSignerGRPCServer{} - -type RemoteSignerGRPCServer struct { - cometservice.BaseService - - validator PrivValidator - logger cometlog.Logger - listenAddr string - - server *grpc.Server - - proto.UnimplementedRemoteSignerServer -} - -func NewRemoteSignerGRPCServer( - logger cometlog.Logger, - validator PrivValidator, - listenAddr string, -) *RemoteSignerGRPCServer { - s := &RemoteSignerGRPCServer{ - validator: validator, - logger: logger, - listenAddr: listenAddr, - } - s.BaseService = *cometservice.NewBaseService(logger, "RemoteSignerGRPCServer", s) - return s -} - -func (s *RemoteSignerGRPCServer) OnStart() error { - s.logger.Info("Remote Signer GRPC Listening", "address", s.listenAddr) - sock, err := net.Listen("tcp", s.listenAddr) - if err != nil { - return err - } - s.server = grpc.NewServer() - proto.RegisterRemoteSignerServer(s.server, s) - reflection.Register(s.server) - return s.server.Serve(sock) -} - -func (s *RemoteSignerGRPCServer) OnStop() { - s.server.GracefulStop() -} - -func (s *RemoteSignerGRPCServer) PubKey(ctx context.Context, req *proto.PubKeyRequest) (*proto.PubKeyResponse, error) { - chainID := req.ChainId - - totalPubKeyRequests.WithLabelValues(chainID).Inc() - - pubKey, err := s.validator.GetPubKey(ctx, chainID) - if err != nil { - s.logger.Error( - "Failed to get Pub Key", - "chain_id", chainID, - "error", err, - ) - return nil, err - } - - return &proto.PubKeyResponse{ - PubKey: pubKey, - }, nil -} - -func (s *RemoteSignerGRPCServer) Sign( - ctx context.Context, - req *proto.SignBlockRequest, -) (*proto.SignBlockResponse, error) { - chainID, block := req.ChainID, BlockFromProto(req.Block) - - signature, timestamp, err := signAndTrack(ctx, s.logger, s.validator, chainID, block) - if err != nil { - return nil, err - } - - return &proto.SignBlockResponse{ - Signature: signature, - Timestamp: timestamp.UnixNano(), - }, nil -} - -func signAndTrack( - ctx context.Context, - logger cometlog.Logger, - validator PrivValidator, - chainID string, - block types.Block, -) ([]byte, time.Time, error) { - signature, timestamp, err := validator.Sign(ctx, chainID, block) - if err != nil { - switch typedErr := err.(type) { - case *BeyondBlockError: - logger.Debug( - "Rejecting sign request", - "type", types.SignType(block.Step), - "chain_id", chainID, - "height", block.Height, - "round", block.Round, - "reason", typedErr.msg, - ) - beyondBlockErrors.WithLabelValues(chainID).Inc() - default: - logger.Error( - "Failed to sign", - "type", types.SignType(block.Step), - "chain_id", chainID, - "height", block.Height, - "round", block.Round, - "error", err, - ) - failedSignVote.WithLabelValues(chainID).Inc() - } - return nil, block.Timestamp, err - } - - // Show signatures provided to each node have the same signature and timestamps - sigLen := 6 - if len(signature) < sigLen { - sigLen = len(signature) - } - logger.Info( - "Signed", - "type", types.SignType(block.Step), - "chain_id", chainID, - "height", block.Height, - "round", block.Round, - "sig", signature[:sigLen], - "ts", block.Timestamp, - ) - - switch block.Step { - case types.StepPropose: - lastProposalHeight.WithLabelValues(chainID).Set(float64(block.Height)) - lastProposalRound.WithLabelValues(chainID).Set(float64(block.Round)) - totalProposalsSigned.WithLabelValues(chainID).Inc() - case types.StepPrevote: - // Determine number of heights since the last Prevote - stepSize := block.Height - previousPrevoteHeight - if previousPrevoteHeight != 0 && stepSize > 1 { - missedPrevotes.WithLabelValues(chainID).Add(float64(stepSize)) - totalMissedPrevotes.WithLabelValues(chainID).Add(float64(stepSize)) - } else { - missedPrevotes.WithLabelValues(chainID).Set(0) - } - - previousPrevoteHeight = block.Height // remember last PrevoteHeight - - metricsTimeKeeper.SetPreviousPrevote(time.Now()) - - lastPrevoteHeight.WithLabelValues(chainID).Set(float64(block.Height)) - lastPrevoteRound.WithLabelValues(chainID).Set(float64(block.Round)) - totalPrevotesSigned.WithLabelValues(chainID).Inc() - case types.StepPrecommit: - stepSize := block.Height - previousPrecommitHeight - if previousPrecommitHeight != 0 && stepSize > 1 { - missedPrecommits.WithLabelValues(chainID).Add(float64(stepSize)) - totalMissedPrecommits.WithLabelValues(chainID).Add(float64(stepSize)) - } else { - missedPrecommits.WithLabelValues(chainID).Set(0) - } - previousPrecommitHeight = block.Height // remember last PrecommitHeight - - metricsTimeKeeper.SetPreviousPrecommit(time.Now()) - - lastPrecommitHeight.WithLabelValues(chainID).Set(float64(block.Height)) - lastPrecommitRound.WithLabelValues(chainID).Set(float64(block.Round)) - totalPrecommitsSigned.WithLabelValues(chainID).Inc() - } - - return signature, timestamp, nil -} diff --git a/signer/testdata/rsa_keys.json b/signer/testdata/rsa_keys.json deleted file mode 100644 index cd1b60c1..00000000 --- a/signer/testdata/rsa_keys.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "rsaKey": "MIIJKAIBAAKCAgEAy3RB4zdFhlpmZQ1Xus+Tp/d7SmVFi8XXxLQJdBB57WV2i78EmtNUZfJHiyril1Mbc4Wzd1634peXNgMCzwKGgzB7hGzoG7BU9ql9cgnQnqHVgnEVX7BFesbOiiiR13ivoI6CsoGPAeOj+z03W18R1XSGpMPy+xeJctOHPEz3gswnkHofCQ8RATpzm/l3fKxBAe3Dtn4rh3p41Hl70tbAOqss9lz48EXvOAfWA16/SJRE39E7hVBI+x3y3PcJ356OjkUfBmt5k2S8zV5Rd8Iy1P9w+bcxFpsu2BkczQQPXElU6VFiZZoAPcpv0d5Xnynd82dmLtohFbqSTPnM/bsexlyMZjf9YfYRTb2rfNWf5R7fHseE7gp8dHAy2fQT2KcNKSYAMkGjgNcWZu8tflvikzoHz8iAlYL6q2bt/plowdJ9TJlOL/G7+Kyuw/+al4EMmmwoH52VXQ7S0k2fbHtek71aDeH8YGKgHhXonXSUzlbVZCkXXXkuzE4J7V5KKqpV1JPiS5ibxNuxGtc8v9joYA1d3w2gslzbzRBbKg4XkLQ9ZA/n7utObOeOI8hgFApBYOqaULHv6nsL+nksziJu02+FGm6o30Fq4PywSeWkVCk7Z0NDfauynFuuKX9cV9ELOrxXIDeUwIGrUNzJLrkF8tL6VlKZpKWQKksnPeDidn0CAwEAAQKCAgAZ/xLkK43QqwBmbRwGnLtrPO4eBW6re24kjjSfBfPuZ4aMOK4n8lTYaerGV4Z4e4AIdM8Hs6yfYWtK1XuoY9Q4Mxu1Qg0ubIuOsP18ctCbVGZpsnVesrFODNUbRi/MbnlSdKMyl+dkAGhVk+7c1/r5YVhK+Va0yr8fUvP2uobtyZRMm64XhDtSAtv+1BN2dLQhwPW+/cQmHXoO/C7cM+CAHpsyJ5MPcIAukqG/8H1Uks1yI9QNJsMMgzkjDtXOIv5oI2Dhex4fvUF6pFCYktHX8YPIBKZbEx0bM6pAcAJecmhNH78I6HKbcqBzGovrpHfdwBqIZgrQMfS7rTeKPDykNZ2aHb3xc2Tv1FOcAx+sVg58eEwyTzhTSriBJVWBlGfHbey09eojRhDMlrMgqTVArT25okKwUF+xl0eKqJlFvspSDN3XV6Yb0D35MXS8qQEFoTyM5b40mUIlgabR7cWu+jPB1sFU3P28NPHRG40fwnr89yYX3gevRPSrigewfVnkb3qMBa/fJ7Khuet7XxWYVCXwLDzRSSgHkEgkY3llc8J0Yg2HHdnNOSLqpRmnRED9li7Ol04Ps6sFB6nVszpEa6D6B7ADg7nVxsHbwrvKZ47Ut8dbGBm9Cpp4hFg9oGcvRdogz2bsjCU4Y90X3nfvR1YIkwoEkj/n5ZbuX1PPKQKCAQEA37ZzyFozo/Y2//heRxWgFPwVzBK27+DYc16WLtZxs+shaiXJ4Zn0DOI5x1VdC50sxHvlERcTQeQhSO+jnXQOkF6YQgylxLuYoFdo2l5jojn2vsHa4Klw9UatpFStCwz+PhD0wK7WhDoEKvwbvQ2wTa/wvDpnAuhhAEh2hMMuDkRpiVulYe40ywQW/NOrfUklAw1D/5NflDGPCYBYreP7pNtYbMRt9zLhOgQSvn9GqbpnnMj88gZDFxNO7jDiUsYO7yFsi01ALJ+T6AfVtKRyOEbjhpOBBpvlpwbUAMHuARjgcTWmvyWPbafIGpiaSX8ThtG8h78n/ITX6+NPF6fIfwKCAQEA6NFO9qLmYJWj8IaJaPKYBB1JdnFXOJvmPiJ4DS8BhLsSoEYsYE83ECXJwTI2F2l6MVyBXq+PPrQKm2vsUEWzzn/Fi+b7jEbjTv379yLylD1fjsMT0NHtN3vq09Yp3kNgTrd1IfCNxZ5A92Vh0f67PjHB3aMXeYd+ydBeIIlLVAgR6nUtkvGmFtuPunUlZiuB6UpMXDjPRN1VrddvQVTgSkPl9WB8wPcShxOz+wL4Hi5DII+ThFdAAh9pnIFaBF1Et/xMl2ss/7hcxqcIItQSBLotU0brPHMvoiSEjHWLuekw/b5noabkrfm8NOhB6Gjrq58oODe9ZrDjiaweh55jAwKCAQEApTqIgW29vlfXf27dkvrx5Q3au4MHAly7AVrW6XkROaVsZI3McYfXrLxZmFQACNfpfKVVJi441u27d7cmzOAu8YosQnw84vT7YVGt67rTM7pD99gN5OjAuSeekETKGeNa1FSJsNZxMe/3rBfQFO3LTVWpJByugINJQYBDqQLPPVJh8EVz/MSG0XsPz2Q2wK4JXBusIVOjwDxqPMZCuQwtjDFFOfBKl81IdCUWAwTWF/3JEQ+RYuAlJSHpphsMzb3iwdOZ67j+sPabs0A2ItliUxZobbj8DvmNwLNWWcjiFIVfH75UjdEcAg1tydbz/VyR+31lFY2l5ufm4h5dCEev2QKCAQAX543NAxLWbebkRlwLe4UiPwOQ9rg25sLwNEfRSrdEMpUKAcqCpP+JV+fsP0SQiNL0CIR7/Vie3ouMQ7uCznVUyYe2AqRnVcv3C1r4mA0CLX8HQH5jXXqWzNFiqMWpvY9A5dNQBcv4s3QGMtGlZxtAmolGQX2ii8f33r4bZx1l5mI4iYmBYfBkvmx2f5q0b9kp4+gNPAQEFRm7/Le+pIFW/ru4wwxsH7I2Tk6XgkmJh8R6rmM+HltDHIiSejGM6yqoHW6byXRYWUylVPcf5FhpRdhriYeTsFv+sPMvHM6Y6xmNpCQt0939AvxRDlveCg/Qkknl48s9pQHn29VSpW+TAoIBAE862157emwegrRYu66ENMMNLbF6YxBpL47iWC1NQ4/8aM5i58edv1VWUw5R441uvAGXk7uzsYkrxmp0nj6UANkjq6n06mayN90kbg4FvjAzEtbbAu8byap+B5xLSuetIc+dVqACKihQ09zwm3d/LFtbrgZ2KGiw/RfvMxxT0HYp9A7NdYT4nf2Aqa3UR4SuxWm4bWWLHHMGeS4N9MuwFP64jLgLrXzyB851Avuz9LJCpNAflE9SQUvTqGwpFvsgEwQcGH+5vcvcBENCYbAwq5hmMnzrAXsA1NnJNqn+oqXG8GIogG7DOa4965QL60TwDu9s/opzV2bMVhVtxDqKSfo=", - "rsaPubs": [ - "MIICCgKCAgEAzo2bGRRrwn1/TFlkJ9yqvOcx0BYvmay+rPQFnDFsKxb+WHHLtwn/juxY0Ub+ABwCgJgBUr4k3G9piFYwtL0R3ton6UulwYMgNQ6cnn8/zmAx/STP/WGYKXRtTR80csC+u8g/kzUK/lX2pGz77BLNxflKf/yfnm3wkCcJecnv2PLW84J3/s6b3TUkS+ygUQL3SB+IN7dI/i1pls7my6pCTOJxIu7TJ+PPahyDkRhE0OapjH0OQIbHXNeCqe71uQwALdf1dwTDl2JeIL7jhGWB8xb2PfeLX+VZsOWUR0NPfs83viS+Pjtz6ndYX1+3+BQxOIutnkUC6IwSBqsG+M2cqElETIgUHpxqRl0QtReq18+GTX9CfFB5hmWgLlGICij9Lnz0zpwtyIQJXn2Cny8XeWi8E9uKpi+4MNkDqwPd2U+wIXBPVBgqPjTByLeish+VaxKV2bHzqManB5WHa0g7WDK9p8OdZ7To8miJF+hdqOZMHnxThY/hr0102ffOq8XCDIfm873Ie2Cn/+KBHwCc6e7XO5ohWKm9WQbsxpmpn3+ru1ekWTkqC8YC7FFpljMCpl9NiGz4edVzSnnL8OU12M1pofEwpbMtlNCzaVJkMzfo9jDRoWxDyKffRYbdp93V1Oio0ab2ou9uZ0Jx0mXIpLyvznRNmDEsj5nrWmbW5jcCAwEAAQ==", - "MIICCgKCAgEA83UMSAbKSL4/W9VAzn+XjqCmhl8og6BoZvukS1pQI0JFrox63hJYavHTQB0DO2iXomfpm9d+J8NHsBsWf7DD/9aNaByGRJ0k5Lde64FfTj6LP9I5yRoKuGGQ0Heuvuisz9DMWRyhkO9hJiyedX7VdPx3VdUW4AX+FWyJ1pKpj0g/s8eYrUFyzISdoq/pRwkVkzHpXqFh3L5ASUjf9eQXGYsQsDI0UDuzZdYD4nitQ5Q0POM7jCgSQQ8d/b0eaF2hCzbZ1UWKx8LzCU7j4NRqrYJluRqkxeEtBeZsq7QX6Hs13tg+wKKCkOI+wt/1tifLE8IA3es0pXm0UstVduaTMeFtLTvIYE9E/0yFC23aFydz1Fny6HBjpfNo6BgzNCurMziOdpiuLy+7luPM+SBJ3YV0D9TVU8Lo0vawPccj3tcKmozeJdBhuedXWAm00mlCw+LueKBUVxti2kwHiDjBDbLDymZYZHR8HYI0KsrycsvemTotZzYXgDjyRful7mPLGecJhRye7xNX9lVUse81C94gmdZXVL2GKY1PquWJvgazg99gta62GrRj127vDcS2UI+6/4aTJwQFvRqWRLvS/MIJyq5eiq1WyDLOT8dOyBlb7+BV55cB7JUTiO7MsMNaX0h/C3iGrTOnh8rmC/20ygHqZC3E1Lw0SezI2r1NzzcCAwEAAQ==", - "MIICCgKCAgEAy3RB4zdFhlpmZQ1Xus+Tp/d7SmVFi8XXxLQJdBB57WV2i78EmtNUZfJHiyril1Mbc4Wzd1634peXNgMCzwKGgzB7hGzoG7BU9ql9cgnQnqHVgnEVX7BFesbOiiiR13ivoI6CsoGPAeOj+z03W18R1XSGpMPy+xeJctOHPEz3gswnkHofCQ8RATpzm/l3fKxBAe3Dtn4rh3p41Hl70tbAOqss9lz48EXvOAfWA16/SJRE39E7hVBI+x3y3PcJ356OjkUfBmt5k2S8zV5Rd8Iy1P9w+bcxFpsu2BkczQQPXElU6VFiZZoAPcpv0d5Xnynd82dmLtohFbqSTPnM/bsexlyMZjf9YfYRTb2rfNWf5R7fHseE7gp8dHAy2fQT2KcNKSYAMkGjgNcWZu8tflvikzoHz8iAlYL6q2bt/plowdJ9TJlOL/G7+Kyuw/+al4EMmmwoH52VXQ7S0k2fbHtek71aDeH8YGKgHhXonXSUzlbVZCkXXXkuzE4J7V5KKqpV1JPiS5ibxNuxGtc8v9joYA1d3w2gslzbzRBbKg4XkLQ9ZA/n7utObOeOI8hgFApBYOqaULHv6nsL+nksziJu02+FGm6o30Fq4PywSeWkVCk7Z0NDfauynFuuKX9cV9ELOrxXIDeUwIGrUNzJLrkF8tL6VlKZpKWQKksnPeDidn0CAwEAAQ==" - ], - "id": 3 -} \ No newline at end of file diff --git a/signer/threshold_signer.go b/signer/threshold_signer.go deleted file mode 100644 index 8e911c17..00000000 --- a/signer/threshold_signer.go +++ /dev/null @@ -1,60 +0,0 @@ -package signer - -import ( - "encoding/json" - "github.com/cometbft/cometbft/privval" - "github.com/strangelove-ventures/horcrux/pkg/types" - tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" - "os" -) - -// Interface for the local signer whether it's a soft sign or HSM -type ThresholdSigner interface { - // PubKey returns the public key bytes for the combination of all cosigners. - PubKey() []byte - - // Sign signs a byte payload with the provided nonces. - Sign(nonces []types.Nonce, payload []byte) ([]byte, error) - - // CombineSignatures combines multiple partial signatures to a full signature. - CombineSignatures([]types.PartialSignature) ([]byte, error) -} - -// LoadThresholdSignerEd25519Key loads the persistent ThresholdSignerEd25519Key from file. -func LoadThresholdSignerEd25519Key(file string) (CosignerEd25519Key, error) { - pvKey := CosignerEd25519Key{} - keyJSONBytes, err := os.ReadFile(file) - if err != nil { - return pvKey, err - } - - err = json.Unmarshal(keyJSONBytes, &pvKey) - if err != nil { - return pvKey, err - } - - return pvKey, nil -} - -// CreateEd25519ThresholdSignShardsFromFile creates CosignerEd25519Key objects from a priv_validator_key.json file -func CreateEd25519ThresholdSignShardsFromFile(priv string, threshold, shards uint8) ([]CosignerEd25519Key, error) { - pv, err := ReadPrivValidatorFile(priv) - if err != nil { - return nil, err - } - return CreateEd25519ThresholdSignShards(pv, threshold, shards), nil -} - -// CreateEd25519ThresholdSignShards creates CosignerEd25519Key objects from a privval.FilePVKey -func CreateEd25519ThresholdSignShards(pv privval.FilePVKey, threshold, shards uint8) []CosignerEd25519Key { - privShards := tsed25519.DealShares(tsed25519.ExpandSecret(pv.PrivKey.Bytes()[:32]), threshold, shards) - out := make([]CosignerEd25519Key, shards) - for i, shard := range privShards { - out[i] = CosignerEd25519Key{ - PubKey: pv.PubKey, - PrivateShard: shard, - ID: i + 1, - } - } - return out -} diff --git a/signer/threshold_signer_bls.go b/signer/threshold_signer_bls.go deleted file mode 100644 index 100b06de..00000000 --- a/signer/threshold_signer_bls.go +++ /dev/null @@ -1,106 +0,0 @@ -package signer - -import ( - "bytes" - "errors" - "fmt" - - "github.com/strangelove-ventures/horcrux/pkg/types" - - "gitlab.com/unit410/edwards25519" - tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" -) - -var _ ThresholdSigner = &ThresholdSignerSoftBLS{} - -type ThresholdSignerSoftBLS struct { - privateKeyShard []byte - pubKey []byte - threshold uint8 - total uint8 -} - -func NewThresholdSignerSoftBLS(config *RuntimeConfig, id int, chainID string) (*ThresholdSignerSoftBLS, error) { - keyFile, err := config.KeyFileExistsCosigner(chainID) - if err != nil { - return nil, err - } - - key, err := LoadThresholdSignerEd25519Key(keyFile) - if err != nil { - return nil, fmt.Errorf("error reading cosigner key: %s", err) - } - - if key.ID != id { - return nil, fmt.Errorf("key shard Index (%d) in (%s) does not match cosigner Index (%d)", key.ID, keyFile, id) - } - - s := ThresholdSignerSoftBLS{ - privateKeyShard: key.PrivateShard, - pubKey: key.PubKey.Bytes(), - threshold: uint8(config.Config.ThresholdModeConfig.Threshold), - total: uint8(len(config.Config.ThresholdModeConfig.Cosigners)), - } - - return &s, nil -} - -func (s *ThresholdSignerSoftBLS) PubKey() []byte { - return s.pubKey -} - -func (s *ThresholdSignerSoftBLS) Sign(nonces []types.Nonce, payload []byte) ([]byte, error) { - nonceShare, noncePub, err := s.sumNonces(nonces) - if err != nil { - return nil, fmt.Errorf("failed to combine nonces: %w", err) - } - - sig := tsed25519.SignWithShare( - payload, s.privateKeyShard, nonceShare, s.pubKey, noncePub) - return append(noncePub, sig...), nil -} - -func (s *ThresholdSignerSoftBLS) sumNonces(nonces []types.Nonce) (tsed25519.Scalar, tsed25519.Element, error) { - shareParts := make([]tsed25519.Scalar, len(nonces)) - publicKeys := make([]tsed25519.Element, len(nonces)) - - for i, n := range nonces { - shareParts[i] = n.Share - publicKeys[i] = n.PubKey - } - - nonceShare := tsed25519.AddScalars(shareParts) - noncePub := tsed25519.AddElements(publicKeys) - - // check bounds for ephemeral share to avoid passing out of bounds valids to SignWithShare - if len(nonceShare) != 32 { - return nil, nil, errors.New("ephemeral share is out of bounds") - } - - var scalarBytes [32]byte - copy(scalarBytes[:], nonceShare) - if !edwards25519.ScMinimal(&scalarBytes) { - return nil, nil, errors.New("ephemeral share is out of bounds") - } - - return nonceShare, noncePub, nil -} - -func (s *ThresholdSignerSoftBLS) CombineSignatures(signatures []types.PartialSignature) ([]byte, error) { - sigIds := make([]int, len(signatures)) - shareSigs := make([][]byte, len(signatures)) - var ephPub []byte - - for i, sig := range signatures { - sigIds[i] = sig.Index - if i == 0 { - ephPub = sig.Signature[:32] - } else if !bytes.Equal(sig.Signature[:32], ephPub) { - return nil, fmt.Errorf("ephemeral public keys do not match") - } - shareSigs[i] = sig.Signature[32:] - } - combinedSig := tsed25519.CombineShares(s.total, sigIds, shareSigs) - - return append(ephPub, combinedSig...), nil -} diff --git a/signer/threshold_signer_soft.go b/signer/threshold_signer_soft.go deleted file mode 100644 index 2991f440..00000000 --- a/signer/threshold_signer_soft.go +++ /dev/null @@ -1,128 +0,0 @@ -package signer - -import ( - "bytes" - "crypto/rand" - "errors" - "fmt" - - "github.com/strangelove-ventures/horcrux/pkg/types" - - "gitlab.com/unit410/edwards25519" - tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" -) - -var _ ThresholdSigner = &ThresholdSignerSoft{} - -type ThresholdSignerSoft struct { - privateKeyShard []byte - pubKey []byte - threshold uint8 - total uint8 -} - -func NewThresholdSignerSoft(config *RuntimeConfig, id int, chainID string) (*ThresholdSignerSoft, error) { - keyFile, err := config.KeyFileExistsCosigner(chainID) - if err != nil { - return nil, err - } - - key, err := LoadThresholdSignerEd25519Key(keyFile) - if err != nil { - return nil, fmt.Errorf("error reading cosigner key: %s", err) - } - - if key.ID != id { - return nil, fmt.Errorf("key shard Index (%d) in (%s) does not match cosigner Index (%d)", key.ID, keyFile, id) - } - - s := ThresholdSignerSoft{ - privateKeyShard: key.PrivateShard, - pubKey: key.PubKey.Bytes(), - threshold: uint8(config.Config.ThresholdModeConfig.Threshold), - total: uint8(len(config.Config.ThresholdModeConfig.Cosigners)), - } - - return &s, nil -} - -func (s *ThresholdSignerSoft) PubKey() []byte { - return s.pubKey -} - -func (s *ThresholdSignerSoft) Sign(nonces []types.Nonce, payload []byte) ([]byte, error) { - nonceShare, noncePub, err := s.sumNonces(nonces) - if err != nil { - return nil, fmt.Errorf("failed to combine nonces: %w", err) - } - - sig := tsed25519.SignWithShare( - payload, s.privateKeyShard, nonceShare, s.pubKey, noncePub) - return append(noncePub, sig...), nil -} - -func (s *ThresholdSignerSoft) sumNonces(nonces []types.Nonce) (tsed25519.Scalar, tsed25519.Element, error) { - shareParts := make([]tsed25519.Scalar, len(nonces)) - publicKeys := make([]tsed25519.Element, len(nonces)) - - for i, n := range nonces { - shareParts[i] = n.Share - publicKeys[i] = n.PubKey - } - - nonceShare := tsed25519.AddScalars(shareParts) - noncePub := tsed25519.AddElements(publicKeys) - - // check bounds for ephemeral share to avoid passing out of bounds valids to SignWithShare - if len(nonceShare) != 32 { - return nil, nil, errors.New("ephemeral share is out of bounds") - } - - var scalarBytes [32]byte - copy(scalarBytes[:], nonceShare) - if !edwards25519.ScMinimal(&scalarBytes) { - return nil, nil, errors.New("ephemeral share is out of bounds") - } - - return nonceShare, noncePub, nil -} - -// GenerateNonces is a function that generates Nonces to be used in the MPC -func GenerateNonces(threshold, total uint8) (types.Nonces, error) { - secret := make([]byte, 32) - if _, err := rand.Read(secret); err != nil { - return types.Nonces{}, err - } - - nonces := types.Nonces{ - PubKey: tsed25519.ScalarMultiplyBase(secret), - Shares: make([][]byte, total), - } - - shares := tsed25519.DealShares(secret, threshold, total) - - for i, sh := range shares { - nonces.Shares[i] = sh - } - - return nonces, nil -} - -func (s *ThresholdSignerSoft) CombineSignatures(signatures []types.PartialSignature) ([]byte, error) { - sigIds := make([]int, len(signatures)) - shareSigs := make([][]byte, len(signatures)) - var ephPub []byte - - for i, sig := range signatures { - sigIds[i] = sig.Index - if i == 0 { - ephPub = sig.Signature[:32] - } else if !bytes.Equal(sig.Signature[:32], ephPub) { - return nil, fmt.Errorf("ephemeral public keys do not match") - } - shareSigs[i] = sig.Signature[32:] - } - combinedSig := tsed25519.CombineShares(s.total, sigIds, shareSigs) - - return append(ephPub, combinedSig...), nil -} diff --git a/signer/cond/cond.go b/src/cond/cond.go similarity index 100% rename from signer/cond/cond.go rename to src/cond/cond.go diff --git a/signer/cond/cond_test.go b/src/cond/cond_test.go similarity index 93% rename from signer/cond/cond_test.go rename to src/cond/cond_test.go index 21b35077..17c4da9e 100644 --- a/signer/cond/cond_test.go +++ b/src/cond/cond_test.go @@ -5,7 +5,7 @@ import ( "sync" "testing" - "github.com/strangelove-ventures/horcrux/signer/cond" + "github.com/strangelove-ventures/horcrux/src/cond" "github.com/stretchr/testify/require" ) diff --git a/signer/config.go b/src/config/config.go similarity index 93% rename from signer/config.go rename to src/config/config.go index edf9bfb2..2ebc38a6 100644 --- a/signer/config.go +++ b/src/config/config.go @@ -1,4 +1,4 @@ -package signer +package config import ( "fmt" @@ -25,6 +25,15 @@ type SignMode string const ( SignModeThreshold SignMode = "threshold" SignModeSingle SignMode = "single" + + /* + Default values for the nouncecahce + */ + // TODO: Is this the best way to do this? + DefaultGetNoncesInterval = 3 * time.Second + DefaultGetNoncesTimeout = 4 * time.Second + DefaultNonceExpiration = 10 * time.Second // half of the local cosigner cache expiration + ) // Config maps to the on-disk yaml format @@ -101,34 +110,6 @@ type RuntimeConfig struct { Config Config } -func (c RuntimeConfig) CosignerSecurityECIES() (*CosignerSecurityECIES, error) { - keyFile, err := c.KeyFileExistsCosignerECIES() - if err != nil { - return nil, err - } - - key, err := LoadCosignerECIESKey(keyFile) - if err != nil { - return nil, fmt.Errorf("error reading cosigner key (%s): %w", keyFile, err) - } - - return NewCosignerSecurityECIES(key), nil -} - -func (c RuntimeConfig) CosignerSecurityRSA() (*CosignerSecurityRSA, error) { - keyFile, err := c.KeyFileExistsCosignerRSA() - if err != nil { - return nil, err - } - - key, err := LoadCosignerRSAKey(keyFile) - if err != nil { - return nil, fmt.Errorf("error reading cosigner key (%s): %w", keyFile, err) - } - - return NewCosignerSecurityRSA(key), nil -} - func (c RuntimeConfig) cachedKeyDirectory() string { if c.Config.PrivValKeyDir != nil { return *c.Config.PrivValKeyDir diff --git a/signer/config_test.go b/src/config/config_test.go similarity index 84% rename from signer/config_test.go rename to src/config/config_test.go index 649eb40a..99978e93 100644 --- a/signer/config_test.go +++ b/src/config/config_test.go @@ -1,4 +1,4 @@ -package signer_test +package config_test import ( "fmt" @@ -8,15 +8,15 @@ import ( "path/filepath" "testing" - "github.com/strangelove-ventures/horcrux/signer" + "github.com/strangelove-ventures/horcrux/src/config" "github.com/stretchr/testify/require" ) const testChainID = "test" func TestNodes(t *testing.T) { - c := signer.Config{ - ChainNodes: signer.ChainNodes{ + c := config.Config{ + ChainNodes: config.ChainNodes{ { PrivValAddr: "tcp://0.0.0.0:1234", }, @@ -32,15 +32,15 @@ func TestNodes(t *testing.T) { func TestValidateSingleSignerConfig(t *testing.T) { type testCase struct { name string - config signer.Config + config config.Config expectErr error } testCases := []testCase{ { name: "valid config", - config: signer.Config{ - ChainNodes: []signer.ChainNode{ + config: config.Config{ + ChainNodes: []config.ChainNode{ { PrivValAddr: "tcp://127.0.0.1:1234", }, @@ -50,8 +50,8 @@ func TestValidateSingleSignerConfig(t *testing.T) { }, { name: "invalid node address", - config: signer.Config{ - ChainNodes: []signer.ChainNode{ + config: config.Config{ + ChainNodes: []config.ChainNode{ { PrivValAddr: "abc://\\invalid_addr", }, @@ -75,19 +75,19 @@ func TestValidateSingleSignerConfig(t *testing.T) { func TestValidateThresholdModeConfig(t *testing.T) { type testCase struct { name string - config signer.Config + config config.Config expectErr error } testCases := []testCase{ { name: "valid config", - config: signer.Config{ - ThresholdModeConfig: &signer.ThresholdModeConfig{ + config: config.Config{ + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: 2, RaftTimeout: "1000ms", GRPCTimeout: "1000ms", - Cosigners: signer.CosignersConfig{ + Cosigners: config.CosignersConfig{ { ShardID: 1, P2PAddr: "tcp://127.0.0.1:2223", @@ -102,7 +102,7 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, }, }, - ChainNodes: []signer.ChainNode{ + ChainNodes: []config.ChainNode{ { PrivValAddr: "tcp://127.0.0.1:1234", }, @@ -118,8 +118,8 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, { name: "no cosigner config", - config: signer.Config{ - ChainNodes: []signer.ChainNode{ + config: config.Config{ + ChainNodes: []config.ChainNode{ { PrivValAddr: "tcp://127.0.0.1:1234", }, @@ -135,12 +135,12 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, { name: "invalid p2p listen", - config: signer.Config{ - ThresholdModeConfig: &signer.ThresholdModeConfig{ + config: config.Config{ + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: 2, RaftTimeout: "1000ms", GRPCTimeout: "1000ms", - Cosigners: signer.CosignersConfig{ + Cosigners: config.CosignersConfig{ { ShardID: 1, P2PAddr: ":2222", @@ -155,7 +155,7 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, }, }, - ChainNodes: []signer.ChainNode{ + ChainNodes: []config.ChainNode{ { PrivValAddr: "tcp://127.0.0.1:1234", }, @@ -175,12 +175,12 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, { name: "not enough cosigners", - config: signer.Config{ - ThresholdModeConfig: &signer.ThresholdModeConfig{ + config: config.Config{ + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: 3, RaftTimeout: "1000ms", GRPCTimeout: "1000ms", - Cosigners: signer.CosignersConfig{ + Cosigners: config.CosignersConfig{ { ShardID: 1, P2PAddr: "tcp://127.0.0.1:2222", @@ -191,7 +191,7 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, }, }, - ChainNodes: []signer.ChainNode{ + ChainNodes: []config.ChainNode{ { PrivValAddr: "tcp://127.0.0.1:1234", }, @@ -207,12 +207,12 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, { name: "invalid raft timeout", - config: signer.Config{ - ThresholdModeConfig: &signer.ThresholdModeConfig{ + config: config.Config{ + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: 2, GRPCTimeout: "1000ms", RaftTimeout: "1000", - Cosigners: signer.CosignersConfig{ + Cosigners: config.CosignersConfig{ { ShardID: 1, P2PAddr: "tcp://127.0.0.1:2222", @@ -227,7 +227,7 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, }, }, - ChainNodes: []signer.ChainNode{ + ChainNodes: []config.ChainNode{ { PrivValAddr: "tcp://127.0.0.1:1234", }, @@ -243,12 +243,12 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, { name: "invalid grpc timeout", - config: signer.Config{ - ThresholdModeConfig: &signer.ThresholdModeConfig{ + config: config.Config{ + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: 2, GRPCTimeout: "1000", RaftTimeout: "1000ms", - Cosigners: signer.CosignersConfig{ + Cosigners: config.CosignersConfig{ { ShardID: 1, P2PAddr: "tcp://127.0.0.1:2222", @@ -263,7 +263,7 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, }, }, - ChainNodes: []signer.ChainNode{ + ChainNodes: []config.ChainNode{ { PrivValAddr: "tcp://127.0.0.1:1234", }, @@ -279,12 +279,12 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, { name: "invalid node address", - config: signer.Config{ - ThresholdModeConfig: &signer.ThresholdModeConfig{ + config: config.Config{ + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: 2, RaftTimeout: "1000ms", GRPCTimeout: "1000ms", - Cosigners: signer.CosignersConfig{ + Cosigners: config.CosignersConfig{ { ShardID: 1, P2PAddr: "tcp://127.0.0.1:2222", @@ -299,7 +299,7 @@ func TestValidateThresholdModeConfig(t *testing.T) { }, }, }, - ChainNodes: []signer.ChainNode{ + ChainNodes: []config.ChainNode{ { PrivValAddr: "abc://\\invalid_addr", }, @@ -322,7 +322,7 @@ func TestValidateThresholdModeConfig(t *testing.T) { func TestRuntimeConfigKeyFilePath(t *testing.T) { dir := t.TempDir() - c := signer.RuntimeConfig{ + c := config.RuntimeConfig{ HomeDir: dir, } @@ -336,7 +336,7 @@ func TestRuntimeConfigKeyFilePath(t *testing.T) { func TestRuntimeConfigPrivValStateFile(t *testing.T) { dir := t.TempDir() - c := signer.RuntimeConfig{ + c := config.RuntimeConfig{ StateDir: dir, } @@ -346,15 +346,15 @@ func TestRuntimeConfigPrivValStateFile(t *testing.T) { func TestRuntimeConfigWriteConfigFile(t *testing.T) { dir := t.TempDir() configFile := filepath.Join(dir, "config.yaml") - c := signer.RuntimeConfig{ + c := config.RuntimeConfig{ ConfigFile: configFile, - Config: signer.Config{ - SignMode: signer.SignModeThreshold, - ThresholdModeConfig: &signer.ThresholdModeConfig{ + Config: config.Config{ + SignMode: config.SignModeThreshold, + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: 2, RaftTimeout: "1000ms", GRPCTimeout: "1000ms", - Cosigners: signer.CosignersConfig{ + Cosigners: config.CosignersConfig{ { ShardID: 1, P2PAddr: "tcp://127.0.0.1:2222", @@ -369,7 +369,7 @@ func TestRuntimeConfigWriteConfigFile(t *testing.T) { }, }, }, - ChainNodes: []signer.ChainNode{ + ChainNodes: []config.ChainNode{ { PrivValAddr: "tcp://127.0.0.1:1234", }, @@ -409,7 +409,7 @@ grpcAddr: "" func TestRuntimeConfigKeyFileExists(t *testing.T) { dir := t.TempDir() - c := signer.RuntimeConfig{ + c := config.RuntimeConfig{ HomeDir: dir, } @@ -455,11 +455,11 @@ func TestRuntimeConfigKeyFileExists(t *testing.T) { } func TestThresholdModeConfigLeaderElectMultiAddress(t *testing.T) { - c := &signer.ThresholdModeConfig{ + c := &config.ThresholdModeConfig{ Threshold: 2, RaftTimeout: "1000ms", GRPCTimeout: "1000ms", - Cosigners: signer.CosignersConfig{ + Cosigners: config.CosignersConfig{ { ShardID: 1, P2PAddr: "tcp://127.0.0.1:2222", @@ -483,13 +483,13 @@ func TestThresholdModeConfigLeaderElectMultiAddress(t *testing.T) { func TestCosignerRSAPubKeysConfigValidate(t *testing.T) { type testCase struct { name string - cosigners signer.CosignersConfig + cosigners config.CosignersConfig expectErr error } testCases := []testCase{ { name: "valid config", - cosigners: signer.CosignersConfig{ + cosigners: config.CosignersConfig{ { ShardID: 1, P2PAddr: "tcp://127.0.0.1:2222", @@ -507,7 +507,7 @@ func TestCosignerRSAPubKeysConfigValidate(t *testing.T) { }, { name: "too many cosigners", - cosigners: signer.CosignersConfig{ + cosigners: config.CosignersConfig{ { ShardID: 2, P2PAddr: "tcp://127.0.0.1:2223", @@ -521,7 +521,7 @@ func TestCosignerRSAPubKeysConfigValidate(t *testing.T) { }, { name: "duplicate cosigner", - cosigners: signer.CosignersConfig{ + cosigners: config.CosignersConfig{ { ShardID: 2, P2PAddr: "tcp://127.0.0.1:2223", @@ -564,7 +564,7 @@ func TestCosignersFromFlag(t *testing.T) { } for _, tc := range testCases { - _, err := signer.CosignersFromFlag(tc.cosigners) + _, err := config.CosignersFromFlag(tc.cosigners) if tc.expectErr == nil { require.NoError(t, err, tc.name) } else { diff --git a/src/connector/iprivvalidator.go b/src/connector/iprivvalidator.go new file mode 100644 index 00000000..b552c01c --- /dev/null +++ b/src/connector/iprivvalidator.go @@ -0,0 +1,16 @@ +package connector + +import ( + "context" + "time" + + "github.com/strangelove-ventures/horcrux/src/types" +) + +// IPrivValidator is a wrapper for tendermint IPrivValidator, +// with additional Stop method for safe shutdown. +type IPrivValidator interface { + Sign(ctx context.Context, chainID string, block types.Block) ([]byte, time.Time, error) + GetPubKey(ctx context.Context, chainID string) ([]byte, error) + Stop() +} diff --git a/signer/remote_signer.go b/src/connector/sentry_signer.go similarity index 82% rename from signer/remote_signer.go rename to src/connector/sentry_signer.go index ef852f26..ddb564de 100644 --- a/signer/remote_signer.go +++ b/src/connector/sentry_signer.go @@ -1,12 +1,17 @@ -package signer +package connector +/* +Connector is the conections between the "sentry" (consensus cosigner) and the Horcrux node. +*/ import ( "context" "fmt" "net" "time" - "github.com/strangelove-ventures/horcrux/pkg/types" + "github.com/strangelove-ventures/horcrux/src/metrics" + + "github.com/strangelove-ventures/horcrux/src/types" cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" cometcryptoencoding "github.com/cometbft/cometbft/crypto/encoding" @@ -21,38 +26,30 @@ import ( const connRetrySec = 2 -// PrivValidator is a wrapper for tendermint PrivValidator, -// with additional Stop method for safe shutdown. -type PrivValidator interface { - Sign(ctx context.Context, chainID string, block types.Block) ([]byte, time.Time, error) - GetPubKey(ctx context.Context, chainID string) ([]byte, error) - Stop() -} - -// ReconnRemoteSigner dials using its dialer and responds to any +// ReconnRemoteSentry dials using its dialer and responds to any // signature requests using its privVal. -type ReconnRemoteSigner struct { +type ReconnRemoteSentry struct { cometservice.BaseService address string privKey cometcryptoed25519.PrivKey - privVal PrivValidator + privVal IPrivValidator // Responds to signature requests from the sentry dialer net.Dialer } -// NewReconnRemoteSigner return a ReconnRemoteSigner that will dial using the given +// NewReconnRemoteSentry return a ReconnRemoteSigner that will dial using the given // dialer and respond to any signature requests over the connection // using the given privVal. // // If the connection is broken, the ReconnRemoteSigner will attempt to reconnect. -func NewReconnRemoteSigner( +func NewReconnRemoteSentry( address string, logger cometlog.Logger, - privVal PrivValidator, + privVal IPrivValidator, dialer net.Dialer, -) *ReconnRemoteSigner { - rs := &ReconnRemoteSigner{ +) *ReconnRemoteSentry { + rs := &ReconnRemoteSentry{ address: address, privVal: privVal, dialer: dialer, @@ -64,17 +61,17 @@ func NewReconnRemoteSigner( } // OnStart implements cmn.Service. -func (rs *ReconnRemoteSigner) OnStart() error { +func (rs *ReconnRemoteSentry) OnStart() error { go rs.loop(context.Background()) return nil } // OnStop implements cmn.Service. -func (rs *ReconnRemoteSigner) OnStop() { +func (rs *ReconnRemoteSentry) OnStop() { rs.privVal.Stop() } -func (rs *ReconnRemoteSigner) establishConnection(ctx context.Context) (net.Conn, error) { +func (rs *ReconnRemoteSentry) establishConnection(ctx context.Context) (net.Conn, error) { ctx, cancel := context.WithTimeout(ctx, connRetrySec*time.Second) defer cancel() @@ -94,7 +91,7 @@ func (rs *ReconnRemoteSigner) establishConnection(ctx context.Context) (net.Conn } // main loop for ReconnRemoteSigner -func (rs *ReconnRemoteSigner) loop(ctx context.Context) { +func (rs *ReconnRemoteSentry) loop(ctx context.Context) { var conn net.Conn for { if !rs.IsRunning() { @@ -108,14 +105,14 @@ func (rs *ReconnRemoteSigner) loop(ctx context.Context) { timer := time.NewTimer(connRetrySec * time.Second) conn, err = rs.establishConnection(ctx) if err == nil { - sentryConnectTries.WithLabelValues(rs.address).Set(0) + metrics.SentryConnectTries.WithLabelValues(rs.address).Set(0) timer.Stop() rs.Logger.Info("Connected to Sentry", "address", rs.address) break } - sentryConnectTries.WithLabelValues(rs.address).Add(1) - totalSentryConnectTries.WithLabelValues(rs.address).Inc() + metrics.SentryConnectTries.WithLabelValues(rs.address).Add(1) + metrics.TotalSentryConnectTries.WithLabelValues(rs.address).Inc() retries++ rs.Logger.Error( "Error establishing connection, will retry", @@ -166,7 +163,7 @@ func (rs *ReconnRemoteSigner) loop(ctx context.Context) { } } -func (rs *ReconnRemoteSigner) handleRequest(req cometprotoprivval.Message) cometprotoprivval.Message { +func (rs *ReconnRemoteSentry) handleRequest(req cometprotoprivval.Message) cometprotoprivval.Message { switch typedReq := req.Sum.(type) { case *cometprotoprivval.Message_SignVoteRequest: return rs.handleSignVoteRequest(typedReq.SignVoteRequest.ChainId, typedReq.SignVoteRequest.Vote) @@ -182,7 +179,7 @@ func (rs *ReconnRemoteSigner) handleRequest(req cometprotoprivval.Message) comet } } -func (rs *ReconnRemoteSigner) handleSignVoteRequest( +func (rs *ReconnRemoteSentry) handleSignVoteRequest( chainID string, vote *cometproto.Vote) cometprotoprivval.Message { msgSum := &cometprotoprivval.Message_SignedVoteResponse{ SignedVoteResponse: &cometprotoprivval.SignedVoteResponse{ @@ -202,7 +199,7 @@ func (rs *ReconnRemoteSigner) handleSignVoteRequest( return cometprotoprivval.Message{Sum: msgSum} } -func (rs *ReconnRemoteSigner) handleSignProposalRequest( +func (rs *ReconnRemoteSentry) handleSignProposalRequest( chainID string, proposal *cometproto.Proposal, ) cometprotoprivval.Message { @@ -230,8 +227,8 @@ func (rs *ReconnRemoteSigner) handleSignProposalRequest( return cometprotoprivval.Message{Sum: msgSum} } -func (rs *ReconnRemoteSigner) handlePubKeyRequest(chainID string) cometprotoprivval.Message { - totalPubKeyRequests.WithLabelValues(chainID).Inc() +func (rs *ReconnRemoteSentry) handlePubKeyRequest(chainID string) cometprotoprivval.Message { + metrics.TotalPubKeyRequests.WithLabelValues(chainID).Inc() msgSum := &cometprotoprivval.Message_PubKeyResponse{PubKeyResponse: &cometprotoprivval.PubKeyResponse{ PubKey: cometprotocrypto.PublicKey{}, Error: nil, @@ -263,7 +260,7 @@ func (rs *ReconnRemoteSigner) handlePubKeyRequest(chainID string) cometprotopriv return cometprotoprivval.Message{Sum: msgSum} } -func (rs *ReconnRemoteSigner) handlePingRequest() cometprotoprivval.Message { +func (rs *ReconnRemoteSentry) handlePingRequest() cometprotoprivval.Message { return cometprotoprivval.Message{ Sum: &cometprotoprivval.Message_PingResponse{ PingResponse: &cometprotoprivval.PingResponse{}, @@ -284,17 +281,17 @@ func getRemoteSignerError(err error) *cometprotoprivval.RemoteSignerError { func StartRemoteSigners( services []cometservice.Service, logger cometlog.Logger, - privVal PrivValidator, + privVal IPrivValidator, nodes []string, ) ([]cometservice.Service, error) { var err error - go StartMetrics() + go metrics.StartMetrics() for _, node := range nodes { // CometBFT requires a connection within 3 seconds of start or crashes // A long timeout such as 30 seconds would cause the sentry to fail in loops // Use a short timeout and dial often to connect within 3 second window dialer := net.Dialer{Timeout: 2 * time.Second} - s := NewReconnRemoteSigner(node, logger, privVal, dialer) + s := NewReconnRemoteSentry(node, logger, privVal, dialer) // The 'server' that sentry connects to err = s.Start() if err != nil { @@ -306,7 +303,7 @@ func StartRemoteSigners( return services, err } -func (rs *ReconnRemoteSigner) closeConn(conn net.Conn) { +func (rs *ReconnRemoteSentry) closeConn(conn net.Conn) { if conn == nil { return } diff --git a/src/connector/sentry_signer_grpc_server.go b/src/connector/sentry_signer_grpc_server.go new file mode 100644 index 00000000..2dddcc31 --- /dev/null +++ b/src/connector/sentry_signer_grpc_server.go @@ -0,0 +1,195 @@ +package connector + +import ( + "context" + "errors" + "net" + "time" + + "github.com/strangelove-ventures/horcrux/src/metrics" + + "github.com/strangelove-ventures/horcrux/src/types" + + cometlog "github.com/cometbft/cometbft/libs/log" + cometservice "github.com/cometbft/cometbft/libs/service" + + // "github.com/strangelove-ventures/horcrux/src/proto" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +var _ proto.ConnectorServer = &SentrySignerGRPCServer{} + +// SentrySignerGRPCServer is the server that listens for signing requests from the "sentry" which is the client +// Sentry (a.k.a client) -> SentrySignerGRPCServer -> Sentry +type SentrySignerGRPCServer struct { + cometservice.BaseService + + validator IPrivValidator + logger cometlog.Logger + listenAddr string + + server *grpc.Server + + proto.UnimplementedConnectorServer +} + +func NewSentrySignerGRPCServer( + logger cometlog.Logger, + validator IPrivValidator, + listenAddr string, +) *SentrySignerGRPCServer { + s := &SentrySignerGRPCServer{ + validator: validator, + logger: logger, + listenAddr: listenAddr, + } + s.BaseService = *cometservice.NewBaseService(logger, "RemoteSignerGRPCServer", s) + return s +} + +func (s *SentrySignerGRPCServer) OnStart() error { + s.logger.Info("Remote Signer GRPC Listening", "address", s.listenAddr) + sock, err := net.Listen("tcp", s.listenAddr) + if err != nil { + return err + } + s.server = grpc.NewServer() + proto.RegisterConnectorServer(s.server, s) + reflection.Register(s.server) + return s.server.Serve(sock) +} + +func (s *SentrySignerGRPCServer) OnStop() { + s.server.GracefulStop() +} + +func (s *SentrySignerGRPCServer) PubKey(ctx context.Context, req *proto.PubKeyRequest) (*proto.PubKeyResponse, error) { + chainID := req.ChainId + + metrics.TotalPubKeyRequests.WithLabelValues(chainID).Inc() + + pubKey, err := s.validator.GetPubKey(ctx, chainID) + if err != nil { + s.logger.Error( + "Failed to get Pub Key", + "chain_id", chainID, + "error", err, + ) + return nil, err + } + + return &proto.PubKeyResponse{ + PubKey: pubKey, + }, nil +} + +func (s *SentrySignerGRPCServer) Sign( + ctx context.Context, + req *proto.SignBlockRequest, +) (*proto.SignBlockResponse, error) { + chainID, block := req.ChainID, types.BlockFromProto(req.Block) + + signature, timestamp, err := signAndTrack(ctx, s.logger, s.validator, chainID, block) + if err != nil { + return nil, err + } + + return &proto.SignBlockResponse{ + Signature: signature, + Timestamp: timestamp.UnixNano(), + }, nil +} + +func signAndTrack( + ctx context.Context, + logger cometlog.Logger, + validator IPrivValidator, + chainID string, + block types.Block, +) ([]byte, time.Time, error) { + signature, timestamp, err := validator.Sign(ctx, chainID, block) + if err != nil { + var typedErr *metrics.BeyondBlockError + switch { + case errors.As(err, &typedErr): + logger.Debug( + "Rejecting sign request", + "type", types.SignType(block.Step), + "chain_id", chainID, + "height", block.Height, + "round", block.Round, + "reason", typedErr.Msg, + ) + metrics.BeyondBlockErrors.WithLabelValues(chainID).Inc() + default: + logger.Error( + "Failed to sign", + "type", types.SignType(block.Step), + "chain_id", chainID, + "height", block.Height, + "round", block.Round, + "error", err, + ) + metrics.FailedSignVote.WithLabelValues(chainID).Inc() + } + return nil, block.Timestamp, err + } + + // Show signatures provided to each node have the same signature and timestamps + sigLen := 6 + if len(signature) < sigLen { + sigLen = len(signature) + } + logger.Info( + "Signed", + "type", types.SignType(block.Step), + "chain_id", chainID, + "height", block.Height, + "round", block.Round, + "sig", signature[:sigLen], + "ts", block.Timestamp, + ) + + switch block.Step { + case types.StepPropose: + metrics.LastProposalHeight.WithLabelValues(chainID).Set(float64(block.Height)) + metrics.LastProposalRound.WithLabelValues(chainID).Set(float64(block.Round)) + metrics.TotalProposalsSigned.WithLabelValues(chainID).Inc() + case types.StepPrevote: + // Determine number of heights since the last Prevote + stepSize := block.Height - metrics.PreviousPrevoteHeight + if metrics.PreviousPrevoteHeight != 0 && stepSize > 1 { + metrics.MissedPrevotes.WithLabelValues(chainID).Add(float64(stepSize)) + metrics.TotalMissedPrevotes.WithLabelValues(chainID).Add(float64(stepSize)) + } else { + metrics.MissedPrevotes.WithLabelValues(chainID).Set(0) + } + + metrics.PreviousPrevoteHeight = block.Height // remember last PrevoteHeight + + metrics.MetricsTimeKeeper.SetPreviousPrevote(time.Now()) + + metrics.LastPrevoteHeight.WithLabelValues(chainID).Set(float64(block.Height)) + metrics.LastPrevoteRound.WithLabelValues(chainID).Set(float64(block.Round)) + metrics.TotalPrevotesSigned.WithLabelValues(chainID).Inc() + case types.StepPrecommit: + stepSize := block.Height - metrics.PreviousPrecommitHeight + if metrics.PreviousPrecommitHeight != 0 && stepSize > 1 { + metrics.MissedPrecommits.WithLabelValues(chainID).Add(float64(stepSize)) + metrics.TotalMissedPrecommits.WithLabelValues(chainID).Add(float64(stepSize)) + } else { + metrics.MissedPrecommits.WithLabelValues(chainID).Set(0) + } + + metrics.PreviousPrecommitHeight = block.Height // remember last PrecommitHeight + metrics.MetricsTimeKeeper.SetPreviousPrecommit(time.Now()) + + metrics.LastPrecommitHeight.WithLabelValues(chainID).Set(float64(block.Height)) + metrics.LastPrecommitRound.WithLabelValues(chainID).Set(float64(block.Round)) + metrics.TotalPrecommitsSigned.WithLabelValues(chainID).Inc() + } + + return signature, timestamp, nil +} diff --git a/signer/remote_cosigner.go b/src/cosigner/grpc_client.go similarity index 50% rename from signer/remote_cosigner.go rename to src/cosigner/grpc_client.go index 62f9df48..8cd3ec78 100644 --- a/signer/remote_cosigner.go +++ b/src/cosigner/grpc_client.go @@ -1,67 +1,87 @@ -package signer +package cosigner import ( "context" - "fmt" "net/url" "time" - cometcrypto "github.com/cometbft/cometbft/crypto" "github.com/google/uuid" - "github.com/strangelove-ventures/horcrux/signer/proto" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + + // "github.com/strangelove-ventures/horcrux/src/proto" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) -var _ Cosigner = &RemoteCosigner{} +// var _ Cosigner = &RemoteCosigner{} -// RemoteCosigner uses CosignerGRPC to request signing from a remote cosigner -type RemoteCosigner struct { +// CosignerClient uses CosignerGRPC to request signing from a remote cosigner +// Remote Cosigner are CLIENTS! to every other cosigner, including the the nodes local cosigner +// It calls the GRPC server of the other cosigner +// TODO: Change name to CosignerClient +type CosignerClient struct { id int address string - client proto.CosignerClient + Client proto.CosignerClient // GRPC Client } -// NewRemoteCosigner returns a newly initialized RemoteCosigner -func NewRemoteCosigner(id int, address string) (*RemoteCosigner, error) { - client, err := getGRPCClient(address) - if err != nil { - return nil, err - } - - cosigner := &RemoteCosigner{ +// Placeholder function because of testing +// TODO: Change name to InitCosignerClient +func InitCosignerClient(id int, address string, client proto.CosignerClient) *CosignerClient { + cosigner := &CosignerClient{ id: id, - address: address, - client: client, + address: address, // address is the P2P URL of the remote cosigner + Client: client, } + return cosigner +} + +// NewCosignerClient returns a newly initialized RemoteCosigner +// TODO: Change name to NewCosignerClient +func NewCosignerClient(id int, address string) (*CosignerClient, error) { + client, err := getGRPCClient(address) // address is the P2P URL of the cosigner server to dial + if err != nil { + return nil, err + } + cosigner := InitCosignerClient(id, address, client) return cosigner, nil + /* + cosigner := &RemoteCosigner{ + id: id, + address: address, + Client: client, + } + + return cosigner, nil + */ } // GetID returns the Index of the remote cosigner // Implements the cosigner interface -func (cosigner *RemoteCosigner) GetIndex() int { +// TODO: Change name from ShamirIndex +func (cosigner *CosignerClient) GetIndex() int { return cosigner.id } // GetAddress returns the P2P URL of the remote cosigner // Implements the cosigner interface -func (cosigner *RemoteCosigner) GetAddress() string { +func (cosigner *CosignerClient) GetAddress() string { return cosigner.address } // GetPubKey returns public key of the validator. // Implements Cosigner interface -func (cosigner *RemoteCosigner) GetPubKey(_ string) (cometcrypto.PubKey, error) { - return nil, fmt.Errorf("unexpected call to RemoteCosigner.GetPubKey") -} +// func (cosigner *RemoteCosigner) GetPubKey(_ string) (cometcrypto.PubKey, error) { +// return nil, fmt.Errorf("unexpected call to RemoteCosigner.GetPubKey") +// } // VerifySignature validates a signed payload against the public key. // Implements Cosigner interface -func (cosigner *RemoteCosigner) VerifySignature(_ string, _, _ []byte) bool { - return false -} +// func (cosigner *RemoteCosigner) VerifySignature(_ string, _, _ []byte) bool { +// return false +// } func getGRPCClient(address string) (proto.CosignerClient, error) { var grpcAddress string @@ -79,7 +99,7 @@ func getGRPCClient(address string) (proto.CosignerClient, error) { } // GetNonces implements the cosigner interface -func (cosigner *RemoteCosigner) GetNonces( +func (cosigner *CosignerClient) GetNonces( ctx context.Context, uuids []uuid.UUID, ) (CosignerUUIDNoncesMultiple, error) { @@ -88,7 +108,7 @@ func (cosigner *RemoteCosigner) GetNonces( us[i] = make([]byte, 16) copy(us[i], u[:]) } - res, err := cosigner.client.GetNonces(ctx, &proto.GetNoncesRequest{ + res, err := cosigner.Client.GetNonces(ctx, &proto.GetNoncesRequest{ Uuids: us, }) if err != nil { @@ -98,17 +118,17 @@ func (cosigner *RemoteCosigner) GetNonces( for i, nonces := range res.Nonces { out[i] = &CosignerUUIDNonces{ UUID: uuid.UUID(nonces.Uuid), - Nonces: CosignerNoncesFromProto(nonces.Nonces), + Nonces: FromProtoToNonces(nonces.Nonces), } } return out, nil } // Implements the cosigner interface -func (cosigner *RemoteCosigner) SetNoncesAndSign( +func (cosigner *CosignerClient) SetNoncesAndSign( ctx context.Context, - req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) { - res, err := cosigner.client.SetNoncesAndSign(ctx, &proto.SetNoncesAndSignRequest{ + req CosignerSetNoncesAndSignRequest) (*SignatureResponse, error) { + res, err := cosigner.Client.SetNoncesAndSign(ctx, &proto.SetNoncesAndSignRequest{ Uuid: req.Nonces.UUID[:], ChainID: req.ChainID, Nonces: req.Nonces.Nonces.toProto(), @@ -118,18 +138,20 @@ func (cosigner *RemoteCosigner) SetNoncesAndSign( if err != nil { return nil, err } - return &CosignerSignResponse{ + return &SignatureResponse{ NoncePublic: res.GetNoncePublic(), Timestamp: time.Unix(0, res.GetTimestamp()), Signature: res.GetSignature(), }, nil } -func (cosigner *RemoteCosigner) Sign( +// TODO: This should move to ThresholdValidator. Its is not the responsibility of the cosigner +/* +func (cosigner *ClientCosigner) Sign( ctx context.Context, req CosignerSignBlockRequest, ) (*CosignerSignBlockResponse, error) { - res, err := cosigner.client.SignBlock(ctx, &proto.SignBlockRequest{ + res, err := cosigner.Client.SignBlock(ctx, &proto.SignBlockRequest{ ChainID: req.ChainID, Block: req.Block.ToProto(), }) @@ -140,3 +162,4 @@ func (cosigner *RemoteCosigner) Sign( Signature: res.GetSignature(), }, nil } +*/ diff --git a/src/cosigner/grpc_server.go b/src/cosigner/grpc_server.go new file mode 100644 index 00000000..5117b830 --- /dev/null +++ b/src/cosigner/grpc_server.go @@ -0,0 +1,100 @@ +package cosigner + +import ( + // "context" + // "fmt" + + // "github.com/strangelove-ventures/horcrux/src/types" + + "context" + + "github.com/google/uuid" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + "github.com/strangelove-ventures/horcrux/src/types" + // "github.com/strangelove-ventures/horcrux/src/proto" +) + +func NewCosignerServer( + // cosigner *cosigner.LocalCosigner, + cosigner *LocalCosigner, +) *CosignerServer { + return &CosignerServer{ + cosigner: cosigner, + } +} + +type CosignerServer struct { + // cosigner *cosigner.LocalCosigner //Change to interface + cosigner *LocalCosigner + // thresholdValidator *ThresholdValidator + // raftStore *RaftStore + // TODO: add logger and not rely on raftStore.logger + + // TODO: Decouple cosignerserver from nodeserver. + proto.UnimplementedCosignerServer + // proto.UnimplementedTresholdValidatorServer +} + +func (rpc *CosignerServer) GetNonces( + ctx context.Context, + req *proto.GetNoncesRequest, +) (*proto.GetNoncesResponse, error) { + uuids := make([]uuid.UUID, len(req.Uuids)) + for i, uuidBytes := range req.Uuids { + uuids[i] = uuid.UUID(uuidBytes) + } + res, err := rpc.cosigner.GetNonces( + ctx, + uuids, + ) + if err != nil { + return nil, err + } + + return &proto.GetNoncesResponse{ + Nonces: res.ToProto(), + }, nil +} + +// TODO: Move to cosigner server +func (rpc *CosignerServer) SetNoncesAndSign( + ctx context.Context, + req *proto.SetNoncesAndSignRequest, +) (*proto.SetNoncesAndSignResponse, error) { + res, err := rpc.cosigner.SetNoncesAndSign(ctx, CosignerSetNoncesAndSignRequest{ + ChainID: req.ChainID, + Nonces: &CosignerUUIDNonces{ + UUID: uuid.UUID(req.Uuid), + Nonces: FromProtoToNonces(req.GetNonces()), + }, + HRST: types.HRSTFromProto(req.GetHrst()), + SignBytes: req.GetSignBytes(), + }) + if err != nil { + rpc.cosigner.logger.Error( + "Failed to sign with shard", + "chain_id", req.ChainID, + "height", req.Hrst.Height, + "round", req.Hrst.Round, + "step", req.Hrst.Step, + "error", err, + ) + return nil, err + } + rpc.cosigner.logger.Info( + "Signed with shard", + "chain_id", req.ChainID, + "height", req.Hrst.Height, + "round", req.Hrst.Round, + "step", req.Hrst.Step, + ) + return &proto.SetNoncesAndSignResponse{ + NoncePublic: res.NoncePublic, + Timestamp: res.Timestamp.UnixNano(), + Signature: res.Signature, + }, nil +} + +func (rpc *CosignerServer) Ping(context.Context, *proto.PingRequest) (*proto.PingResponse, error) { + return &proto.PingResponse{}, nil +} diff --git a/src/cosigner/icosigner.go b/src/cosigner/icosigner.go new file mode 100644 index 00000000..81c2e613 --- /dev/null +++ b/src/cosigner/icosigner.go @@ -0,0 +1,104 @@ +package cosigner + +/* +Package cosigner: +Cosinger is responsible for the network MPC communication between threshold signers. +The subdirectories are the different implementations of the MPC protocol. + +You can think of it as: +- LocalCosigner is the server (we understand that local here is confussing but it is because it is local to the node) +- RemoteCosigner is the client +*/ +import ( + "context" + + "github.com/google/uuid" +) + +// MPC is the interface for the MPC protocol +// Its responsibility is to communicate with the other cosigners + +type iCosigner interface { + // GetIndex gets the index of the cosigner + // The index is the shamir index: 1, 2, etc... + GetIndex() int + + // Get the P2P URL (GRPC) + GetAddress() string + + // Get the combined public key + // TODO: Change name to FetchPubKey + // GetPubKey(chainID string) (cometcrypto.PubKey, error) + + // VerifySignature(chainID string, payload, signature []byte) bool + + // Get nonces for all cosigner shards + GetNonces(ctx context.Context, uuids []uuid.UUID) (CosignerUUIDNoncesMultiple, error) + + // Sign the requested bytes + SetNoncesAndSign(ctx context.Context, req CosignerSetNoncesAndSignRequest) (*SignatureResponse, error) +} + +type iHealth interface { + Start(ctx context.Context) error +} +type iNonceCache interface { + Start(ctx context.Context) error +} +type iNoncePruner interface { + Start(ctx context.Context) error +} + +/* + type iServer interface { + SetNoncesAndSign() + // TODO - add methods + } + + type iClient interface { + SetNoncesAndSign() + // TODO - add methods + } +*/ + +type MPC struct { + // logger log.Logger, + chainID string + + // our own cosigner (i.e server) + // MyCosigner *LocalCosigner // TODO Should be an interface as well. + server iCosigner // TODO Should be an interface as well. + + // Cosigner peers (i.e the nodes clients to other servers) + clients []iCosigner // "i.e clients to call" (map is slowr than slice but we need to be able to remove elements) + + serverHealth iHealth + + nonceCache iNonceCache + noncePruner iNoncePruner +} + +// Takes a signature request from the validator and returns a signature response +func (mpc *MPC) SignBlock(ctx context.Context) error { + return nil +} + +func (mpc *MPC) Stop(ctx context.Context) error { + return nil +} + +func (mpc *MPC) Start(ctx context.Context) error { + // mpc.logger.Info("Starting serverHealth services") + go mpc.serverHealth.Start(ctx) + + // mpc.logger.Info("Starting serverHealth services") + go mpc.nonceCache.Start(ctx) + + // mpc.logger.Info("Starting noncePruner services") + go mpc.noncePruner.Start(ctx) + + // Should start the servers/clients? + // TODO: Should we start the clients and servers here? + + return nil +} diff --git a/signer/cosigner_security.go b/src/cosigner/icosigner_security.go similarity index 87% rename from signer/cosigner_security.go rename to src/cosigner/icosigner_security.go index 996a52a2..5cf5344f 100644 --- a/signer/cosigner_security.go +++ b/src/cosigner/icosigner_security.go @@ -1,7 +1,7 @@ -package signer +package cosigner // CosignerSecurity is an interface for the security layer of the cosigner. -type CosignerSecurity interface { +type ICosignerSecurity interface { // GetID returns the Index of the cosigner. GetID() int @@ -10,7 +10,7 @@ type CosignerSecurity interface { id int, noncePub []byte, nonceShare []byte, - ) (CosignerNonce, error) + ) (Nonce, error) // DecryptAndVerify decrypts the nonce and verifies the signature to authenticate the source cosigner. DecryptAndVerify( diff --git a/src/cosigner/ithresholdsigner.go b/src/cosigner/ithresholdsigner.go new file mode 100644 index 00000000..02e6c95a --- /dev/null +++ b/src/cosigner/ithresholdsigner.go @@ -0,0 +1,25 @@ +package cosigner + +import "github.com/strangelove-ventures/horcrux/src/types" + +// Interface for the local cosigner whether it's a soft sign or HSM +// The ThresholdSigner interface is used to sign messages with a threshold signature. +type IThresholdSigner interface { + // GetPubKey returns the public key bytes for the combination of all cosigners. + GetPubKey() []byte + + // Sign signs a byte payload with the provided nonces. + Sign(nonces []types.Nonce, payload []byte) ([]byte, error) + + // CombineSignatures combines multiple partial signatures to a full signature. + CombineSignatures([]types.PartialSignature) ([]byte, error) +} + +type IThresholdDealer interface { + GenerateNonces(threshold, total uint8) (types.Nonces, error) +} + +type IThreshold interface { + IThresholdSigner + IThresholdDealer +} diff --git a/signer/cosigner_key_shares.go b/src/cosigner/nodesecurity/cosigner_key_shares.go similarity index 78% rename from signer/cosigner_key_shares.go rename to src/cosigner/nodesecurity/cosigner_key_shares.go index 5f4f6248..c6f168ef 100644 --- a/signer/cosigner_key_shares.go +++ b/src/cosigner/nodesecurity/cosigner_key_shares.go @@ -1,4 +1,4 @@ -package signer +package nodesecurity import ( "crypto/rand" @@ -6,8 +6,6 @@ import ( "encoding/json" "os" - cometjson "github.com/cometbft/cometbft/libs/json" - "github.com/cometbft/cometbft/privval" "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/secp256k1" "golang.org/x/sync/errgroup" @@ -30,27 +28,6 @@ func CreateCosignerRSAShards(shards int) ([]CosignerRSAKey, error) { return out, nil } -// ReadPrivValidatorFile reads in a privval.FilePVKey from a given file. -func ReadPrivValidatorFile(priv string) (out privval.FilePVKey, err error) { - var bz []byte - if bz, err = os.ReadFile(priv); err != nil { - return - } - if err = cometjson.Unmarshal(bz, &out); err != nil { - return - } - return -} - -// WriteCosignerEd25519ShardFile writes a cosigner Ed25519 key to a given file name. -func WriteCosignerEd25519ShardFile(cosigner CosignerEd25519Key, file string) error { - jsonBytes, err := json.Marshal(&cosigner) - if err != nil { - return err - } - return os.WriteFile(file, jsonBytes, 0600) -} - // WriteCosignerRSAShardFile writes a cosigner RSA key to a given file name. func WriteCosignerRSAShardFile(cosigner CosignerRSAKey, file string) error { jsonBytes, err := json.Marshal(&cosigner) diff --git a/signer/cosigner_security_ecies.go b/src/cosigner/nodesecurity/cosigner_security_ecies.go similarity index 94% rename from signer/cosigner_security_ecies.go rename to src/cosigner/nodesecurity/cosigner_security_ecies.go index c5af8857..4a9d95f3 100644 --- a/signer/cosigner_security_ecies.go +++ b/src/cosigner/nodesecurity/cosigner_security_ecies.go @@ -1,4 +1,4 @@ -package signer +package nodesecurity import ( "crypto/ecdsa" @@ -12,10 +12,11 @@ import ( cometjson "github.com/cometbft/cometbft/libs/json" "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/strangelove-ventures/horcrux/src/cosigner" "golang.org/x/sync/errgroup" ) -var _ CosignerSecurity = &CosignerSecurityECIES{} +var _ cosigner.ICosignerSecurity = &CosignerSecurityECIES{} // CosignerSecurityECIES is an implementation of CosignerSecurity // using ECIES for encryption and ECDSA for digital signature. @@ -136,8 +137,9 @@ func (c *CosignerSecurityECIES) GetID() int { } // EncryptAndSign encrypts the nonce and signs it for authentication. -func (c *CosignerSecurityECIES) EncryptAndSign(id int, noncePub []byte, nonceShare []byte) (CosignerNonce, error) { - nonce := CosignerNonce{ +func (c *CosignerSecurityECIES) EncryptAndSign( + id int, noncePub []byte, nonceShare []byte) (cosigner.Nonce, error) { + nonce := cosigner.Nonce{ SourceID: c.key.ID, } @@ -206,7 +208,7 @@ func (c *CosignerSecurityECIES) DecryptAndVerify( return nil, nil, fmt.Errorf("unknown cosigner: %d", id) } - digestMsg := CosignerNonce{ + digestMsg := cosigner.Nonce{ SourceID: id, PubKey: encryptedNoncePub, Share: encryptedNonceShare, diff --git a/signer/cosigner_security_ecies_test.go b/src/cosigner/nodesecurity/cosigner_security_ecies_test.go similarity index 68% rename from signer/cosigner_security_ecies_test.go rename to src/cosigner/nodesecurity/cosigner_security_ecies_test.go index aa7d04d8..73c7cf4f 100644 --- a/signer/cosigner_security_ecies_test.go +++ b/src/cosigner/nodesecurity/cosigner_security_ecies_test.go @@ -1,4 +1,4 @@ -package signer +package nodesecurity_test import ( "crypto/rand" @@ -7,6 +7,8 @@ import ( "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" ) @@ -25,20 +27,20 @@ func TestCosignerECIES(t *testing.T) { pubs[i] = &key.PublicKey } - securities := make([]CosignerSecurity, 3) + securities := make([]nodesecurity.CosignerSecurityECIES, 3) for i := 0; i < 3; i++ { - key := CosignerECIESKey{ + key := nodesecurity.CosignerECIESKey{ ID: i + 1, ECIESKey: keys[i], ECIESPubs: pubs, } - securities[i] = NewCosignerSecurityECIES(key) + securities[i] = *nodesecurity.NewCosignerSecurityECIES(key) bz, err := json.Marshal(&key) require.NoError(t, err) - var key2 CosignerECIESKey + var key2 nodesecurity.CosignerECIESKey require.NoError(t, json.Unmarshal(bz, &key2)) require.Equal(t, key, key2) @@ -50,12 +52,12 @@ func TestCosignerECIES(t *testing.T) { } } - err := testCosignerSecurity(t, securities) + err := testCosignerSecurityECIES(t, securities) require.ErrorContains(t, err, "ecies: invalid message") require.ErrorContains(t, err, "failed to decrypt") } -func testCosignerSecurity(t *testing.T, securities []CosignerSecurity) error { +func testCosignerSecurityRSA(t *testing.T, securities []*nodesecurity.CosignerSecurityRSA) error { var ( mockPub = []byte("mock_pub") mockShare = []byte("mock_share") @@ -75,6 +77,25 @@ func testCosignerSecurity(t *testing.T, securities []CosignerSecurity) error { return err } +func testCosignerSecurityECIES(t *testing.T, securities []nodesecurity.CosignerSecurityECIES) error { + var ( + mockPub = []byte("mock_pub") + mockShare = []byte("mock_share") + ) + + nonce, err := securities[0].EncryptAndSign(2, mockPub, mockShare) + require.NoError(t, err) + + decryptedPub, decryptedShare, err := securities[1].DecryptAndVerify(1, nonce.PubKey, nonce.Share, nonce.Signature) + require.NoError(t, err) + + require.Equal(t, mockPub, decryptedPub) + require.Equal(t, mockShare, decryptedShare) + + _, _, err = securities[2].DecryptAndVerify(1, nonce.PubKey, nonce.Share, nonce.Signature) + + return err +} func TestConcurrentIterateCosignerECIES(t *testing.T) { if testing.Short() { t.Skip("skipping in short mode") @@ -91,14 +112,15 @@ func TestConcurrentIterateCosignerECIES(t *testing.T) { pubs[i] = &key.PublicKey } - securities := make([]CosignerSecurity, 3) + securities := make([]*nodesecurity.CosignerSecurityECIES, 3) for i := 0; i < 3; i++ { - securities[i] = NewCosignerSecurityECIES(CosignerECIESKey{ - ID: i + 1, - ECIESKey: keys[i], - ECIESPubs: pubs, - }) + securities[i] = nodesecurity.NewCosignerSecurityECIES( + nodesecurity.CosignerECIESKey{ + ID: i + 1, + ECIESKey: keys[i], + ECIESPubs: pubs, + }) } for i := 0; i < 5000; i++ { diff --git a/signer/cosigner_security_rsa.go b/src/cosigner/nodesecurity/cosigner_security_rsa.go similarity index 94% rename from signer/cosigner_security_rsa.go rename to src/cosigner/nodesecurity/cosigner_security_rsa.go index a2d02675..e0059ec9 100644 --- a/signer/cosigner_security_rsa.go +++ b/src/cosigner/nodesecurity/cosigner_security_rsa.go @@ -1,4 +1,4 @@ -package signer +package nodesecurity import ( "crypto" @@ -11,10 +11,11 @@ import ( "os" cometjson "github.com/cometbft/cometbft/libs/json" + "github.com/strangelove-ventures/horcrux/src/cosigner" "golang.org/x/sync/errgroup" ) -var _ CosignerSecurity = &CosignerSecurityRSA{} +var _ cosigner.ICosignerSecurity = &CosignerSecurityRSA{} // CosignerSecurityRSA is an implementation of CosignerSecurity using RSA for encryption and P5S for digital signature. type CosignerSecurityRSA struct { @@ -22,7 +23,7 @@ type CosignerSecurityRSA struct { rsaPubKeys map[int]CosignerRSAPubKey } -// CosignerRSAKey is a cosigner's RSA public key. +// CosignerRSAPubKey is a cosigner's RSA public key. type CosignerRSAPubKey struct { ID int PublicKey rsa.PublicKey @@ -128,8 +129,8 @@ func (c *CosignerSecurityRSA) GetID() int { } // EncryptAndSign encrypts the nonce and signs it for authentication. -func (c *CosignerSecurityRSA) EncryptAndSign(id int, noncePub []byte, nonceShare []byte) (CosignerNonce, error) { - nonce := CosignerNonce{ +func (c *CosignerSecurityRSA) EncryptAndSign(id int, noncePub []byte, nonceShare []byte) (cosigner.Nonce, error) { + nonce := cosigner.Nonce{ SourceID: c.key.ID, } @@ -194,7 +195,7 @@ func (c *CosignerSecurityRSA) DecryptAndVerify( return nil, nil, fmt.Errorf("unknown cosigner: %d", id) } - digestMsg := CosignerNonce{ + digestMsg := cosigner.Nonce{ SourceID: id, PubKey: encryptedNoncePub, Share: encryptedNonceShare, diff --git a/signer/cosigner_security_rsa_test.go b/src/cosigner/nodesecurity/cosigner_security_rsa_test.go similarity index 79% rename from signer/cosigner_security_rsa_test.go rename to src/cosigner/nodesecurity/cosigner_security_rsa_test.go index 7830d9af..b4dfde83 100644 --- a/signer/cosigner_security_rsa_test.go +++ b/src/cosigner/nodesecurity/cosigner_security_rsa_test.go @@ -1,4 +1,4 @@ -package signer +package nodesecurity_test import ( "crypto/rand" @@ -6,10 +6,17 @@ import ( "encoding/json" "testing" + "github.com/strangelove-ventures/horcrux/src/cosigner" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" ) +const ( + bitSize = 2048 +) + func TestCosignerRSA(t *testing.T) { t.Parallel() @@ -24,20 +31,20 @@ func TestCosignerRSA(t *testing.T) { pubKeys[i] = &key.PublicKey } - securities := make([]CosignerSecurity, 3) + securities := make([]*nodesecurity.CosignerSecurityRSA, 3) for i := 0; i < 3; i++ { - key := CosignerRSAKey{ + key := nodesecurity.CosignerRSAKey{ ID: i + 1, RSAKey: *keys[i], RSAPubs: pubKeys, } - securities[i] = NewCosignerSecurityRSA(key) + securities[i] = nodesecurity.NewCosignerSecurityRSA(key) bz, err := json.Marshal(&key) require.NoError(t, err) - var key2 CosignerRSAKey + var key2 nodesecurity.CosignerRSAKey require.NoError(t, json.Unmarshal(bz, &key2)) require.Equal(t, key, key2) @@ -51,7 +58,7 @@ func TestCosignerRSA(t *testing.T) { } } - err := testCosignerSecurity(t, securities) + err := testCosignerSecurityRSA(t, securities) require.ErrorIs(t, rsa.ErrDecryption, err) } @@ -67,10 +74,10 @@ func TestConcurrentIterateCosignerRSA(t *testing.T) { pubKeys[i] = &key.PublicKey } - securities := make([]CosignerSecurity, 3) + securities := make([]cosigner.ICosignerSecurity, 3) for i := 0; i < 3; i++ { - securities[i] = NewCosignerSecurityRSA(CosignerRSAKey{ + securities[i] = nodesecurity.NewCosignerSecurityRSA(nodesecurity.CosignerRSAKey{ ID: i + 1, RSAKey: *keys[i], RSAPubs: pubKeys, diff --git a/signer/local_cosigner.go b/src/cosigner/server.go similarity index 77% rename from signer/local_cosigner.go rename to src/cosigner/server.go index 1236dd8d..7d830a7c 100644 --- a/signer/local_cosigner.go +++ b/src/cosigner/server.go @@ -1,4 +1,4 @@ -package signer +package cosigner import ( "context" @@ -7,7 +7,12 @@ import ( "sync" "time" - "github.com/strangelove-ventures/horcrux/pkg/types" + "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/metrics" + "github.com/strangelove-ventures/horcrux/src/tss" + "github.com/strangelove-ventures/horcrux/src/tss/tss25519" + + "github.com/strangelove-ventures/horcrux/src/types" cometcrypto "github.com/cometbft/cometbft/crypto" cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" @@ -16,32 +21,39 @@ import ( "golang.org/x/sync/errgroup" ) -var _ Cosigner = &LocalCosigner{} +// var _ signer.ICosigner = &LocalCosigner{} // double the CosignerNonceCache expiration so that sign requests from the leader // never reference nonces which have expired here in the LocalCosigner. const nonceExpiration = 20 * time.Second +const ErrUnexpectedState = "unexpected state, metadata does not exist for U:" + // LocalCosigner responds to sign requests. // It maintains a high watermark to avoid double-signing. // Signing is thread safe. +// TODO: Change name to CosignerServer type LocalCosigner struct { logger cometlog.Logger - config *RuntimeConfig - security CosignerSecurity - chainState sync.Map // TODO: Add type so its not any? + config *config.RuntimeConfig + security ICosignerSecurity // TODO: Should be in a node above. + chainState sync.Map // chainState is a map of chainID to *ChainState address string pendingDiskWG sync.WaitGroup nonces map[uuid.UUID]*types.NoncesWithExpiration // protects the nonces map noncesMu sync.RWMutex + + // dealer generates nonces according to the "DKG" protocol + dealer IThresholdDealer } +// TODO: Change name to NewCosignerServer func NewLocalCosigner( logger cometlog.Logger, - config *RuntimeConfig, - security CosignerSecurity, + config *config.RuntimeConfig, + security ICosignerSecurity, address string, ) *LocalCosigner { return &LocalCosigner{ @@ -50,6 +62,7 @@ func NewLocalCosigner( security: security, address: address, nonces: make(map[uuid.UUID]*types.NoncesWithExpiration), + dealer: &tss25519.NonceGenerator{}, // TODO: Change to interface } } @@ -58,8 +71,12 @@ type ChainState struct { // lastSignState stores the last sign state for an HRS we have fully signed // incremented whenever we are asked to sign an HRS lastSignState *types.SignState - // signer generates nonces, combines nonces, signs, and verifies signatures. - signer ThresholdSigner + + // signer, combines signatures, signs, and verifies signatures. + signer IThresholdSigner + + // dealer generates nonces according to the "DKG" protocol + // dealer IThresholdDealer } // StartNoncePruner periodically prunes nonces that have expired. @@ -129,9 +146,9 @@ func (cosigner *LocalCosigner) SaveLastSignedState(chainID string, signState typ ) } -// waitForSignStatesToFlushToDisk waits for all state file writes queued +// WaitForSignStatesToFlushToDisk waits for all state file writes queued // in SaveLastSignedState to complete before termination. -func (cosigner *LocalCosigner) waitForSignStatesToFlushToDisk() { +func (cosigner *LocalCosigner) WaitForSignStatesToFlushToDisk() { cosigner.pendingDiskWG.Wait() } @@ -147,12 +164,14 @@ func (cosigner *LocalCosigner) GetAddress() string { return cosigner.address } +// TODO: Rename to fetchChainState func (cosigner *LocalCosigner) getChainState(chainID string) (*ChainState, error) { cs, ok := cosigner.chainState.Load(chainID) if !ok { return nil, fmt.Errorf("failed to load chain state for %s", chainID) } + // Asserting cs (type any) is actually of type *ChainState ccs, ok := cs.(*ChainState) if !ok { return nil, fmt.Errorf("expected: (*ChainState), actual: (%T)", cs) @@ -173,7 +192,7 @@ func (cosigner *LocalCosigner) GetPubKey(chainID string) (cometcrypto.PubKey, er return nil, err } - return cometcryptoed25519.PubKey(ccs.signer.PubKey()), nil + return cometcryptoed25519.PubKey(ccs.signer.GetPubKey()), nil } // CombineSignatures combines partial signatures into a full signature. @@ -187,7 +206,8 @@ func (cosigner *LocalCosigner) CombineSignatures(chainID string, signatures []ty } // VerifySignature validates a signed payload against the public key. -// Implements Cosigner interface +// Implements Cosigner interface Should not be part of Cosigner. +// Should be part of a higher level interface. func (cosigner *LocalCosigner) VerifySignature(chainID string, payload, signature []byte) bool { if err := cosigner.LoadSignStateIfNecessary(chainID); err != nil { return false @@ -201,16 +221,16 @@ func (cosigner *LocalCosigner) VerifySignature(chainID string, payload, signatur sig := make([]byte, len(signature)) copy(sig, signature) - return cometcryptoed25519.PubKey(ccs.signer.PubKey()).VerifySignature(payload, sig) + return cometcryptoed25519.PubKey(ccs.signer.GetPubKey()).VerifySignature(payload, sig) } // Sign the sign request using the cosigner's shard // Return the signed bytes or an error // Implements Cosigner interface -func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignResponse, error) { +func (cosigner *LocalCosigner) sign(req SignatureRequest) (SignatureResponse, error) { chainID := req.ChainID - res := CosignerSignResponse{} + res := SignatureResponse{} ccs, err := cosigner.getChainState(chainID) if err != nil { @@ -218,7 +238,7 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon } // This function has multiple exit points. Only start time can be guaranteed - metricsTimeKeeper.SetPreviousLocalSignStart(time.Now()) + metrics.MetricsTimeKeeper.SetPreviousLocalSignStart(time.Now()) hrst, err := types.UnpackHRST(req.SignBytes) if err != nil { @@ -258,7 +278,8 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon }, &cosigner.pendingDiskWG) if err != nil { - if _, isSameHRSError := err.(*types.SameHRSError); !isSameHRSError { + var sameHRSError *types.SameHRSError + if !errors.As(err, &sameHRSError) { return res, err } } @@ -270,7 +291,7 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon res.Signature = sig // Note - Function may return before this line so elapsed time for Finish may be multiple block times - metricsTimeKeeper.SetPreviousLocalSignFinish(time.Now()) + metrics.MetricsTimeKeeper.SetPreviousLocalSignFinish(time.Now()) return res, nil } @@ -279,11 +300,13 @@ func (cosigner *LocalCosigner) generateNonces() ([]types.Nonces, error) { total := len(cosigner.config.Config.ThresholdModeConfig.Cosigners) meta := make([]types.Nonces, total) - nonces, err := GenerateNonces( - uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), - uint8(total), - ) + // TODO: This should only generate nonces for the cosigners that are online + // although it might doesnt matter if we arent doing DKG + // Should call an interface: dealnonce or something + threshold := uint8(cosigner.config.Config.ThresholdModeConfig.Threshold) + nonces, err := cosigner.dealer.GenerateNonces(uint8(threshold), uint8(total)) if err != nil { + fmt.Println("Error is: ", err) return nil, err } @@ -306,9 +329,8 @@ func (cosigner *LocalCosigner) LoadSignStateIfNecessary(chainID string) error { return err } - var signer ThresholdSigner - - signer, err = NewThresholdSignerSoft(cosigner.config, cosigner.GetIndex(), chainID) + var signer IThresholdSigner + signer, err = tss.NewThresholdEd25519SignerSoft(cosigner.config, cosigner.GetIndex(), chainID) if err != nil { return err } @@ -326,7 +348,7 @@ func (cosigner *LocalCosigner) GetNonces( _ context.Context, uuids []uuid.UUID, ) (CosignerUUIDNoncesMultiple, error) { - metricsTimeKeeper.SetPreviousLocalNonce(time.Now()) + metrics.MetricsTimeKeeper.SetPreviousLocalNonce(time.Now()) total := len(cosigner.config.Config.ThresholdModeConfig.Cosigners) @@ -343,9 +365,14 @@ func (cosigner *LocalCosigner) GetNonces( u := u outerEg.Go(func() error { + meta, err := cosigner.generateNoncesIfNecessary(u) + if err != nil { + return err + } + var eg errgroup.Group - nonces := make([]CosignerNonce, total-1) + nonces := make([]Nonce, total-1) for i := 0; i < total; i++ { peerID := i + 1 @@ -356,7 +383,7 @@ func (cosigner *LocalCosigner) GetNonces( i := i eg.Go(func() error { - secretPart, err := cosigner.getNonce(u, peerID) + secretPart, err := cosigner.getNonce(meta, peerID) if i >= id { nonces[i-1] = secretPart @@ -390,15 +417,16 @@ func (cosigner *LocalCosigner) GetNonces( func (cosigner *LocalCosigner) generateNoncesIfNecessary(uuid uuid.UUID) (*types.NoncesWithExpiration, error) { // protects the meta map - cosigner.noncesMu.Lock() - defer cosigner.noncesMu.Unlock() - - if nonces, ok := cosigner.nonces[uuid]; ok { + cosigner.noncesMu.RLock() + nonces, ok := cosigner.nonces[uuid] + cosigner.noncesMu.RUnlock() + if ok { return nonces, nil } newNonces, err := cosigner.generateNonces() if err != nil { + fmt.Println(err) return nil, err } @@ -407,25 +435,23 @@ func (cosigner *LocalCosigner) generateNoncesIfNecessary(uuid uuid.UUID) (*types Expiration: time.Now().Add(nonceExpiration), } + cosigner.noncesMu.Lock() cosigner.nonces[uuid] = &res + cosigner.noncesMu.Unlock() + return &res, nil } // Get the ephemeral secret part for an ephemeral share // The ephemeral secret part is encrypted for the receiver func (cosigner *LocalCosigner) getNonce( - uuid uuid.UUID, + meta *types.NoncesWithExpiration, peerID int, -) (CosignerNonce, error) { - zero := CosignerNonce{} +) (Nonce, error) { + zero := Nonce{} id := cosigner.GetIndex() - meta, err := cosigner.generateNoncesIfNecessary(uuid) - if err != nil { - return zero, err - } - ourCosignerMeta := meta.Nonces[id-1] nonce, err := cosigner.security.EncryptAndSign(peerID, ourCosignerMeta.PubKey, ourCosignerMeta.Shares[peerID-1]) if err != nil { @@ -436,7 +462,7 @@ func (cosigner *LocalCosigner) getNonce( } // setNonce stores a nonce provided by another cosigner -func (cosigner *LocalCosigner) setNonce(uuid uuid.UUID, nonce CosignerNonce) error { +func (cosigner *LocalCosigner) setNonce(uuid uuid.UUID, nonce Nonce) error { // Verify the source signature if nonce.Signature == nil { return errors.New("signature field is required") @@ -456,7 +482,8 @@ func (cosigner *LocalCosigner) setNonce(uuid uuid.UUID, nonce CosignerNonce) err // generate metadata placeholder if !ok { return fmt.Errorf( - "unexpected state, metadata does not exist for U: %s", + "%s %s", + ErrUnexpectedState, uuid, ) } @@ -473,7 +500,7 @@ func (cosigner *LocalCosigner) setNonce(uuid uuid.UUID, nonce CosignerNonce) err func (cosigner *LocalCosigner) SetNoncesAndSign( _ context.Context, - req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) { + req CosignerSetNoncesAndSignRequest) (*SignatureResponse, error) { chainID := req.ChainID if err := cosigner.LoadSignStateIfNecessary(chainID); err != nil { @@ -497,7 +524,7 @@ func (cosigner *LocalCosigner) SetNoncesAndSign( return nil, err } - res, err := cosigner.sign(CosignerSignRequest{ + res, err := cosigner.sign(SignatureRequest{ UUID: req.Nonces.UUID, ChainID: chainID, SignBytes: req.SignBytes, diff --git a/signer/local_cosigner_test.go b/src/cosigner/server_test.go similarity index 69% rename from signer/local_cosigner_test.go rename to src/cosigner/server_test.go index 0dc690a8..4764d5e0 100644 --- a/signer/local_cosigner_test.go +++ b/src/cosigner/server_test.go @@ -1,4 +1,4 @@ -package signer +package cosigner_test import ( "context" @@ -10,7 +10,11 @@ import ( "testing" "time" - "github.com/strangelove-ventures/horcrux/pkg/types" + "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/cosigner" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + "github.com/strangelove-ventures/horcrux/src/tss" + "github.com/strangelove-ventures/horcrux/src/types" cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/libs/log" @@ -40,7 +44,7 @@ func TestLocalCosignerSignRSA3of5(t *testing.T) { func testLocalCosignerSignRSA(t *testing.T, threshold, total uint8) { t.Parallel() - security := make([]CosignerSecurity, total) + security := make([]cosigner.ICosignerSecurity, total) keys := make([]*rsa.PrivateKey, total) pubKeys := make([]*rsa.PublicKey, total) @@ -53,8 +57,8 @@ func testLocalCosignerSignRSA(t *testing.T, threshold, total uint8) { } for i, k := range keys { - security[i] = NewCosignerSecurityRSA( - CosignerRSAKey{ + security[i] = nodesecurity.NewCosignerSecurityRSA( + nodesecurity.CosignerRSAKey{ ID: i + 1, RSAKey: *k, RSAPubs: pubKeys, @@ -76,7 +80,7 @@ func TestLocalCosignerSignECIES3of5(t *testing.T) { func testLocalCosignerSignECIES(t *testing.T, threshold, total uint8) { t.Parallel() - security := make([]CosignerSecurity, total) + security := make([]cosigner.ICosignerSecurity, total) keys := make([]*ecies.PrivateKey, total) pubKeys := make([]*ecies.PublicKey, total) @@ -89,8 +93,8 @@ func testLocalCosignerSignECIES(t *testing.T, threshold, total uint8) { } for i, k := range keys { - security[i] = NewCosignerSecurityECIES( - CosignerECIESKey{ + security[i] = nodesecurity.NewCosignerSecurityECIES( + nodesecurity.CosignerECIESKey{ ID: i + 1, ECIESKey: k, ECIESPubs: pubKeys, @@ -101,18 +105,19 @@ func testLocalCosignerSignECIES(t *testing.T, threshold, total uint8) { testLocalCosignerSign(t, threshold, total, security) } -func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []CosignerSecurity) { +func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []cosigner.ICosignerSecurity) { privateKey := cometcryptoed25519.GenPrivKey() privKeyBytes := [64]byte{} copy(privKeyBytes[:], privateKey[:]) privShards := tsed25519.DealShares(tsed25519.ExpandSecret(privKeyBytes[:32]), threshold, total) - pubKey := privateKey.PubKey() + // Returns the public key from the private key and type asserts it to an tss key. + pubKey := privateKey.PubKey().(tss.PubKey) - cfg := Config{ - ThresholdModeConfig: &ThresholdModeConfig{ + cfg := config.Config{ + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: int(threshold), - Cosigners: make(CosignersConfig, total), + Cosigners: make(config.CosignersConfig, total), }, } @@ -120,12 +125,12 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi tmpDir := t.TempDir() - thresholdCosigners := make([]*LocalCosigner, threshold) - nonces := make([][]CosignerNonce, threshold) + thresholdCosigners := make([]*cosigner.LocalCosigner, threshold) + nonces := make([][]cosigner.Nonce, threshold) now := time.Now() - hrst := types.HRSTKey{ + hrst := types.HRST{ Height: 1, Round: 0, Step: 2, @@ -138,37 +143,37 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi for i := 0; i < int(total); i++ { id := i + 1 - key := CosignerEd25519Key{ + key := tss.Ed25519Key{ PubKey: pubKey, PrivateShard: privShards[i], ID: id, } - cfg.ThresholdModeConfig.Cosigners[i] = CosignerConfig{ + cfg.ThresholdModeConfig.Cosigners[i] = config.CosignerConfig{ ShardID: id, } cosignerDir := filepath.Join(tmpDir, fmt.Sprintf("cosigner%d", id)) err := os.Mkdir(cosignerDir, 0700) require.NoError(t, err) - - cosigner := NewLocalCosigner( + runtimeconfig := &config.RuntimeConfig{ + HomeDir: cosignerDir, + StateDir: cosignerDir, + Config: cfg, + } + cosigner := cosigner.NewLocalCosigner( log.NewNopLogger(), - &RuntimeConfig{ - HomeDir: cosignerDir, - StateDir: cosignerDir, - Config: cfg, - }, + runtimeconfig, security[i], "", ) keyBz, err := key.MarshalJSON() require.NoError(t, err) - err = os.WriteFile(cosigner.config.KeyFilePathCosigner(testChainID), keyBz, 0600) + err = os.WriteFile(runtimeconfig.KeyFilePathCosigner(testChainID), keyBz, 0600) require.NoError(t, err) - defer cosigner.waitForSignStatesToFlushToDisk() + defer cosigner.WaitForSignStatesToFlushToDisk() err = cosigner.LoadSignStateIfNecessary(testChainID) require.NoError(t, err) @@ -196,8 +201,8 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi sigs := make([]types.PartialSignature, threshold) - for i, cosigner := range thresholdCosigners { - cosignerNonces := make([]CosignerNonce, 0, threshold-1) + for i, localCosigner := range thresholdCosigners { + cosignerNonces := make([]cosigner.Nonce, 0, threshold-1) for j, nonce := range nonces { if i == j { @@ -205,14 +210,14 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi } for _, n := range nonce { - if n.DestinationID == cosigner.GetIndex() { + if n.DestinationID == localCosigner.GetIndex() { cosignerNonces = append(cosignerNonces, n) } } } - sigRes, err := cosigner.SetNoncesAndSign(ctx, CosignerSetNoncesAndSignRequest{ - Nonces: &CosignerUUIDNonces{ + sigRes, err := localCosigner.SetNoncesAndSign(ctx, cosigner.CosignerSetNoncesAndSignRequest{ + Nonces: &cosigner.CosignerUUIDNonces{ UUID: u, Nonces: cosignerNonces, }, @@ -223,7 +228,7 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi require.NoError(t, err) sigs[i] = types.PartialSignature{ - Index: cosigner.GetIndex(), + Index: localCosigner.GetIndex(), Signature: sigRes.Signature, } } diff --git a/src/cosigner/types.go b/src/cosigner/types.go new file mode 100644 index 00000000..519aee99 --- /dev/null +++ b/src/cosigner/types.go @@ -0,0 +1,117 @@ +package cosigner + +import ( + "time" + + "github.com/strangelove-ventures/horcrux/src/types" + + "github.com/google/uuid" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + // "github.com/strangelove-ventures/horcrux/src/proto" +) + +// SignatureRequest is sent to a co-signer to obtain their signature for the SignBytes +// The SignBytes should be a serialized block +type SignatureRequest struct { + ChainID string + SignBytes []byte + UUID uuid.UUID +} + +type SignatureResponse struct { + NoncePublic []byte + Timestamp time.Time + Signature []byte +} + +type Nonce struct { + SourceID int // Client ID + DestinationID int // Server ID + PubKey []byte + Share []byte + Signature []byte +} + +func (secretPart *Nonce) toProto() *proto.Nonce { + return &proto.Nonce{ + SourceID: int32(secretPart.SourceID), + DestinationID: int32(secretPart.DestinationID), + PubKey: secretPart.PubKey, + Share: secretPart.Share, + Signature: secretPart.Signature, + } +} + +// Nonces is a list of CosignerNonce +type Nonces []Nonce + +func (secretParts Nonces) toProto() (out []*proto.Nonce) { + for _, secretPart := range secretParts { + out = append(out, secretPart.toProto()) + } + return +} + +// FromProtoToNonce converts a proto.Nonce to a Nonce +func FromProtoToNonce(secretPart *proto.Nonce) Nonce { + return Nonce{ + SourceID: int(secretPart.SourceID), + DestinationID: int(secretPart.DestinationID), + PubKey: secretPart.PubKey, + Share: secretPart.Share, + Signature: secretPart.Signature, + } +} + +func FromProtoToNonces(secretParts []*proto.Nonce) []Nonce { + out := make([]Nonce, len(secretParts)) + for i, secretPart := range secretParts { + out[i] = FromProtoToNonce(secretPart) + } + return out +} + +type CosignerSignBlockRequest struct { + ChainID string + Block *types.Block +} + +type CosignerSignBlockResponse struct { + Signature []byte +} + +// CosignerUUIDNonces +type CosignerUUIDNonces struct { + UUID uuid.UUID // UUID is the unique identifier of the nonce + Nonces Nonces +} + +func (n *CosignerUUIDNonces) For(id int) *CosignerUUIDNonces { + res := &CosignerUUIDNonces{UUID: n.UUID} + for _, nonce := range n.Nonces { + if nonce.DestinationID == id { + res.Nonces = append(res.Nonces, nonce) + } + } + return res +} + +type CosignerUUIDNoncesMultiple []*CosignerUUIDNonces + +func (n CosignerUUIDNoncesMultiple) ToProto() []*proto.UUIDNonce { + out := make([]*proto.UUIDNonce, len(n)) + for i, nonces := range n { + out[i] = &proto.UUIDNonce{ + Uuid: nonces.UUID[:], + Nonces: nonces.Nonces.toProto(), + } + } + return out +} + +type CosignerSetNoncesAndSignRequest struct { + ChainID string + Nonces *CosignerUUIDNonces + HRST types.HRST + SignBytes []byte +} diff --git a/signer/metrics.go b/src/metrics/metrics.go similarity index 73% rename from signer/metrics.go rename to src/metrics/metrics.go index 2145ba77..c0f09ff0 100644 --- a/signer/metrics.go +++ b/src/metrics/metrics.go @@ -1,4 +1,6 @@ -package signer +package metrics + +// Package metrics provides a set of log metrics and prometheus metrics import ( "sync" @@ -8,6 +10,22 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" ) +/* +/Placeholders for ERRORS +*/ + +type BeyondBlockError struct { + Msg string +} + +func (e *BeyondBlockError) Error() string { return e.Msg } + +type SameBlockError struct { + Msg string +} + +func (e *SameBlockError) Error() string { return e.Msg } + type metricsTimer struct { mu sync.Mutex previousPrecommit, previousPrevote time.Time @@ -60,28 +78,31 @@ func (mt *metricsTimer) UpdatePrometheusMetrics() { defer mt.mu.Unlock() // Update Prometheus Gauges - secondsSinceLastPrecommit.Set(time.Since(mt.previousPrecommit).Seconds()) - secondsSinceLastPrevote.Set(time.Since(mt.previousPrevote).Seconds()) - secondsSinceLastLocalSignStart.Set(time.Since(mt.previousLocalSignStart).Seconds()) - secondsSinceLastLocalSignFinish.Set(time.Since(mt.previousLocalSignFinish).Seconds()) - secondsSinceLastLocalNonceTime.Set(time.Since(mt.previousLocalNonce).Seconds()) + SecondsSinceLastPrecommit.Set(time.Since(mt.previousPrecommit).Seconds()) + SecondsSinceLastPrevote.Set(time.Since(mt.previousPrevote).Seconds()) + SecondsSinceLastLocalSignStart.Set(time.Since(mt.previousLocalSignStart).Seconds()) + SecondsSinceLastLocalSignFinish.Set(time.Since(mt.previousLocalSignFinish).Seconds()) + SecondsSinceLastLocalNonceTime.Set(time.Since(mt.previousLocalNonce).Seconds()) } var ( // Variables to calculate Prometheus Metrics - previousPrecommitHeight = int64(0) - previousPrevoteHeight = int64(0) - metricsTimeKeeper = newMetricsTimer() + PreviousPrecommitHeight = int64(0) + PreviousPrevoteHeight = int64(0) + MetricsTimeKeeper = newMetricsTimer() + + /* + Prometheus Metrics + */ - // Prometheus Metrics - totalPubKeyRequests = promauto.NewCounterVec( + TotalPubKeyRequests = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_pubkey_requests", Help: "Total times public key requested (High count may indicate validator restarts)", }, []string{"chain_id"}, ) - lastPrecommitHeight = promauto.NewGaugeVec( + LastPrecommitHeight = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_last_precommit_height", Help: "Last Height Precommit Signed", @@ -89,7 +110,7 @@ var ( []string{"chain_id"}, ) - lastPrevoteHeight = promauto.NewGaugeVec( + LastPrevoteHeight = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_last_prevote_height", Help: "Last Height Prevote Signed", @@ -97,28 +118,28 @@ var ( []string{"chain_id"}, ) - lastProposalHeight = promauto.NewGaugeVec( + LastProposalHeight = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_last_proposal_height", Help: "Last Height Proposal Signed", }, []string{"chain_id"}, ) - lastPrecommitRound = promauto.NewGaugeVec( + LastPrecommitRound = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_last_precommit_round", Help: "Last Round Precommit Signed", }, []string{"chain_id"}, ) - lastPrevoteRound = promauto.NewGaugeVec( + LastPrevoteRound = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_last_prevote_round", Help: "Last Round Prevote Signed", }, []string{"chain_id"}, ) - lastProposalRound = promauto.NewGaugeVec( + LastProposalRound = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_last_proposal_round", Help: "Last Round Proposal Signed", @@ -126,21 +147,21 @@ var ( []string{"chain_id"}, ) - totalPrecommitsSigned = promauto.NewCounterVec( + TotalPrecommitsSigned = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_precommits_signed", Help: "Total Precommit Signed", }, []string{"chain_id"}, ) - totalPrevotesSigned = promauto.NewCounterVec( + TotalPrevotesSigned = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_prevotes_signed", Help: "Total Prevote Signed", }, []string{"chain_id"}, ) - totalProposalsSigned = promauto.NewCounterVec( + TotalProposalsSigned = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_proposals_signed", Help: "Total Proposal Signed", @@ -148,51 +169,51 @@ var ( []string{"chain_id"}, ) - secondsSinceLastPrecommit = promauto.NewGauge(prometheus.GaugeOpts{ + SecondsSinceLastPrecommit = promauto.NewGauge(prometheus.GaugeOpts{ Name: "signer_seconds_since_last_precommit", Help: "Seconds Since Last Precommit (Useful for Signing Co-Signer Node, Single Signer)", }) - secondsSinceLastPrevote = promauto.NewGauge(prometheus.GaugeOpts{ + SecondsSinceLastPrevote = promauto.NewGauge(prometheus.GaugeOpts{ Name: "signer_seconds_since_last_prevote", Help: "Seconds Since Last Prevote (Useful for Signing Co-Signer Node, Single Signer)", }) - secondsSinceLastLocalSignStart = promauto.NewGauge(prometheus.GaugeOpts{ + SecondsSinceLastLocalSignStart = promauto.NewGauge(prometheus.GaugeOpts{ Name: "signer_seconds_since_last_local_sign_start_time", Help: "Seconds Since Last Local Start Sign (May increase beyond block time, Rarely important) ", }) - secondsSinceLastLocalSignFinish = promauto.NewGauge(prometheus.GaugeOpts{ + SecondsSinceLastLocalSignFinish = promauto.NewGauge(prometheus.GaugeOpts{ Name: "signer_seconds_since_last_local_sign_finish_time", Help: "Seconds Since Last Local Finish Sign (Should stay below 2 * Block Time)", }) - secondsSinceLastLocalNonceTime = promauto.NewGauge(prometheus.GaugeOpts{ + SecondsSinceLastLocalNonceTime = promauto.NewGauge(prometheus.GaugeOpts{ Name: "signer_seconds_since_last_local_ephemeral_share_time", Help: "Seconds Since Last Local Ephemeral Share Sign " + "(Should not increase beyond block time; If high, may indicate raft joining issue for CoSigner) ", }) - missedPrecommits = promauto.NewGaugeVec( + MissedPrecommits = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_missed_precommits", Help: "Consecutive Precommit Missed", }, []string{"chain_id"}, ) - missedPrevotes = promauto.NewGaugeVec( + MissedPrevotes = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_missed_prevotes", Help: "Consecutive Prevote Missed", }, []string{"chain_id"}, ) - totalMissedPrecommits = promauto.NewCounterVec( + TotalMissedPrecommits = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_missed_precommits", Help: "Total Precommit Missed", }, []string{"chain_id"}, ) - totalMissedPrevotes = promauto.NewCounterVec( + TotalMissedPrevotes = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_missed_prevotes", Help: "Total Prevote Missed", @@ -200,41 +221,41 @@ var ( []string{"chain_id"}, ) - missedNonces = promauto.NewGaugeVec( + MissedNonces = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_missed_ephemeral_shares", Help: "Consecutive Threshold Signature Parts Missed", }, []string{"peerid"}, ) - totalMissedNonces = promauto.NewCounterVec( + TotalMissedNonces = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_missed_ephemeral_shares", Help: "Total Threshold Signature Parts Missed", }, []string{"peerid"}, ) - drainedNonceCache = promauto.NewGauge( + DrainedNonceCache = promauto.NewGauge( prometheus.GaugeOpts{ Name: "signer_drained_nonce_cache", Help: "Consecutive Nonces Requested When Cache is Drained", }, ) - totalDrainedNonceCache = promauto.NewCounter( + TotalDrainedNonceCache = promauto.NewCounter( prometheus.CounterOpts{ Name: "signer_total_drained_nonce_cache", Help: "Total Nonces Requested When Cache is Drained", }, ) - sentryConnectTries = promauto.NewGaugeVec( + SentryConnectTries = promauto.NewGaugeVec( prometheus.GaugeOpts{ Name: "signer_sentry_connect_tries", Help: "Consecutive Number of times sentry TCP connect has been tried (High count may indicate validator restarts)", }, []string{"node"}, ) - totalSentryConnectTries = promauto.NewCounterVec( + TotalSentryConnectTries = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_sentry_connect_tries", Help: "Total Number of times sentry TCP connect has been tried (High count may indicate validator restarts)", @@ -242,14 +263,14 @@ var ( []string{"node"}, ) - beyondBlockErrors = promauto.NewCounterVec( + BeyondBlockErrors = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_beyond_block_errors", Help: "Total Times Signing Started but duplicate height/round request arrives", }, []string{"chain_id"}, ) - failedSignVote = promauto.NewCounterVec( + FailedSignVote = promauto.NewCounterVec( prometheus.CounterOpts{ Name: "signer_total_failed_sign_vote", Help: "Total Times Signer Failed to sign block - Unstarted and Unexepcted Height", @@ -257,47 +278,47 @@ var ( []string{"chain_id"}, ) - totalRaftLeader = promauto.NewCounter(prometheus.CounterOpts{ + TotalRaftLeader = promauto.NewCounter(prometheus.CounterOpts{ Name: "signer_total_raft_leader", Help: "Total Times Signer is Raft Leader", }) - totalNotRaftLeader = promauto.NewCounter(prometheus.CounterOpts{ + TotalNotRaftLeader = promauto.NewCounter(prometheus.CounterOpts{ Name: "signer_total_raft_not_leader", Help: "Total Times Signer is NOT Raft Leader (Proxy signing to Raft Leader)", }) - totalRaftLeaderElectionTimeout = promauto.NewCounter(prometheus.CounterOpts{ + TotalRaftLeaderElectionTimeout = promauto.NewCounter(prometheus.CounterOpts{ Name: "signer_total_raft_leader_election_timeout", Help: "Total Times Raft Leader Failed Election (Lacking Peers)", }) - totalInvalidSignature = promauto.NewCounter(prometheus.CounterOpts{ + TotalInvalidSignature = promauto.NewCounter(prometheus.CounterOpts{ Name: "signer_error_total_invalid_signatures", Help: "Total Times Combined Signature is Invalid", }) - totalInsufficientCosigners = promauto.NewCounter(prometheus.CounterOpts{ + TotalInsufficientCosigners = promauto.NewCounter(prometheus.CounterOpts{ Name: "signer_error_total_insufficient_cosigners", Help: "Total Times Cosigners doesn't reach threshold", }) - timedSignBlockThresholdLag = promauto.NewSummary(prometheus.SummaryOpts{ + TimedSignBlockThresholdLag = promauto.NewSummary(prometheus.SummaryOpts{ Name: "signer_sign_block_threshold_lag_seconds", Help: "Seconds taken to get threshold of cosigners available", Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }) - timedSignBlockCosignerLag = promauto.NewSummary(prometheus.SummaryOpts{ + TimedSignBlockCosignerLag = promauto.NewSummary(prometheus.SummaryOpts{ Name: "signer_sign_block_cosigner_lag_seconds", Help: "Seconds taken to get all cosigner signatures", Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }) - timedSignBlockLag = promauto.NewSummary(prometheus.SummaryOpts{ + TimedSignBlockLag = promauto.NewSummary(prometheus.SummaryOpts{ Name: "signer_sign_block_lag_seconds", Help: "Seconds taken to sign block", Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }) - timedCosignerNonceLag = promauto.NewSummaryVec( + TimedCosignerNonceLag = promauto.NewSummaryVec( prometheus.SummaryOpts{ Name: "signer_cosigner_ephemeral_share_lag_seconds", Help: "Time taken to get cosigner ephemeral share", @@ -305,7 +326,7 @@ var ( }, []string{"peerid"}, ) - timedCosignerSignLag = promauto.NewSummaryVec( + TimedCosignerSignLag = promauto.NewSummaryVec( prometheus.SummaryOpts{ Name: "signer_cosigner_sign_lag_seconds", Help: "Time taken to get cosigner signature", @@ -318,7 +339,7 @@ var ( func StartMetrics() { // Update elapsed times on an interval basis for { - metricsTimeKeeper.UpdatePrometheusMetrics() + MetricsTimeKeeper.UpdatePrometheusMetrics() // Prometheus often only polls every 1 to every few seconds // Frequent updates minimize reporting error. diff --git a/signer/multiresolver/multi.go b/src/multiresolver/multi.go similarity index 100% rename from signer/multiresolver/multi.go rename to src/multiresolver/multi.go diff --git a/signer/multiresolver/multi_test.go b/src/multiresolver/multi_test.go similarity index 83% rename from signer/multiresolver/multi_test.go rename to src/multiresolver/multi_test.go index 9baa80ca..4b72c6f3 100644 --- a/signer/multiresolver/multi_test.go +++ b/src/multiresolver/multi_test.go @@ -10,9 +10,12 @@ import ( "time" grpcretry "github.com/grpc-ecosystem/go-grpc-middleware/retry" - "github.com/strangelove-ventures/horcrux/signer" - "github.com/strangelove-ventures/horcrux/signer/multiresolver" - "github.com/strangelove-ventures/horcrux/signer/proto" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + "github.com/strangelove-ventures/horcrux/src/cosigner" + "github.com/strangelove-ventures/horcrux/src/multiresolver" + "github.com/strangelove-ventures/horcrux/src/node" + + // "github.com/strangelove-ventures/horcrux/src/proto" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -26,7 +29,7 @@ func createListener(nodeID string, homedir string) (string, func(), error) { port := strconv.Itoa(sock.Addr().(*net.TCPAddr).Port) - s := signer.NewRaftStore( + s := node.NewRaftStore( nodeID, homedir, "127.0.0.1:"+port, @@ -39,7 +42,7 @@ func createListener(nodeID string, homedir string) (string, func(), error) { } grpcServer := grpc.NewServer() - proto.RegisterCosignerServer(grpcServer, signer.NewCosignerGRPCServer(nil, nil, s)) + proto.RegisterCosignerServer(grpcServer, cosigner.NewCosignerServer(nil)) transportManager.Register(grpcServer) go func() { @@ -90,7 +93,7 @@ func TestMultiResolver(t *testing.T) { ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) defer cancelFunc() - grpcClient := proto.NewCosignerClient(connDNS) + grpcClient := proto.NewNodeServiceClient(connDNS) _, err = grpcClient.GetLeader(ctx, &proto.GetLeaderRequest{}) require.NoError(t, err) @@ -103,7 +106,7 @@ func TestMultiResolver(t *testing.T) { require.NoError(t, err) defer connIP.Close() - grpcClient = proto.NewCosignerClient(connIP) + grpcClient = proto.NewNodeServiceClient(connIP) _, err = grpcClient.GetLeader(ctx, &proto.GetLeaderRequest{}) require.NoError(t, err) } diff --git a/src/node/chain-1_priv_validator_state.json b/src/node/chain-1_priv_validator_state.json new file mode 100644 index 00000000..3dcf53f0 --- /dev/null +++ b/src/node/chain-1_priv_validator_state.json @@ -0,0 +1,9 @@ +{ + "height": "1", + "round": "20", + "step": 1, + "nonce_public": null, + "signature": "GNwkS4b0VQPhqkXYsROeorGBnLwMLKtivZJS6H68CxWgujYQU8nIFF81Xj+MwLum5i9MKEvLEtqek9SBCtbmCQ==", + "signbytes": "2A0820110100000000000000191400000000000000320B088092B8C398FEFFFFFF013A07636861696E2D31", + "FilePath": "chain-1_priv_validator_state.json" +} \ No newline at end of file diff --git a/signer/cosigner_health.go b/src/node/cosigner_health.go similarity index 69% rename from signer/cosigner_health.go rename to src/node/cosigner_health.go index 3c50e09b..8e505a8b 100644 --- a/signer/cosigner_health.go +++ b/src/node/cosigner_health.go @@ -1,4 +1,4 @@ -package signer +package node import ( "context" @@ -6,8 +6,11 @@ import ( "sync" "time" + "github.com/strangelove-ventures/horcrux/src/cosigner" + cometlog "github.com/cometbft/cometbft/libs/log" - "github.com/strangelove-ventures/horcrux/signer/proto" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + // "github.com/strangelove-ventures/horcrux/src/proto" ) const ( @@ -16,14 +19,14 @@ const ( type CosignerHealth struct { logger cometlog.Logger - cosigners []Cosigner + cosigners []ICosigner rtt map[int]int64 mu sync.RWMutex - leader Leader + leader ILeader } -func NewCosignerHealth(logger cometlog.Logger, cosigners []Cosigner, leader Leader) *CosignerHealth { +func NewCosignerHealth(logger cometlog.Logger, cosigners []ICosigner, leader ILeader) *CosignerHealth { return &CosignerHealth{ logger: logger, cosigners: cosigners, @@ -38,8 +41,8 @@ func (ch *CosignerHealth) Reconcile(ctx context.Context) { } var wg sync.WaitGroup wg.Add(len(ch.cosigners)) - for _, cosigner := range ch.cosigners { - if rc, ok := cosigner.(*RemoteCosigner); ok { + for _, remotecosigner := range ch.cosigners { + if rc, ok := remotecosigner.(*cosigner.CosignerClient); ok { go ch.updateRTT(ctx, rc, &wg) } } @@ -59,13 +62,13 @@ func (ch *CosignerHealth) Start(ctx context.Context) { } } -func (ch *CosignerHealth) MarkUnhealthy(cosigner Cosigner) { +func (ch *CosignerHealth) MarkUnhealthy(cosigner ICosigner) { ch.mu.Lock() defer ch.mu.Unlock() ch.rtt[cosigner.GetIndex()] = -1 } -func (ch *CosignerHealth) updateRTT(ctx context.Context, cosigner *RemoteCosigner, wg *sync.WaitGroup) { +func (ch *CosignerHealth) updateRTT(ctx context.Context, cosigner *cosigner.CosignerClient, wg *sync.WaitGroup) { defer wg.Done() rtt := int64(-1) @@ -78,7 +81,7 @@ func (ch *CosignerHealth) updateRTT(ctx context.Context, cosigner *RemoteCosigne ctx, cancel := context.WithTimeout(ctx, 1*time.Second) defer cancel() - _, err := cosigner.client.Ping(ctx, &proto.PingRequest{}) + _, err := cosigner.Client.Ping(ctx, &proto.PingRequest{}) if err != nil { ch.logger.Error("Failed to ping", "cosigner", cosigner.GetIndex(), "error", err) return @@ -86,11 +89,11 @@ func (ch *CosignerHealth) updateRTT(ctx context.Context, cosigner *RemoteCosigne rtt = time.Since(start).Nanoseconds() } -func (ch *CosignerHealth) GetFastest() []Cosigner { +func (ch *CosignerHealth) GetFastest() []ICosigner { ch.mu.RLock() defer ch.mu.RUnlock() - fastest := make([]Cosigner, len(ch.cosigners)) + fastest := make([]ICosigner, len(ch.cosigners)) copy(fastest, ch.cosigners) sort.Slice(fastest, func(i, j int) bool { diff --git a/src/node/cosigner_health_test.go b/src/node/cosigner_health_test.go new file mode 100644 index 00000000..5d72fd72 --- /dev/null +++ b/src/node/cosigner_health_test.go @@ -0,0 +1,76 @@ +package node + +import ( + "os" + "sync" + "testing" + + cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/strangelove-ventures/horcrux/src/cosigner" + "github.com/strangelove-ventures/horcrux/src/types" + "github.com/stretchr/testify/require" +) + +var _ ILeader = (*MockLeader)(nil) + +type MockThresholdValidator struct { + myCosigner *cosigner.LocalCosigner +} + +type MockLeader struct { + id int + + mu sync.Mutex + leader *MockThresholdValidator +} + +func (m *MockLeader) IsLeader() bool { + m.mu.Lock() + defer m.mu.Unlock() + return m.leader != nil && m.leader.myCosigner.GetIndex() == m.id +} + +func (m *MockLeader) SetLeader(tv *MockThresholdValidator) { + m.mu.Lock() + defer m.mu.Unlock() + m.leader = tv +} + +func (m *MockLeader) GetLeader() int { + return m.id +} + +func (m *MockLeader) ShareSigned(_ types.ChainSignStateConsensus) error { + return nil +} + +func TestCosignerHealth(t *testing.T) { + + cosigner2 := cosigner.InitCosignerClient(2, "", nil) + cosigner3 := cosigner.InitCosignerClient(3, "", nil) + cosigner4 := cosigner.InitCosignerClient(4, "", nil) + cosigner5 := cosigner.InitCosignerClient(5, "", nil) + + var cosigners []ICosigner + cosigners = append(cosigners, cosigner2, cosigner3, cosigner4, cosigner5) + + ch := NewCosignerHealth( + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), + cosigners, + &MockLeader{id: 1}, + ) + + ch.rtt = map[int]int64{ + 2: 200, + 3: -1, + 4: 100, + 5: 300, + } + + fastest := ch.GetFastest() + + require.Len(t, fastest, 4) + + require.Equal(t, 4, fastest[0].GetIndex()) + require.Equal(t, 2, fastest[1].GetIndex()) +} diff --git a/signer/cosigner_nonce_cache.go b/src/node/cosigner_nonce_cache.go similarity index 77% rename from signer/cosigner_nonce_cache.go rename to src/node/cosigner_nonce_cache.go index 78edcf42..c531213e 100644 --- a/signer/cosigner_nonce_cache.go +++ b/src/node/cosigner_nonce_cache.go @@ -1,28 +1,36 @@ -package signer +package node import ( "context" "fmt" "sync" + "sync/atomic" "time" + "github.com/strangelove-ventures/horcrux/src/cosigner" + + "github.com/strangelove-ventures/horcrux/src/metrics" + cometlog "github.com/cometbft/cometbft/libs/log" "github.com/google/uuid" ) +/* const ( + defaultGetNoncesInterval = 3 * time.Second defaultGetNoncesTimeout = 4 * time.Second defaultNonceExpiration = 10 * time.Second // half of the local cosigner cache expiration -) +) +*/ type CosignerNonceCache struct { logger cometlog.Logger - cosigners []Cosigner + cosigners []ICosigner // RemoteCosigner? - leader Leader + leader ILeader - lastReconcileNonces lastCount + lastReconcileNonces atomic.Uint64 lastReconcileTime time.Time getNoncesInterval time.Duration @@ -31,7 +39,7 @@ type CosignerNonceCache struct { threshold uint8 - cache NonceCache + cache *NonceCache pruner NonceCachePruner @@ -86,29 +94,6 @@ func (m *movingAverage) average() float64 { return weightedSum / duration } -type lastCount struct { - count int - mu sync.RWMutex -} - -func (lc *lastCount) Set(n int) { - lc.mu.Lock() - defer lc.mu.Unlock() - lc.count = n -} - -func (lc *lastCount) Inc() { - lc.mu.Lock() - defer lc.mu.Unlock() - lc.count++ -} - -func (lc *lastCount) Get() int { - lc.mu.RLock() - defer lc.mu.RUnlock() - return lc.count -} - type NonceCachePruner interface { PruneNonces() int } @@ -134,14 +119,38 @@ func (nc *NonceCache) Delete(index int) { nc.cache = append(nc.cache[:index], nc.cache[index+1:]...) } +func (nc *NonceCache) PruneNonces() int { + nc.mu.Lock() + defer nc.mu.Unlock() + nonExpiredIndex := -1 + for i := 0; i < len(nc.cache); i++ { + if time.Now().Before(nc.cache[i].Expiration) { + nonExpiredIndex = i + break + } + } + + var deleteCount int + if nonExpiredIndex == -1 { + // No non-expired nonces, delete everything + deleteCount = len(nc.cache) + nc.cache = nil + } else { + // Prune everything up to the non-expired nonce + deleteCount = nonExpiredIndex + nc.cache = nc.cache[nonExpiredIndex:] + } + return deleteCount +} + type CosignerNoncesRel struct { - Cosigner Cosigner - Nonces CosignerNonces + Cosigner ICosigner + Nonces cosigner.Nonces } type CachedNonceSingle struct { - Cosigner Cosigner - Nonces CosignerUUIDNoncesMultiple + Cosigner ICosigner + Nonces cosigner.CosignerUUIDNoncesMultiple } type CachedNonce struct { @@ -157,8 +166,8 @@ type CachedNonce struct { func NewCosignerNonceCache( logger cometlog.Logger, - cosigners []Cosigner, - leader Leader, + cosigners []ICosigner, + leader ILeader, getNoncesInterval time.Duration, getNoncesTimeout time.Duration, nonceExpiration time.Duration, @@ -174,12 +183,14 @@ func NewCosignerNonceCache( nonceExpiration: nonceExpiration, threshold: threshold, pruner: pruner, - empty: make(chan struct{}, 1), - movingAverage: newMovingAverage(4 * getNoncesInterval), // weighted average over 4 intervals + cache: new(NonceCache), + // buffer up to 1000 empty events so that we don't ever block + empty: make(chan struct{}, 1000), + movingAverage: newMovingAverage(4 * getNoncesInterval), // weighted average over 4 intervals } // the only time pruner is expected to be non-nil is during tests, otherwise we use the cache logic. if pruner == nil { - cnc.pruner = cnc + cnc.pruner = cnc.cache } return cnc @@ -211,9 +222,9 @@ func (cnc *CosignerNonceCache) reconcile(ctx context.Context) { remainingNonces := cnc.cache.Size() timeSinceLastReconcile := time.Since(cnc.lastReconcileTime) - lastReconcileNonces := cnc.lastReconcileNonces.Get() + lastReconcileNonces := cnc.lastReconcileNonces.Load() // calculate nonces per minute - noncesPerMin := float64(lastReconcileNonces-remainingNonces-pruned) / timeSinceLastReconcile.Minutes() + noncesPerMin := float64(int(lastReconcileNonces)-remainingNonces-pruned) / timeSinceLastReconcile.Minutes() if noncesPerMin < 0 { noncesPerMin = 0 } @@ -230,7 +241,7 @@ func (cnc *CosignerNonceCache) reconcile(ctx context.Context) { additional := t - remainingNonces defer func() { - cnc.lastReconcileNonces.Set(remainingNonces + additional) + cnc.lastReconcileNonces.Store(uint64(remainingNonces + additional)) cnc.lastReconcileTime = time.Now() }() @@ -282,15 +293,15 @@ func (cnc *CosignerNonceCache) LoadN(ctx context.Context, n int) { n, err := p.GetNonces(ctx, uuids) if err != nil { // Significant missing shares may lead to signature failure - missedNonces.WithLabelValues(p.GetAddress()).Add(float64(1)) - totalMissedNonces.WithLabelValues(p.GetAddress()).Inc() + metrics.MissedNonces.WithLabelValues(p.GetAddress()).Add(float64(1)) + metrics.TotalMissedNonces.WithLabelValues(p.GetAddress()).Inc() cnc.logger.Error("Failed to get nonces from peer", "peer", p.GetIndex(), "error", err) return } - missedNonces.WithLabelValues(p.GetAddress()).Set(0) - timedCosignerNonceLag.WithLabelValues(p.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) + metrics.MissedNonces.WithLabelValues(p.GetAddress()).Set(0) + metrics.TimedCosignerNonceLag.WithLabelValues(p.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) nonces[i] = &CachedNonceSingle{ Cosigner: p, @@ -325,28 +336,32 @@ func (cnc *CosignerNonceCache) LoadN(ctx context.Context, n int) { } func (cnc *CosignerNonceCache) Start(ctx context.Context) { - cnc.lastReconcileNonces.Set(cnc.cache.Size()) + cnc.lastReconcileNonces.Store(uint64(cnc.cache.Size())) cnc.lastReconcileTime = time.Now() - ticker := time.NewTicker(cnc.getNoncesInterval) + ticker := time.NewTimer(cnc.getNoncesInterval) for { select { case <-ctx.Done(): return case <-ticker.C: - cnc.reconcile(ctx) case <-cnc.empty: - cnc.reconcile(ctx) + // clear out channel + for len(cnc.empty) > 0 { + <-cnc.empty + } } + cnc.reconcile(ctx) + ticker.Reset(cnc.getNoncesInterval) } } -func (cnc *CosignerNonceCache) GetNonces(fastestPeers []Cosigner) (*CosignerUUIDNonces, error) { +func (cnc *CosignerNonceCache) GetNonces(fastestPeers []ICosigner) (*cosigner.CosignerUUIDNonces, error) { cnc.cache.mu.Lock() defer cnc.cache.mu.Unlock() CheckNoncesLoop: for i, cn := range cnc.cache.cache { - var nonces CosignerNonces + var nonces cosigner.Nonces for _, p := range fastestPeers { found := false for _, n := range cn.Nonces { @@ -365,20 +380,20 @@ CheckNoncesLoop: // remove this set of nonces from the cache cnc.cache.Delete(i) - if len(cnc.cache.cache) == 0 { + if len(cnc.cache.cache) == 0 && len(cnc.empty) == 0 { cnc.logger.Debug("Nonce cache is empty, triggering reload") cnc.empty <- struct{}{} } // all peers found - return &CosignerUUIDNonces{ + return &cosigner.CosignerUUIDNonces{ UUID: cn.UUID, Nonces: nonces, }, nil } // increment so it's taken into account in the nonce burn rate in the next reconciliation - cnc.lastReconcileNonces.Inc() + cnc.lastReconcileNonces.Add(1) // no nonces found cosignerInts := make([]int, len(fastestPeers)) @@ -388,29 +403,7 @@ CheckNoncesLoop: return nil, fmt.Errorf("no nonces found involving cosigners %+v", cosignerInts) } -func (cnc *CosignerNonceCache) PruneNonces() int { - cnc.cache.mu.Lock() - defer cnc.cache.mu.Unlock() - nonExpiredIndex := len(cnc.cache.cache) - 1 - for i := len(cnc.cache.cache) - 1; i >= 0; i-- { - if time.Now().Before(cnc.cache.cache[i].Expiration) { - nonExpiredIndex = i - break - } - if i == 0 { - deleteCount := len(cnc.cache.cache) - cnc.cache.cache = nil - return deleteCount - } - } - deleteCount := len(cnc.cache.cache) - nonExpiredIndex - 1 - if nonExpiredIndex != len(cnc.cache.cache)-1 { - cnc.cache.cache = cnc.cache.cache[:nonExpiredIndex+1] - } - return deleteCount -} - -func (cnc *CosignerNonceCache) ClearNonces(cosigner Cosigner) { +func (cnc *CosignerNonceCache) ClearNonces(cosigner ICosigner) { cnc.cache.mu.Lock() defer cnc.cache.mu.Unlock() for i := 0; i < len(cnc.cache.cache); i++ { diff --git a/src/node/cosigner_nonce_cache_test_go.txt b/src/node/cosigner_nonce_cache_test_go.txt new file mode 100644 index 00000000..9091d84c --- /dev/null +++ b/src/node/cosigner_nonce_cache_test_go.txt @@ -0,0 +1,473 @@ +package signer + +import ( + "context" + "os" + "sync" + "testing" + "time" + + cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/google/uuid" + "github.com/strangelove-ventures/horcrux/pkg/nodes" + "github.com/stretchr/testify/require" +) + +func TestNonceCache(_ *testing.T) { + nc := NonceCache{} + for i := 0; i < 10; i++ { + nc.Add(&CachedNonce{UUID: uuid.New(), Expiration: time.Now().Add(1 * time.Second)}) + } + + nc.Delete(nc.Size() - 1) + nc.Delete(0) +} + +func TestMovingAverage(t *testing.T) { + ma := newMovingAverage(12 * time.Second) + + ma.add(3*time.Second, 500) + require.Len(t, ma.items, 1) + require.Equal(t, float64(500), ma.average()) + + ma.add(3*time.Second, 100) + require.Len(t, ma.items, 2) + require.Equal(t, float64(300), ma.average()) + + ma.add(6*time.Second, 600) + require.Len(t, ma.items, 3) + require.Equal(t, float64(450), ma.average()) + + // should kick out the first one + ma.add(3*time.Second, 500) + require.Len(t, ma.items, 3) + require.Equal(t, float64(450), ma.average()) + + // should kick out the second one + ma.add(6*time.Second, 500) + require.Len(t, ma.items, 3) + require.Equal(t, float64(540), ma.average()) + + for i := 0; i < 5; i++ { + ma.add(2500*time.Millisecond, 1000) + } + + require.Len(t, ma.items, 5) + require.Equal(t, float64(1000), ma.average()) +} + +func TestClearNonces(t *testing.T) { + lcs, _ := getTestLocalCosigners(t, 2, 3) + cosigners := make([]nodes.Cosigner, len(lcs)) + for i, lc := range lcs { + cosigners[i] = lc + } + + cnc := CosignerNonceCache{ + threshold: 2, + cache: new(NonceCache), + } + + for i := 0; i < 10; i++ { + // When deleting nonce for cosigner 1 ([0]), + // these nonce will drop below threshold and be deleted. + cnc.cache.Add(&CachedNonce{ + UUID: uuid.New(), + Expiration: time.Now().Add(1 * time.Second), + Nonces: []CosignerNoncesRel{ + {Cosigner: cosigners[0]}, + {Cosigner: cosigners[1]}, + }, + }) + // When deleting nonce for cosigner 1 ([0]), these nonces will still be above threshold, + // so they will remain without cosigner 1. + cnc.cache.Add(&CachedNonce{ + UUID: uuid.New(), + Expiration: time.Now().Add(1 * time.Second), + Nonces: []CosignerNoncesRel{ + {Cosigner: cosigners[0]}, + {Cosigner: cosigners[1]}, + {Cosigner: cosigners[2]}, + }, + }) + } + + require.Equal(t, 20, cnc.cache.Size()) + + cnc.ClearNonces(cosigners[0]) + + require.Equal(t, 10, cnc.cache.Size()) + + for _, n := range cnc.cache.cache { + require.Len(t, n.Nonces, 2) + oneFound := false + twoFound := false + for _, cnr := range n.Nonces { + if cnr.Cosigner == cosigners[1] { + oneFound = true + } + if cnr.Cosigner == cosigners[2] { + twoFound = true + } + } + require.True(t, oneFound) + require.True(t, twoFound) + } + + cnc.ClearNonces(cosigners[1]) + + require.Equal(t, 0, cnc.cache.Size()) +} + +type mockPruner struct { + cache *NonceCache + count int + pruned int + mu sync.Mutex +} + +func (mp *mockPruner) PruneNonces() int { + pruned := mp.cache.PruneNonces() + mp.mu.Lock() + defer mp.mu.Unlock() + mp.count++ + mp.pruned += pruned + return pruned +} + +func (mp *mockPruner) Result() (int, int) { + mp.mu.Lock() + defer mp.mu.Unlock() + return mp.count, mp.pruned +} + +func TestNonceCacheDemand(t *testing.T) { + lcs, _ := getTestLocalCosigners(t, 2, 3) + cosigners := make([]nodes.Cosigner, len(lcs)) + for i, lc := range lcs { + cosigners[i] = lc + } + + mp := &mockPruner{} + + nonceCache := NewCosignerNonceCache( + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), + cosigners, + &MockLeader{id: 1, leader: &MockThresholdValidator{myCosigner: lcs[0]}}, + 500*time.Millisecond, + 100*time.Millisecond, + defaultNonceExpiration, + 2, + mp, + ) + + mp.cache = nonceCache.cache + + ctx, cancel := context.WithCancel(context.Background()) + + nonceCache.LoadN(ctx, 500) + + go nonceCache.Start(ctx) + + for i := 0; i < 3000; i++ { + _, err := nonceCache.GetNonces([]nodes.Cosigner{cosigners[0], cosigners[1]}) + require.NoError(t, err) + time.Sleep(10 * time.Millisecond) + require.Greater(t, nonceCache.cache.Size(), 0) + } + + size := nonceCache.cache.Size() + + require.Greater(t, size, 0) + + cancel() + + require.LessOrEqual(t, size, nonceCache.target(nonceCache.movingAverage.average())) + + count, pruned := mp.Result() + + require.Greater(t, count, 0, "count of pruning calls must be greater than 0") + require.Equal(t, 0, pruned, "no nonces should have been pruned") +} + +func TestNonceCacheExpiration(t *testing.T) { + lcs, _ := getTestLocalCosigners(t, 2, 3) + cosigners := make([]nodes.Cosigner, len(lcs)) + for i, lc := range lcs { + cosigners[i] = lc + } + + mp := &mockPruner{} + + noncesExpiration := 1000 * time.Millisecond + getNoncesInterval := noncesExpiration / 5 + getNoncesTimeout := 10 * time.Millisecond + nonceCache := NewCosignerNonceCache( + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), + cosigners, + &MockLeader{id: 1, leader: &MockThresholdValidator{myCosigner: lcs[0]}}, + getNoncesInterval, + getNoncesTimeout, + noncesExpiration, + 2, + mp, + ) + + mp.cache = nonceCache.cache + + ctx, cancel := context.WithCancel(context.Background()) + + const loadN = 100 + // Load first set of 100 nonces + nonceCache.LoadN(ctx, loadN) + + go nonceCache.Start(ctx) + + // Sleep for 1/2 nonceExpiration, no nonces should have expired yet + time.Sleep(noncesExpiration / 2) + + // Load second set of 100 nonces + nonceCache.LoadN(ctx, loadN) + + // Wait for first set of nonces to expire + wait for the interval to have run + time.Sleep((noncesExpiration / 2) + getNoncesInterval) + + count, pruned := mp.Result() + + // we should have pruned at least 5 times after + // waiting for 1200ms with a reconcile interval of 200ms + require.GreaterOrEqual(t, count, 5) + + // we should have pruned only the first set of nonces + // The second set of nonces should not have expired yet and we should not have load any more + require.Equal(t, pruned, loadN) + + cancel() + + // the cache should be 100 (loadN) as the second set should not have expired. + require.LessOrEqual(t, nonceCache.cache.Size(), loadN) +} + +func TestNonceCachePrune(t *testing.T) { + type testCase struct { + name string + nonces []*CachedNonce + expected []*CachedNonce + } + + now := time.Now() + + testCases := []testCase{ + { + name: "no nonces", + nonces: nil, + expected: nil, + }, + { + name: "no expired nonces", + nonces: []*CachedNonce{ + { + UUID: uuid.MustParse("d6ef381f-6234-432d-b204-d8957fe60360"), + Expiration: now.Add(1 * time.Second), + }, + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(2 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(3 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + expected: []*CachedNonce{ + { + UUID: uuid.MustParse("d6ef381f-6234-432d-b204-d8957fe60360"), + Expiration: now.Add(1 * time.Second), + }, + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(2 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(3 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + }, + { + name: "first nonce is expired", + nonces: []*CachedNonce{ + { + UUID: uuid.MustParse("d6ef381f-6234-432d-b204-d8957fe60360"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(2 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(3 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + expected: []*CachedNonce{ + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(2 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(3 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + }, + { + name: "all but last nonce expired", + nonces: []*CachedNonce{ + { + UUID: uuid.MustParse("d6ef381f-6234-432d-b204-d8957fe60360"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + expected: []*CachedNonce{ + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(4 * time.Second), + }, + }, + }, + { + name: "all nonces expired", + nonces: []*CachedNonce{ + { + UUID: uuid.MustParse("d6ef381f-6234-432d-b204-d8957fe60360"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("cdc3673d-7946-459a-b458-cbbde0eecd04"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("38c6a201-0b8b-46eb-ab69-c7b2716d408e"), + Expiration: now.Add(-1 * time.Second), + }, + { + UUID: uuid.MustParse("5caf5ab2-d460-430f-87fa-8ed2983ae8fb"), + Expiration: now.Add(-1 * time.Second), + }, + }, + expected: nil, + }, + } + + for _, tc := range testCases { + nc := NonceCache{ + cache: tc.nonces, + } + + pruned := nc.PruneNonces() + + require.Equal(t, len(tc.nonces)-len(tc.expected), pruned, tc.name) + + require.Equal(t, tc.expected, nc.cache, tc.name) + } +} + +func TestNonceCacheDemandSlow(t *testing.T) { + lcs, _ := getTestLocalCosigners(t, 2, 3) + cosigners := make([]nodes.Cosigner, len(lcs)) + for i, lc := range lcs { + cosigners[i] = lc + } + + nonceCache := NewCosignerNonceCache( + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), + cosigners, + &MockLeader{id: 1, leader: &MockThresholdValidator{myCosigner: lcs[0]}}, + 90*time.Millisecond, + 100*time.Millisecond, + 500*time.Millisecond, + 2, + nil, + ) + + ctx, cancel := context.WithCancel(context.Background()) + + go nonceCache.Start(ctx) + + for i := 0; i < 10; i++ { + time.Sleep(200 * time.Millisecond) + require.Greater(t, nonceCache.cache.Size(), 0) + _, err := nonceCache.GetNonces([]nodes.Cosigner{cosigners[0], cosigners[1]}) + require.NoError(t, err) + } + + cancel() + + require.LessOrEqual(t, nonceCache.cache.Size(), nonceCache.target(300)) +} + +func TestNonceCacheDemandSlowDefault(t *testing.T) { + if testing.Short() { + t.Skip() + } + lcs, _ := getTestLocalCosigners(t, 2, 3) + cosigners := make([]nodes.Cosigner, len(lcs)) + for i, lc := range lcs { + cosigners[i] = lc + } + + nonceCache := NewCosignerNonceCache( + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), + cosigners, + &MockLeader{id: 1, leader: &MockThresholdValidator{myCosigner: lcs[0]}}, + defaultGetNoncesInterval, + defaultGetNoncesTimeout, + defaultNonceExpiration, + 2, + nil, + ) + + ctx, cancel := context.WithCancel(context.Background()) + + go nonceCache.Start(ctx) + + for i := 0; i < 10; i++ { + time.Sleep(7 * time.Second) + require.Greater(t, nonceCache.cache.Size(), 0) + _, err := nonceCache.GetNonces([]nodes.Cosigner{cosigners[0], cosigners[1]}) + require.NoError(t, err) + } + + cancel() + + require.LessOrEqual(t, nonceCache.cache.Size(), nonceCache.target(60/7)) +} diff --git a/signer/file.go b/src/node/file.go similarity index 98% rename from signer/file.go rename to src/node/file.go index 840935bb..bc7ccab5 100644 --- a/signer/file.go +++ b/src/node/file.go @@ -1,4 +1,4 @@ -package signer +package node import ( "bytes" @@ -7,7 +7,7 @@ import ( "os" "time" - htypes "github.com/strangelove-ventures/horcrux/pkg/types" + htypes "github.com/strangelove-ventures/horcrux/src/types" "github.com/cosmos/gogoproto/proto" @@ -153,7 +153,7 @@ func GenFilePV(keyFilePath, stateFilePath string) *FilePV { } // If loadState is true, we load from the stateFilePath. Otherwise, we use an empty LastSignState. -func LoadFilePV(keyFilePath, stateFilePath string, loadState bool) (*FilePV, error) { +func LoadPVFile(keyFilePath, stateFilePath string, loadState bool) (*FilePV, error) { keyJSONBytes, err := os.ReadFile(keyFilePath) if err != nil { return nil, err diff --git a/src/node/grpc_client.go b/src/node/grpc_client.go new file mode 100644 index 00000000..074a921d --- /dev/null +++ b/src/node/grpc_client.go @@ -0,0 +1,30 @@ +package node + +import ( + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + // "github.com/strangelove-ventures/horcrux/src/proto" +) + +// var _ Cosigner = &RemoteCosigner{} + +// CosignerClient uses CosignerGRPC to request signing from a remote cosigner +// Remote Cosigner are CLIENTS! to every other cosigner, including the the nodes local cosigner +// It calls the GRPC server of the other cosigner +// TODO: Change name to CosignerClient +type NodeClient struct { + id int + address string + + Client proto.NodeServiceClient // GRPC Client +} + +// Placeholder function because of testing +// TODO: Change name to InitCosignerClient +func InitThresholdClient(id int, address string, client proto.NodeServiceClient) *NodeClient { + nodeclient := &NodeClient{ + id: id, + address: address, // address is the P2P URL of the remote cosigner + Client: client, + } + return nodeclient +} diff --git a/src/node/grpc_server.go b/src/node/grpc_server.go new file mode 100644 index 00000000..f2cb74fe --- /dev/null +++ b/src/node/grpc_server.go @@ -0,0 +1,181 @@ +package node + +import ( + "context" + "fmt" + + "github.com/strangelove-ventures/horcrux/src/types" + + "github.com/hashicorp/raft" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + // "github.com/strangelove-ventures/horcrux/src/proto" +) + +/* +This is the NodeGRPCServer. It is the GRPC server for the node. +*/ + +// TODO: Remove this as we want NodeGRPCServer to NodeGRPCServer +var _ proto.NodeServiceServer = &NodeGRPCServer{} + +// Client is the GRPC client for the node. +// Client implements the proto.NodeClient interface. +type Client struct { + id int // Index of the cosigner + address string // Address to the other nodes + client proto.NodeServiceClient +} + +// NodeGRPCClient is the GRPC client for the node. +// NodeGRPCClient implements the proto.NodeClient interface. +type GRPCClient struct { + // cosigner proto.CosignerClient + Clients []Client +} + +// NodeGRPCServer is the GRPC server for the node. +// NodeGRPCServer implements the proto.NodeServer interface. +type NodeGRPCServer struct { + // We are not allowed to have a cosigner here as it is a circular dependency. + // and cosigner should not be "connected to the node" only to themselves. + // cosigner *cosigner.LocalCosigner //Change to interface + thresholdValidator *ThresholdValidator + raftStore *RaftStore // Our consensus algorithm + // TODO: add logger and not rely on raftStore.logger + + // TODO: Decouple cosignerserver from nodeserver. + // proto.UnimplementedCosignerServer + proto.UnimplementedNodeServiceServer +} + +func NewNodeGRPCServer( + // cosigner *cosigner.LocalCosigner, + thresholdValidator *ThresholdValidator, + raftStore *RaftStore, +) *NodeGRPCServer { + return &NodeGRPCServer{ + // cosigner: cosigner, + thresholdValidator: thresholdValidator, + raftStore: raftStore, + } +} + +// TransferLeadership transfers leadership to another candidate or to the next eligible candidate. +// TransferLeadership implements the proto.NodeServer interface. +func (rpc *NodeGRPCServer) TransferLeadership( + _ context.Context, + req *proto.TransferLeadershipRequest, +) (*proto.TransferLeadershipResponse, error) { + if rpc.raftStore.raft.State() != raft.Leader { + return &proto.TransferLeadershipResponse{}, nil + } + leaderID := req.GetLeaderID() + if leaderID != "" { + // TODO: Change to RPC call of ThresholdValidator + for _, c := range rpc.thresholdValidator.mpc.peerCosigners { + shardIndex := fmt.Sprint(c.GetIndex()) + if shardIndex == leaderID { + raftAddress := p2pURLToRaftAddress(c.GetAddress()) + // TODO: Change to logging + fmt.Printf("Transferring leadership to Index: %s - Address: %s\n", shardIndex, raftAddress) + rpc.raftStore.raft.LeadershipTransferToServer(raft.ServerID(shardIndex), raft.ServerAddress(raftAddress)) + return &proto.TransferLeadershipResponse{LeaderID: shardIndex, LeaderAddress: raftAddress}, nil + } + } + } + fmt.Printf("Transferring leadership to next candidate\n") + rpc.raftStore.raft.LeadershipTransfer() + return &proto.TransferLeadershipResponse{}, nil +} + +// SignBlock imlements the proto.NodeServer interface. +func (rpc *NodeGRPCServer) SignBlock( + ctx context.Context, + req *proto.SignBlockRequest, +) (*proto.SignBlockResponse, error) { + // The local signs the block + + res, _, err := rpc.thresholdValidator.Sign(ctx, req.ChainID, types.BlockFromProto(req.Block)) + if err != nil { + return nil, err + } + return &proto.SignBlockResponse{ + Signature: res, + }, nil +} + +// GetLeader implements the proto.NodeServer interface. +func (rpc *NodeGRPCServer) GetLeader( + context.Context, + *proto.GetLeaderRequest, +) (*proto.GetLeaderResponse, error) { + leader := rpc.raftStore.GetLeader() + return &proto.GetLeaderResponse{Leader: int32(leader)}, nil +} + +/* ALL BELOW THIS LINE IS SHOULD BE MOVED TO COSIGNER +func (rpc *NodeGRPCServer) GetNonces( + ctx context.Context, + req *proto.GetNoncesRequest, + ) (*proto.GetNoncesResponse, error) { + uuids := make([]uuid.UUID, len(req.Uuids)) + for i, uuidBytes := range req.Uuids { + uuids[i] = uuid.UUID(uuidBytes) + } + res, err := rpc.thresholdValidator.MyCosigner.GetNonces( + ctx, + uuids, + ) + if err != nil { + return nil, err + } + + return &proto.GetNoncesResponse{ + Nonces: res.ToProto(), + }, nil + } + + // TODO: Move to cosigner server + func (rpc *NodeGRPCServer) SetNoncesAndSign( + ctx context.Context, + req *proto.SetNoncesAndSignRequest, + ) (*proto.SetNoncesAndSignResponse, error) { + res, err := rpc.thresholdValidator.MyCosigner.SetNoncesAndSign(ctx, cosigner.CosignerSetNoncesAndSignRequest{ + ChainID: req.ChainID, + Nonces: &cosigner.CosignerUUIDNonces{ + UUID: uuid.UUID(req.Uuid), + Nonces: cosigner.FromProtoToNonces(req.GetNonces()), + }, + HRST: types.HRSTFromProto(req.GetHrst()), + SignBytes: req.GetSignBytes(), + }) + if err != nil { + rpc.raftStore.logger.Error( + "Failed to sign with shard", + "chain_id", req.ChainID, + "height", req.Hrst.Height, + "round", req.Hrst.Round, + "step", req.Hrst.Step, + "error", err, + ) + return nil, err + } + rpc.raftStore.logger.Info( + "Signed with shard", + "chain_id", req.ChainID, + "height", req.Hrst.Height, + "round", req.Hrst.Round, + "step", req.Hrst.Step, + ) + return &proto.SetNoncesAndSignResponse{ + NoncePublic: res.NoncePublic, + Timestamp: res.Timestamp.UnixNano(), + Signature: res.Signature, + }, nil +} + +func (rpc *NodeGRPCServer) Ping(context.Context, *proto.PingRequest) (*proto.PingResponse, error) { + return &proto.PingResponse{}, nil +} + +*/ diff --git a/src/node/icosigner.go b/src/node/icosigner.go new file mode 100644 index 00000000..be69e4e9 --- /dev/null +++ b/src/node/icosigner.go @@ -0,0 +1,51 @@ +package node + +import ( + "context" + + "github.com/google/uuid" + + "github.com/strangelove-ventures/horcrux/src/cosigner" +) + +// Cosigner interface is a set of methods for an m-of-n threshold signature. +// This interface abstracts the underlying key storage and management +type iMPC interface { + Pubkey() + Sign() + Start() + Stop() +} + +type ICosigner interface { + // GetIndex gets the index of the cosigner + // The index is the shamir index: 1, 2, etc... + GetIndex() int + + // Get the P2P URL (GRPC) + GetAddress() string + + // Get the combined public key + // TODO: Change name to FetchPubKey + // GetPubKey(chainID string) (cometcrypto.PubKey, error) + + // VerifySignature(chainID string, payload, signature []byte) bool + + // Get nonces for all cosigner shards + GetNonces(ctx context.Context, uuids []uuid.UUID) (cosigner.CosignerUUIDNoncesMultiple, error) + + // Sign the requested bytes + SetNoncesAndSign(ctx context.Context, req cosigner.CosignerSetNoncesAndSignRequest) (*cosigner.SignatureResponse, error) +} + +// TODO: Move to MPC package +type ICosigners []ICosigner // ICosigners is a list of ICosigner's +func (cosigners ICosigners) GetByIndex(id int) ICosigner { + // TODO: Add error handling + for _, cosigner := range cosigners { + if cosigner.GetIndex() == id { + return cosigner + } + } + return nil +} diff --git a/src/node/ileader.go b/src/node/ileader.go new file mode 100644 index 00000000..35481b88 --- /dev/null +++ b/src/node/ileader.go @@ -0,0 +1,22 @@ +package node + +import "github.com/strangelove-ventures/horcrux/src/types" + +// ILeader is an interface for the detecting if the current cosigner is the leader and performing leader actions. +// The leader is responsible for sharing the last signed state with the other cosigners. +// The leader is also responsible for detecting if the node is the leader. +// TODO: The leader is rresponsible for starting the signing process. +// BasciaConsensus Algorithm for the ThresholdValidator. +type ILeader interface { + // IsLeader returns true if the cosigner is the leader otherwise false. + IsLeader() bool + + // ShareSigned shares the last signed state with the other cosigners. + ShareSigned(lss types.ChainSignStateConsensus) error + + // Get current leader + GetLeader() int + + // Sign block + // SignBlock(block types.Block) error +} diff --git a/src/node/leader_mock_test.go b/src/node/leader_mock_test.go new file mode 100644 index 00000000..e7fa2491 --- /dev/null +++ b/src/node/leader_mock_test.go @@ -0,0 +1,41 @@ +package node_test + +import ( + "sync" + + "github.com/strangelove-ventures/horcrux/src/cosigner" + "github.com/strangelove-ventures/horcrux/src/node" + "github.com/strangelove-ventures/horcrux/src/types" +) + +var _ node.ILeader = (*MockLeader)(nil) + +type MockThresholdValidator struct { + myCosigner *cosigner.LocalCosigner +} + +type MockLeader struct { + id int + mu sync.Mutex + leader *node.ThresholdValidator +} + +func (m *MockLeader) IsLeader() bool { + m.mu.Lock() + defer m.mu.Unlock() + return m.leader != nil && m.GetLeader() == m.id +} + +func (m *MockLeader) SetLeader(tv *node.ThresholdValidator) { + m.mu.Lock() + defer m.mu.Unlock() + m.leader = tv +} + +func (m *MockLeader) GetLeader() int { + return m.id +} + +func (m *MockLeader) ShareSigned(_ types.ChainSignStateConsensus) error { + return nil +} diff --git a/signer/raft_events.go b/src/node/raft_events.go similarity index 83% rename from signer/raft_events.go rename to src/node/raft_events.go index d6bed305..8a17489e 100644 --- a/signer/raft_events.go +++ b/src/node/raft_events.go @@ -1,9 +1,9 @@ -package signer +package node import ( "encoding/json" - "github.com/strangelove-ventures/horcrux/pkg/types" + "github.com/strangelove-ventures/horcrux/src/types" ) const ( @@ -40,5 +40,5 @@ func (f *fsm) handleLSSEvent(value string) { return } _ = f.thresholdValidator.SaveLastSignedState(lss.ChainID, lss.SignStateConsensus) - _ = f.cosigner.SaveLastSignedState(lss.ChainID, lss.SignStateConsensus) + _ = f.thresholdValidator.mpc.MyCosigner.SaveLastSignedState(lss.ChainID, lss.SignStateConsensus) } diff --git a/signer/raft_store.go b/src/node/raft_store.go similarity index 89% rename from signer/raft_store.go rename to src/node/raft_store.go index 27e2271a..dce071bd 100644 --- a/signer/raft_store.go +++ b/src/node/raft_store.go @@ -1,11 +1,11 @@ // Provides a simple distributed key-value store. The keys and // associated values are changed via distributed consensus, meaning that the -// values are changed only when a majority of nodes in the cluster agree on +// values are changed only when a majority of cosigner in the cluster agree on // the new value. // // Distributed consensus is provided via the Raft algorithm, specifically the // Hashicorp implementation. -package signer +package node import ( "encoding/json" @@ -19,7 +19,9 @@ import ( "sync" "time" - "github.com/strangelove-ventures/horcrux/pkg/types" + "github.com/strangelove-ventures/horcrux/src/cosigner" + + "github.com/strangelove-ventures/horcrux/src/types" "github.com/Jille/raft-grpc-leader-rpc/leaderhealth" raftgrpctransport "github.com/Jille/raft-grpc-transport" @@ -28,14 +30,15 @@ import ( "github.com/cometbft/cometbft/libs/service" "github.com/hashicorp/raft" boltdb "github.com/hashicorp/raft-boltdb/v2" - "github.com/strangelove-ventures/horcrux/signer/proto" + + // "github.com/strangelove-ventures/horcrux/src/proto" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/reflection" ) -var _ Leader = (*RaftStore)(nil) - const ( retainSnapshotCount = 2 ) @@ -46,7 +49,8 @@ type command struct { Value string `json:"value,omitempty"` } -// Store is a simple key-value store, where all changes are made via Raft consensus. +// RaftStore is a simple key-value store, where all changes are made via Raft consensus. +// RafStore implements ILeader. type RaftStore struct { service.BaseService @@ -54,22 +58,22 @@ type RaftStore struct { RaftDir string RaftBind string RaftTimeout time.Duration - Cosigners []Cosigner + // Cosigners []ICosigner mu sync.Mutex m map[string]string // The key-value store for the system. raft *raft.Raft // The consensus mechanism - logger log.Logger - cosigner *LocalCosigner + logger log.Logger + // mycosigner *cosigner.LocalCosigner thresholdValidator *ThresholdValidator } // New returns a new Store. func NewRaftStore( nodeID string, directory string, bindAddress string, timeout time.Duration, - logger log.Logger, cosigner *LocalCosigner, cosigners []Cosigner) *RaftStore { + logger log.Logger, cosigner *cosigner.LocalCosigner, cosigners []ICosigner) *RaftStore { cosignerRaftStore := &RaftStore{ NodeID: nodeID, RaftDir: directory, @@ -77,18 +81,21 @@ func NewRaftStore( RaftTimeout: timeout, m: make(map[string]string), logger: logger, - cosigner: cosigner, - Cosigners: cosigners, + // mycosigner: cosigner, + // Cosigners: cosigners, } cosignerRaftStore.BaseService = *service.NewBaseService(logger, "CosignerRaftStore", cosignerRaftStore) return cosignerRaftStore } -func (s *RaftStore) SetThresholdValidator(thresholdValidator *ThresholdValidator) { +func (s *RaftStore) SetThresholdValidator(thresholdValidator *ThresholdValidator, mycosigner *cosigner.LocalCosigner) { s.thresholdValidator = thresholdValidator + s.thresholdValidator.mpc.MyCosigner = mycosigner // TODO: Refactor out the use of cosigner. } +// TODO: Should move away from this initilisation method +// and instead use a "service" framework. func (s *RaftStore) init() error { host := p2pURLToRaftAddress(s.RaftBind) _, port, err := net.SplitHostPort(host) @@ -105,7 +112,9 @@ func (s *RaftStore) init() error { return err } grpcServer := grpc.NewServer() - proto.RegisterCosignerServer(grpcServer, NewCosignerGRPCServer(s.cosigner, s.thresholdValidator, s)) + // TODO: RegisterCosignerServer + // proto.RegisterCosignerServer(grpcServer, NewNodeGRPCServer(s.thresholdValidator, s)) + proto.RegisterNodeServiceServer(grpcServer, NewNodeGRPCServer(s.thresholdValidator, s)) transportManager.Register(grpcServer) leaderhealth.Setup(s.raft, grpcServer, []string{"Leader"}) raftadmin.Register(grpcServer, s.raft) @@ -186,7 +195,7 @@ func (s *RaftStore) Open() (*raftgrpctransport.Manager, error) { }, }, } - for _, c := range s.Cosigners { + for _, c := range s.thresholdValidator.mpc.peerCosigners { configuration.Servers = append(configuration.Servers, raft.Server{ ID: raft.ServerID(fmt.Sprint(c.GetIndex())), // TODO: Refactor out the use of cosigner. Address: raft.ServerAddress(p2pURLToRaftAddress(c.GetAddress())), diff --git a/signer/raft_store_test.go b/src/node/raft_store_test.go similarity index 68% rename from signer/raft_store_test.go rename to src/node/raft_store_test.go index c7b051e8..b47bdb88 100644 --- a/signer/raft_store_test.go +++ b/src/node/raft_store_test.go @@ -1,4 +1,4 @@ -package signer +package node_test import ( "crypto/rand" @@ -6,6 +6,12 @@ import ( "testing" "time" + "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/cosigner" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + "github.com/strangelove-ventures/horcrux/src/node" + tss "github.com/strangelove-ventures/horcrux/src/tss" + cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/libs/log" "github.com/ethereum/go-ethereum/crypto/ecies" @@ -13,6 +19,8 @@ import ( "github.com/stretchr/testify/require" ) +var _ node.ILeader = (*node.RaftStore)(nil) + // Test_StoreInMemOpenSingleNode tests that a command can be applied to the log // stored in RAM. func Test_StoreInMemOpenSingleNode(t *testing.T) { @@ -24,17 +32,17 @@ func Test_StoreInMemOpenSingleNode(t *testing.T) { eciesKey, err := ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) require.NoError(t, err) - key := CosignerEd25519Key{ + key := tss.Ed25519Key{ PubKey: dummyPub, PrivateShard: []byte{}, ID: 1, } - cosigner := NewLocalCosigner( + cosigner := cosigner.NewLocalCosigner( log.NewNopLogger(), - &RuntimeConfig{}, - NewCosignerSecurityECIES( - CosignerECIESKey{ + &config.RuntimeConfig{}, + nodesecurity.NewCosignerSecurityECIES( + nodesecurity.CosignerECIESKey{ ID: key.ID, ECIESKey: eciesKey, ECIESPubs: []*ecies.PublicKey{&eciesKey.PublicKey}, @@ -42,16 +50,18 @@ func Test_StoreInMemOpenSingleNode(t *testing.T) { "", ) - s := &RaftStore{ - NodeID: "1", - RaftDir: tmpDir, - RaftBind: "127.0.0.1:0", - RaftTimeout: 1 * time.Second, - m: make(map[string]string), - logger: nil, - cosigner: cosigner, - } + s := node.NewRaftStore("1", tmpDir, "127.0.0.1:0", 1*time.Second, nil, cosigner, nil) + /* + s := &signer.RaftStore{ + NodeID: "1", + RaftDir: tmpDir, + RaftBind: "127.0.0.1:0", + RaftTimeout: 1 * time.Second, + logger: nil, + cosigner: cosigner, + } + */ if _, err := s.Open(); err != nil { t.Fatalf("failed to open store: %s", err) } diff --git a/signer/single_signer_validator.go b/src/node/single_signer_validator.go similarity index 88% rename from signer/single_signer_validator.go rename to src/node/single_signer_validator.go index 52e60b00..5b05e33a 100644 --- a/signer/single_signer_validator.go +++ b/src/node/single_signer_validator.go @@ -1,4 +1,4 @@ -package signer +package node import ( "context" @@ -7,15 +7,15 @@ import ( "sync" "time" - "github.com/strangelove-ventures/horcrux/pkg/types" -) + "github.com/strangelove-ventures/horcrux/src/config" -var _ PrivValidator = &SingleSignerValidator{} + "github.com/strangelove-ventures/horcrux/src/types" +) // SingleSignerValidator guards access to an underlying PrivValidator by using mutexes // for each of the PrivValidator interface functions type SingleSignerValidator struct { - config *RuntimeConfig + config *config.RuntimeConfig chainState sync.Map } @@ -31,7 +31,7 @@ type SingleSignerChainState struct { // NewSingleSignerValidator constructs a validator for single-sign mode (not recommended). // NewThresholdValidator is recommended, but single-sign mode can be used for convenience. -func NewSingleSignerValidator(config *RuntimeConfig) *SingleSignerValidator { +func NewSingleSignerValidator(config *config.RuntimeConfig) *SingleSignerValidator { return &SingleSignerValidator{ config: config, } @@ -81,12 +81,12 @@ func (pv *SingleSignerValidator) loadChainStateIfNecessary(chainID string) (*Sin } // The only scenario in which we want to create a new state file // on disk is when the state file does not exist. - filePV, err = LoadFilePV(keyFile, stateFile, false) + filePV, err = LoadPVFile(keyFile, stateFile, false) if err != nil { return nil, err } } else { - filePV, err = LoadFilePV(keyFile, stateFile, true) + filePV, err = LoadPVFile(keyFile, stateFile, true) if err != nil { return nil, err } diff --git a/signer/single_signer_validator_test.go b/src/node/single_signer_validator_test.go similarity index 83% rename from signer/single_signer_validator_test.go rename to src/node/single_signer_validator_test.go index 81341e28..583da3f8 100644 --- a/signer/single_signer_validator_test.go +++ b/src/node/single_signer_validator_test.go @@ -1,11 +1,14 @@ -package signer +package node_test import ( "context" "path/filepath" "time" - "github.com/strangelove-ventures/horcrux/pkg/types" + "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/connector" + "github.com/strangelove-ventures/horcrux/src/node" + "github.com/strangelove-ventures/horcrux/src/types" "os" "testing" @@ -19,8 +22,16 @@ import ( "github.com/stretchr/testify/require" ) +const ( + testChainID = "chain-1" + testChainID2 = "chain-2" + BitSize = 4096 +) + +var _ connector.IPrivValidator = &node.SingleSignerValidator{} + func TestSingleSignerValidator(t *testing.T) { - t.Skip("TODO: fix this test when run with 'make test'") + // t.Skip("TODO: fix this test when run with 'make test'") tmpDir := t.TempDir() stateDir := filepath.Join(tmpDir, "state") @@ -28,7 +39,7 @@ func TestSingleSignerValidator(t *testing.T) { err := os.MkdirAll(stateDir, 0700) require.NoError(t, err) - runtimeConfig := &RuntimeConfig{ + runtimeConfig := &config.RuntimeConfig{ HomeDir: tmpDir, StateDir: filepath.Join(tmpDir, "state"), } @@ -48,7 +59,7 @@ func TestSingleSignerValidator(t *testing.T) { err = os.WriteFile(runtimeConfig.KeyFilePathSingleSigner("different"), marshaled, 0600) require.NoError(t, err) - validator := NewSingleSignerValidator(runtimeConfig) + validator := node.NewSingleSignerValidator(runtimeConfig) proposal := cometproto.Proposal{ Height: 1, @@ -98,7 +109,7 @@ func TestSingleSignerValidator(t *testing.T) { require.NoError(t, err) // reinitialize validator to make sure new runtime will not allow double sign - validator = NewSingleSignerValidator(runtimeConfig) + validator = node.NewSingleSignerValidator(runtimeConfig) _, _, err = validator.Sign(ctx, testChainID, types.ProposalToBlock(testChainID, &proposal)) require.Error(t, err, "double sign!") diff --git a/signer/testdata/testdata.go b/src/node/testdata/testdata.go similarity index 100% rename from signer/testdata/testdata.go rename to src/node/testdata/testdata.go diff --git a/signer/threshold_validator.go b/src/node/threshold_validator.go similarity index 63% rename from signer/threshold_validator.go rename to src/node/threshold_validator.go index 4ba23ffc..8cc537ab 100644 --- a/signer/threshold_validator.go +++ b/src/node/threshold_validator.go @@ -1,4 +1,4 @@ -package signer +package node import ( "bytes" @@ -6,48 +6,146 @@ import ( "errors" "fmt" "os" + "strings" "sync" "time" - "github.com/strangelove-ventures/horcrux/pkg/types" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + "github.com/strangelove-ventures/horcrux/src/cosigner" + + "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/metrics" + + "github.com/strangelove-ventures/horcrux/src/types" "github.com/cometbft/cometbft/libs/log" cometrpcjsontypes "github.com/cometbft/cometbft/rpc/jsonrpc/types" "github.com/google/uuid" - "github.com/strangelove-ventures/horcrux/signer/proto" "golang.org/x/sync/errgroup" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) -var _ PrivValidator = &ThresholdValidator{} +// TODO: Must be a better way to do this? +type nodecacheconfigs struct { + defaultGetNoncesInterval time.Duration + defaultGetNoncesTimeout time.Duration + defaultNonceExpiration time.Duration +} + +func nodecacheconfig() nodecacheconfigs { + return nodecacheconfigs{ + defaultGetNoncesInterval: config.DefaultGetNoncesInterval, + defaultGetNoncesTimeout: config.DefaultGetNoncesTimeout, + defaultNonceExpiration: config.DefaultNonceExpiration, + } +} + +// ThresholdValidator is the server that responds to sign requests from the "sentry" client +// Implements the [connector.IPrivValidator] interface. +/* +TODO: Move some parts of this to the MPC +*/ + +type MPC struct { + MyCosigner *cosigner.LocalCosigner // TODO Should be an interface as well. + // peer cosigners + peerCosigners ICosigners // "i.e clients to call" + // our own cosigner + + cosignerHealth *CosignerHealth + nonceCache *CosignerNonceCache +} + +func (mpc *MPC) Sign( + ctx context.Context) { +} +func (mpc *MPC) GetClientIndex(index int) ICosigner { + return mpc.peerCosigners[index] +} + +// TODO: This is not called anywhere it seems! +type ThresholdClient struct { + id int + address string + + Client proto.NodeServiceClient // GRPC Client + + nonceCache *CosignerNonceCache +} +func (tc *ThresholdClient) SignBlock( + ctx context.Context, + chainID string, + block types.Block, +) ([]byte, time.Time, error) { + res, err := tc.Client.SignBlock(ctx, &proto.SignBlockRequest{ + ChainID: chainID, + Block: block.ToProto(), + }) + if err != nil { + return nil, time.Time{}, err + } + return res.GetSignature(), block.Timestamp, nil +} + +type ThresholdClients []ThresholdClient + +func (clients ThresholdClients) GetClientIndex(index int) (ThresholdClient, error) { + if index > (len(clients) + 1) { + return ThresholdClient{}, fmt.Errorf("index out of range") + } + return clients[index-1], nil +} + +// ThresholdValidator is the conductor for the threshold signing process. type ThresholdValidator struct { - config *RuntimeConfig + config *config.RuntimeConfig + clients ThresholdClients threshold int - grpcTimeout time.Duration - - chainState sync.Map + grpcTimeout time.Duration // TODO ask if this should move to icosigner? - // our own cosigner - myCosigner *LocalCosigner + // chainSignState is the watermark for sent blocks we have started to process + chainSignState sync.Map // - chainSignState["chainid"] -> types.chainSignState - // peer cosigners - peerCosigners Cosigners + mpc *MPC - leader Leader + leader ILeader // Basically our RAFT implementation logger log.Logger pendingDiskWG sync.WaitGroup maxWaitForSameBlockAttempts int + // TODO: Should separate node and cosigner health +} - cosignerHealth *CosignerHealth +type StillWaitingForBlockError struct { + msg string +} - nonceCache *CosignerNonceCache +func (e *StillWaitingForBlockError) Error() string { return e.msg } + +func newStillWaitingForBlockError(chainID string, hrs types.HRS) *StillWaitingForBlockError { + return &StillWaitingForBlockError{ + msg: fmt.Sprintf("[%s] Still waiting for block %d.%d.%d", + chainID, hrs.Height, hrs.Round, hrs.Step), + } +} + +type SameBlockError struct { + msg string +} + +func (e *SameBlockError) Error() string { return e.msg } + +func newSameBlockError(chainID string, hrs types.HRS) *SameBlockError { + return &SameBlockError{ + msg: fmt.Sprintf("[%s] Same block: %d.%d.%d", + chainID, hrs.Height, hrs.Round, hrs.Step), + } } type ChainSignState struct { @@ -57,62 +155,89 @@ type ChainSignState struct { lastSignStateMutex *sync.Mutex // stores the last sign state that we've started progress on - lastSignStateInitiated *types.SignState + lastInitiatedSignState *types.SignState lastSignStateInitiatedMutex *sync.Mutex } +type BeyondBlockError struct { + msg string +} + +func (e *BeyondBlockError) Error() string { return e.msg } + +func (pv *ThresholdValidator) newBeyondBlockError(chainID string, hrs types.HRS) *BeyondBlockError { + css := pv.mustLoadChainState(chainID) + + lss := css.lastInitiatedSignState + return &BeyondBlockError{ + msg: fmt.Sprintf("[%s] Progress already started on block %d.%d.%d, skipping %d.%d.%d", + chainID, + lss.Height, lss.Round, lss.Step, + hrs.Height, hrs.Round, hrs.Step, + ), + } +} + // NewThresholdValidator creates and returns a new ThresholdValidator func NewThresholdValidator( logger log.Logger, - config *RuntimeConfig, + config *config.RuntimeConfig, threshold int, grpcTimeout time.Duration, maxWaitForSameBlockAttempts int, - myCosigner *LocalCosigner, - peerCosigners []Cosigner, - leader Leader, + myCosigner *cosigner.LocalCosigner, + peerCosigners []ICosigner, + leader ILeader, ) *ThresholdValidator { - allCosigners := make([]Cosigner, len(peerCosigners)+1) + allCosigners := make([]ICosigner, len(peerCosigners)+1) allCosigners[0] = myCosigner copy(allCosigners[1:], peerCosigners) - for _, cosigner := range peerCosigners { - logger.Debug("Peer cosigner", "id", cosigner.GetIndex()) + for _, peer := range peerCosigners { + logger.Debug("Peer peer", "id", peer.GetIndex()) } + nodecacheconfig := nodecacheconfig() nc := NewCosignerNonceCache( logger, allCosigners, leader, - defaultGetNoncesInterval, - defaultGetNoncesTimeout, - defaultNonceExpiration, + nodecacheconfig.defaultGetNoncesInterval, + nodecacheconfig.defaultGetNoncesTimeout, + nodecacheconfig.defaultNonceExpiration, uint8(threshold), nil, ) + fmt.Printf("peerCosigner: %v\nleader: %v\n", peerCosigners, leader) + nch := NewCosignerHealth(logger, peerCosigners, leader) + fmt.Printf("nch: %v\n", nch) return &ThresholdValidator{ logger: logger, config: config, threshold: threshold, grpcTimeout: grpcTimeout, maxWaitForSameBlockAttempts: maxWaitForSameBlockAttempts, - myCosigner: myCosigner, - peerCosigners: peerCosigners, + mpc: &MPC{MyCosigner: myCosigner, peerCosigners: peerCosigners, cosignerHealth: nch, nonceCache: nc}, leader: leader, - cosignerHealth: NewCosignerHealth(logger, peerCosigners, leader), - nonceCache: nc, } } +func (pv *ThresholdValidator) getLeaderClient(index int) (ThresholdClient, error) { + client, err := pv.clients.GetClientIndex(index) + if err != nil { + return client, err + } + return client, nil +} + // Start starts the ThresholdValidator. func (pv *ThresholdValidator) Start(ctx context.Context) error { pv.logger.Info("Starting ThresholdValidator services") - go pv.cosignerHealth.Start(ctx) - - go pv.nonceCache.Start(ctx) - - go pv.myCosigner.StartNoncePruner(ctx) + // TODO: Should be moved to MPC + go pv.mpc.cosignerHealth.Start(ctx) + go pv.mpc.nonceCache.Start(ctx) + go pv.mpc.MyCosigner.StartNoncePruner(ctx) return nil } @@ -130,30 +255,30 @@ func (pv *ThresholdValidator) SaveLastSignedState(chainID string, signState type } func (pv *ThresholdValidator) mustLoadChainState(chainID string) ChainSignState { - cs, ok := pv.chainState.Load(chainID) + cs, ok := pv.chainSignState.Load(chainID) // if !ok { panic(fmt.Errorf("failed to load chain state for %s", chainID)) } css, ok := cs.(ChainSignState) if !ok { - panic(fmt.Errorf("expected: (ChainSignState), actual: (%T)", cs)) + panic(fmt.Errorf("expected: type (ChainSignState), actual type is: (%T)", cs)) } return css } -// SaveLastSignedStateInitiated updates the high watermark height/round/step (HRS) for an initiated +// saveLastSignedStateInitiated updates the high watermark height/round/step (HRS) for an initiated // sign process if it is greater than the current high watermark. A mutex is used to avoid concurrent // state updates. The disk write is scheduled in a separate goroutine which will perform an atomic write. // pendingDiskWG is used upon termination in pendingDiskWG to ensure all writes have completed. -func (pv *ThresholdValidator) SaveLastSignedStateInitiated( +func (pv *ThresholdValidator) saveLastSignedStateInitiated( chainID string, block *types.Block) ([]byte, time.Time, error) { css := pv.mustLoadChainState(chainID) height, round, step := block.Height, block.Round, block.Step - err := css.lastSignStateInitiated.Save(types.NewSignStateConsensus(height, round, step), &pv.pendingDiskWG) + err := css.lastInitiatedSignState.Save(types.NewSignStateConsensus(height, round, step), &pv.pendingDiskWG) if err == nil { // good to sign return nil, time.Time{}, nil @@ -167,7 +292,7 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated( if sameBlockErr == nil { return existingSignature, block.Timestamp, nil } - return nil, existingTimestamp, pv.newBeyondBlockError(chainID, block.HRSKey()) + return nil, existingTimestamp, pv.newBeyondBlockError(chainID, block.GetHRS()) } if sameBlockErr == nil { @@ -201,7 +326,7 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated( css.lastSignState.CondWaitWithTimeout(pv.grpcTimeout) // check if HRS exists in cache now - ssc, ok := css.lastSignState.GetCache(block.HRSKey()) + ssc, ok := css.lastSignState.GetCache(block.GetHRS()) if !ok { pv.logger.Debug( "Block does not yet exist in cache while waiting for signature", @@ -216,7 +341,8 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated( if sameBlockErr == nil { return existingSignature, existingTimestamp, nil } - if _, ok := sameBlockErr.(*StillWaitingForBlockError); !ok { + var stillWaitingForBlockError *StillWaitingForBlockError + if !errors.As(sameBlockErr, &stillWaitingForBlockError) { return nil, existingTimestamp, fmt.Errorf( "same block error in loop, but we are not still waiting for signature: %w", sameBlockErr, @@ -244,7 +370,7 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated( // notifyBlockSignError will alert any waiting goroutines that an error // has occurred during signing and a retry can be attempted. -func (pv *ThresholdValidator) notifyBlockSignError(chainID string, hrs types.HRSKey, signBytes []byte) { +func (pv *ThresholdValidator) notifyBlockSignError(chainID string, hrs types.HRS, signBytes []byte) { css := pv.mustLoadChainState(chainID) css.lastSignState.Lock() // Call alias lock function. @@ -269,76 +395,21 @@ func (pv *ThresholdValidator) Stop() { func (pv *ThresholdValidator) waitForSignStatesToFlushToDisk() { pv.pendingDiskWG.Wait() - pv.myCosigner.waitForSignStatesToFlushToDisk() + pv.mpc.MyCosigner.WaitForSignStatesToFlushToDisk() } // GetPubKey returns the public key of the validator. // Implements PrivValidator. func (pv *ThresholdValidator) GetPubKey(_ context.Context, chainID string) ([]byte, error) { - pubKey, err := pv.myCosigner.GetPubKey(chainID) + pubKey, err := pv.mpc.MyCosigner.GetPubKey(chainID) if err != nil { return nil, err } return pubKey.Bytes(), nil } -func BlockFromProto(block *proto.Block) types.Block { - return types.Block{ - Height: block.Height, - Round: block.Round, - Step: int8(block.Step), - SignBytes: block.SignBytes, - Timestamp: time.Unix(0, block.Timestamp), - } -} - -type BeyondBlockError struct { - msg string -} - -func (e *BeyondBlockError) Error() string { return e.msg } - -func (pv *ThresholdValidator) newBeyondBlockError(chainID string, hrs types.HRSKey) *BeyondBlockError { - css := pv.mustLoadChainState(chainID) - - lss := css.lastSignStateInitiated - return &BeyondBlockError{ - msg: fmt.Sprintf("[%s] Progress already started on block %d.%d.%d, skipping %d.%d.%d", - chainID, - lss.Height, lss.Round, lss.Step, - hrs.Height, hrs.Round, hrs.Step, - ), - } -} - -type StillWaitingForBlockError struct { - msg string -} - -func (e *StillWaitingForBlockError) Error() string { return e.msg } - -func newStillWaitingForBlockError(chainID string, hrs types.HRSKey) *StillWaitingForBlockError { - return &StillWaitingForBlockError{ - msg: fmt.Sprintf("[%s] Still waiting for block %d.%d.%d", - chainID, hrs.Height, hrs.Round, hrs.Step), - } -} - -type SameBlockError struct { - msg string -} - -func (e *SameBlockError) Error() string { return e.msg } - -func newSameBlockError(chainID string, hrs types.HRSKey) *SameBlockError { - return &SameBlockError{ - msg: fmt.Sprintf("[%s] Same block: %d.%d.%d", - chainID, hrs.Height, hrs.Round, hrs.Step), - } -} - func (pv *ThresholdValidator) LoadSignStateIfNecessary(chainID string) error { - if _, ok := pv.chainState.Load(chainID); ok { + if _, ok := pv.chainSignState.Load(chainID); ok { return nil } @@ -347,18 +418,18 @@ func (pv *ThresholdValidator) LoadSignStateIfNecessary(chainID string) error { return err } - lastSignStateInitiated := signState.FreshCache() - lastSignStateInitiated.FilePath = os.DevNull + lastInitiatedSignState := signState.FreshCache() + lastInitiatedSignState.FilePath = os.DevNull - pv.chainState.Store(chainID, ChainSignState{ + pv.chainSignState.Store(chainID, ChainSignState{ lastSignState: signState, - lastSignStateInitiated: lastSignStateInitiated, + lastInitiatedSignState: lastInitiatedSignState, lastSignStateMutex: &sync.Mutex{}, lastSignStateInitiatedMutex: &sync.Mutex{}, }) - return pv.myCosigner.LoadSignStateIfNecessary(chainID) + return pv.mpc.MyCosigner.LoadSignStateIfNecessary(chainID) } // getExistingBlockSignature returns the existing block signature and no error if the signature is valid for the block. @@ -367,14 +438,14 @@ func (pv *ThresholdValidator) LoadSignStateIfNecessary(chainID string) error { func (pv *ThresholdValidator) getExistingBlockSignature(chainID string, block *types.Block) ([]byte, time.Time, error) { css := pv.mustLoadChainState(chainID) - latestBlock, existingSignature := css.lastSignState.GetFromCache(block.HRSKey()) + latestBlock, existingSignature := css.lastSignState.GetFromCache(block.GetHRS()) if existingSignature != nil { // signature exists in cache, so compare against that return pv.compareBlockSignatureAgainstSSC(chainID, block, existingSignature) } // signature does not exist in cache, so compare against latest signed block. - return nil, block.Timestamp, pv.compareBlockSignatureAgainstHRS(chainID, block, latestBlock) + return nil, block.Timestamp, compareBlockSignatureAgainstHRS(pv, chainID, block, latestBlock) } // compareBlockSignatureAgainstSSC compares a block's HRS against a cached signature. @@ -394,8 +465,9 @@ func (pv *ThresholdValidator) compareBlockSignatureAgainstSSC( ) ([]byte, time.Time, error) { stamp, signBytes := block.Timestamp, block.SignBytes - if err := pv.compareBlockSignatureAgainstHRS(chainID, block, existingSignature.HRSKey()); err != nil { - if _, ok := err.(*SameBlockError); !ok { + if err := compareBlockSignatureAgainstHRS(pv, chainID, block, existingSignature.HRSKey()); err != nil { + var sameBlockError *metrics.SameBlockError + if !errors.As(err, &sameBlockError) { return nil, stamp, err } } @@ -417,12 +489,13 @@ func (pv *ThresholdValidator) compareBlockSignatureAgainstSSC( // compareBlockSignatureAgainstHRS returns a BeyondBlockError if the hrs is greater than the // block. It returns a StillWaitingForBlockError if the hrs is less than the block. If returns nil if the hrs is // equal to the block. -func (pv *ThresholdValidator) compareBlockSignatureAgainstHRS( +func compareBlockSignatureAgainstHRS( + pv *ThresholdValidator, chainID string, block *types.Block, - hrs types.HRSKey, + hrs types.HRS, ) error { - blockHRS := block.HRSKey() + blockHRS := block.GetHRS() if hrs.GreaterThan(blockHRS) { return pv.newBeyondBlockError(chainID, blockHRS) @@ -437,11 +510,11 @@ func (pv *ThresholdValidator) compareBlockSignatureAgainstHRS( func (pv *ThresholdValidator) getNoncesFallback( ctx context.Context, -) (*CosignerUUIDNonces, []Cosigner, error) { - nonces := make(map[Cosigner]CosignerNonces) +) (*cosigner.CosignerUUIDNonces, []ICosigner, error) { + nonces := make(map[ICosigner]cosigner.Nonces) - drainedNonceCache.Inc() - totalDrainedNonceCache.Inc() + metrics.DrainedNonceCache.Inc() + metrics.TotalDrainedNonceCache.Inc() var wg sync.WaitGroup wg.Add(pv.threshold) @@ -450,9 +523,9 @@ func (pv *ThresholdValidator) getNoncesFallback( u := uuid.New() - allCosigners := make([]Cosigner, len(pv.peerCosigners)+1) - allCosigners[0] = pv.myCosigner - copy(allCosigners[1:], pv.peerCosigners) + allCosigners := make([]ICosigner, len(pv.mpc.peerCosigners)+1) + allCosigners[0] = pv.mpc.MyCosigner + copy(allCosigners[1:], pv.mpc.peerCosigners) for _, c := range allCosigners { go pv.waitForPeerNonces(ctx, u, c, &wg, nonces, &mu) @@ -464,8 +537,8 @@ func (pv *ThresholdValidator) getNoncesFallback( return nil, nil, errors.New("timed out waiting for ephemeral shares") } - var thresholdNonces CosignerNonces - thresholdCosigners := make([]Cosigner, len(nonces)) + var thresholdNonces cosigner.Nonces + thresholdCosigners := make([]ICosigner, len(nonces)) i := 0 for c, n := range nonces { thresholdCosigners[i] = c @@ -474,7 +547,7 @@ func (pv *ThresholdValidator) getNoncesFallback( thresholdNonces = append(thresholdNonces, n...) } - return &CosignerUUIDNonces{ + return &cosigner.CosignerUUIDNonces{ UUID: u, Nonces: thresholdNonces, }, thresholdCosigners, nil @@ -497,23 +570,23 @@ func waitUntilCompleteOrTimeout(wg *sync.WaitGroup, timeout time.Duration) bool func (pv *ThresholdValidator) waitForPeerNonces( ctx context.Context, u uuid.UUID, - peer Cosigner, + peer ICosigner, wg *sync.WaitGroup, - nonces map[Cosigner]CosignerNonces, + nonces map[ICosigner]cosigner.Nonces, mu sync.Locker, ) { peerStartTime := time.Now() peerNonces, err := peer.GetNonces(ctx, []uuid.UUID{u}) if err != nil { - missedNonces.WithLabelValues(peer.GetAddress()).Inc() - totalMissedNonces.WithLabelValues(peer.GetAddress()).Inc() + metrics.MissedNonces.WithLabelValues(peer.GetAddress()).Inc() + metrics.TotalMissedNonces.WithLabelValues(peer.GetAddress()).Inc() pv.logger.Error("Error getting nonces", "cosigner", peer.GetIndex(), "err", err) return } - missedNonces.WithLabelValues(peer.GetAddress()).Set(0) - timedCosignerNonceLag.WithLabelValues(peer.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) + metrics.MissedNonces.WithLabelValues(peer.GetAddress()).Set(0) + metrics.TimedCosignerNonceLag.WithLabelValues(peer.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) // Check so that wg.Done is not called more than (threshold - 1) times which causes hardlock mu.Lock() @@ -524,6 +597,20 @@ func (pv *ThresholdValidator) waitForPeerNonces( mu.Unlock() } +func (pv *ThresholdValidator) SignBlock( + ctx context.Context, + req *proto.SignBlockRequest, + +) (*proto.SignBlockResponse, error) { + res, _, err := pv.Sign(ctx, req.ChainID, types.BlockFromProto(req.Block)) + if err != nil { + return nil, err + } + return &proto.SignBlockResponse{ + Signature: res, + }, nil +} + func (pv *ThresholdValidator) proxyIfNecessary( ctx context.Context, chainID string, @@ -531,6 +618,7 @@ func (pv *ThresholdValidator) proxyIfNecessary( ) (bool, []byte, time.Time, error) { height, round, step, stamp := block.Height, block.Round, block.Step, block.Timestamp + // Don't proxy if we are the leader if pv.leader.IsLeader() { return false, nil, time.Time{}, nil } @@ -538,17 +626,19 @@ func (pv *ThresholdValidator) proxyIfNecessary( leader := pv.leader.GetLeader() // TODO is there a better way than to poll during leader election? + // NOTE: This is really strange? for i := 0; i < 500 && leader == -1; i++ { time.Sleep(10 * time.Millisecond) leader = pv.leader.GetLeader() } if leader == -1 { - totalRaftLeaderElectionTimeout.Inc() + metrics.TotalRaftLeaderElectionTimeout.Inc() return true, nil, stamp, fmt.Errorf("timed out waiting for raft leader") } - if leader == pv.myCosigner.GetIndex() { + // Don't proxy call if we are the leader + if leader == pv.mpc.MyCosigner.GetIndex() { return false, nil, time.Time{}, nil } @@ -558,30 +648,43 @@ func (pv *ThresholdValidator) proxyIfNecessary( "round", round, "step", step, ) - totalNotRaftLeader.Inc() + metrics.TotalNotRaftLeader.Inc() - cosignerLeader := pv.peerCosigners.GetByID(leader) - if cosignerLeader == nil { - return true, nil, stamp, fmt.Errorf("failed to find cosigner with id %d", leader) + // Get Node by leader index + // TODO: Change so the this is the node. + nodeLeader, err := pv.getLeaderClient(leader) // This is a nodeclient + if err != nil { + return true, nil, stamp, fmt.Errorf("failed to find cosigner (node) with id %d", leader) } - signRes, err := cosignerLeader.(*RemoteCosigner).Sign(ctx, CosignerSignBlockRequest{ - ChainID: chainID, - Block: &block, - }) + // Here we actually sending the request to the node who is leader. Should be + // ProxySign + // nodeLeader.(*cosigner.CosignerClient) + signRes, stamp, err := nodeLeader.SignBlock(ctx, chainID, block) + if err != nil { - if _, ok := err.(*cometrpcjsontypes.RPCError); ok { - rpcErrUnwrapped := err.(*cometrpcjsontypes.RPCError).Data + var RPCError *cometrpcjsontypes.RPCError + if errors.As(err, &RPCError) { + var RPCError *cometrpcjsontypes.RPCError + errors.As(err, &RPCError) + rpcErrUnwrapped := RPCError.Data // Need to return BeyondBlockError after proxy since the error type will be lost over RPC if len(rpcErrUnwrapped) > 33 && rpcErrUnwrapped[:33] == "Progress already started on block" { - return true, nil, stamp, &BeyondBlockError{msg: rpcErrUnwrapped} + return true, nil, stamp, &metrics.BeyondBlockError{Msg: rpcErrUnwrapped} } } return true, nil, stamp, err } - return true, signRes.Signature, stamp, nil + + return true, signRes, stamp, nil + + // return false, nil, stamp, nil } +// Sign returns the signature in byte for the given block and the time +// This function is called by the sentry and its responsible for in its turn calling the MPC +// to get a valid signature to return to the sentry. +// Sign implements the [connector.IPrivValidator] interface. func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block types.Block) ([]byte, time.Time, error) { height, round, step, stamp, signBytes := block.Height, block.Round, block.Step, block.Timestamp, block.SignBytes @@ -597,27 +700,33 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty } // Only the leader can execute this function. Followers can handle the requests, - // but they just need to proxy the request to the raft leader + // but they just need to proxy the request to the raft leader which handles the orchestration of signing isProxied, proxySig, proxyStamp, err := pv.proxyIfNecessary(ctx, chainID, block) if isProxied { + // Returns the proxy signature if the request was proxied return proxySig, proxyStamp, err } - totalRaftLeader.Inc() + // isSigned, Sig, Stamp, err := pv.Sign(ctx, chainID, block) + + metrics.TotalRaftLeader.Inc() log.Debug("I am the leader. Managing the sign process for this block") timeStartSignBlock := time.Now() - hrst := types.HRSTKey{ - Height: height, - Round: round, - Step: step, - Timestamp: stamp.UnixNano(), - } + hrst := block.ToHRST() + /* + types.HRST{ + Height: height, + Round: round, + Step: step, + Timestamp: stamp.UnixNano(), + } + */ // Keep track of the last block that we began the signing process for. Only allow one attempt per block - existingSignature, existingTimestamp, err := pv.SaveLastSignedStateInitiated(chainID, &block) + existingSignature, existingTimestamp, err := pv.saveLastSignedStateInitiated(chainID, &block) if err != nil { return nil, stamp, fmt.Errorf("error saving last sign state initiated: %w", err) } @@ -626,17 +735,25 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty return existingSignature, existingTimestamp, nil } - numPeers := len(pv.peerCosigners) + // More or less everything belov here shoud be moved to a "cosigners" + // package. This is the actual MPC. + // MPC.SignBlock() is the actual function that does the MPC. + numPeers := len(pv.mpc.peerCosigners) total := uint8(numPeers + 1) peerStartTime := time.Now() - cosignersOrderedByFastest := pv.cosignerHealth.GetFastest() - cosignersForThisBlock := make([]Cosigner, pv.threshold) - cosignersForThisBlock[0] = pv.myCosigner + fmt.Println("pv.cosignerHealth: ", pv.mpc.cosignerHealth) + cosignersOrderedByFastest := pv.mpc.cosignerHealth.GetFastest() + + fmt.Println("cosignersOrderedByFastest", len(cosignersOrderedByFastest)) + cosignersForThisBlock := make([]ICosigner, pv.threshold) + + fmt.Println("cosignersForThisBlock", len(cosignersForThisBlock), pv.threshold) + cosignersForThisBlock[0] = pv.mpc.MyCosigner copy(cosignersForThisBlock[1:], cosignersOrderedByFastest[:pv.threshold-1]) - nonces, err := pv.nonceCache.GetNonces(cosignersForThisBlock) + nonces, err := pv.mpc.nonceCache.GetNonces(cosignersForThisBlock) var dontIterateFastestCosigners bool @@ -644,17 +761,17 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty var fallbackErr error nonces, cosignersForThisBlock, fallbackErr = pv.getNoncesFallback(ctx) if fallbackErr != nil { - pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + pv.notifyBlockSignError(chainID, block.GetHRS(), signBytes) return nil, stamp, fmt.Errorf("failed to get nonces: %w", errors.Join(err, fallbackErr)) } dontIterateFastestCosigners = true } else { - drainedNonceCache.Set(0) + metrics.DrainedNonceCache.Set(0) } nextFastestCosignerIndex := pv.threshold - 1 var nextFastestCosignerIndexMu sync.Mutex - getNextFastestCosigner := func() Cosigner { + getNextFastestCosigner := func() ICosigner { nextFastestCosignerIndexMu.Lock() defer nextFastestCosignerIndexMu.Unlock() if nextFastestCosignerIndex >= len(cosignersOrderedByFastest) { @@ -665,11 +782,11 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty return cosigner } - timedSignBlockThresholdLag.Observe(time.Since(timeStartSignBlock).Seconds()) + metrics.TimedSignBlockThresholdLag.Observe(time.Since(timeStartSignBlock).Seconds()) - for _, peer := range pv.peerCosigners { - missedNonces.WithLabelValues(peer.GetAddress()).Set(0) - timedCosignerNonceLag.WithLabelValues(peer.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) + for _, peer := range pv.mpc.peerCosigners { + metrics.MissedNonces.WithLabelValues(peer.GetAddress()).Set(0) + metrics.TimedCosignerNonceLag.WithLabelValues(peer.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) } cosignersForThisBlockInt := make([]int, len(cosignersForThisBlock)) @@ -682,53 +799,58 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty shareSignatures := make([][]byte, total) var eg errgroup.Group - for _, cosigner := range cosignersForThisBlock { - cosigner := cosigner + for _, remote_Cosigner := range cosignersForThisBlock { + // NOTE: This is really odd isnt it? + remoteCosigner := remote_Cosigner eg.Go(func() error { - for cosigner != nil { + for remoteCosigner != nil { signCtx, cancel := context.WithTimeout(ctx, pv.grpcTimeout) defer cancel() peerStartTime := time.Now() // set peerNonces and sign in single rpc call. - sigRes, err := cosigner.SetNoncesAndSign(signCtx, CosignerSetNoncesAndSignRequest{ + sigRes, err := remoteCosigner.SetNoncesAndSign(signCtx, cosigner.CosignerSetNoncesAndSignRequest{ ChainID: chainID, - Nonces: nonces.For(cosigner.GetIndex()), + Nonces: nonces.For(remoteCosigner.GetIndex()), HRST: hrst, SignBytes: signBytes, }) if err != nil { log.Error( "Cosigner failed to set nonces and sign", - "cosigner", cosigner.GetIndex(), + "cosigner", remoteCosigner.GetIndex(), "err", err.Error(), ) - if cosigner.GetIndex() == pv.myCosigner.GetIndex() { + if strings.Contains(err.Error(), cosigner.ErrUnexpectedState) { + pv.mpc.nonceCache.ClearNonces(remoteCosigner) + } + + if remoteCosigner.GetIndex() == pv.mpc.MyCosigner.GetIndex() { return err } if c := status.Code(err); c == codes.DeadlineExceeded || c == codes.NotFound || c == codes.Unavailable { - pv.cosignerHealth.MarkUnhealthy(cosigner) - pv.nonceCache.ClearNonces(cosigner) + pv.mpc.cosignerHealth.MarkUnhealthy(remoteCosigner) + pv.mpc.nonceCache.ClearNonces(remoteCosigner) } if dontIterateFastestCosigners { - cosigner = nil + remoteCosigner = nil continue } // this will only work if the next cosigner has the nonces we've already decided to use for this block // otherwise the sign attempt will fail - cosigner = getNextFastestCosigner() + remoteCosigner = getNextFastestCosigner() continue } - if cosigner != pv.myCosigner { - timedCosignerSignLag.WithLabelValues(cosigner.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) + if remoteCosigner != pv.mpc.MyCosigner { + metrics.TimedCosignerSignLag.WithLabelValues(remoteCosigner.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) } - shareSignatures[cosigner.GetIndex()-1] = sigRes.Signature + shareSignatures[remoteCosigner.GetIndex()-1] = sigRes.Signature return nil } @@ -737,11 +859,11 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty } if err := eg.Wait(); err != nil { - pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + pv.notifyBlockSignError(chainID, block.GetHRS(), signBytes) return nil, stamp, fmt.Errorf("error from cosigner(s): %s", err) } - timedSignBlockCosignerLag.Observe(time.Since(timeStartSignBlock).Seconds()) + metrics.TimedSignBlockCosignerLag.Observe(time.Since(timeStartSignBlock).Seconds()) // collect all valid responses into array of partial signatures shareSigs := make([]types.PartialSignature, 0, pv.threshold) @@ -762,23 +884,23 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty } if len(shareSigs) < pv.threshold { - totalInsufficientCosigners.Inc() - pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + metrics.TotalInsufficientCosigners.Inc() + pv.notifyBlockSignError(chainID, block.GetHRS(), signBytes) return nil, stamp, errors.New("not enough co-signers") } // assemble into final signature - signature, err := pv.myCosigner.CombineSignatures(chainID, shareSigs) + signature, err := pv.mpc.MyCosigner.CombineSignatures(chainID, shareSigs) if err != nil { - pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + pv.notifyBlockSignError(chainID, block.GetHRS(), signBytes) return nil, stamp, fmt.Errorf("error combining signatures: %w", err) } // verify the combined signature before saving to watermark - if !pv.myCosigner.VerifySignature(chainID, signBytes, signature) { - totalInvalidSignature.Inc() + if !pv.mpc.MyCosigner.VerifySignature(chainID, signBytes, signature) { + metrics.TotalInvalidSignature.Inc() - pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + pv.notifyBlockSignError(chainID, block.GetHRS(), signBytes) return nil, stamp, errors.New("combined signature is not valid") } @@ -800,9 +922,10 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty err = css.lastSignState.Save(newLss.SignStateConsensus, &pv.pendingDiskWG) css.lastSignStateMutex.Unlock() if err != nil { - if _, isSameHRSError := err.(*types.SameHRSError); !isSameHRSError { + var sameHRSError *types.SameHRSError + if !errors.As(err, &sameHRSError) { - pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + pv.notifyBlockSignError(chainID, block.GetHRS(), signBytes) return nil, stamp, fmt.Errorf("error saving last sign state: %w", err) } } @@ -817,7 +940,7 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block ty timeSignBlock := time.Since(timeStartSignBlock) timeSignBlockSec := timeSignBlock.Seconds() - timedSignBlockLag.Observe(timeSignBlockSec) + metrics.TimedSignBlockLag.Observe(timeSignBlockSec) log.Info( "Signed", diff --git a/signer/threshold_validator_test.go b/src/node/threshold_validator_test.go similarity index 73% rename from signer/threshold_validator_test.go rename to src/node/threshold_validator_test.go index 95aca51d..60b2d437 100644 --- a/signer/threshold_validator_test.go +++ b/src/node/threshold_validator_test.go @@ -1,16 +1,21 @@ -package signer +package node_test import ( "bytes" "context" "crypto/rand" "fmt" - mrand "math/rand" "path/filepath" - "sync" "time" - "github.com/strangelove-ventures/horcrux/pkg/types" + "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/connector" + "github.com/strangelove-ventures/horcrux/src/cosigner" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + "github.com/strangelove-ventures/horcrux/src/node" + "github.com/strangelove-ventures/horcrux/src/tss" + + "github.com/strangelove-ventures/horcrux/src/types" "os" "testing" @@ -18,40 +23,112 @@ import ( cometcrypto "github.com/cometbft/cometbft/crypto" cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/crypto/tmhash" - cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/cometbft/cometbft/libs/log" cometrand "github.com/cometbft/cometbft/libs/rand" cometproto "github.com/cometbft/cometbft/proto/tendermint/types" - comet "github.com/cometbft/cometbft/types" "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/stretchr/testify/require" - tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" + ted25519 "gitlab.com/unit410/threshold-ed25519/pkg" "golang.org/x/sync/errgroup" ) +var ( + testConfig *config.RuntimeConfig // use this global config in tests + +) +var _ connector.IPrivValidator = &node.ThresholdValidator{} + +const ( + defaultGetNoncesInterval = 3 * time.Second + defaultGetNoncesTimeout = 4 * time.Second + defaultNonceExpiration = 10 * time.Second // half of the local cosigner cache expiration + +) + +type MockValidator struct { + *node.ThresholdValidator + nonceCache *node.CosignerNonceCache + // cosignerHealth *node.CosignerHealth +} + +func NewMockValidator( + logger log.Logger, + config *config.RuntimeConfig, + threshold int, + grpcTimeout time.Duration, + maxWaitForSameBlockAttempts int, + myCosigner *cosigner.LocalCosigner, + peerCosigners []node.ICosigner, + leader node.ILeader, +) *MockValidator { + allCosigners := make([]node.ICosigner, len(peerCosigners)+1) + allCosigners[0] = myCosigner + copy(allCosigners[1:], peerCosigners) + + for _, peer := range peerCosigners { + logger.Debug("Peer peer", "id", peer.GetIndex()) + } + + nc := node.NewCosignerNonceCache( + logger, + allCosigners, + leader, + defaultGetNoncesInterval, + defaultGetNoncesTimeout, + defaultNonceExpiration, + uint8(threshold), + nil, + ) + // nch := node.NewCosignerHealth(logger, peerCosigners, leader) + return &MockValidator{ + node.NewThresholdValidator(logger, config, threshold, grpcTimeout, maxWaitForSameBlockAttempts, + allCosigners[0].(*cosigner.LocalCosigner), peerCosigners[1:], leader), nc, + } +} + +func TestMain(m *testing.M) { + // optional alternative config via ENV VAR `CONFIG` + // if path := os.Getenv("CONFIG"); path != "" { + + // conf, err := LoadConfig(path) + // if err != nil { + // log.Fatalf("Failed to load config file %q : %v", path, err) + // } + + // testConfig = &conf + + // } else { + // testConfig = &config.RuntimeConfig{} + // } + testConfig = &config.RuntimeConfig{} + // call flag.Parse() here if TestMain uses flags + os.Exit(m.Run()) +} func TestThresholdValidator2of2(t *testing.T) { - testThresholdValidator(t, 2, 2) + testThresholdValidator(t, 2, 2, testConfig) } func TestThresholdValidator3of3(t *testing.T) { - testThresholdValidator(t, 3, 3) + testThresholdValidator(t, 3, 3, testConfig) } func TestThresholdValidator2of3(t *testing.T) { - testThresholdValidator(t, 2, 3) + testThresholdValidator(t, 2, 3, testConfig) } func TestThresholdValidator3of5(t *testing.T) { - testThresholdValidator(t, 3, 5) + testThresholdValidator(t, 3, 5, testConfig) } func loadKeyForLocalCosigner( - cosigner *LocalCosigner, + cosigner *cosigner.LocalCosigner, pubKey cometcrypto.PubKey, chainID string, privateShard []byte, + config *config.RuntimeConfig, ) error { - key := CosignerEd25519Key{ + key := tss.Ed25519Key{ PubKey: pubKey, PrivateShard: privateShard, ID: cosigner.GetIndex(), @@ -62,13 +139,15 @@ func loadKeyForLocalCosigner( return err } - return os.WriteFile(cosigner.config.KeyFilePathCosigner(chainID), keyBz, 0600) + return os.WriteFile(config.KeyFilePathCosigner(chainID), keyBz, 0600) } - -func testThresholdValidator(t *testing.T, threshold, total uint8) { +func testThresholdValidator(t *testing.T, threshold, total uint8, configuration *config.RuntimeConfig) { cosigners, pubKey := getTestLocalCosigners(t, threshold, total) - thresholdCosigners := make([]Cosigner, 0, threshold-1) + fmt.Println("cosigners", threshold, total, len(cosigners)) + + thresholdCosigners := make([]node.ICosigner, 0, threshold-1) + fmt.Println("thresholdCosigners", threshold, total, len(thresholdCosigners)) for i, cosigner := range cosigners { require.Equal(t, i+1, cosigner.GetIndex()) @@ -80,9 +159,9 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { leader := &MockLeader{id: 1} - validator := NewThresholdValidator( - cometlog.NewNopLogger(), - cosigners[0].config, + validator := NewMockValidator( + log.NewNopLogger(), + configuration, int(threshold), time.Second, 1, @@ -92,7 +171,10 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { ) defer validator.Stop() - leader.leader = validator + // var mockvalidator *MockValidator + // mockvalidator = mockvalidator(validator) + + leader.leader = validator.ThresholdValidator ctx := context.Background() @@ -106,7 +188,6 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { } block := types.ProposalToBlock(testChainID, &proposal) - signature, _, err := validator.Sign(ctx, testChainID, block) require.NoError(t, err) @@ -167,9 +248,9 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { require.NoError(t, err) // reinitialize validator to make sure new runtime will not allow double sign - newValidator := NewThresholdValidator( - cometlog.NewNopLogger(), - cosigners[0].config, + newValidator := NewMockValidator( + log.NewNopLogger(), + configuration, int(threshold), time.Second, 1, @@ -295,10 +376,10 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { } } -func getTestLocalCosigners(t *testing.T, threshold, total uint8) ([]*LocalCosigner, cometcrypto.PubKey) { +func getTestLocalCosigners(t *testing.T, threshold, total uint8) ([]*cosigner.LocalCosigner, cometcrypto.PubKey) { eciesKeys := make([]*ecies.PrivateKey, total) pubKeys := make([]*ecies.PublicKey, total) - cosigners := make([]*LocalCosigner, total) + cosigners := make([]*cosigner.LocalCosigner, total) for i := uint8(0); i < total; i++ { eciesKey, err := ecies.GenerateKey(rand.Reader, secp256k1.S256(), nil) @@ -311,14 +392,14 @@ func getTestLocalCosigners(t *testing.T, threshold, total uint8) ([]*LocalCosign privateKey := cometcryptoed25519.GenPrivKey() privKeyBytes := privateKey[:] - privShards := tsed25519.DealShares(tsed25519.ExpandSecret(privKeyBytes[:32]), threshold, total) + privShards := ted25519.DealShares(ted25519.ExpandSecret(privKeyBytes[:32]), threshold, total) tmpDir := t.TempDir() - cosignersConfig := make(CosignersConfig, total) + cosignersConfig := make(config.CosignersConfig, total) for i := range pubKeys { - cosignersConfig[i] = CosignerConfig{ + cosignersConfig[i] = config.CosignerConfig{ ShardID: i + 1, } } @@ -328,22 +409,22 @@ func getTestLocalCosigners(t *testing.T, threshold, total uint8) ([]*LocalCosign err := os.MkdirAll(cosignerDir, 0777) require.NoError(t, err) - cosignerConfig := &RuntimeConfig{ + cosignerConfig := &config.RuntimeConfig{ HomeDir: cosignerDir, StateDir: cosignerDir, - Config: Config{ - ThresholdModeConfig: &ThresholdModeConfig{ + Config: config.Config{ + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: int(threshold), Cosigners: cosignersConfig, }, }, } - cosigner := NewLocalCosigner( - cometlog.NewNopLogger(), + cosigner := cosigner.NewLocalCosigner( + log.NewNopLogger(), cosignerConfig, - NewCosignerSecurityECIES( - CosignerECIESKey{ + nodesecurity.NewCosignerSecurityECIES( + nodesecurity.CosignerECIESKey{ ID: i + 1, ECIESKey: eciesKeys[i], ECIESPubs: pubKeys, @@ -355,49 +436,50 @@ func getTestLocalCosigners(t *testing.T, threshold, total uint8) ([]*LocalCosign cosigners[i] = cosigner - err = loadKeyForLocalCosigner(cosigner, privateKey.PubKey(), testChainID, privShards[i]) + err = loadKeyForLocalCosigner(cosigner, privateKey.PubKey(), testChainID, privShards[i], cosignerConfig) require.NoError(t, err) - err = loadKeyForLocalCosigner(cosigner, privateKey.PubKey(), testChainID2, privShards[i]) + err = loadKeyForLocalCosigner(cosigner, privateKey.PubKey(), testChainID2, privShards[i], cosignerConfig) require.NoError(t, err) } return cosigners, privateKey.PubKey() } +/* func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) { - cosigners, pubKey := getTestLocalCosigners(t, threshold, total) + peers, pubKey := getTestLocalCosigners(t, threshold, total) - thresholdValidators := make([]*ThresholdValidator, 0, total) - var leader *ThresholdValidator + thresholdValidators := make([]*MockThresholdValidator, 0, total) + var leader *signer.ThresholdValidator leaders := make([]*MockLeader, total) ctx := context.Background() - for i, cosigner := range cosigners { - peers := make([]Cosigner, 0, len(cosigners)-1) - for j, otherCosigner := range cosigners { + for i, peer := range peers { + peers := make([]signer.ICosigner, 0, len(peers)-1) + for j, otherCosigner := range peers { if i != j { peers = append(peers, otherCosigner) } } - leaders[i] = &MockLeader{id: cosigner.GetIndex(), leader: leader} - tv := NewThresholdValidator( + leaders[i] = &MockLeader{id: peer.GetIndex(), leader: leader} + tv := NewMockValidator( cometlog.NewNopLogger(), - cosigner.config, + peer.config, int(threshold), time.Second, 1, - cosigner, + peer, peers, leaders[i], ) if i == 0 { - leader = tv - leaders[i].leader = tv + leader = tv.ThresholdValidator + leaders[i].leader = tv.ThresholdValidator } - thresholdValidators = append(thresholdValidators, tv) + thresholdValidators = append(thresholdValidators, tv.ThresholdValidator) defer tv.Stop() err := tv.LoadSignStateIfNecessary(testChainID) @@ -430,7 +512,7 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) for _, l := range leaders { l.SetLeader(newLeader) } - t.Logf("New leader: %d", newLeader.myCosigner.GetIndex()) + t.Logf("New leader: %d", newLeader.MyCosigner.GetIndex()) // time with new leader time.Sleep(time.Duration(mrand.Intn(50)+100) * time.Millisecond) //nolint:gosec @@ -577,3 +659,4 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) func TestThresholdValidatorLeaderElection2of3(t *testing.T) { testThresholdValidatorLeaderElection(t, 2, 3) } +*/ diff --git a/signer/proto/cosigner.pb.go b/src/proto/cosigner.pb.go similarity index 100% rename from signer/proto/cosigner.pb.go rename to src/proto/cosigner.pb.go diff --git a/signer/proto/remote_signer.pb.go b/src/proto/remote_signer.pb.go similarity index 98% rename from signer/proto/remote_signer.pb.go rename to src/proto/remote_signer.pb.go index 810048ae..a8c6f502 100644 --- a/signer/proto/remote_signer.pb.go +++ b/src/proto/remote_signer.pb.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. -// source: strangelove/horcrux/remote_signer.proto +// source: strangelove/horcrux/connector.proto package proto @@ -121,7 +121,7 @@ func init() { } func init() { - proto.RegisterFile("strangelove/horcrux/remote_signer.proto", fileDescriptor_afd7664cd19b584a) + proto.RegisterFile("strangelove/horcrux/connector.proto", fileDescriptor_afd7664cd19b584a) } var fileDescriptor_afd7664cd19b584a = []byte{ @@ -259,7 +259,7 @@ var _RemoteSigner_serviceDesc = grpc.ServiceDesc{ }, }, Streams: []grpc.StreamDesc{}, - Metadata: "strangelove/horcrux/remote_signer.proto", + Metadata: "strangelove/horcrux/connector.proto", } func (m *PubKeyRequest) Marshal() (dAtA []byte, err error) { diff --git a/src/tss/doc.go b/src/tss/doc.go new file mode 100644 index 00000000..d7f96ed8 --- /dev/null +++ b/src/tss/doc.go @@ -0,0 +1,44 @@ +package tss + +/* +// Package tss provides functionality related to threshold signature schemes. +// It includes functions for key generation, signing, and verification. +// +// The tss package includes the following primary types: +// +// - Key: Represents a key used in threshold signature schemes. +// +// - Signature: Represents a signature produced by a threshold signature scheme. +// +// - Verifier: Provides functions for verifying signatures. +// +// - Signer: Provides functions for generating signatures. +// +// Example usage: +// +// // Generate a new key. +// key, err := tss.NewKey() +// if err != nil { +// log.Fatalf("failed to generate key: %v", err) +// } +// +// // Create a new signer. +// signer := tss.NewSigner(key) +// +// // Sign a message. +// msg := []byte("hello, world") +// sig, err := signer.Sign(msg) +// if err != nil { +// log.Fatalf("failed to sign message: %v", err) +// } +// +// // Create a new verifier. +// verifier := tss.NewVerifier(key) +// +// // Verify the signature. +// if !verifier.Verify(msg, sig) { +// log.Fatalf("failed to verify signature") +// } +// +// Note: This is a simplified example. The actual usage may be different depending on the specifics of the threshold signature scheme. +*/ diff --git a/src/tss/threshold_signer.go b/src/tss/threshold_signer.go new file mode 100644 index 00000000..2a8ff716 --- /dev/null +++ b/src/tss/threshold_signer.go @@ -0,0 +1,123 @@ +package tss + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/cometbft/cometbft/privval" + "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/tss/tss25519" + + cometbytes "github.com/cometbft/cometbft/libs/bytes" +) + +type Address = cometbytes.HexBytes + +type PubKey interface { + Address() Address + Bytes() []byte + VerifySignature(msg []byte, sig []byte) bool + // Equals(PubKey) bool + Type() string +} + +func NewThresholdEd25519SignerSoft(config *config.RuntimeConfig, id int, chainID string) (*tss25519.SignerSoft, error) { + keyFile, err := config.KeyFileExistsCosigner(chainID) + if err != nil { + return nil, err + } + + key, err := LoadVaultKeyFromFile(keyFile) + if err != nil { + return nil, fmt.Errorf("error reading Vault key: %s", err) + } + + if key.ID != id { + return nil, fmt.Errorf("key shard Index (%d) in (%s) does not match cosigner Index (%d)", key.ID, keyFile, id) + } + return tss25519.NewSignerSoft( + key.PrivateShard, + key.PubKey.Bytes(), + uint8(config.Config.ThresholdModeConfig.Threshold), + uint8(len(config.Config.ThresholdModeConfig.Cosigners)), + uint8(key.ID), + ) +} + +// LoadVaultKeyFromFile loads the persistent ThresholdSignerKey from file. + +func LoadVaultKeyFromFile(file string) (Ed25519Key, error) { + // pvKey := VaultKey{} + var pvKey Ed25519Key + keyJSONBytes, err := os.ReadFile(file) + if err != nil || len(keyJSONBytes) == 0 { + fmt.Printf("Could not read key from file %s", file) + return pvKey, err + } + + err = json.Unmarshal(keyJSONBytes, &pvKey) + if err != nil { + fmt.Printf("Could not unmarshal key from file %s", file) + return pvKey, err + } + + return pvKey, nil +} + +type ChainPrivate interface { + privval.FilePVKey +} + +// type Handler[VPK VaultPrivateKey, CP ChainPrivate] func(CP, uint8, uint8) []VPK +// type ChainHandler[CP ChainPrivate] func(string) (CP, error) +type fn func([]byte, uint8, uint8) map[uint8][]byte + +// ted25519.GenerateEd25519ThresholdSignShards() + +// CreatePersistentEd25519ThresholdSignShardsFromFile creates Ed25519Key objects from a priv_validator_key.json file +func CreatePersistentEd25519ThresholdSignShardsFromFile(filename string, threshold, shards uint8) ([]VaultKey, error) { + pv, err := ReadCometBFTPrivValidatorFile(filename) + if err != nil { + fmt.Printf("Could not create shard from file %s", filename) + return nil, err + } + + pubkey := pv.PubKey.(PubKey) + persistentKeys, err := generatePersistentThresholdSignShards(pv.PrivKey.Bytes(), pubkey, tss25519.GenerateSignatureShards, threshold, shards) + return persistentKeys, err + +} + +func GeneratePersistentThresholdSignShards[Key Ed25519Key](privateKey []byte, publicKey PubKey, threshold uint8, shards uint8) ([]Key, error) { + keys := tss25519.GenerateSignatureShards(privateKey, threshold, shards) + // Transform ed25519Keys to VaultKey type + + vaultKeys := make([]Key, len(keys)) + for id, key := range keys { + vaultKeys[id-1] = Key{ + PubKey: publicKey, + PrivateShard: key, + ID: int(id), + } + } + return vaultKeys, nil +} + +// CreatePersistentThresholdSignShardsFromFile creates objects from a priv_validator_key.json file +func generatePersistentThresholdSignShards(privateKey []byte, publicKey PubKey, function fn, threshold uint8, shards uint8) ([]VaultKey, error) { + keys := function(privateKey, threshold, shards) + // Transform ed25519Keys to VaultKey type + vaultKeys := make([]VaultKey, len(keys)) + + fmt.Printf("Number of keys to create: %d\n", len(keys)) + for id, key := range keys { + fmt.Printf("VaultKey: %d\n", id) + vaultKeys[id-1] = VaultKey{ + PubKey: publicKey, + PrivateShard: key, + ID: int(id), + } + } + return vaultKeys, nil +} diff --git a/src/tss/tss25519/threshold-ed25519_signer_soft.go b/src/tss/tss25519/threshold-ed25519_signer_soft.go new file mode 100644 index 00000000..12877ad2 --- /dev/null +++ b/src/tss/tss25519/threshold-ed25519_signer_soft.go @@ -0,0 +1,159 @@ +package tss25519 + +import ( + "bytes" + "crypto/rand" + "errors" + "fmt" + + "github.com/strangelove-ventures/horcrux/src/types" + + "gitlab.com/unit410/edwards25519" + ted25519 "gitlab.com/unit410/threshold-ed25519/pkg" +) + +// var _ IThresholdSigner = &ThresholdSignerSoft{} + +// GenerateSignatureShards creates a map of shards from a private key +func GenerateSignatureShards(pv []byte, threshold, shards uint8) map[uint8][]byte { + privShards := ted25519.DealShares(ted25519.ExpandSecret(pv[:32]), threshold, shards) + // TODO: Check that the length of privShards is equal to the number of shards + // TODO: Check that the pubkey is the same for all shards + out := make(map[uint8][]byte, shards) + for id, shard := range privShards { + id := uint8(id + 1) + out[id] = shard + } + return out +} + +/* +type AssymetricKey struct { + privateKey []byte + privateShard []byte +} + +type AssymetricKeyShard struct { + AssymetricKey + threshold uint8 + total uint8 + id uint8 // ID is the Shamir index or this shard. + +} + +type SignerDealer struct { + SignerSoft +} +*/ + +// SignerSoft is a threshold signer that uses the threshold-ed25519 library +// to perform the signing operations. +// Its only responsibility is to sign a payload and combine signatures +type SignerSoft struct { + privateKeyShard []byte + pubKey []byte + threshold uint8 + total uint8 + id uint8 +} + +func NewSignerSoft(privateKeyShard []byte, pubKey []byte, threshold, total, id uint8) (*SignerSoft, error) { + s := SignerSoft{ + privateKeyShard: privateKeyShard, + pubKey: pubKey, + threshold: threshold, + total: total, + id: id, + } + + return &s, nil +} + +func (s *SignerSoft) GetPubKey() []byte { + return s.pubKey +} + +// Sign signs a byte payload with the provided nonces. +// The return are a "partial signature". +func (s *SignerSoft) Sign(nonces []types.Nonce, payload []byte) ([]byte, error) { + // sum the nonces to get the ephemeral public key and share + nonceShare, noncePub, err := sumNonces(nonces) + if err != nil { + return nil, fmt.Errorf("failed to combine nonces: %w", err) + } + + sig := ted25519.SignWithShare( + payload, s.privateKeyShard, nonceShare, s.pubKey, noncePub) + return append(noncePub, sig...), nil +} + +func sumNonces(nonces []types.Nonce) (ted25519.Scalar, ted25519.Element, error) { + shareParts := make([]ted25519.Scalar, len(nonces)) + publicKeys := make([]ted25519.Element, len(nonces)) + + for i, n := range nonces { + shareParts[i] = n.Share + publicKeys[i] = n.PubKey + } + + nonceShare := ted25519.AddScalars(shareParts) + noncePub := ted25519.AddElements(publicKeys) + + // check bounds for ephemeral share to avoid passing out of bounds valids to SignWithShare + if len(nonceShare) != 32 { + return nil, nil, errors.New("ephemeral share is out of bounds") + } + + var scalarBytes [32]byte + copy(scalarBytes[:], nonceShare) + if !edwards25519.ScMinimal(&scalarBytes) { + return nil, nil, errors.New("ephemeral share is out of bounds") + } + + return nonceShare, noncePub, nil +} + +type NonceGenerator struct { +} + +// GenerateNonces is a function (methods) that generates Nonces to be used in the MPC signature +func (ng NonceGenerator) GenerateNonces(threshold, total uint8) (types.Nonces, error) { + secret := make([]byte, 32) + if _, err := rand.Read(secret); err != nil { + return types.Nonces{}, err + } + + nonces := types.Nonces{ + PubKey: ted25519.ScalarMultiplyBase(secret), + Shares: make([][]byte, total), + } + + // The length of shares is equal to total + shares := ted25519.DealShares(secret, threshold, total) + + for i, sh := range shares { + nonces.Shares[i] = sh + } + + return nonces, nil +} + +// CombineSignatures combines partial signatures into a full signature +func (s *SignerSoft) CombineSignatures(signatures []types.PartialSignature) ([]byte, error) { + sigIds := make([]int, len(signatures)) + shareSigs := make([][]byte, len(signatures)) + var ephPub []byte + + for i, sig := range signatures { + sigIds[i] = sig.Index + if i == 0 { + ephPub = sig.Signature[:32] + } else if !bytes.Equal(sig.Signature[:32], ephPub) { + return nil, fmt.Errorf("ephemeral public keys do not match") + } + shareSigs[i] = sig.Signature[32:] + } + combinedSig := ted25519.CombineShares(s.total, sigIds, shareSigs) + + return append(ephPub, combinedSig...), nil +} diff --git a/src/tss/types.go b/src/tss/types.go new file mode 100644 index 00000000..bca4aa6d --- /dev/null +++ b/src/tss/types.go @@ -0,0 +1,167 @@ +package tss + +import ( + "encoding/json" + "os" + + cometcrypto "github.com/cometbft/cometbft/crypto" + cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" + cometcryptoencoding "github.com/cometbft/cometbft/crypto/encoding" + cometjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/privval" + cometprotocrypto "github.com/cometbft/cometbft/proto/tendermint/crypto" + "github.com/tendermint/go-amino" +) + +/* +type ISignerKey interface { + MarshalJSON() ([]byte, error) + UnmarshalJSON(data []byte) error +} +*/ + +// Ed25519Key is a single Ed255219 key shard for an m-of-n threshold signer. +// TODO: This should be renamed to SignerEd25519 and tbh Private shard should private. +type PersistentEd25519Key struct { + pubKey PubKey // Public key of the persistent shard. Pubkey is the same for all shards. + privateShard []byte // + index int // Shamir index of this shard +} + +/* + type Ed25519Key struct { + PubKey cometcrypto.PubKey `json:"pubKey"` + PrivateShard []byte `json:"privateShard"` + Id int `json:"id"` + } + +// TODO: Should not actually get this. + + func (key *Ed25519Key) ID() int { + return key.Id + } +*/ + +// Public Private + +type AssymetricKey struct { + PubKey cometcrypto.PubKey `json:"pubKey"` + PrivateShard []byte `json:"privateShard"` +} + +type AssymetricKeyShard struct { + AssymetricKey + ID int `json:"id"` // ID is the Shamir index or this shard. + +} + +type Ed25519Key struct { + PubKey PubKey `json:"pubKey"` + PrivateShard []byte `json:"privateShard"` + ID int `json:"id"` +} + +type VaultKey Ed25519Key + +// TODO: redo to a function. +func (key *Ed25519Key) MarshalJSON() ([]byte, error) { + type Alias Ed25519Key + + protoPubkey, err := cometcryptoencoding.PubKeyToProto(key.PubKey.(cometcrypto.PubKey)) + if err != nil { + return nil, err + } + + protoBytes, err := protoPubkey.Marshal() + if err != nil { + return nil, err + } + + return json.Marshal(&struct { + PubKey []byte `json:"pubKey"` + *Alias + }{ + PubKey: protoBytes, + Alias: (*Alias)(key), + }) +} + +func (key *Ed25519Key) UnmarshalJSON(data []byte) error { + type Alias Ed25519Key + + aux := &struct { + PubkeyBytes []byte `json:"pubKey"` + *Alias + }{ + Alias: (*Alias)(key), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + var pubkey cometcrypto.PubKey + var protoPubkey cometprotocrypto.PublicKey + err := protoPubkey.Unmarshal(aux.PubkeyBytes) + + // Prior to the tendermint protobuf migration, the public key bytes in key files + // were encoded using the go-amino libraries via + // cdc.MarshalBinaryBare(key.PubKey) + // + // To support reading the public key bytes from these key files, we fallback to + // amino unmarshalling if the protobuf unmarshalling fails + if err != nil { + var pub cometcryptoed25519.PubKey + codec := amino.NewCodec() + codec.RegisterInterface((*cometcrypto.PubKey)(nil), nil) + codec.RegisterConcrete(cometcryptoed25519.PubKey{}, "tendermint/PubKeyEd25519", nil) + errInner := codec.UnmarshalBinaryBare(aux.PubkeyBytes, &pub) + if errInner != nil { + return err + } + pubkey = pub + } else { + pubkey, err = cometcryptoencoding.PubKeyFromProto(protoPubkey) + if err != nil { + return err + } + } + + key.PubKey = pubkey.(PubKey) + return nil +} + +// ReadCometBFTPrivValidatorFile reads in a Comet privval.FilePVKey from a given file. +func ReadCometBFTPrivValidatorFile(filename string) (out privval.FilePVKey, err error) { + var bz []byte + if bz, err = os.ReadFile(filename); err != nil { + return + } + if err = cometjson.Unmarshal(bz, &out); err != nil { + return + } + return +} + +func WriteCometBFTPrivValidatorFile(filename string, pv privval.FilePVKey) (err error) { + var bz []byte + if bz, err = cometjson.Marshal(pv); err != nil { + return err + } + return os.WriteFile(filename, bz, 0600) +} + +type VaultPrivateKey interface { + Ed25519Key | VaultKey +} + +// WriteToFile writes a key structure to a given file name. +// +// It json.Marshal's the key structure and writes the result to to file with the +// 0600 permission. +func WriteToFile[VPK VaultPrivateKey](privateshare VPK, file string) error { + jsonBytes, err := json.Marshal(&privateshare) + if err != nil { + return err + } + return os.WriteFile(file, jsonBytes, 0600) +} diff --git a/src/types/block.go b/src/types/block.go new file mode 100644 index 00000000..4574b517 --- /dev/null +++ b/src/types/block.go @@ -0,0 +1,145 @@ +package types + +import ( + "time" + + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + + // "github.com/strangelove-ventures/horcrux/src/proto" + + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" + comet "github.com/cometbft/cometbft/types" +) + +/* + type Block struct { + HRST + SignBytes []byte + Timestamp time.Time + } +*/ +type Block struct { + Height int64 + Round int64 + Step int8 + SignBytes []byte + Timestamp time.Time +} + +func (block Block) GetHRS() HRS { + return HRS{ + Height: block.Height, + Round: block.Round, + Step: block.Step, + } +} + +func (block Block) ToHRST() HRST { + return HRST{ + Height: block.Height, + Round: block.Round, + Step: block.Step, + Timestamp: block.Timestamp.UnixNano(), + } +} + +func (block Block) ToProto() *proto.Block { + return &proto.Block{ + Height: block.Height, + Round: block.Round, + Step: int32(block.Step), + SignBytes: block.SignBytes, + Timestamp: block.Timestamp.UnixNano(), + } +} + +func BlockFromProto(block *proto.Block) Block { + return Block{ + // HRST: HRST{ + Height: block.Height, + Round: block.Round, + Step: int8(block.Step), + // }, + SignBytes: block.SignBytes, + Timestamp: time.Unix(0, block.Timestamp), + } +} + +const ( + StepPropose int8 = 1 + StepPrevote int8 = 2 + StepPrecommit int8 = 3 + blocksTocache = 3 +) + +func SignType(step int8) string { + switch step { + case StepPropose: + return "proposal" + case StepPrevote: + return "prevote" + case StepPrecommit: + return "precommit" + default: + return "unknown" + } +} + +func CanonicalVoteToStep(vote *cometproto.CanonicalVote) int8 { + switch vote.Type { + case cometproto.PrevoteType: + return StepPrevote + case cometproto.PrecommitType: + return StepPrecommit + default: + panic("Unknown vote type") + } +} + +func VoteToStep(vote *cometproto.Vote) int8 { + switch vote.Type { + case cometproto.PrevoteType: + return StepPrevote + case cometproto.PrecommitType: + return StepPrecommit + default: + panic("Unknown vote type") + } +} + +func VoteToBlock(chainID string, vote *cometproto.Vote) Block { + return Block{ + Height: vote.Height, + Round: int64(vote.Round), + Step: VoteToStep(vote), + SignBytes: comet.VoteSignBytes(chainID, vote), + Timestamp: vote.Timestamp, + } +} + +func ProposalToStep(_ *cometproto.Proposal) int8 { + return StepPropose +} + +func ProposalToBlock(chainID string, proposal *cometproto.Proposal) Block { + return Block{ + Height: proposal.Height, + Round: int64(proposal.Round), + Step: ProposalToStep(proposal), + SignBytes: comet.ProposalSignBytes(chainID, proposal), + Timestamp: proposal.Timestamp, + } +} + +func StepToType(step int8) cometproto.SignedMsgType { + switch step { + case StepPropose: + return cometproto.ProposalType + case StepPrevote: + return cometproto.PrevoteType + case StepPrecommit: + return cometproto.PrecommitType + default: + panic("Unknown step") + } +} diff --git a/src/types/hrs.go b/src/types/hrs.go new file mode 100644 index 00000000..e6d116a7 --- /dev/null +++ b/src/types/hrs.go @@ -0,0 +1,71 @@ +package types + +import ( + // "github.com/strangelove-ventures/horcrux/src/proto" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" +) + +// HRS represents the HRS (Height, Round Step) metadata map. +type HRS struct { + Height int64 + Round int64 + Step int8 +} + +// GreaterThan returns true if the HRS struct is greater than the other HRS struct. +func (hrs HRS) GreaterThan(other HRS) bool { + if hrs.Height > other.Height { + return true + } + if hrs.Height < other.Height { + return false + } + if hrs.Round > other.Round { + return true + } + if hrs.Round < other.Round { + return false + } + return hrs.Step > other.Step +} + +// LessThan returns true if the HRS is less than the other HRS. +func (hrs HRS) LessThan(other HRS) bool { + return hrs != other && !hrs.GreaterThan(other) +} + +// HRST represents the HRS metadata key with a timestamp. +type HRST struct { + Height int64 + Round int64 + Step int8 + Timestamp int64 +} + +// HRS returns the HRS portion of the HRSTKey. +func (hrst HRST) HRS() HRS { + return HRS{ + Height: hrst.Height, + Round: hrst.Round, + Step: hrst.Step, + } +} + +// HRSTFromProto returns a HRSTKey from a proto.HRST. +func HRSTFromProto(hrst *proto.HRST) HRST { + return HRST{ + Height: hrst.GetHeight(), + Round: hrst.GetRound(), + Step: int8(hrst.GetStep()), + Timestamp: hrst.GetTimestamp(), + } +} + +func (hrst HRST) ToProto() *proto.HRST { + return &proto.HRST{ + Height: hrst.Height, + Round: hrst.Round, + Step: int32(hrst.Step), + Timestamp: hrst.Timestamp, + } +} diff --git a/pkg/types/nonce.go b/src/types/nonce.go similarity index 100% rename from pkg/types/nonce.go rename to src/types/nonce.go diff --git a/pkg/types/serialization.go b/src/types/serialization.go similarity index 76% rename from pkg/types/serialization.go rename to src/types/serialization.go index d8785b9a..0874d769 100644 --- a/pkg/types/serialization.go +++ b/src/types/serialization.go @@ -25,20 +25,20 @@ func WriteMsg(writer io.Writer, msg cometprotoprivval.Message) (err error) { } // UnpackHRS deserializes sign bytes and gets the height, round, and step -func UnpackHRST(signBytes []byte) (HRSTKey, error) { +func UnpackHRST(signBytes []byte) (HRST, error) { { var proposal cometproto.CanonicalProposal if err := protoio.UnmarshalDelimited(signBytes, &proposal); err == nil { - return HRSTKey{proposal.Height, proposal.Round, StepPropose, proposal.Timestamp.UnixNano()}, nil + return HRST{proposal.Height, proposal.Round, StepPropose, proposal.Timestamp.UnixNano()}, nil } } { var vote cometproto.CanonicalVote if err := protoio.UnmarshalDelimited(signBytes, &vote); err == nil { - return HRSTKey{vote.Height, vote.Round, CanonicalVoteToStep(&vote), vote.Timestamp.UnixNano()}, nil + return HRST{vote.Height, vote.Round, CanonicalVoteToStep(&vote), vote.Timestamp.UnixNano()}, nil } } - return HRSTKey{0, 0, 0, 0}, errors.New("could not UnpackHRS from sign bytes") + return HRST{0, 0, 0, 0}, errors.New("could not UnpackHRS from sign bytes") } diff --git a/pkg/types/serialization_test.go b/src/types/serialization_test.go similarity index 100% rename from pkg/types/serialization_test.go rename to src/types/serialization_test.go diff --git a/pkg/types/sign_state.go b/src/types/sign_state.go similarity index 82% rename from pkg/types/sign_state.go rename to src/types/sign_state.go index 0a6d8421..eb08c0c6 100644 --- a/pkg/types/sign_state.go +++ b/src/types/sign_state.go @@ -14,90 +14,10 @@ import ( "github.com/cometbft/cometbft/libs/protoio" "github.com/cometbft/cometbft/libs/tempfile" cometproto "github.com/cometbft/cometbft/proto/tendermint/types" - comet "github.com/cometbft/cometbft/types" "github.com/gogo/protobuf/proto" - "github.com/strangelove-ventures/horcrux/signer/cond" + "github.com/strangelove-ventures/horcrux/src/cond" ) -const ( - StepPropose int8 = 1 - StepPrevote int8 = 2 - StepPrecommit int8 = 3 - blocksTocache = 3 -) - -func SignType(step int8) string { - switch step { - case StepPropose: - return "proposal" - case StepPrevote: - return "prevote" - case StepPrecommit: - return "precommit" - default: - return "unknown" - } -} - -func CanonicalVoteToStep(vote *cometproto.CanonicalVote) int8 { - switch vote.Type { - case cometproto.PrevoteType: - return StepPrevote - case cometproto.PrecommitType: - return StepPrecommit - default: - panic("Unknown vote type") - } -} - -func VoteToStep(vote *cometproto.Vote) int8 { - switch vote.Type { - case cometproto.PrevoteType: - return StepPrevote - case cometproto.PrecommitType: - return StepPrecommit - default: - panic("Unknown vote type") - } -} - -func VoteToBlock(chainID string, vote *cometproto.Vote) Block { - return Block{ - Height: vote.Height, - Round: int64(vote.Round), - Step: VoteToStep(vote), - SignBytes: comet.VoteSignBytes(chainID, vote), - Timestamp: vote.Timestamp, - } -} - -func ProposalToStep(_ *cometproto.Proposal) int8 { - return StepPropose -} - -func ProposalToBlock(chainID string, proposal *cometproto.Proposal) Block { - return Block{ - Height: proposal.Height, - Round: int64(proposal.Round), - Step: ProposalToStep(proposal), - SignBytes: comet.ProposalSignBytes(chainID, proposal), - Timestamp: proposal.Timestamp, - } -} - -func StepToType(step int8) cometproto.SignedMsgType { - switch step { - case StepPropose: - return cometproto.ProposalType - case StepPrevote: - return cometproto.PrevoteType - case StepPrecommit: - return cometproto.PrecommitType - default: - panic("Unknown step") - } -} - // SignState stores signing information for high level watermark management. type SignState struct { Height int64 `json:"height"` @@ -110,17 +30,18 @@ type SignState struct { FilePath string // mu protects the cache and is used for signaling with Cond. - mu sync.RWMutex // private to avoid marshall issues - cache map[HRSKey]SignStateConsensus // private to avoid marshall issues - cond *cond.Cond // private to avoid marshall issues + mu sync.RWMutex // private to avoid marshall issues + cache map[HRS]SignStateConsensus // private to avoid marshall issues + cond *cond.Cond // private to avoid marshall issues } -func (signState *SignState) GetCache(hrs HRSKey) (SignStateConsensus, bool) { +// GetCache is a get wrapper for [SignState.cache] +func (signState *SignState) GetCache(hrs HRS) (SignStateConsensus, bool) { ssc, err := signState.cache[hrs] return ssc, err } -func (signState *SignState) SetCache(hrs HRSKey, signStateConsensus SignStateConsensus) { +func (signState *SignState) SetCache(hrs HRS, signStateConsensus SignStateConsensus) { signState.cache[hrs] = signStateConsensus } @@ -130,12 +51,12 @@ func (signState *SignState) CondWaitWithTimeout(t time.Duration) { signState.cond.WaitWithTimeout(t) } -// Condunlock unlocks the SignState's cond.L field. +// CondUnlock unlocks the SignState's cond.L field. func (signState *SignState) CondUnlock() { signState.cond.L.Unlock() } -// CondWait notifies all goroutines waiting on the SignState's cond field that something has changed. +// CondBroadcast notifies all waiting goroutines that something has changed. func (signState *SignState) CondBroadcast() { signState.cond.Broadcast() } @@ -154,10 +75,10 @@ func (signState *SignState) Lock() { func (signState *SignState) Unlock() { signState.mu.Unlock() } -func (signState *SignState) ExistingSignatureOrErrorIfRegression(hrst HRSTKey, signBytes []byte) ([]byte, error) { +func (signState *SignState) ExistingSignatureOrErrorIfRegression(hrst HRST, signBytes []byte) ([]byte, error) { return signState.existingSignatureOrErrorIfRegression(hrst, signBytes) } -func (signState *SignState) existingSignatureOrErrorIfRegression(hrst HRSTKey, signBytes []byte) ([]byte, error) { +func (signState *SignState) existingSignatureOrErrorIfRegression(hrst HRST, signBytes []byte) ([]byte, error) { signState.mu.RLock() defer signState.mu.RUnlock() @@ -183,18 +104,18 @@ func (signState *SignState) existingSignatureOrErrorIfRegression(hrst HRSTKey, s return nil, nil } -func (signState *SignState) HRSKey() HRSKey { +func (signState *SignState) hrs() HRS { signState.mu.RLock() defer signState.mu.RUnlock() - return HRSKey{ + return HRS{ Height: signState.Height, Round: signState.Round, Step: signState.Step, } } -func (signState *SignState) hrsKeyLocked() HRSKey { - return HRSKey{ +func (signState *SignState) hrsKeyLocked() HRS { + return HRS{ Height: signState.Height, Round: signState.Round, Step: signState.Step, @@ -209,8 +130,8 @@ type SignStateConsensus struct { SignBytes cometbytes.HexBytes } -func (signState SignStateConsensus) HRSKey() HRSKey { - return HRSKey{ +func (signState SignStateConsensus) HRSKey() HRS { + return HRS{ Height: signState.Height, Round: signState.Round, Step: signState.Step, @@ -245,7 +166,7 @@ func newConflictingDataError(existingSignBytes, newSignBytes []byte) *Conflictin // GetFromCache will return the latest signed block within the SignState // and the relevant SignStateConsensus from the cache, if present. -func (signState *SignState) GetFromCache(hrs HRSKey) (HRSKey, *SignStateConsensus) { +func (signState *SignState) GetFromCache(hrs HRS) (HRS, *SignStateConsensus) { signState.mu.RLock() defer signState.mu.RUnlock() latestBlock := signState.hrsKeyLocked() @@ -399,7 +320,7 @@ var ErrEmptySignBytes = errors.New("no SignBytes found") // Returns true if the HRS matches the arguments and the SignBytes are not empty (indicating // we have already signed for this HRS, and can reuse the existing signature). // It panics if the HRS matches the arguments, there's a SignBytes, but no Signature. -func (signState *SignState) CheckHRS(hrst HRSTKey) (bool, error) { +func (signState *SignState) CheckHRS(hrst HRST) (bool, error) { if signState.Height > hrst.Height { return false, newHeightRegressionError(hrst.Height, signState.Height) } @@ -432,22 +353,22 @@ type SameHRSError struct { func (e *SameHRSError) Error() string { return e.msg } -func newSameHRSError(hrs HRSKey) *SameHRSError { +func newSameHRSError(hrs HRS) *SameHRSError { return &SameHRSError{ msg: fmt.Sprintf("HRS is the same as current: %d:%d:%d", hrs.Height, hrs.Round, hrs.Step), } } func (signState *SignState) GetErrorIfLessOrEqual(height int64, round int64, step int8) error { - hrs := HRSKey{Height: height, Round: round, Step: step} - signStateHRS := signState.HRSKey() + hrs := HRS{Height: height, Round: round, Step: step} + signStateHRS := signState.hrs() if signStateHRS.GreaterThan(hrs) { return errors.New("regression not allowed") } if hrs == signStateHRS { // same HRS as current - return newSameHRSError(HRSKey{Height: height, Round: round, Step: step}) + return newSameHRSError(HRS{Height: height, Round: round, Step: step}) } // Step is greater, so all good return nil @@ -463,14 +384,14 @@ func (signState *SignState) FreshCache() *SignState { NoncePublic: signState.NoncePublic, Signature: signState.Signature, SignBytes: signState.SignBytes, - cache: make(map[HRSKey]SignStateConsensus), + cache: make(map[HRS]SignStateConsensus), FilePath: signState.FilePath, } newSignState.cond = cond.New(&newSignState.mu) - newSignState.cache[HRSKey{ + newSignState.cache[HRS{ Height: signState.Height, Round: signState.Round, Step: signState.Step, @@ -516,7 +437,7 @@ func LoadOrCreateSignState(filepath string) (*SignState, error) { // Make an empty sign state and save it. state := &SignState{ FilePath: filepath, - cache: make(map[HRSKey]SignStateConsensus), + cache: make(map[HRS]SignStateConsensus), } state.cond = cond.New(&state.mu) diff --git a/test/go.mod b/test/go.mod index e68ada3e..8fbc0fb3 100644 --- a/test/go.mod +++ b/test/go.mod @@ -43,19 +43,14 @@ require ( github.com/DataDog/zstd v1.5.5 // indirect github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect - github.com/Jille/raft-grpc-leader-rpc v1.1.0 // indirect - github.com/Jille/raft-grpc-transport v1.4.0 // indirect - github.com/Jille/raftadmin v1.2.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/StirlingMarketingGroup/go-namecase v1.0.0 // indirect - github.com/armon/go-metrics v0.4.1 // indirect github.com/avast/retry-go/v4 v4.5.1 // indirect github.com/aws/aws-sdk-go v1.44.334 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/bits-and-blooms/bitset v1.8.0 // indirect - github.com/boltdb/bolt v1.3.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash v1.1.0 // indirect @@ -129,23 +124,17 @@ require ( github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-getter v1.7.3 // indirect github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-metrics v0.5.2 // indirect - github.com/hashicorp/go-msgpack v1.1.5 // indirect - github.com/hashicorp/go-msgpack/v2 v2.1.1 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.5.2 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hashicorp/raft v1.6.0 // indirect - github.com/hashicorp/raft-boltdb/v2 v2.2.2 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/holiman/uint256 v1.2.3 // indirect diff --git a/test/go.sum b/test/go.sum index c5533b23..03d62792 100644 --- a/test/go.sum +++ b/test/go.sum @@ -172,6 +172,7 @@ cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= +cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= @@ -232,7 +233,6 @@ github.com/ChainSafe/go-schnorrkel v1.0.0 h1:3aDA67lAykLaG1y3AOjs88dMxC88PgUuHRr github.com/ChainSafe/go-schnorrkel v1.0.0/go.mod h1:dpzHYVxLZcp8pjlV+O+UR8K0Hp/z7vcchBSbMBEhCw4= github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420 h1:oknQF/iIhf5lVjbwjsVDzDByupRhga8nhA3NAmwyHDA= github.com/ComposableFi/go-subkey/v2 v2.0.0-tm03420/go.mod h1:KYkiMX5AbOlXXYfxkrYPrRPV6EbVUALTQh5ptUOJzu8= -github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= @@ -240,12 +240,6 @@ github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25Yn github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw= github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc= github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U= -github.com/Jille/raft-grpc-leader-rpc v1.1.0 h1:u36rmA4tjp+4FSdZ17jg/1sfSCYNQIe5bzzwvW0iVTM= -github.com/Jille/raft-grpc-leader-rpc v1.1.0/go.mod h1:l+pK+uPuqpFDFcPmyUPSng4257UXrST0Vc3Lo4XwVB0= -github.com/Jille/raft-grpc-transport v1.4.0 h1:Kwk+IceQD8MpLKOulBu2ignX+aZAEjOhffEhN44sdzQ= -github.com/Jille/raft-grpc-transport v1.4.0/go.mod h1:afVUd8LQKUUo3V/ToLBH3mbSyvivRlMYCDK0eJRGTfQ= -github.com/Jille/raftadmin v1.2.1 h1:P35JYYJmyHnsJ7sEjdXVRRvaqXUQUuNjIqPFf5n3CEM= -github.com/Jille/raftadmin v1.2.1/go.mod h1:CocBqmOFhfk8oIgbzZKYACdm5PizUXfzupqTQV17Pgk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= @@ -277,10 +271,6 @@ github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= -github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= @@ -303,8 +293,6 @@ github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= @@ -418,6 +406,7 @@ github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= @@ -480,9 +469,9 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/ethereum/go-ethereum v1.13.5 h1:U6TCRciCqZRe4FPXmy1sMGxTfuk8P7u2UoinF3VbaFk= github.com/ethereum/go-ethereum v1.13.5/go.mod h1:yMTu38GSuyxaYzQMViqNmQ1s3cE84abZexQmTgenWk0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -490,10 +479,12 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/getsentry/sentry-go v0.25.0 h1:q6Eo+hS+yoJlTO3uu/azhQadsD8V+jQn2D8VvX1eOyI= github.com/getsentry/sentry-go v0.25.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -688,16 +679,12 @@ github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIv github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-getter v1.7.3 h1:bN2+Fw9XPFvOCjB0UOevFIMICZ7G2XSQHzfvLUyOM5E= github.com/hashicorp/go-getter v1.7.3/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= -github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -706,14 +693,7 @@ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh github.com/hashicorp/go-metrics v0.5.2 h1:ErEYO2f//CjKsUDw4SmLzelsK6L3ZmOAR/4P9iS7ruY= github.com/hashicorp/go-metrics v0.5.2/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs= -github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= -github.com/hashicorp/go-msgpack/v2 v2.1.1 h1:xQEY9yB2wnHitoSzk/B9UjXWRQ67QKu5AOm8aFp8N3I= -github.com/hashicorp/go-msgpack/v2 v2.1.1/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y= github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= @@ -725,13 +705,13 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -739,16 +719,6 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/raft v1.1.0/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= -github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= -github.com/hashicorp/raft v1.3.7/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= -github.com/hashicorp/raft v1.6.0 h1:tkIAORZy2GbJ2Trp5eUSggLXDPOJLXC+JJLNMMqtgtM= -github.com/hashicorp/raft v1.6.0/go.mod h1:Xil5pDgeGwRWuX4uPUmwa+7Vagg4N804dz6mhNi6S7o= -github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= -github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea h1:RxcPJuutPRM8PUOyiweMmkuNO+RJyfy2jds2gfvgNmU= -github.com/hashicorp/raft-boltdb v0.0.0-20210409134258-03c10cc3d4ea/go.mod h1:qRd6nFJYYS6Iqnc/8HcUmko2/2Gw8qTFEmxDLii6W5I= -github.com/hashicorp/raft-boltdb/v2 v2.2.2 h1:rlkPtOllgIcKLxVT4nutqlTH2NRFn+tO1wwZk/4Dxqw= -github.com/hashicorp/raft-boltdb/v2 v2.2.2/go.mod h1:N8YgaZgNJLpZC+h+by7vDu5rzsRgONThTEeUS3zWbfY= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= @@ -819,8 +789,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kraken-hpc/go-fork v0.1.1 h1:O3X/ynoNy/eS7UIcZYef8ndFq2RXEIOue9kZqyzF0Sk= -github.com/kraken-hpc/go-fork v0.1.1/go.mod h1:uu0e5h+V4ONH5Qk/xuVlyNXJXy/swhqGIEMK7w+9dNc= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -839,16 +807,12 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -950,6 +914,7 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.28.1 h1:MijcGUbfYuznzK/5R4CPNoUP/9Xvuo20sXfEm6XxoTA= +github.com/onsi/gomega v1.28.1/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -998,7 +963,6 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= @@ -1014,7 +978,6 @@ github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= @@ -1024,7 +987,6 @@ github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16 github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -1056,7 +1018,9 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= @@ -1075,13 +1039,16 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= @@ -1093,6 +1060,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= +github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= github.com/strangelove-ventures/interchaintest/v8 v8.0.0 h1:z6hz23NJEhRJxZUH1vuDNTVscRwnUii+K4En9lfyndc= github.com/strangelove-ventures/interchaintest/v8 v8.0.0/go.mod h1:0LEZ1dXKlAYOm18X6s8AC4y5e6uAjJ0L69A6JpUkjDc= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -1117,6 +1085,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= @@ -1158,7 +1127,6 @@ gitlab.com/unit410/edwards25519 v0.0.0-20220725154547-61980033348e/go.mod h1:lTS gitlab.com/unit410/threshold-ed25519 v0.0.0-20220812172601-56783212c4cc h1:ESmaQH1+uOLW0wOIPT4EVLh+gwKdmKovr+eZg4jWUNs= gitlab.com/unit410/threshold-ed25519 v0.0.0-20220812172601-56783212c4cc/go.mod h1:1CIpwbV+rSyzoBVr6Ok9vvEP7UHrW/d0zEB+jU6+E3o= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= @@ -1179,7 +1147,6 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -1207,6 +1174,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1219,6 +1187,7 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= +golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1294,7 +1263,6 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210907225631-ff17edfbf26d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1308,6 +1276,7 @@ golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1334,6 +1303,7 @@ golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1361,13 +1331,11 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1376,7 +1344,6 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1423,7 +1390,6 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1453,10 +1419,12 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1475,6 +1443,7 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1483,7 +1452,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -1538,6 +1506,7 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1598,6 +1567,7 @@ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= google.golang.org/api v0.152.0 h1:t0r1vPnfMc260S2Ci+en7kfCZaLOPs5KI0sVV/6jZrY= +google.golang.org/api v0.152.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1720,6 +1690,7 @@ google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= diff --git a/test/horcrux_test.go b/test/horcrux_test.go index 4f15bf4a..1887ff9a 100644 --- a/test/horcrux_test.go +++ b/test/horcrux_test.go @@ -6,9 +6,12 @@ import ( "sync" "testing" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + "github.com/cometbft/cometbft/crypto" dockertypes "github.com/docker/docker/api/types" - "github.com/strangelove-ventures/horcrux/signer" + "github.com/strangelove-ventures/horcrux/src/config" + tss "github.com/strangelove-ventures/horcrux/src/tss" interchaintest "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" @@ -24,61 +27,61 @@ const ( ) // Test2Of3SignerOneSentry will spin up a chain with one single-node validator and one horcrux validator -// the horcrux validator will have three cosigner nodes with a threshold of two, and one sentry node +// the horcrux validator will have three cosigner cosigner with a threshold of two, and one sentry node func Test2Of3SignerOneSentry(t *testing.T) { testChainSingleNodeAndHorcruxThreshold(t, 2, 3, 2, 1, 1) } // Test2Of3SignerTwoSentries will spin up a chain with one single-node validator and one horcrux validator -// the horcrux validator will have three cosigner nodes with a threshold of two, and two sentry nodes +// the horcrux validator will have three cosigner cosigner with a threshold of two, and two sentry cosigner // checks that no slashing occurs func Test2Of3SignerTwoSentries(t *testing.T) { testChainSingleNodeAndHorcruxThreshold(t, 2, 3, 2, 2, 2) } // Test2Of3SignerThreeSentries will spin up a chain with one single-node validator and one horcrux validator -// the horcrux validator will have three cosigner nodes with a threshold of two, and three sentry nodes +// the horcrux validator will have three cosigner cosigner with a threshold of two, and three sentry cosigner // where each cosigner connects to all sentries func Test2Of3SignerThreeSentries(t *testing.T) { testChainSingleNodeAndHorcruxThreshold(t, 2, 3, 2, 3, 3) } // Test2Of3SignerThreeSentriesUniqueConnection will spin up a chain with one single-node validator and one horcrux validator -// the horcrux validator will have three cosigner nodes with a threshold of two, and three sentry nodes +// the horcrux validator will have three cosigner cosigner with a threshold of two, and three sentry cosigner // where each cosigner only connects to one sentry func Test2Of3SignerThreeSentriesUniqueConnection(t *testing.T) { testChainSingleNodeAndHorcruxThreshold(t, 2, 3, 2, 3, 1) } // Test2Of3SignerOneSentry will spin up a chain with one single-node validator and one horcrux validator -// the horcrux validator will have three cosigner nodes with a threshold of two, and one sentry node +// the horcrux validator will have three cosigner cosigner with a threshold of two, and one sentry node func Test3Of5SignerOneSentry(t *testing.T) { testChainSingleNodeAndHorcruxThreshold(t, 2, 5, 3, 1, 1) } // Test3Of5SignerTwoSentries will spin up a chain with one single-node validator and one horcrux validator -// the horcrux validator will have five cosigner nodes with a threshold of three, and two sentry nodes +// the horcrux validator will have five cosigner cosigner with a threshold of three, and two sentry cosigner // where each cosigner connects to all sentries. func Test3Of5SignerTwoSentries(t *testing.T) { testChainSingleNodeAndHorcruxThreshold(t, 2, 5, 3, 2, 2) } // Test3Of5SignerFiveSentries will spin up a chain with one single-node validator and one horcrux validator -// the horcrux validator will have five cosigner nodes with a threshold of three, and five sentry nodes +// the horcrux validator will have five cosigner cosigner with a threshold of three, and five sentry cosigner // where each cosigner connects to all sentries. func Test3Of5SignerFiveSentries(t *testing.T) { testChainSingleNodeAndHorcruxThreshold(t, 2, 5, 3, 5, 5) } // Test3Of5SignerFiveSentriesUniqueConnection will spin up a chain with one single-node validator and one horcrux validator -// the horcrux validator will have three cosigner nodes with a threshold of two, and three sentry nodes +// the horcrux validator will have three cosigner cosigner with a threshold of two, and three sentry cosigner // where each cosigner only connects to one sentry. func Test3Of5SignerFiveSentriesUniqueConnection(t *testing.T) { testChainSingleNodeAndHorcruxThreshold(t, 2, 5, 3, 5, 1) } // Test4Of7SignerTwoSentries will spin up a chain with one single-node validator and one horcrux validator -// the horcrux validator will have seven cosigner nodes with a threshold of four, and two sentry nodes +// the horcrux validator will have seven cosigner cosigner with a threshold of four, and two sentry cosigner // where each cosigner connects to all sentries. func Test4Of7SignerTwoSentries(t *testing.T) { testChainSingleNodeAndHorcruxThreshold(t, 2, 7, 4, 2, 2) @@ -137,7 +140,7 @@ func TestUpgradeValidatorToHorcrux(t *testing.T) { requireHealthyValidator(t, cw.chain.Validators[0], pubKey.Address()) } -// TestDownedSigners2of3 tests taking down 2 nodes at a time in the 2/3 threshold horcrux cluster for a period of time. +// TestDownedSigners2of3 tests taking down 2 cosigner at a time in the 2/3 threshold horcrux cluster for a period of time. func TestDownedSigners2of3(t *testing.T) { ctx := context.Background() @@ -180,7 +183,7 @@ func TestDownedSigners2of3(t *testing.T) { } } -// TestDownedSigners3of5 tests taking down 2 nodes at a time in the 3/5 threshold horcrux cluster for a period of time. +// TestDownedSigners3of5 tests taking down 2 cosigner at a time in the 3/5 threshold horcrux cluster for a period of time. func TestDownedSigners3of5(t *testing.T) { ctx := context.Background() @@ -203,7 +206,7 @@ func TestDownedSigners3of5(t *testing.T) { cosigners := ourValidator.Sidecars - // Test taking down 2 nodes at a time in the signer cluster for a period of time + // Test taking down 2 cosigner at a time in the signer cluster for a period of time for i := 0; i < len(cosigners); i++ { cosigner1 := cosigners[i] var cosigner2 *cosmos.SidecarProcess @@ -279,7 +282,7 @@ func TestLeaderElection2of3(t *testing.T) { break } - // electing a specific leader can fail, but this is okay as long as all nodes agree on one leader. + // electing a specific leader can fail, but this is okay as long as all cosigner agree on one leader. // will retry electing the specific leader in the next iteration. var commonLeader int for i, s := range cosigners { @@ -356,13 +359,13 @@ func TestMultipleChainHorcrux(t *testing.T) { for i := 0; i < totalChains; i++ { chainConfigs[i] = &cosignerChainConfig{ sentries: make([]cosmos.ChainNodes, sentriesPerSigner), - shards: make([]signer.CosignerEd25519Key, totalSigners), + shards: make([]tss.Ed25519Key, totalSigners), } } cosignerSidecars := make(horcruxSidecars, totalSigners) - eciesShards, err := signer.CreateCosignerECIESShards(totalSigners) + eciesShards, err := nodesecurity.CreateCosignerECIESShards(totalSigners) require.NoError(t, err) var wg sync.WaitGroup @@ -450,7 +453,7 @@ func TestMultipleChainHorcrux(t *testing.T) { type cosignerChainConfig struct { chainID string - shards []signer.CosignerEd25519Key + shards []tss.Ed25519Key sentries []cosmos.ChainNodes } @@ -464,7 +467,7 @@ type horcruxSidecars []horcruxSidecarProcess func configureAndStartSidecars( ctx context.Context, t *testing.T, - eciesShards []signer.CosignerECIESKey, + eciesShards []nodesecurity.CosignerECIESKey, sidecars horcruxSidecars, threshold int, wg *sync.WaitGroup, @@ -476,9 +479,9 @@ func configureAndStartSidecars( totalSigners := len(sidecars) - cosignersConfig := make(signer.CosignersConfig, totalSigners) + cosignersConfig := make(config.CosignersConfig, totalSigners) for i, s := range sidecars { - cosignersConfig[i] = signer.CosignerConfig{ + cosignersConfig[i] = config.CosignerConfig{ ShardID: i + 1, P2PAddr: fmt.Sprintf("tcp://%s:%s", s.cosigner.HostName(), signerPort), } @@ -492,14 +495,14 @@ func configureAndStartSidecars( numSentries += len(chainConfig.sentries[i]) } - chainNodes := make(signer.ChainNodes, 0, numSentries) + chainNodes := make(config.ChainNodes, 0, numSentries) ed25519Shards := make([]chainEd25519Shard, len(chainConfigs)) for j, chainConfig := range chainConfigs { if s.proxy == nil { for _, sentry := range chainConfig.sentries[i] { - chainNodes = append(chainNodes, signer.ChainNode{ + chainNodes = append(chainNodes, config.ChainNode{ PrivValAddr: fmt.Sprintf("tcp://%s:1234", sentry.HostName()), }) } @@ -516,9 +519,9 @@ func configureAndStartSidecars( grpcAddr = ":5555" } - config := signer.Config{ - SignMode: signer.SignModeThreshold, - ThresholdModeConfig: &signer.ThresholdModeConfig{ + config := config.Config{ + SignMode: config.SignModeThreshold, + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: threshold, Cosigners: cosignersConfig, GRPCTimeout: "200ms", @@ -591,13 +594,13 @@ func TestHorcruxProxyGRPC(t *testing.T) { for i := 0; i < totalChains; i++ { chainConfigs[i] = &cosignerChainConfig{ sentries: make([]cosmos.ChainNodes, sentriesPerSigner), - shards: make([]signer.CosignerEd25519Key, totalSigners), + shards: make([]tss.Ed25519Key, totalSigners), } } cosignerSidecars := make(horcruxSidecars, totalSigners) - eciesShards, err := signer.CreateCosignerECIESShards(totalSigners) + eciesShards, err := nodesecurity.CreateCosignerECIESShards(totalSigners) require.NoError(t, err) var wg sync.WaitGroup diff --git a/test/validator.go b/test/validator.go index 93fbefbb..e1062f10 100644 --- a/test/validator.go +++ b/test/validator.go @@ -14,7 +14,9 @@ import ( "github.com/cosmos/cosmos-sdk/types/bech32" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/docker/docker/client" - "github.com/strangelove-ventures/horcrux/signer/proto" + "github.com/strangelove-ventures/horcrux/proto/strangelove/proto" + + // "github.com/strangelove-ventures/horcrux/src/proto" interchaintest "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" @@ -52,7 +54,7 @@ const ( type chainWrapper struct { chain *cosmos.CosmosChain totalValidators int // total number of validators on chain at genesis - totalSentries int // number of additional sentry nodes + totalSentries int // number of additional sentry cosigner modifyGenesis func(cc ibc.ChainConfig, b []byte) ([]byte, error) preGenesis func(*chainWrapper) func(ibc.ChainConfig) error } @@ -189,7 +191,7 @@ func getPrivvalKey(ctx context.Context, node *cosmos.ChainNode) (privval.FilePVK return pvKey, nil } -// enablePrivvalListener enables the privval listener on the given sentry nodes. +// enablePrivvalListener enables the privval listener on the given sentry cosigner. func enablePrivvalListener( ctx context.Context, logger *zap.Logger, @@ -297,7 +299,7 @@ func getLeader(ctx context.Context, cosigner *cosmos.SidecarProcess) (int, error ctx, cancelFunc := context.WithTimeout(ctx, 10*time.Second) defer cancelFunc() - grpcClient := proto.NewCosignerClient(conn) + grpcClient := proto.NewNodeServiceClient(conn) res, err := grpcClient.GetLeader(ctx, &proto.GetLeaderRequest{}) if err != nil { diff --git a/test/validator_single.go b/test/validator_single.go index c7919ab8..0b13adc3 100644 --- a/test/validator_single.go +++ b/test/validator_single.go @@ -10,7 +10,7 @@ import ( cometjson "github.com/cometbft/cometbft/libs/json" "github.com/cometbft/cometbft/privval" "github.com/docker/docker/client" - "github.com/strangelove-ventures/horcrux/signer" + "github.com/strangelove-ventures/horcrux/src/config" interchaintest "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" @@ -24,7 +24,7 @@ import ( func testChainSingleNodeAndHorcruxSingle( t *testing.T, totalValidators int, // total number of validators on chain (one horcrux + single node for the rest) - totalSentries int, // number of sentry nodes for the single horcrux validator + totalSentries int, // number of sentry cosigner for the single horcrux validator ) { ctx := context.Background() cw, pubKey := startChainSingleNodeAndHorcruxSingle(ctx, t, totalValidators, totalSentries) @@ -40,7 +40,7 @@ func startChainSingleNodeAndHorcruxSingle( ctx context.Context, t *testing.T, totalValidators int, // total number of validators on chain (one horcrux + single node for the rest) - totalSentries int, // number of sentry nodes for the single horcrux validator + totalSentries int, // number of sentry cosigner for the single horcrux validator ) (*chainWrapper, crypto.PubKey) { client, network := interchaintest.DockerSetup(t) logger := zaptest.NewLogger(t) @@ -84,15 +84,15 @@ func preGenesisSingleNodeAndHorcruxSingle( return err } - chainNodes := make(signer.ChainNodes, len(sentries)) + chainNodes := make(config.ChainNodes, len(sentries)) for i, sentry := range sentries { - chainNodes[i] = signer.ChainNode{ + chainNodes[i] = config.ChainNode{ PrivValAddr: fmt.Sprintf("tcp://%s:1234", sentry.HostName()), } } - config := signer.Config{ - SignMode: signer.SignModeSingle, + config := config.Config{ + SignMode: config.SignModeSingle, ChainNodes: chainNodes, } @@ -118,7 +118,7 @@ func writeConfigAndKeysSingle( ctx context.Context, chainID string, singleSigner *cosmos.SidecarProcess, - config signer.Config, + config config.Config, pvKey privval.FilePVKey, ) error { configBz, err := json.Marshal(config) diff --git a/test/validator_threshold.go b/test/validator_threshold.go index b74d2482..9adebef5 100644 --- a/test/validator_threshold.go +++ b/test/validator_threshold.go @@ -12,7 +12,9 @@ import ( "github.com/docker/docker/client" dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" - "github.com/strangelove-ventures/horcrux/signer" + "github.com/strangelove-ventures/horcrux/src/config" + "github.com/strangelove-ventures/horcrux/src/cosigner/nodesecurity" + tss "github.com/strangelove-ventures/horcrux/src/tss" interchaintest "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" @@ -30,7 +32,7 @@ func testChainSingleNodeAndHorcruxThreshold( totalValidators int, // total number of validators on chain (one horcrux + single node for the rest) totalSigners int, // total number of signers for the single horcrux validator threshold uint8, // key shard threshold, and therefore how many horcrux signers must participate to sign a block - totalSentries int, // number of sentry nodes for the single horcrux validator + totalSentries int, // number of sentry cosigner for the single horcrux validator sentriesPerSigner int, // how many sentries should each horcrux signer connect to (min: 1, max: totalSentries) ) { ctx := context.Background() @@ -53,7 +55,7 @@ func startChainSingleNodeAndHorcruxThreshold( totalValidators int, // total number of validators on chain (one horcrux + single node for the rest) totalSigners int, // total number of signers for the single horcrux validator threshold uint8, // key shard threshold, and therefore how many horcrux signers must participate to sign a block - totalSentries int, // number of sentry nodes for the single horcrux validator + totalSentries int, // number of sentry cosigner for the single horcrux validator sentriesPerSigner int, // how many sentries should each horcrux signer connect to (min: 1, max: totalSentries) ) (*chainWrapper, crypto.PubKey) { client, network := interchaintest.DockerSetup(t) @@ -182,12 +184,12 @@ func convertValidatorToHorcrux( return nil, err } - eciesShards, err := signer.CreateCosignerECIESShards(totalSigners) + eciesShards, err := nodesecurity.CreateCosignerECIESShards(totalSigners) if err != nil { return nil, err } - cosigners := make(signer.CosignersConfig, totalSigners) + cosigners := make(config.CosignersConfig, totalSigners) for i := 0; i < totalSigners; i++ { _, err := horcruxSidecar(ctx, validator, fmt.Sprintf("cosigner-%d", i+1), client, network) @@ -195,7 +197,7 @@ func convertValidatorToHorcrux( return nil, err } - cosigners[i] = signer.CosignerConfig{ + cosigners[i] = config.CosignerConfig{ ShardID: i + 1, P2PAddr: fmt.Sprintf("tcp://%s:%s", validator.Sidecars[i].HostName(), signerPort), } @@ -206,16 +208,16 @@ func convertValidatorToHorcrux( cosigner := validator.Sidecars[i] sentriesForCosigner := sentriesForCosigners[i] - chainNodes := make(signer.ChainNodes, len(sentriesForCosigner)) + chainNodes := make(config.ChainNodes, len(sentriesForCosigner)) for i, sentry := range sentriesForCosigner { - chainNodes[i] = signer.ChainNode{ + chainNodes[i] = config.ChainNode{ PrivValAddr: fmt.Sprintf("tcp://%s:1234", sentry.HostName()), } } - config := signer.Config{ - SignMode: signer.SignModeThreshold, - ThresholdModeConfig: &signer.ThresholdModeConfig{ + config := config.Config{ + SignMode: config.SignModeThreshold, + ThresholdModeConfig: &config.ThresholdModeConfig{ Threshold: int(threshold), Cosigners: cosigners, GRPCTimeout: "200ms", @@ -251,29 +253,29 @@ func convertValidatorToHorcrux( } // getPrivvalKey gets the privval key from the validator and creates threshold shards from it. -func getShardedPrivvalKey(ctx context.Context, node *cosmos.ChainNode, threshold uint8, shards uint8) ([]signer.CosignerEd25519Key, crypto.PubKey, error) { +func getShardedPrivvalKey(ctx context.Context, node *cosmos.ChainNode, threshold uint8, shards uint8) ([]tss.Ed25519Key, crypto.PubKey, error) { pvKey, err := getPrivvalKey(ctx, node) if err != nil { return nil, nil, err } - ed25519Shards := signer.CreateEd25519ThresholdSignShards(pvKey, threshold, shards) + vaultKeys, err := tss.GeneratePersistentThresholdSignShards(pvKey.PrivKey.Bytes(), pvKey.PubKey, threshold, shards) - return ed25519Shards, pvKey.PubKey, nil + return vaultKeys, pvKey.PubKey, nil } // chainEd25519Shard is a wrapper for a chain Index and a shard of an ed25519 consensus key. type chainEd25519Shard struct { chainID string - key signer.CosignerEd25519Key + key tss.Ed25519Key } // writeConfigAndKeysThreshold writes the config and keys for a horcrux cosigner to the sidecar's docker volume. func writeConfigAndKeysThreshold( ctx context.Context, cosigner *cosmos.SidecarProcess, - config signer.Config, - eciesKey signer.CosignerECIESKey, + config config.Config, + eciesKey nodesecurity.CosignerECIESKey, ed25519Shards ...chainEd25519Shard, ) error { configBz, err := yaml.Marshal(config) @@ -333,7 +335,7 @@ func getSentriesForCosignerConnection(sentries cosmos.ChainNodes, numSigners int } } - // Each node in the signer cluster is connected to the number of sentry nodes specified by sentriesPerSigner + // Each node in the signer cluster is connected to the number of sentry cosigner specified by sentriesPerSigner } else if sentriesPerSigner > 1 { sentriesIndex := 0