From 6b92c3424414a04cc4626c6ed53bd783b963ee3b Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Mon, 13 Nov 2023 18:58:25 -0700 Subject: [PATCH] Pre-share nonces --- cmd/horcrux/cmd/start.go | 2 +- cmd/horcrux/cmd/threshold.go | 6 + go.mod | 1 + go.sum | 2 + proto/strangelove/horcrux/cosigner.proto | 23 +- signer/cosigner.go | 59 +- signer/cosigner_grpc_server.go | 29 +- signer/cosigner_health.go | 92 +++ signer/cosigner_health_test.go | 29 + signer/cosigner_nonce_cache.go | 189 ++++++ signer/cosigner_nonce_cache_test.go | 43 ++ signer/leader_mock.go | 3 +- signer/local_cosigner.go | 247 +++---- signer/local_cosigner_test.go | 18 +- signer/proto/cosigner.pb.go | 799 +++++++++++++++++++---- signer/remote_cosigner.go | 41 +- signer/remote_signer.go | 10 +- signer/remote_signer_grpc_server.go | 11 +- signer/single_signer_validator.go | 5 +- signer/single_signer_validator_test.go | 17 +- signer/threshold_signer.go | 3 - signer/threshold_signer_soft.go | 10 +- signer/threshold_validator.go | 227 ++----- signer/threshold_validator_test.go | 78 ++- test/go.sum | 4 +- 25 files changed, 1370 insertions(+), 578 deletions(-) create mode 100644 signer/cosigner_health.go create mode 100644 signer/cosigner_health_test.go create mode 100644 signer/cosigner_nonce_cache.go create mode 100644 signer/cosigner_nonce_cache_test.go diff --git a/cmd/horcrux/cmd/start.go b/cmd/horcrux/cmd/start.go index 7e8bf721..f8820533 100644 --- a/cmd/horcrux/cmd/start.go +++ b/cmd/horcrux/cmd/start.go @@ -48,7 +48,7 @@ func startCmd() *cobra.Command { switch config.Config.SignMode { case signer.SignModeThreshold: - services, val, err = NewThresholdValidator(logger) + services, val, err = NewThresholdValidator(cmd.Context(), logger) if err != nil { return err } diff --git a/cmd/horcrux/cmd/threshold.go b/cmd/horcrux/cmd/threshold.go index 3f8e4050..743a9e35 100644 --- a/cmd/horcrux/cmd/threshold.go +++ b/cmd/horcrux/cmd/threshold.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "fmt" "os" "path/filepath" @@ -14,6 +15,7 @@ import ( const maxWaitForSameBlockAttempts = 3 func NewThresholdValidator( + ctx context.Context, logger cometlog.Logger, ) ([]cometservice.Service, *signer.ThresholdValidator, error) { if err := config.Config.ValidateThresholdModeConfig(); err != nil { @@ -92,5 +94,9 @@ func NewThresholdValidator( raftStore.SetThresholdValidator(val) + if err := val.Start(ctx); err != nil { + return nil, nil, fmt.Errorf("failed to start threshold validator: %w", err) + } + return services, val, nil } diff --git a/go.mod b/go.mod index 23284c56..00892c41 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/cosmos/gogoproto v1.4.10 github.com/ethereum/go-ethereum v1.12.0 github.com/gogo/protobuf v1.3.2 + github.com/google/uuid v1.3.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/hashicorp/raft v1.5.0 github.com/hashicorp/raft-boltdb/v2 v2.2.2 diff --git a/go.sum b/go.sum index 7c840123..bb545f47 100644 --- a/go.sum +++ b/go.sum @@ -285,6 +285,8 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= diff --git a/proto/strangelove/horcrux/cosigner.proto b/proto/strangelove/horcrux/cosigner.proto index 16d1d742..788f71dc 100644 --- a/proto/strangelove/horcrux/cosigner.proto +++ b/proto/strangelove/horcrux/cosigner.proto @@ -9,6 +9,7 @@ service Cosigner { rpc GetNonces (GetNoncesRequest) returns (GetNoncesResponse) {} rpc TransferLeadership (TransferLeadershipRequest) returns (TransferLeadershipResponse) {} rpc GetLeader (GetLeaderRequest) returns (GetLeaderResponse) {} + rpc Ping(PingRequest) returns (PingResponse) {} } message Block { @@ -37,6 +38,11 @@ message Nonce { bytes signature = 5; } +message UUIDNonce { + bytes uuid = 1; + repeated Nonce nonces = 2; +} + message HRST { int64 height = 1; int64 round = 2; @@ -45,10 +51,11 @@ message HRST { } message SetNoncesAndSignRequest { - repeated Nonce nonces = 1; - HRST hrst = 2; - bytes signBytes = 3; - string chainID = 4; + bytes uuid = 1; + repeated Nonce nonces = 2; + HRST hrst = 3; + bytes signBytes = 4; + string chainID = 5; } message SetNoncesAndSignResponse { @@ -58,12 +65,11 @@ message SetNoncesAndSignResponse { } message GetNoncesRequest { - HRST hrst = 1; - string chainID = 2; + repeated bytes uuids = 1; } message GetNoncesResponse { - repeated Nonce nonces = 1; + repeated UUIDNonce nonces = 1; } message TransferLeadershipRequest { @@ -80,3 +86,6 @@ message GetLeaderRequest {} message GetLeaderResponse { string leader = 1; } + +message PingRequest {} +message PingResponse {} \ No newline at end of file diff --git a/signer/cosigner.go b/signer/cosigner.go index 2c39fb12..cf737744 100644 --- a/signer/cosigner.go +++ b/signer/cosigner.go @@ -1,9 +1,11 @@ package signer import ( + "context" "time" cometcrypto "github.com/cometbft/cometbft/crypto" + "github.com/google/uuid" "github.com/strangelove-ventures/horcrux/signer/proto" ) @@ -23,10 +25,10 @@ type Cosigner interface { VerifySignature(chainID string, payload, signature []byte) bool // Get nonces for all cosigner shards - GetNonces(chainID string, hrst HRSTKey) (*CosignerNoncesResponse, error) + GetNonces(ctx context.Context, uuids []uuid.UUID) (CosignerUUIDNoncesMultiple, error) // Sign the requested bytes - SetNoncesAndSign(req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) + SetNoncesAndSign(ctx context.Context, req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) } // CosignerSignRequest is sent to a co-signer to obtain their signature for the SignBytes @@ -34,6 +36,7 @@ type Cosigner interface { type CosignerSignRequest struct { ChainID string SignBytes []byte + UUID uuid.UUID } type CosignerSignResponse struct { @@ -87,18 +90,6 @@ func CosignerNoncesFromProto(secretParts []*proto.Nonce) []CosignerNonce { return out } -type CosignerSetNonceRequest struct { - ChainID string - SourceID int - PubKey []byte - Share []byte - Signature []byte - Height int64 - Round int64 - Step int8 - Timestamp time.Time -} - type CosignerSignBlockRequest struct { ChainID string Block *Block @@ -107,14 +98,48 @@ type CosignerSignBlockRequest struct { type CosignerSignBlockResponse struct { Signature []byte } +type CosignerUUIDNonces struct { + UUID uuid.UUID + Nonces CosignerNonces +} + +func (c *CosignerUUIDNonces) For(id int) *CosignerUUIDNonces { + res := &CosignerUUIDNonces{UUID: c.UUID} + for _, nonce := range c.Nonces { + if nonce.DestinationID == id { + res.Nonces = append(res.Nonces, nonce) + } + } + return res +} -type CosignerNoncesResponse struct { - Nonces []CosignerNonce +type CosignerUUIDNoncesMultiple []*CosignerUUIDNonces + +func (n *CosignerUUIDNonces) toProto() *proto.UUIDNonce { + out := &proto.UUIDNonce{ + Uuid: n.UUID[:], + Nonces: make([]*proto.Nonce, len(n.Nonces)), + } + for i, nonce := range n.Nonces { + out.Nonces[i] = nonce.toProto() + } + return out +} + +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 []CosignerNonce + Nonces *CosignerUUIDNonces HRST HRSTKey SignBytes []byte } diff --git a/signer/cosigner_grpc_server.go b/signer/cosigner_grpc_server.go index 7e1df747..36b4a054 100644 --- a/signer/cosigner_grpc_server.go +++ b/signer/cosigner_grpc_server.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/google/uuid" "github.com/hashicorp/raft" "github.com/strangelove-ventures/horcrux/signer/proto" ) @@ -30,10 +31,10 @@ func NewCosignerGRPCServer( } func (rpc *CosignerGRPCServer) SignBlock( - _ context.Context, + ctx context.Context, req *proto.SignBlockRequest, ) (*proto.SignBlockResponse, error) { - res, _, err := rpc.thresholdValidator.SignBlock(req.ChainID, BlockFromProto(req.Block)) + res, _, err := rpc.thresholdValidator.Sign(ctx, req.ChainID, BlockFromProto(req.Block)) if err != nil { return nil, err } @@ -43,12 +44,15 @@ func (rpc *CosignerGRPCServer) SignBlock( } func (rpc *CosignerGRPCServer) SetNoncesAndSign( - _ context.Context, + ctx context.Context, req *proto.SetNoncesAndSignRequest, ) (*proto.SetNoncesAndSignResponse, error) { - res, err := rpc.cosigner.SetNoncesAndSign(CosignerSetNoncesAndSignRequest{ - ChainID: req.ChainID, - Nonces: CosignerNoncesFromProto(req.GetNonces()), + res, err := rpc.cosigner.SetNoncesAndSign(ctx, CosignerSetNoncesAndSignRequest{ + ChainID: req.ChainID, + Nonces: &CosignerUUIDNonces{ + UUID: uuid.UUID(req.Uuid), + Nonces: CosignerNoncesFromProto(req.GetNonces()), + }, HRST: HRSTKeyFromProto(req.GetHrst()), SignBytes: req.GetSignBytes(), }) @@ -78,18 +82,23 @@ func (rpc *CosignerGRPCServer) SetNoncesAndSign( } func (rpc *CosignerGRPCServer) GetNonces( - _ context.Context, + 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( - req.ChainID, - HRSTKeyFromProto(req.GetHrst()), + ctx, + uuids, ) if err != nil { return nil, err } + return &proto.GetNoncesResponse{ - Nonces: CosignerNonces(res.Nonces).toProto(), + Nonces: res.toProto(), }, nil } diff --git a/signer/cosigner_health.go b/signer/cosigner_health.go new file mode 100644 index 00000000..cf916880 --- /dev/null +++ b/signer/cosigner_health.go @@ -0,0 +1,92 @@ +package signer + +import ( + "context" + "sort" + "sync" + "time" + + "github.com/strangelove-ventures/horcrux/signer/proto" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +const ( + pingInterval = 5 * time.Second +) + +type CosignerHealth struct { + cosigners []Cosigner + rtt map[int]int64 + mu sync.RWMutex + + leader Leader +} + +func NewCosignerHealth(cosigners []Cosigner, leader Leader) *CosignerHealth { + return &CosignerHealth{ + cosigners: cosigners, + rtt: make(map[int]int64), + leader: leader, + } +} + +func (ch *CosignerHealth) Start(ctx context.Context) { + ticker := time.NewTicker(pingInterval) + for { + if ch.leader.IsLeader() { + for _, cosigner := range ch.cosigners { + go ch.updateRTT(ctx, cosigner) + } + } + select { + case <-ctx.Done(): + return + case <-ticker.C: + // continue + } + } +} + +func (ch *CosignerHealth) updateRTT(ctx context.Context, cosigner Cosigner) { + var rtt int64 + defer func() { + ch.mu.Lock() + defer ch.mu.Unlock() + ch.rtt[cosigner.GetID()] = rtt + }() + start := time.Now() + conn, err := grpc.Dial(cosigner.GetAddress(), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return + } + client := proto.NewCosignerClient(conn) + _, err = client.Ping(ctx, &proto.PingRequest{}) + if err != nil { + rtt = -1 + } else { + rtt = time.Since(start).Nanoseconds() + } +} + +func (ch *CosignerHealth) GetFastest(n int) []Cosigner { + ch.mu.RLock() + defer ch.mu.RUnlock() + + fastest := make([]Cosigner, len(ch.cosigners)) + copy(fastest, ch.cosigners) + + sort.Slice(fastest, func(i, j int) bool { + rtt1, ok1 := ch.rtt[fastest[i].GetID()] + rtt2, ok2 := ch.rtt[fastest[j].GetID()] + if rtt1 == -1 || !ok1 { + return false + } + if rtt2 == -1 || !ok2 { + return true + } + return rtt1 < rtt2 + }) + + return fastest[:n] +} diff --git a/signer/cosigner_health_test.go b/signer/cosigner_health_test.go new file mode 100644 index 00000000..d1c1990a --- /dev/null +++ b/signer/cosigner_health_test.go @@ -0,0 +1,29 @@ +package signer + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCosignerHealth(t *testing.T) { + ch := NewCosignerHealth([]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(2) + + require.Len(t, fastest, 2) + require.Equal(t, 4, fastest[0].GetID()) + require.Equal(t, 2, fastest[1].GetID()) +} diff --git a/signer/cosigner_nonce_cache.go b/signer/cosigner_nonce_cache.go new file mode 100644 index 00000000..6212a8b3 --- /dev/null +++ b/signer/cosigner_nonce_cache.go @@ -0,0 +1,189 @@ +package signer + +import ( + "context" + "sync" + "time" + + cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/google/uuid" +) + +const defaultGetNoncesInterval = 5 * time.Second +const defaultGetNoncesTimeout = 4 * time.Second + +type CosignerNonceCache struct { + logger cometlog.Logger + cosigners []Cosigner + readyNonces chan *CosignerUUIDNonces + + leader Leader + + lastReconcileNonces int + lastReconcileTime time.Time + noncesPerMinute float64 + + getNoncesInterval time.Duration + getNoncesTimeout time.Duration +} + +func NewCosignerNonceCache( + logger cometlog.Logger, + cosigners []Cosigner, + leader Leader, + getNoncesInterval time.Duration, + getNoncesTimeout time.Duration, +) *CosignerNonceCache { + return &CosignerNonceCache{ + logger: logger, + readyNonces: make(chan *CosignerUUIDNonces, 10000), + cosigners: cosigners, + leader: leader, + getNoncesInterval: getNoncesInterval, + getNoncesTimeout: getNoncesTimeout, + } +} + +func (cnc *CosignerNonceCache) getUuids(n int) []uuid.UUID { + uuids := make([]uuid.UUID, n) + for i := 0; i < n; i++ { + uuids[i] = uuid.New() + } + return uuids +} + +func (cnc *CosignerNonceCache) reconcile(ctx context.Context) { + cnc.logger.Debug("Reconciling nonces") + remainingNonces := len(cnc.readyNonces) + timeSinceLastReconcile := time.Since(cnc.lastReconcileTime) + + // calculate nonces per minute + noncesPerMin := float64(cnc.lastReconcileNonces-remainingNonces) / timeSinceLastReconcile.Minutes() + + if cnc.noncesPerMinute == 0 { + // initialize nonces per minute for weighted average + cnc.noncesPerMinute = noncesPerMin + } else { + // weighted average over last 2 intervals + cnc.noncesPerMinute = (cnc.noncesPerMinute + noncesPerMin) / 2 + } + + defer func() { + cnc.lastReconcileNonces = len(cnc.readyNonces) + cnc.lastReconcileTime = time.Now() + }() + + // calculate how many nonces we need to load to keep up with demand + // load 120% the number of nonces we need to keep up with demand + + target := int((cnc.noncesPerMinute / 60) * cnc.getNoncesInterval.Seconds() * 1.2) + additional := target - remainingNonces + if additional < 0 { + // we're ahead of demand, don't load any more + cnc.logger.Debug( + "Cosigner nonce cache ahead of demand", + "target", target, + "remaining", remainingNonces, + "noncesPerMin", cnc.noncesPerMinute, + ) + + return + } + + cnc.logger.Debug( + "Loading additional nonces to meet demand", + "target", target, + "remaining", remainingNonces, + "additional", additional, + "noncesPerMin", cnc.noncesPerMinute, + ) + + cnc.LoadN(ctx, additional) +} + +func (cnc *CosignerNonceCache) LoadN(ctx context.Context, n int) { + uuids := cnc.getUuids(n) + nonces := make([]CosignerUUIDNoncesMultiple, len(cnc.cosigners)) + var wg sync.WaitGroup + wg.Add(len(cnc.cosigners)) + for i, p := range cnc.cosigners { + i := i + p := p + go func() { + defer wg.Done() + ctx, cancel := context.WithTimeout(ctx, cnc.getNoncesTimeout) + defer cancel() + n, err := p.GetNonces(ctx, uuids) + if err != nil { + cnc.logger.Error("Failed to get nonces from peer", "peer", p.GetID(), "error", err) + return + } + nonces[i] = n + }() + } + wg.Wait() + for i, u := range uuids { + nonce := &CosignerUUIDNonces{ + UUID: u, + } + for _, n := range nonces { + if n == nil { + continue + } + nonce.Nonces = append(nonce.Nonces, n[i].Nonces...) + } + cnc.readyNonces <- nonce + } + cnc.logger.Debug("Loaded nonces", "count", n) +} + +func (cnc *CosignerNonceCache) Start(ctx context.Context) { + // tiered startup to quickly bootstrap nonces for immediate signing + for i := 1; i < 10; i++ { + cnc.LoadN(ctx, i*20) + } + + cnc.lastReconcileNonces = len(cnc.readyNonces) + cnc.lastReconcileTime = time.Now() + + ticker := time.NewTicker(cnc.getNoncesInterval) + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + if cnc.leader.IsLeader() { + cnc.reconcile(ctx) + } + } + } +} + +func (cnc *CosignerNonceCache) GetNonces(ctx context.Context, fastestPeers []Cosigner) (*CosignerUUIDNonces, error) { +CheckNoncesLoop: + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case out := <-cnc.readyNonces: + for _, p := range fastestPeers { + found := false + for _, n := range out.Nonces { + if n.SourceID == p.GetID() { + found = true + break + } + } + if !found { + // this set of nonces doesn't have the peer we need + // TODO this uuid should be discarded on all cosigners + // send delete request via raft? + continue CheckNoncesLoop + } + } + + // all peers found + return out, nil + } + } +} diff --git a/signer/cosigner_nonce_cache_test.go b/signer/cosigner_nonce_cache_test.go new file mode 100644 index 00000000..f096519c --- /dev/null +++ b/signer/cosigner_nonce_cache_test.go @@ -0,0 +1,43 @@ +package signer + +import ( + "context" + "os" + "testing" + "time" + + cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/stretchr/testify/require" +) + +func TestNonceCacheDemand(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]}}, + 500*time.Millisecond, + 100*time.Millisecond, + ) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + go nonceCache.Start(ctx) + + for i := 0; i < 3000; i++ { + nonceCache.GetNonces(ctx, []Cosigner{cosigners[0], cosigners[1]}) + time.Sleep(10 * time.Millisecond) + require.Greater(t, len(nonceCache.readyNonces), 0) + } + + require.Greater(t, len(nonceCache.readyNonces), 0) + + target := int(nonceCache.noncesPerMinute * .01) + require.LessOrEqual(t, len(nonceCache.readyNonces), target) +} diff --git a/signer/leader_mock.go b/signer/leader_mock.go index 0d459cfe..b211a3d7 100644 --- a/signer/leader_mock.go +++ b/signer/leader_mock.go @@ -1,6 +1,7 @@ package signer import ( + "context" "errors" "sync" "time" @@ -50,7 +51,7 @@ func (m *MockLeader) SignBlock(req CosignerSignBlockRequest) (*CosignerSignBlock SignBytes: req.Block.SignBytes, Timestamp: req.Block.Timestamp, } - res, _, err := l.SignBlock(req.ChainID, block) + res, _, err := l.Sign(context.TODO(), req.ChainID, block) if err != nil { return nil, err } diff --git a/signer/local_cosigner.go b/signer/local_cosigner.go index ad8f6a5b..e1d1be40 100644 --- a/signer/local_cosigner.go +++ b/signer/local_cosigner.go @@ -1,6 +1,7 @@ package signer import ( + "context" "errors" "fmt" "sync" @@ -9,6 +10,7 @@ import ( cometcrypto "github.com/cometbft/cometbft/crypto" cometcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" cometlog "github.com/cometbft/cometbft/libs/log" + "github.com/google/uuid" "golang.org/x/sync/errgroup" ) @@ -24,6 +26,10 @@ type LocalCosigner struct { chainState sync.Map address string pendingDiskWG sync.WaitGroup + + nonces map[uuid.UUID][]Nonces + // protects the nonces map + noncesMu sync.RWMutex } func NewLocalCosigner( @@ -37,6 +43,7 @@ func NewLocalCosigner( config: config, security: security, address: address, + nonces: make(map[uuid.UUID][]Nonces), } } @@ -44,21 +51,15 @@ 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 *SignState - - // Signing is thread safe - mutex is used for putting locks so only one goroutine can r/w to the function - mu sync.RWMutex // signer generates nonces, combines nonces, signs, and verifies signatures. signer ThresholdSigner - - // Height, Round, Step -> metadata - nonces map[HRSTKey][]Nonces } -func (ccs *ChainState) combinedNonces(myID int, threshold uint8, hrst HRSTKey) ([]Nonce, error) { - ccs.mu.RLock() - defer ccs.mu.RUnlock() +func (cosigner *LocalCosigner) combinedNonces(myID int, threshold uint8, uuid uuid.UUID) ([]Nonce, error) { + cosigner.noncesMu.RLock() + defer cosigner.noncesMu.RUnlock() - nonces, ok := ccs.nonces[hrst] + nonces, ok := cosigner.nonces[uuid] if !ok { return nil, errors.New("no metadata at HRS") } @@ -80,15 +81,6 @@ func (ccs *ChainState) combinedNonces(myID int, threshold uint8, hrst HRSTKey) ( return combinedNonces, nil } -type CosignerGetNonceRequest struct { - ChainID string - ID int - Height int64 - Round int64 - Step int8 - Timestamp time.Time -} - // Save updates the high watermark height/round/step (HRS) 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. @@ -174,7 +166,10 @@ func (cosigner *LocalCosigner) VerifySignature(chainID string, payload, signatur return false } - return cometcryptoed25519.PubKey(ccs.signer.PubKey()).VerifySignature(payload, signature) + sig := make([]byte, len(signature)) + copy(sig, signature) + + return cometcryptoed25519.PubKey(ccs.signer.PubKey()).VerifySignature(payload, sig) } // Sign the sign request using the cosigner's shard @@ -208,7 +203,7 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon return res, nil } - nonces, err := ccs.combinedNonces(cosigner.GetID(), uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), hrst) + nonces, err := cosigner.combinedNonces(cosigner.GetID(), uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), req.UUID) if err != nil { return res, err } @@ -232,15 +227,9 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon } } - ccs.mu.Lock() - for existingKey := range ccs.nonces { - // delete any HRS lower than our signed level - // we will not be providing parts for any lower HRS - if existingKey.HRSKey().LessThan(hrst.HRSKey()) { - delete(ccs.nonces, existingKey) - } - } - ccs.mu.Unlock() + cosigner.noncesMu.Lock() + delete(cosigner.nonces, req.UUID) + cosigner.noncesMu.Unlock() res.Signature = sig @@ -250,17 +239,14 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon return res, nil } -func (cosigner *LocalCosigner) dealShares(req CosignerGetNonceRequest) ([]Nonces, error) { - chainID := req.ChainID - - ccs, err := cosigner.getChainState(chainID) - if err != nil { - return nil, err - } - - meta := make([]Nonces, len(cosigner.config.Config.ThresholdModeConfig.Cosigners)) +func (cosigner *LocalCosigner) generateNonces(uuid uuid.UUID) ([]Nonces, error) { + total := len(cosigner.config.Config.ThresholdModeConfig.Cosigners) + meta := make([]Nonces, total) - nonces, err := ccs.signer.GenerateNonces() + nonces, err := GenerateNonces( + uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), + uint8(total), + ) if err != nil { return nil, err } @@ -293,7 +279,6 @@ func (cosigner *LocalCosigner) LoadSignStateIfNecessary(chainID string) error { cosigner.chainState.Store(chainID, &ChainState{ lastSignState: signState, - nonces: make(map[HRSTKey][]Nonces), signer: signer, }) @@ -301,125 +286,106 @@ func (cosigner *LocalCosigner) LoadSignStateIfNecessary(chainID string) error { } func (cosigner *LocalCosigner) GetNonces( - chainID string, - hrst HRSTKey, -) (*CosignerNoncesResponse, error) { + _ context.Context, + uuids []uuid.UUID, +) (CosignerUUIDNoncesMultiple, error) { metricsTimeKeeper.SetPreviousLocalNonce(time.Now()) - if err := cosigner.LoadSignStateIfNecessary(chainID); err != nil { - return nil, err - } - total := len(cosigner.config.Config.ThresholdModeConfig.Cosigners) - res := &CosignerNoncesResponse{ - Nonces: make([]CosignerNonce, total-1), - } + res := make(CosignerUUIDNoncesMultiple, len(uuids)) id := cosigner.GetID() - var eg errgroup.Group + var outerEg errgroup.Group // getting nonces requires encrypting and signing for each cosigner, // so we perform these operations in parallel. - for i := 0; i < total; i++ { - peerID := i + 1 - if peerID == id { - continue - } + for j, u := range uuids { + j := j + u := u - i := i + outerEg.Go(func() error { + var eg errgroup.Group - eg.Go(func() error { - secretPart, err := cosigner.getNonce(CosignerGetNonceRequest{ - ChainID: chainID, - ID: peerID, - Height: hrst.Height, - Round: hrst.Round, - Step: hrst.Step, - Timestamp: time.Unix(0, hrst.Timestamp), - }) - - if i >= id { - res.Nonces[i-1] = secretPart - } else { - res.Nonces[i] = secretPart + nonces := make([]CosignerNonce, total-1) + + for i := 0; i < total; i++ { + peerID := i + 1 + if peerID == id { + continue + } + + i := i + + eg.Go(func() error { + secretPart, err := cosigner.getNonce(u, peerID) + + if i >= id { + nonces[i-1] = secretPart + } else { + nonces[i] = secretPart + } + + return err + }) } - return err + if err := eg.Wait(); err != nil { + return err + } + + res[j] = &CosignerUUIDNonces{ + UUID: u, + Nonces: nonces, + } + + return nil }) } - if err := eg.Wait(); err != nil { + if err := outerEg.Wait(); err != nil { return nil, err } - cosigner.logger.Debug( - "Generated nonces", - "chain_id", chainID, - "height", hrst.Height, - "round", hrst.Round, - "step", hrst.Step, - ) - return res, nil } -func (cosigner *LocalCosigner) dealSharesIfNecessary(chainID string, hrst HRSTKey) ([]Nonces, error) { - ccs, err := cosigner.getChainState(chainID) - if err != nil { - return nil, err - } - +func (cosigner *LocalCosigner) generateNoncesIfNecessary(uuid uuid.UUID) ([]Nonces, error) { // protects the meta map - ccs.mu.Lock() - defer ccs.mu.Unlock() + cosigner.noncesMu.Lock() + defer cosigner.noncesMu.Unlock() - nonces, ok := ccs.nonces[hrst] - if ok { + if nonces, ok := cosigner.nonces[uuid]; ok { return nonces, nil } - newNonces, err := cosigner.dealShares(CosignerGetNonceRequest{ - ChainID: chainID, - Height: hrst.Height, - Round: hrst.Round, - Step: hrst.Step, - Timestamp: time.Unix(0, hrst.Timestamp), - }) - + newNonces, err := cosigner.generateNonces(uuid) if err != nil { return nil, err } - ccs.nonces[hrst] = newNonces + cosigner.nonces[uuid] = newNonces return newNonces, nil } // Get the ephemeral secret part for an ephemeral share // The ephemeral secret part is encrypted for the receiver func (cosigner *LocalCosigner) getNonce( - req CosignerGetNonceRequest, + uuid uuid.UUID, + peerID int, ) (CosignerNonce, error) { - chainID := req.ChainID zero := CosignerNonce{} - hrst := HRSTKey{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - Timestamp: req.Timestamp.UnixNano(), - } - id := cosigner.GetID() - meta, err := cosigner.dealSharesIfNecessary(chainID, hrst) + meta, err := cosigner.generateNoncesIfNecessary(uuid) if err != nil { return zero, err } ourCosignerMeta := meta[id-1] - nonce, err := cosigner.security.EncryptAndSign(req.ID, ourCosignerMeta.PubKey, ourCosignerMeta.Shares[req.ID-1]) + nonce, err := cosigner.security.EncryptAndSign(peerID, ourCosignerMeta.PubKey, ourCosignerMeta.Shares[peerID-1]) if err != nil { return zero, err } @@ -428,59 +394,43 @@ func (cosigner *LocalCosigner) getNonce( } // setNonce stores a nonce provided by another cosigner -func (cosigner *LocalCosigner) setNonce(req CosignerSetNonceRequest) error { - chainID := req.ChainID - - ccs, err := cosigner.getChainState(chainID) - if err != nil { - return err - } - +func (cosigner *LocalCosigner) setNonce(uuid uuid.UUID, nonce CosignerNonce) error { // Verify the source signature - if req.Signature == nil { + if nonce.Signature == nil { return errors.New("signature field is required") } noncePub, nonceShare, err := cosigner.security.DecryptAndVerify( - req.SourceID, req.PubKey, req.Share, req.Signature) + nonce.SourceID, nonce.PubKey, nonce.Share, nonce.Signature) if err != nil { return err } - hrst := HRSTKey{ - Height: req.Height, - Round: req.Round, - Step: req.Step, - Timestamp: req.Timestamp.UnixNano(), - } - // protects the meta map - ccs.mu.Lock() - defer ccs.mu.Unlock() + cosigner.noncesMu.Lock() + defer cosigner.noncesMu.Unlock() - nonces, ok := ccs.nonces[hrst] + nonces, ok := cosigner.nonces[uuid] // generate metadata placeholder if !ok { return fmt.Errorf( - "unexpected state, metadata does not exist for H: %d, R: %d, S: %d, T: %d", - hrst.Height, - hrst.Round, - hrst.Step, - hrst.Timestamp, + "unexpected state, metadata does not exist for U: %s", + uuid, ) } // set slot - if nonces[req.SourceID-1].Shares == nil { - nonces[req.SourceID-1].Shares = make([][]byte, len(cosigner.config.Config.ThresholdModeConfig.Cosigners)) + if nonces[nonce.SourceID-1].Shares == nil { + nonces[nonce.SourceID-1].Shares = make([][]byte, len(cosigner.config.Config.ThresholdModeConfig.Cosigners)) } - nonces[req.SourceID-1].Shares[cosigner.GetID()-1] = nonceShare - nonces[req.SourceID-1].PubKey = noncePub + nonces[nonce.SourceID-1].Shares[cosigner.GetID()-1] = nonceShare + nonces[nonce.SourceID-1].PubKey = noncePub return nil } func (cosigner *LocalCosigner) SetNoncesAndSign( + _ context.Context, req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) { chainID := req.ChainID @@ -493,21 +443,11 @@ func (cosigner *LocalCosigner) SetNoncesAndSign( // setting nonces requires decrypting and verifying signature from each cosigner, // so we perform these operations in parallel. - for _, secretPart := range req.Nonces { + for _, secretPart := range req.Nonces.Nonces { secretPart := secretPart eg.Go(func() error { - return cosigner.setNonce(CosignerSetNonceRequest{ - ChainID: chainID, - SourceID: secretPart.SourceID, - PubKey: secretPart.PubKey, - Share: secretPart.Share, - Signature: secretPart.Signature, - Height: req.HRST.Height, - Round: req.HRST.Round, - Step: req.HRST.Step, - Timestamp: time.Unix(0, req.HRST.Timestamp), - }) + return cosigner.setNonce(req.Nonces.UUID, secretPart) }) } @@ -516,6 +456,7 @@ func (cosigner *LocalCosigner) SetNoncesAndSign( } res, err := cosigner.sign(CosignerSignRequest{ + UUID: req.Nonces.UUID, ChainID: chainID, SignBytes: req.SignBytes, }) diff --git a/signer/local_cosigner_test.go b/signer/local_cosigner_test.go index da4e907f..462da002 100644 --- a/signer/local_cosigner_test.go +++ b/signer/local_cosigner_test.go @@ -1,6 +1,7 @@ package signer import ( + "context" "crypto/rand" "crypto/rsa" "fmt" @@ -15,6 +16,7 @@ import ( comet "github.com/cometbft/cometbft/types" "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/secp256k1" + "github.com/google/uuid" "github.com/stretchr/testify/require" tsed25519 "gitlab.com/unit410/threshold-ed25519/pkg" ) @@ -112,6 +114,8 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi }, } + ctx := context.Background() + tmpDir := t.TempDir() thresholdCosigners := make([]*LocalCosigner, threshold) @@ -126,6 +130,9 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi Timestamp: now.UnixNano(), } + u, err := uuid.NewRandom() + require.NoError(t, err) + for i := 0; i < int(total); i++ { id := i + 1 @@ -169,10 +176,10 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi if i < int(threshold) { thresholdCosigners[i] = cosigner - nonce, err := cosigner.GetNonces(testChainID, hrst) + res, err := cosigner.GetNonces(ctx, []uuid.UUID{u}) require.NoError(t, err) - nonces[i] = nonce.Nonces + nonces[i] = res[0].Nonces } } @@ -202,9 +209,12 @@ func testLocalCosignerSign(t *testing.T, threshold, total uint8, security []Cosi } } - sigRes, err := cosigner.SetNoncesAndSign(CosignerSetNoncesAndSignRequest{ + sigRes, err := cosigner.SetNoncesAndSign(ctx, CosignerSetNoncesAndSignRequest{ + Nonces: &CosignerUUIDNonces{ + UUID: u, + Nonces: cosignerNonces, + }, ChainID: testChainID, - Nonces: cosignerNonces, HRST: hrst, SignBytes: signBytes, }) diff --git a/signer/proto/cosigner.pb.go b/signer/proto/cosigner.pb.go index 7254a04a..9fa3333a 100644 --- a/signer/proto/cosigner.pb.go +++ b/signer/proto/cosigner.pb.go @@ -283,6 +283,58 @@ func (m *Nonce) GetSignature() []byte { return nil } +type UUIDNonce struct { + 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 (m *UUIDNonce) Reset() { *m = UUIDNonce{} } +func (m *UUIDNonce) String() string { return proto.CompactTextString(m) } +func (*UUIDNonce) ProtoMessage() {} +func (*UUIDNonce) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{4} +} +func (m *UUIDNonce) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UUIDNonce) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UUIDNonce.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UUIDNonce) XXX_Merge(src proto.Message) { + xxx_messageInfo_UUIDNonce.Merge(m, src) +} +func (m *UUIDNonce) XXX_Size() int { + return m.Size() +} +func (m *UUIDNonce) XXX_DiscardUnknown() { + xxx_messageInfo_UUIDNonce.DiscardUnknown(m) +} + +var xxx_messageInfo_UUIDNonce proto.InternalMessageInfo + +func (m *UUIDNonce) GetUuid() []byte { + if m != nil { + return m.Uuid + } + return nil +} + +func (m *UUIDNonce) GetNonces() []*Nonce { + if m != nil { + return m.Nonces + } + return nil +} + type HRST struct { Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` Round int64 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` @@ -294,7 +346,7 @@ func (m *HRST) Reset() { *m = HRST{} } func (m *HRST) String() string { return proto.CompactTextString(m) } func (*HRST) ProtoMessage() {} func (*HRST) Descriptor() ([]byte, []int) { - return fileDescriptor_b7a1f695b94b848a, []int{4} + return fileDescriptor_b7a1f695b94b848a, []int{5} } func (m *HRST) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -352,17 +404,18 @@ func (m *HRST) GetTimestamp() int64 { } type SetNoncesAndSignRequest struct { - Nonces []*Nonce `protobuf:"bytes,1,rep,name=nonces,proto3" json:"nonces,omitempty"` - Hrst *HRST `protobuf:"bytes,2,opt,name=hrst,proto3" json:"hrst,omitempty"` - SignBytes []byte `protobuf:"bytes,3,opt,name=signBytes,proto3" json:"signBytes,omitempty"` - ChainID string `protobuf:"bytes,4,opt,name=chainID,proto3" json:"chainID,omitempty"` + 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 (m *SetNoncesAndSignRequest) Reset() { *m = SetNoncesAndSignRequest{} } func (m *SetNoncesAndSignRequest) String() string { return proto.CompactTextString(m) } func (*SetNoncesAndSignRequest) ProtoMessage() {} func (*SetNoncesAndSignRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b7a1f695b94b848a, []int{5} + return fileDescriptor_b7a1f695b94b848a, []int{6} } func (m *SetNoncesAndSignRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -391,6 +444,13 @@ func (m *SetNoncesAndSignRequest) XXX_DiscardUnknown() { var xxx_messageInfo_SetNoncesAndSignRequest proto.InternalMessageInfo +func (m *SetNoncesAndSignRequest) GetUuid() []byte { + if m != nil { + return m.Uuid + } + return nil +} + func (m *SetNoncesAndSignRequest) GetNonces() []*Nonce { if m != nil { return m.Nonces @@ -429,7 +489,7 @@ func (m *SetNoncesAndSignResponse) Reset() { *m = SetNoncesAndSignRespon func (m *SetNoncesAndSignResponse) String() string { return proto.CompactTextString(m) } func (*SetNoncesAndSignResponse) ProtoMessage() {} func (*SetNoncesAndSignResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b7a1f695b94b848a, []int{6} + return fileDescriptor_b7a1f695b94b848a, []int{7} } func (m *SetNoncesAndSignResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -480,15 +540,14 @@ func (m *SetNoncesAndSignResponse) GetSignature() []byte { } type GetNoncesRequest struct { - Hrst *HRST `protobuf:"bytes,1,opt,name=hrst,proto3" json:"hrst,omitempty"` - ChainID string `protobuf:"bytes,2,opt,name=chainID,proto3" json:"chainID,omitempty"` + Uuids [][]byte `protobuf:"bytes,1,rep,name=uuids,proto3" json:"uuids,omitempty"` } func (m *GetNoncesRequest) Reset() { *m = GetNoncesRequest{} } func (m *GetNoncesRequest) String() string { return proto.CompactTextString(m) } func (*GetNoncesRequest) ProtoMessage() {} func (*GetNoncesRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b7a1f695b94b848a, []int{7} + return fileDescriptor_b7a1f695b94b848a, []int{8} } func (m *GetNoncesRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -517,29 +576,22 @@ func (m *GetNoncesRequest) XXX_DiscardUnknown() { var xxx_messageInfo_GetNoncesRequest proto.InternalMessageInfo -func (m *GetNoncesRequest) GetHrst() *HRST { +func (m *GetNoncesRequest) GetUuids() [][]byte { if m != nil { - return m.Hrst + return m.Uuids } return nil } -func (m *GetNoncesRequest) GetChainID() string { - if m != nil { - return m.ChainID - } - return "" -} - type GetNoncesResponse struct { - Nonces []*Nonce `protobuf:"bytes,1,rep,name=nonces,proto3" json:"nonces,omitempty"` + Nonces []*UUIDNonce `protobuf:"bytes,1,rep,name=nonces,proto3" json:"nonces,omitempty"` } func (m *GetNoncesResponse) Reset() { *m = GetNoncesResponse{} } func (m *GetNoncesResponse) String() string { return proto.CompactTextString(m) } func (*GetNoncesResponse) ProtoMessage() {} func (*GetNoncesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b7a1f695b94b848a, []int{8} + return fileDescriptor_b7a1f695b94b848a, []int{9} } func (m *GetNoncesResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -568,7 +620,7 @@ func (m *GetNoncesResponse) XXX_DiscardUnknown() { var xxx_messageInfo_GetNoncesResponse proto.InternalMessageInfo -func (m *GetNoncesResponse) GetNonces() []*Nonce { +func (m *GetNoncesResponse) GetNonces() []*UUIDNonce { if m != nil { return m.Nonces } @@ -583,7 +635,7 @@ func (m *TransferLeadershipRequest) Reset() { *m = TransferLeadershipReq func (m *TransferLeadershipRequest) String() string { return proto.CompactTextString(m) } func (*TransferLeadershipRequest) ProtoMessage() {} func (*TransferLeadershipRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b7a1f695b94b848a, []int{9} + return fileDescriptor_b7a1f695b94b848a, []int{10} } func (m *TransferLeadershipRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -628,7 +680,7 @@ func (m *TransferLeadershipResponse) Reset() { *m = TransferLeadershipRe func (m *TransferLeadershipResponse) String() string { return proto.CompactTextString(m) } func (*TransferLeadershipResponse) ProtoMessage() {} func (*TransferLeadershipResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b7a1f695b94b848a, []int{10} + return fileDescriptor_b7a1f695b94b848a, []int{11} } func (m *TransferLeadershipResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -678,7 +730,7 @@ func (m *GetLeaderRequest) Reset() { *m = GetLeaderRequest{} } func (m *GetLeaderRequest) String() string { return proto.CompactTextString(m) } func (*GetLeaderRequest) ProtoMessage() {} func (*GetLeaderRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_b7a1f695b94b848a, []int{11} + return fileDescriptor_b7a1f695b94b848a, []int{12} } func (m *GetLeaderRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -715,7 +767,7 @@ func (m *GetLeaderResponse) Reset() { *m = GetLeaderResponse{} } func (m *GetLeaderResponse) String() string { return proto.CompactTextString(m) } func (*GetLeaderResponse) ProtoMessage() {} func (*GetLeaderResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_b7a1f695b94b848a, []int{12} + return fileDescriptor_b7a1f695b94b848a, []int{13} } func (m *GetLeaderResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -751,11 +803,84 @@ func (m *GetLeaderResponse) GetLeader() string { return "" } +type PingRequest struct { +} + +func (m *PingRequest) Reset() { *m = PingRequest{} } +func (m *PingRequest) String() string { return proto.CompactTextString(m) } +func (*PingRequest) ProtoMessage() {} +func (*PingRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{14} +} +func (m *PingRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PingRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PingRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PingRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PingRequest.Merge(m, src) +} +func (m *PingRequest) XXX_Size() int { + return m.Size() +} +func (m *PingRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PingRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_PingRequest proto.InternalMessageInfo + +type PingResponse struct { +} + +func (m *PingResponse) Reset() { *m = PingResponse{} } +func (m *PingResponse) String() string { return proto.CompactTextString(m) } +func (*PingResponse) ProtoMessage() {} +func (*PingResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_b7a1f695b94b848a, []int{15} +} +func (m *PingResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PingResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PingResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PingResponse.Merge(m, src) +} +func (m *PingResponse) XXX_Size() int { + return m.Size() +} +func (m *PingResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PingResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PingResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*Block)(nil), "strangelove.horcrux.Block") proto.RegisterType((*SignBlockRequest)(nil), "strangelove.horcrux.SignBlockRequest") proto.RegisterType((*SignBlockResponse)(nil), "strangelove.horcrux.SignBlockResponse") proto.RegisterType((*Nonce)(nil), "strangelove.horcrux.Nonce") + proto.RegisterType((*UUIDNonce)(nil), "strangelove.horcrux.UUIDNonce") proto.RegisterType((*HRST)(nil), "strangelove.horcrux.HRST") proto.RegisterType((*SetNoncesAndSignRequest)(nil), "strangelove.horcrux.SetNoncesAndSignRequest") proto.RegisterType((*SetNoncesAndSignResponse)(nil), "strangelove.horcrux.SetNoncesAndSignResponse") @@ -765,6 +890,8 @@ func init() { proto.RegisterType((*TransferLeadershipResponse)(nil), "strangelove.horcrux.TransferLeadershipResponse") proto.RegisterType((*GetLeaderRequest)(nil), "strangelove.horcrux.GetLeaderRequest") proto.RegisterType((*GetLeaderResponse)(nil), "strangelove.horcrux.GetLeaderResponse") + proto.RegisterType((*PingRequest)(nil), "strangelove.horcrux.PingRequest") + proto.RegisterType((*PingResponse)(nil), "strangelove.horcrux.PingResponse") } func init() { @@ -772,50 +899,54 @@ func init() { } var fileDescriptor_b7a1f695b94b848a = []byte{ - // 681 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x55, 0xdf, 0x4e, 0xd4, 0x4e, - 0x14, 0xde, 0xd9, 0x6d, 0xf7, 0xc7, 0x1e, 0xf8, 0x25, 0x30, 0x1a, 0x2c, 0x8d, 0xd9, 0x6c, 0x26, - 0x6a, 0x48, 0x94, 0x5d, 0x83, 0x26, 0x5e, 0x83, 0x26, 0x68, 0x34, 0x68, 0x0a, 0x57, 0x4a, 0x48, - 0xba, 0xdd, 0x61, 0xdb, 0xb8, 0x74, 0x96, 0x99, 0x29, 0xc2, 0x03, 0x78, 0xcf, 0x8d, 0x6f, 0xe2, - 0x43, 0x78, 0xc9, 0xa5, 0x97, 0x06, 0x5e, 0xc4, 0xcc, 0x74, 0x5a, 0xda, 0xd2, 0x45, 0x62, 0xbc, - 0x6a, 0xcf, 0xe9, 0xf9, 0xf3, 0x7d, 0xe7, 0x7c, 0x33, 0x05, 0x22, 0x24, 0xf7, 0xe3, 0x31, 0x9d, - 0xb0, 0x63, 0x3a, 0x08, 0x19, 0x0f, 0x78, 0x72, 0x32, 0x08, 0x98, 0x88, 0xc6, 0x31, 0xe5, 0xfd, - 0x29, 0x67, 0x92, 0xe1, 0x3b, 0x85, 0x98, 0xbe, 0x89, 0x21, 0x5f, 0x11, 0xd8, 0x9b, 0x13, 0x16, - 0x7c, 0xc6, 0xcb, 0xd0, 0x0e, 0x69, 0x34, 0x0e, 0xa5, 0x83, 0x7a, 0x68, 0xb5, 0xe5, 0x19, 0x0b, - 0xdf, 0x05, 0x9b, 0xb3, 0x24, 0x1e, 0x39, 0x4d, 0xed, 0x4e, 0x0d, 0x8c, 0xc1, 0x12, 0x92, 0x4e, - 0x9d, 0x56, 0x0f, 0xad, 0xda, 0x9e, 0x7e, 0xc7, 0xf7, 0xa1, 0xa3, 0x1a, 0x6e, 0x9e, 0x4a, 0x2a, - 0x1c, 0xab, 0x87, 0x56, 0x17, 0xbc, 0x2b, 0x87, 0xfa, 0x2a, 0xa3, 0x43, 0x2a, 0xa4, 0x7f, 0x38, - 0x75, 0x6c, 0x5d, 0xeb, 0xca, 0x41, 0xf6, 0x61, 0x71, 0x47, 0x85, 0x2a, 0x28, 0x1e, 0x3d, 0x4a, - 0xa8, 0x90, 0xd8, 0x81, 0xff, 0x82, 0xd0, 0x8f, 0xe2, 0x37, 0xaf, 0x34, 0xa4, 0x8e, 0x97, 0x99, - 0xf8, 0x29, 0xd8, 0x43, 0x15, 0xa9, 0x31, 0xcd, 0xaf, 0xbb, 0xfd, 0x1a, 0x6a, 0xfd, 0xb4, 0x56, - 0x1a, 0x48, 0xde, 0xc3, 0x52, 0xa1, 0xbe, 0x98, 0xb2, 0x58, 0xd0, 0x0c, 0xb0, 0x2f, 0x13, 0x4e, - 0x75, 0x0b, 0x03, 0x58, 0x3b, 0xca, 0x80, 0x9b, 0x55, 0xc0, 0xdf, 0x10, 0xd8, 0xdb, 0x2c, 0x0e, - 0x28, 0x76, 0x61, 0x4e, 0xb0, 0x84, 0x07, 0xd4, 0xe0, 0xb4, 0xbd, 0xdc, 0xc6, 0x0f, 0xe0, 0xff, - 0x11, 0x15, 0x32, 0x8a, 0x7d, 0x19, 0x31, 0x45, 0xa4, 0xa9, 0x03, 0xca, 0x4e, 0x35, 0xfa, 0x69, - 0x32, 0x7c, 0x4b, 0x4f, 0xf5, 0x38, 0x17, 0x3c, 0x63, 0xa9, 0xd1, 0x8b, 0xd0, 0xe7, 0xd4, 0x0c, - 0x33, 0x35, 0xca, 0xa8, 0xed, 0x0a, 0x6a, 0x72, 0x00, 0xd6, 0x6b, 0x6f, 0x67, 0xf7, 0xdf, 0xac, - 0xf3, 0x8a, 0xbf, 0x55, 0xe5, 0xff, 0x1d, 0xc1, 0xbd, 0x1d, 0x2a, 0xf5, 0x08, 0xc4, 0x46, 0x3c, - 0x52, 0xd3, 0xcd, 0x16, 0xb7, 0x0e, 0xed, 0x58, 0xfb, 0x1d, 0xd4, 0x6b, 0xcd, 0xdc, 0x8f, 0x4e, - 0xf5, 0x4c, 0x24, 0x5e, 0x03, 0x2b, 0xe4, 0x42, 0x9a, 0x8d, 0xae, 0xd4, 0x66, 0x28, 0x62, 0x9e, - 0x0e, 0x2b, 0x6b, 0xad, 0x55, 0xd5, 0x5a, 0x41, 0x39, 0x56, 0x49, 0x39, 0xe4, 0x04, 0x9c, 0xeb, - 0xa8, 0x8d, 0x1c, 0x7a, 0x30, 0xaf, 0xc1, 0x7c, 0x48, 0x86, 0x93, 0x28, 0x30, 0x82, 0x28, 0xba, - 0x6e, 0x96, 0x44, 0x79, 0x31, 0xad, 0xea, 0x62, 0x3e, 0xc1, 0xe2, 0x56, 0xd6, 0x39, 0x1b, 0x54, - 0x46, 0x1a, 0xdd, 0x8e, 0x74, 0x81, 0x56, 0xb3, 0x4c, 0x6b, 0x0b, 0x96, 0x0a, 0xc5, 0x0d, 0x9f, - 0xbf, 0x58, 0x03, 0x79, 0x01, 0x2b, 0xbb, 0xdc, 0x8f, 0xc5, 0x01, 0xe5, 0xef, 0xa8, 0x3f, 0xa2, - 0x5c, 0x84, 0xd1, 0x34, 0x83, 0xeb, 0xc2, 0xdc, 0x44, 0x3b, 0xf3, 0x13, 0x99, 0xdb, 0x64, 0x1f, - 0xdc, 0xba, 0x44, 0x03, 0xe5, 0x86, 0x4c, 0x75, 0x46, 0xd2, 0xf7, 0x8d, 0xd1, 0x88, 0x53, 0x21, - 0x0c, 0xb7, 0xb2, 0x93, 0x60, 0x3d, 0xbe, 0xb4, 0xb4, 0xc1, 0x43, 0x1e, 0x6b, 0xd6, 0x99, 0xcf, - 0xb4, 0x5a, 0x86, 0x76, 0x9a, 0x69, 0x1a, 0x19, 0x6b, 0xfd, 0xcc, 0x82, 0xb9, 0x97, 0xe6, 0x46, - 0xc4, 0x7b, 0xd0, 0xc9, 0xaf, 0x03, 0xfc, 0xb0, 0x76, 0x2e, 0xd5, 0xeb, 0xc8, 0x7d, 0xf4, 0xa7, - 0xb0, 0x14, 0x00, 0x69, 0xe0, 0x23, 0x58, 0xac, 0x8a, 0x0c, 0x3f, 0xa9, 0xcf, 0xae, 0x3f, 0x41, - 0xee, 0xda, 0x2d, 0xa3, 0xf3, 0x96, 0x7b, 0xd0, 0xc9, 0x05, 0x30, 0x83, 0x50, 0x55, 0x7d, 0x33, - 0x08, 0x5d, 0xd3, 0x11, 0x69, 0xe0, 0x2f, 0x80, 0xaf, 0x2f, 0x17, 0xf7, 0x6b, 0xf3, 0x67, 0xca, - 0xc7, 0x1d, 0xdc, 0x3a, 0xbe, 0x42, 0x2b, 0xfd, 0x34, 0x9b, 0x56, 0x49, 0x15, 0xb3, 0x69, 0x95, - 0x85, 0x42, 0x1a, 0x9b, 0xdb, 0x3f, 0x2e, 0xba, 0xe8, 0xfc, 0xa2, 0x8b, 0x7e, 0x5d, 0x74, 0xd1, - 0xd9, 0x65, 0xb7, 0x71, 0x7e, 0xd9, 0x6d, 0xfc, 0xbc, 0xec, 0x36, 0x3e, 0x3e, 0x1f, 0x47, 0x32, - 0x4c, 0x86, 0xfd, 0x80, 0x1d, 0x0e, 0x0a, 0xd5, 0xd6, 0x8e, 0x69, 0xac, 0x0e, 0xb3, 0xc8, 0xff, - 0xb1, 0xa9, 0x9e, 0x06, 0xfa, 0x0f, 0x3b, 0x6c, 0xeb, 0xc7, 0xb3, 0xdf, 0x01, 0x00, 0x00, 0xff, - 0xff, 0x36, 0x4a, 0x79, 0x05, 0x8e, 0x07, 0x00, 0x00, + // 744 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x51, 0x4f, 0xd3, 0x50, + 0x14, 0x5e, 0xb7, 0x76, 0xb2, 0x33, 0x30, 0x70, 0x25, 0x58, 0x1a, 0xb3, 0xcc, 0x1b, 0x35, 0x4b, + 0x94, 0xcd, 0x4c, 0xa3, 0xcf, 0x20, 0x89, 0x12, 0x14, 0x49, 0x07, 0x2f, 0x86, 0x90, 0x74, 0xdd, + 0x65, 0x6d, 0x1c, 0xed, 0xb8, 0xf7, 0x16, 0xe1, 0x07, 0xf8, 0xee, 0x8b, 0xff, 0x89, 0x47, 0x1e, + 0x7d, 0xd3, 0xc0, 0x1f, 0x31, 0xf7, 0xf6, 0xb6, 0xac, 0xa5, 0x03, 0x1e, 0x78, 0x5a, 0xcf, 0xe9, + 0x77, 0xee, 0xf9, 0xbe, 0x6f, 0x5f, 0x9b, 0x02, 0x66, 0x9c, 0x3a, 0xc1, 0x90, 0x8c, 0xc2, 0x63, + 0xd2, 0xf1, 0x42, 0xea, 0xd2, 0xe8, 0xa4, 0xe3, 0x86, 0xcc, 0x1f, 0x06, 0x84, 0xb6, 0xc7, 0x34, + 0xe4, 0x21, 0x7a, 0x34, 0x81, 0x69, 0x2b, 0x0c, 0xfe, 0xa9, 0x81, 0xb1, 0x36, 0x0a, 0xdd, 0xef, + 0x68, 0x09, 0xaa, 0x1e, 0xf1, 0x87, 0x1e, 0x37, 0xb5, 0xa6, 0xd6, 0xaa, 0xd8, 0xaa, 0x42, 0x8b, + 0x60, 0xd0, 0x30, 0x0a, 0x06, 0x66, 0x59, 0xb6, 0xe3, 0x02, 0x21, 0xd0, 0x19, 0x27, 0x63, 0xb3, + 0xd2, 0xd4, 0x5a, 0x86, 0x2d, 0xaf, 0xd1, 0x13, 0xa8, 0x89, 0x85, 0x6b, 0xa7, 0x9c, 0x30, 0x53, + 0x6f, 0x6a, 0xad, 0x59, 0xfb, 0xaa, 0x21, 0xee, 0x72, 0xff, 0x90, 0x30, 0xee, 0x1c, 0x8e, 0x4d, + 0x43, 0x9e, 0x75, 0xd5, 0xc0, 0xfb, 0x30, 0xdf, 0x13, 0x50, 0x41, 0xc5, 0x26, 0x47, 0x11, 0x61, + 0x1c, 0x99, 0xf0, 0xc0, 0xf5, 0x1c, 0x3f, 0xd8, 0x58, 0x97, 0x94, 0x6a, 0x76, 0x52, 0xa2, 0xd7, + 0x60, 0xf4, 0x05, 0x52, 0x72, 0xaa, 0x77, 0xad, 0x76, 0x81, 0xb4, 0x76, 0x7c, 0x56, 0x0c, 0xc4, + 0x5f, 0x61, 0x61, 0xe2, 0x7c, 0x36, 0x0e, 0x03, 0x46, 0x12, 0xc2, 0x0e, 0x8f, 0x28, 0x91, 0x2b, + 0x14, 0x61, 0xd9, 0xc8, 0x12, 0x2e, 0xe7, 0x09, 0xff, 0xd6, 0xc0, 0xd8, 0x0a, 0x03, 0x97, 0x20, + 0x0b, 0x66, 0x58, 0x18, 0x51, 0x97, 0x28, 0x9e, 0x86, 0x9d, 0xd6, 0xe8, 0x19, 0xcc, 0x0d, 0x08, + 0xe3, 0x7e, 0xe0, 0x70, 0x3f, 0x14, 0x42, 0xca, 0x12, 0x90, 0x6d, 0x0a, 0xeb, 0xc7, 0x51, 0x7f, + 0x93, 0x9c, 0x4a, 0x3b, 0x67, 0x6d, 0x55, 0x09, 0xeb, 0x99, 0xe7, 0x50, 0xa2, 0xcc, 0x8c, 0x8b, + 0x2c, 0x6b, 0x23, 0xc7, 0x1a, 0xf7, 0xa0, 0xb6, 0xbb, 0xbb, 0xb1, 0x1e, 0x53, 0x43, 0xa0, 0x47, + 0x91, 0x3f, 0x50, 0xda, 0xe4, 0x35, 0xea, 0x42, 0x35, 0x10, 0x37, 0x99, 0x59, 0x6e, 0x56, 0xa6, + 0x9a, 0x27, 0xe7, 0x6d, 0x85, 0xc4, 0x07, 0xa0, 0x7f, 0xb2, 0x7b, 0x3b, 0xf7, 0x93, 0x91, 0x2b, + 0x53, 0xf5, 0xbc, 0xa9, 0x67, 0x1a, 0x3c, 0xee, 0x11, 0x2e, 0x97, 0xb3, 0xd5, 0x60, 0x20, 0xfe, + 0xb2, 0x24, 0x0d, 0xf7, 0xa4, 0x05, 0xad, 0x80, 0xee, 0x51, 0xc6, 0x25, 0xab, 0x7a, 0x77, 0xb9, + 0x70, 0x42, 0x88, 0xb5, 0x25, 0xec, 0x96, 0x50, 0x4f, 0x44, 0xd4, 0xc8, 0x44, 0x14, 0x9f, 0x80, + 0x79, 0x5d, 0x89, 0xca, 0x5d, 0x13, 0xea, 0x92, 0xcc, 0x76, 0xd4, 0x1f, 0xf9, 0xae, 0x52, 0x34, + 0xd9, 0xba, 0x39, 0x7b, 0xd9, 0x04, 0x54, 0xf2, 0x09, 0x68, 0xc1, 0xfc, 0xc7, 0x64, 0x73, 0x62, + 0xde, 0x22, 0x18, 0xc2, 0x30, 0x66, 0x6a, 0xcd, 0x8a, 0x48, 0x92, 0x2c, 0xf0, 0x26, 0x2c, 0x4c, + 0x20, 0x15, 0xb9, 0x77, 0xa9, 0xa7, 0x9a, 0xf4, 0xb4, 0x51, 0xe8, 0x50, 0x9a, 0xb1, 0x34, 0x23, + 0xef, 0x61, 0x79, 0x87, 0x3a, 0x01, 0x3b, 0x20, 0xf4, 0x33, 0x71, 0x06, 0x84, 0x32, 0xcf, 0x1f, + 0x27, 0xfb, 0x2d, 0x98, 0x19, 0xc9, 0x66, 0xfa, 0x2c, 0xa7, 0x35, 0xde, 0x07, 0xab, 0x68, 0x50, + 0xd1, 0xb9, 0x61, 0x52, 0x3c, 0x5d, 0xf1, 0xf5, 0xea, 0x60, 0x40, 0x09, 0x63, 0xd2, 0xa9, 0x9a, + 0x9d, 0x6d, 0x62, 0x24, 0xfd, 0x88, 0x8f, 0x56, 0x7c, 0xf0, 0x4b, 0xa9, 0x3c, 0xe9, 0xa9, 0x55, + 0x4b, 0x50, 0x8d, 0x27, 0xd5, 0x22, 0x55, 0xe1, 0x39, 0xa8, 0x6f, 0xfb, 0xc1, 0x30, 0x99, 0x7d, + 0x08, 0xb3, 0x71, 0x19, 0x8f, 0x75, 0xff, 0xea, 0x30, 0xf3, 0x41, 0xbd, 0x6a, 0xd1, 0x1e, 0xd4, + 0xd2, 0xf7, 0x0c, 0x7a, 0x5e, 0x68, 0x5d, 0xfe, 0x3d, 0x67, 0xbd, 0xb8, 0x0d, 0x16, 0x2f, 0xc2, + 0x25, 0x74, 0x04, 0xf3, 0xf9, 0x50, 0xa1, 0x57, 0xc5, 0xd3, 0xc5, 0x4f, 0x91, 0xb5, 0x72, 0x47, + 0x74, 0xba, 0x72, 0x0f, 0x6a, 0x69, 0x46, 0xa6, 0x08, 0xca, 0xa7, 0x6d, 0x8a, 0xa0, 0x6b, 0x51, + 0xc3, 0x25, 0xf4, 0x03, 0xd0, 0xf5, 0xff, 0x1e, 0xb5, 0x0b, 0xe7, 0xa7, 0xa6, 0xcb, 0xea, 0xdc, + 0x19, 0x9f, 0x93, 0x15, 0xdf, 0x9a, 0x2e, 0x2b, 0x13, 0x9a, 0xe9, 0xb2, 0xb2, 0x39, 0xc2, 0x25, + 0xf4, 0x05, 0x74, 0x11, 0x11, 0xd4, 0x2c, 0x9c, 0x98, 0x08, 0x93, 0xf5, 0xf4, 0x06, 0x44, 0x72, + 0xdc, 0xda, 0xd6, 0xd9, 0x45, 0x43, 0x3b, 0xbf, 0x68, 0x68, 0xff, 0x2e, 0x1a, 0xda, 0xaf, 0xcb, + 0x46, 0xe9, 0xfc, 0xb2, 0x51, 0xfa, 0x73, 0xd9, 0x28, 0x7d, 0x7b, 0x3b, 0xf4, 0xb9, 0x17, 0xf5, + 0xdb, 0x6e, 0x78, 0xd8, 0x99, 0x38, 0x68, 0xe5, 0x98, 0x04, 0xe2, 0x5d, 0xc0, 0xd2, 0x6f, 0x81, + 0x38, 0x9e, 0x1d, 0xf9, 0x25, 0xd0, 0xaf, 0xca, 0x9f, 0x37, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, + 0x9f, 0xb6, 0xf6, 0x12, 0x36, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -835,6 +966,7 @@ type CosignerClient interface { GetNonces(ctx context.Context, in *GetNoncesRequest, opts ...grpc.CallOption) (*GetNoncesResponse, error) TransferLeadership(ctx context.Context, in *TransferLeadershipRequest, opts ...grpc.CallOption) (*TransferLeadershipResponse, error) GetLeader(ctx context.Context, in *GetLeaderRequest, opts ...grpc.CallOption) (*GetLeaderResponse, error) + Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) } type cosignerClient struct { @@ -890,6 +1022,15 @@ func (c *cosignerClient) GetLeader(ctx context.Context, in *GetLeaderRequest, op 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, "/strangelove.horcrux.Cosigner/Ping", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // CosignerServer is the server API for Cosigner service. type CosignerServer interface { SignBlock(context.Context, *SignBlockRequest) (*SignBlockResponse, error) @@ -897,6 +1038,7 @@ type CosignerServer interface { GetNonces(context.Context, *GetNoncesRequest) (*GetNoncesResponse, error) TransferLeadership(context.Context, *TransferLeadershipRequest) (*TransferLeadershipResponse, error) GetLeader(context.Context, *GetLeaderRequest) (*GetLeaderResponse, error) + Ping(context.Context, *PingRequest) (*PingResponse, error) } // UnimplementedCosignerServer can be embedded to have forward compatible implementations. @@ -918,6 +1060,9 @@ func (*UnimplementedCosignerServer) TransferLeadership(ctx context.Context, req func (*UnimplementedCosignerServer) GetLeader(ctx context.Context, req *GetLeaderRequest) (*GetLeaderResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetLeader not implemented") } +func (*UnimplementedCosignerServer) Ping(ctx context.Context, req *PingRequest) (*PingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") +} func RegisterCosignerServer(s grpc1.Server, srv CosignerServer) { s.RegisterService(&_Cosigner_serviceDesc, srv) @@ -1013,6 +1158,24 @@ func _Cosigner_GetLeader_Handler(srv interface{}, ctx context.Context, dec func( 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: "/strangelove.horcrux.Cosigner/Ping", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CosignerServer).Ping(ctx, req.(*PingRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Cosigner_serviceDesc = grpc.ServiceDesc{ ServiceName: "strangelove.horcrux.Cosigner", HandlerType: (*CosignerServer)(nil), @@ -1037,6 +1200,10 @@ var _Cosigner_serviceDesc = grpc.ServiceDesc{ MethodName: "GetLeader", Handler: _Cosigner_GetLeader_Handler, }, + { + MethodName: "Ping", + Handler: _Cosigner_Ping_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "strangelove/horcrux/cosigner.proto", @@ -1223,6 +1390,50 @@ func (m *Nonce) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *UUIDNonce) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UUIDNonce) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UUIDNonce) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Nonces) > 0 { + for iNdEx := len(m.Nonces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Nonces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCosigner(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Uuid) > 0 { + i -= len(m.Uuid) + copy(dAtA[i:], m.Uuid) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.Uuid))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *HRST) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1291,14 +1502,14 @@ func (m *SetNoncesAndSignRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) copy(dAtA[i:], m.ChainID) i = encodeVarintCosigner(dAtA, i, uint64(len(m.ChainID))) i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a } if len(m.SignBytes) > 0 { i -= len(m.SignBytes) copy(dAtA[i:], m.SignBytes) i = encodeVarintCosigner(dAtA, i, uint64(len(m.SignBytes))) i-- - dAtA[i] = 0x1a + dAtA[i] = 0x22 } if m.Hrst != nil { { @@ -1310,7 +1521,7 @@ func (m *SetNoncesAndSignRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintCosigner(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x12 + dAtA[i] = 0x1a } if len(m.Nonces) > 0 { for iNdEx := len(m.Nonces) - 1; iNdEx >= 0; iNdEx-- { @@ -1323,9 +1534,16 @@ func (m *SetNoncesAndSignRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) i = encodeVarintCosigner(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0xa + dAtA[i] = 0x12 } } + if len(m.Uuid) > 0 { + i -= len(m.Uuid) + copy(dAtA[i:], m.Uuid) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.Uuid))) + i-- + dAtA[i] = 0xa + } return len(dAtA) - i, nil } @@ -1391,24 +1609,14 @@ func (m *GetNoncesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.ChainID) > 0 { - i -= len(m.ChainID) - copy(dAtA[i:], m.ChainID) - i = encodeVarintCosigner(dAtA, i, uint64(len(m.ChainID))) - i-- - dAtA[i] = 0x12 - } - if m.Hrst != nil { - { - size, err := m.Hrst.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintCosigner(dAtA, i, uint64(size)) + if len(m.Uuids) > 0 { + for iNdEx := len(m.Uuids) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Uuids[iNdEx]) + copy(dAtA[i:], m.Uuids[iNdEx]) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.Uuids[iNdEx]))) + i-- + dAtA[i] = 0xa } - i-- - dAtA[i] = 0xa } return len(dAtA) - i, nil } @@ -1570,6 +1778,52 @@ func (m *GetLeaderResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *PingRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PingRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PingRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *PingResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PingResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PingResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintCosigner(dAtA []byte, offset int, v uint64) int { offset -= sovCosigner(v) base := offset @@ -1666,6 +1920,25 @@ func (m *Nonce) Size() (n int) { return n } +func (m *UUIDNonce) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Uuid) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + if len(m.Nonces) > 0 { + for _, e := range m.Nonces { + l = e.Size() + n += 1 + l + sovCosigner(uint64(l)) + } + } + return n +} + func (m *HRST) Size() (n int) { if m == nil { return 0 @@ -1693,6 +1966,10 @@ func (m *SetNoncesAndSignRequest) Size() (n int) { } var l int _ = l + l = len(m.Uuid) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } if len(m.Nonces) > 0 { for _, e := range m.Nonces { l = e.Size() @@ -1740,13 +2017,11 @@ func (m *GetNoncesRequest) Size() (n int) { } var l int _ = l - if m.Hrst != nil { - l = m.Hrst.Size() - n += 1 + l + sovCosigner(uint64(l)) - } - l = len(m.ChainID) - if l > 0 { - n += 1 + l + sovCosigner(uint64(l)) + if len(m.Uuids) > 0 { + for _, b := range m.Uuids { + l = len(b) + n += 1 + l + sovCosigner(uint64(l)) + } } return n } @@ -1818,6 +2093,24 @@ func (m *GetLeaderResponse) Size() (n int) { return n } +func (m *PingRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *PingResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovCosigner(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2395,6 +2688,124 @@ func (m *Nonce) Unmarshal(dAtA []byte) error { } return nil } +func (m *UUIDNonce) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UUIDNonce: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UUIDNonce: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uuid = append(m.Uuid[:0], dAtA[iNdEx:postIndex]...) + if m.Uuid == nil { + m.Uuid = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nonces", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Nonces = append(m.Nonces, &Nonce{}) + if err := m.Nonces[len(m.Nonces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *HRST) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2551,6 +2962,40 @@ func (m *SetNoncesAndSignRequest) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Uuid = append(m.Uuid[:0], dAtA[iNdEx:postIndex]...) + if m.Uuid == nil { + m.Uuid = []byte{} + } + iNdEx = postIndex + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Nonces", wireType) } @@ -2584,7 +3029,7 @@ func (m *SetNoncesAndSignRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 2: + case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hrst", wireType) } @@ -2620,7 +3065,7 @@ func (m *SetNoncesAndSignRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 3: + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SignBytes", wireType) } @@ -2654,7 +3099,7 @@ func (m *SetNoncesAndSignRequest) Unmarshal(dAtA []byte) error { m.SignBytes = []byte{} } iNdEx = postIndex - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) } @@ -2875,45 +3320,9 @@ func (m *GetNoncesRequest) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Hrst", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Uuids", wireType) } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowCosigner - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthCosigner - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthCosigner - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.Hrst == nil { - m.Hrst = &HRST{} - } - if err := m.Hrst.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) - } - var stringLen uint64 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowCosigner @@ -2923,23 +3332,23 @@ func (m *GetNoncesRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLen |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - intStringLen := int(stringLen) - if intStringLen < 0 { + if byteLen < 0 { return ErrInvalidLengthCosigner } - postIndex := iNdEx + intStringLen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthCosigner } if postIndex > l { return io.ErrUnexpectedEOF } - m.ChainID = string(dAtA[iNdEx:postIndex]) + m.Uuids = append(m.Uuids, make([]byte, postIndex-iNdEx)) + copy(m.Uuids[len(m.Uuids)-1], dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex @@ -3020,7 +3429,7 @@ func (m *GetNoncesResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Nonces = append(m.Nonces, &Nonce{}) + m.Nonces = append(m.Nonces, &UUIDNonce{}) if err := m.Nonces[len(m.Nonces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -3374,6 +3783,106 @@ func (m *GetLeaderResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *PingRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PingRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PingRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PingResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PingResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PingResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipCosigner(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCosigner + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipCosigner(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/signer/remote_cosigner.go b/signer/remote_cosigner.go index 9f4dcc7a..e520d367 100644 --- a/signer/remote_cosigner.go +++ b/signer/remote_cosigner.go @@ -7,11 +7,14 @@ import ( "time" cometcrypto "github.com/cometbft/cometbft/crypto" + "github.com/google/uuid" "github.com/strangelove-ventures/horcrux/signer/proto" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) +var _ Cosigner = &RemoteCosigner{} + // RemoteCosigner uses CosignerGRPC to request signing from a remote cosigner type RemoteCosigner struct { id int @@ -77,41 +80,49 @@ func (cosigner *RemoteCosigner) getGRPCClient() (proto.CosignerClient, *grpc.Cli // Implements the cosigner interface func (cosigner *RemoteCosigner) GetNonces( - chainID string, - req HRSTKey, -) (*CosignerNoncesResponse, error) { + ctx context.Context, + uuids []uuid.UUID, +) (CosignerUUIDNoncesMultiple, error) { client, conn, err := cosigner.getGRPCClient() if err != nil { return nil, err } defer conn.Close() - context, cancelFunc := getContext() - defer cancelFunc() - res, err := client.GetNonces(context, &proto.GetNoncesRequest{ - ChainID: chainID, - Hrst: req.toProto(), + us := make([][]byte, len(uuids)) + for i, u := range uuids { + us[i] = make([]byte, 16) + copy(us[i], u[:]) + } + res, err := client.GetNonces(ctx, &proto.GetNoncesRequest{ + Uuids: us, }) if err != nil { return nil, err } - return &CosignerNoncesResponse{ - Nonces: CosignerNoncesFromProto(res.GetNonces()), - }, nil + out := make(CosignerUUIDNoncesMultiple, len(res.Nonces)) + for i, nonces := range res.Nonces { + out[i] = &CosignerUUIDNonces{ + UUID: uuid.UUID(nonces.Uuid), + Nonces: CosignerNoncesFromProto(nonces.Nonces), + } + } + return out, nil } // Implements the cosigner interface func (cosigner *RemoteCosigner) SetNoncesAndSign( + ctx context.Context, req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) { client, conn, err := cosigner.getGRPCClient() if err != nil { return nil, err } defer conn.Close() - context, cancelFunc := getContext() - defer cancelFunc() - res, err := client.SetNoncesAndSign(context, &proto.SetNoncesAndSignRequest{ + + res, err := client.SetNoncesAndSign(ctx, &proto.SetNoncesAndSignRequest{ + Uuid: req.Nonces.UUID[:], ChainID: req.ChainID, - Nonces: CosignerNonces(req.Nonces).toProto(), + Nonces: CosignerNonces(req.Nonces.Nonces).toProto(), Hrst: req.HRST.toProto(), SignBytes: req.SignBytes, }) diff --git a/signer/remote_signer.go b/signer/remote_signer.go index 94e3a029..7f0e3db7 100644 --- a/signer/remote_signer.go +++ b/signer/remote_signer.go @@ -22,8 +22,8 @@ const connRetrySec = 2 // PrivValidator is a wrapper for tendermint PrivValidator, // with additional Stop method for safe shutdown. type PrivValidator interface { - Sign(chainID string, block Block) ([]byte, time.Time, error) - GetPubKey(chainID string) ([]byte, error) + Sign(ctx context.Context, chainID string, block Block) ([]byte, time.Time, error) + GetPubKey(ctx context.Context, chainID string) ([]byte, error) Stop() } @@ -186,7 +186,7 @@ func (rs *ReconnRemoteSigner) handleSignVoteRequest(chainID string, vote *cometp Error: nil, }} - signature, timestamp, err := signAndTrack(rs.Logger, rs.privVal, chainID, VoteToBlock(chainID, vote)) + signature, timestamp, err := signAndTrack(context.TODO(), rs.Logger, rs.privVal, chainID, VoteToBlock(chainID, vote)) if err != nil { msgSum.SignedVoteResponse.Error = getRemoteSignerError(err) return cometprotoprivval.Message{Sum: msgSum} @@ -208,7 +208,7 @@ func (rs *ReconnRemoteSigner) handleSignProposalRequest( }, } - signature, timestamp, err := signAndTrack(rs.Logger, rs.privVal, chainID, ProposalToBlock(chainID, proposal)) + signature, timestamp, err := signAndTrack(context.TODO(), rs.Logger, rs.privVal, chainID, ProposalToBlock(chainID, proposal)) if err != nil { msgSum.SignedProposalResponse.Error = getRemoteSignerError(err) return cometprotoprivval.Message{Sum: msgSum} @@ -226,7 +226,7 @@ func (rs *ReconnRemoteSigner) handlePubKeyRequest(chainID string) cometprotopriv Error: nil, }} - pubKey, err := rs.privVal.GetPubKey(chainID) + pubKey, err := rs.privVal.GetPubKey(context.TODO(), chainID) if err != nil { rs.Logger.Error( "Failed to get Pub Key", diff --git a/signer/remote_signer_grpc_server.go b/signer/remote_signer_grpc_server.go index 194db584..97f33100 100644 --- a/signer/remote_signer_grpc_server.go +++ b/signer/remote_signer_grpc_server.go @@ -57,10 +57,10 @@ func (s *RemoteSignerGRPCServer) OnStop() { s.server.GracefulStop() } -func (s *RemoteSignerGRPCServer) PubKey(_ context.Context, req *proto.PubKeyRequest) (*proto.PubKeyResponse, error) { +func (s *RemoteSignerGRPCServer) PubKey(ctx context.Context, req *proto.PubKeyRequest) (*proto.PubKeyResponse, error) { totalPubKeyRequests.Inc() - pubKey, err := s.validator.GetPubKey(req.ChainId) + pubKey, err := s.validator.GetPubKey(ctx, req.ChainId) if err != nil { s.logger.Error( "Failed to get Pub Key", @@ -76,12 +76,12 @@ func (s *RemoteSignerGRPCServer) PubKey(_ context.Context, req *proto.PubKeyRequ } func (s *RemoteSignerGRPCServer) Sign( - _ context.Context, + ctx context.Context, req *proto.SignBlockRequest, ) (*proto.SignBlockResponse, error) { chainID, block := req.ChainID, BlockFromProto(req.Block) - signature, timestamp, err := signAndTrack(s.logger, s.validator, chainID, block) + signature, timestamp, err := signAndTrack(ctx, s.logger, s.validator, chainID, block) if err != nil { return nil, err } @@ -93,12 +93,13 @@ func (s *RemoteSignerGRPCServer) Sign( } func signAndTrack( + ctx context.Context, logger cometlog.Logger, validator PrivValidator, chainID string, block Block, ) ([]byte, time.Time, error) { - signature, timestamp, err := validator.Sign(chainID, block) + signature, timestamp, err := validator.Sign(ctx, chainID, block) if err != nil { switch typedErr := err.(type) { case *BeyondBlockError: diff --git a/signer/single_signer_validator.go b/signer/single_signer_validator.go index 6f5357b0..ee0d40ff 100644 --- a/signer/single_signer_validator.go +++ b/signer/single_signer_validator.go @@ -1,6 +1,7 @@ package signer import ( + "context" "fmt" "os" "sync" @@ -35,7 +36,7 @@ func NewSingleSignerValidator(config *RuntimeConfig) *SingleSignerValidator { } // GetPubKey implements types.PrivValidator -func (pv *SingleSignerValidator) GetPubKey(chainID string) ([]byte, error) { +func (pv *SingleSignerValidator) GetPubKey(_ context.Context, chainID string) ([]byte, error) { chainState, err := pv.loadChainStateIfNecessary(chainID) if err != nil { return nil, err @@ -48,7 +49,7 @@ func (pv *SingleSignerValidator) GetPubKey(chainID string) ([]byte, error) { } // SignVote implements types.PrivValidator -func (pv *SingleSignerValidator) Sign(chainID string, block Block) ([]byte, time.Time, error) { +func (pv *SingleSignerValidator) Sign(_ context.Context, chainID string, block Block) ([]byte, time.Time, error) { chainState, err := pv.loadChainStateIfNecessary(chainID) if err != nil { return nil, block.Timestamp, err diff --git a/signer/single_signer_validator_test.go b/signer/single_signer_validator_test.go index c751bda4..240270cb 100644 --- a/signer/single_signer_validator_test.go +++ b/signer/single_signer_validator_test.go @@ -1,6 +1,7 @@ package signer import ( + "context" "path/filepath" "time" @@ -54,7 +55,9 @@ func TestSingleSignerValidator(t *testing.T) { block := ProposalToBlock(testChainID, &proposal) - signature, _, err := validator.Sign(testChainID, block) + ctx := context.Background() + + signature, _, err := validator.Sign(ctx, testChainID, block) require.NoError(t, err) require.True(t, privateKey.PubKey().VerifySignature(block.SignBytes, signature)) @@ -62,7 +65,7 @@ func TestSingleSignerValidator(t *testing.T) { proposal.Timestamp = time.Now() // should be able to sign same proposal with only differing timestamp - _, _, err = validator.Sign(testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.NoError(t, err) // construct different block ID for proposal at same height as highest signed @@ -78,28 +81,28 @@ func TestSingleSignerValidator(t *testing.T) { } // should not be able to sign same proposal at same height as highest signed with different BlockID - _, _, err = validator.Sign(testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.Error(t, err, "double sign!") proposal.Round = 19 // should not be able to sign lower than highest signed - _, _, err = validator.Sign(testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.Error(t, err, "double sign!") // lower LSS should sign for different chain ID - _, _, err = validator.Sign("different", ProposalToBlock("different", &proposal)) + _, _, err = validator.Sign(ctx, "different", ProposalToBlock("different", &proposal)) require.NoError(t, err) // reinitialize validator to make sure new runtime will not allow double sign validator = NewSingleSignerValidator(runtimeConfig) - _, _, err = validator.Sign(testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.Error(t, err, "double sign!") proposal.Round = 21 // signing higher block now should succeed - _, _, err = validator.Sign(testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.NoError(t, err) } diff --git a/signer/threshold_signer.go b/signer/threshold_signer.go index f4ebbd37..b404ad10 100644 --- a/signer/threshold_signer.go +++ b/signer/threshold_signer.go @@ -5,9 +5,6 @@ type ThresholdSigner interface { // PubKey returns the public key bytes for the combination of all cosigners. PubKey() []byte - // GenerateNonces deals nonces for all cosigners. - GenerateNonces() (Nonces, error) - // Sign signs a byte payload with the provided nonces. Sign(nonces []Nonce, payload []byte) ([]byte, error) diff --git a/signer/threshold_signer_soft.go b/signer/threshold_signer_soft.go index b3715737..c94500cc 100644 --- a/signer/threshold_signer_soft.go +++ b/signer/threshold_signer_soft.go @@ -85,7 +85,7 @@ func (s *ThresholdSignerSoft) sumNonces(nonces []Nonce) (tsed25519.Scalar, tsed2 return nonceShare, noncePub, nil } -func (s *ThresholdSignerSoft) GenerateNonces() (Nonces, error) { +func GenerateNonces(threshold, total uint8) (Nonces, error) { secret := make([]byte, 32) if _, err := rand.Read(secret); err != nil { return Nonces{}, err @@ -93,13 +93,13 @@ func (s *ThresholdSignerSoft) GenerateNonces() (Nonces, error) { nonces := Nonces{ PubKey: tsed25519.ScalarMultiplyBase(secret), - Shares: make([][]byte, s.total), + Shares: make([][]byte, total), } - shares := tsed25519.DealShares(secret, s.threshold, s.total) + shares := tsed25519.DealShares(secret, threshold, total) - for i, s := range shares { - nonces.Shares[i] = s + for i, sh := range shares { + nonces.Shares[i] = sh } return nonces, nil diff --git a/signer/threshold_validator.go b/signer/threshold_validator.go index caee7e70..d1ffb4da 100644 --- a/signer/threshold_validator.go +++ b/signer/threshold_validator.go @@ -2,6 +2,7 @@ package signer import ( "bytes" + "context" "errors" "fmt" "os" @@ -11,6 +12,7 @@ import ( "github.com/cometbft/cometbft/libs/log" cometrpcjsontypes "github.com/cometbft/cometbft/rpc/jsonrpc/types" "github.com/strangelove-ventures/horcrux/signer/proto" + "golang.org/x/sync/errgroup" ) var _ PrivValidator = &ThresholdValidator{} @@ -37,6 +39,10 @@ type ThresholdValidator struct { pendingDiskWG sync.WaitGroup maxWaitForSameBlockAttempts int + + cosignerHealth *CosignerHealth + + nonceCache *CosignerNonceCache } type ChainSignState struct { @@ -70,9 +76,22 @@ func NewThresholdValidator( myCosigner: myCosigner, peerCosigners: peerCosigners, leader: leader, + cosignerHealth: NewCosignerHealth(peerCosigners, leader), + nonceCache: NewCosignerNonceCache(logger, append(peerCosigners, myCosigner), leader, defaultGetNoncesInterval, defaultGetNoncesTimeout), } } +// 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) + + return nil +} + // SaveLastSignedState updates the high watermark height/round/step (HRS) for a completed // 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. @@ -225,7 +244,7 @@ func (pv *ThresholdValidator) waitForSignStatesToFlushToDisk() { // GetPubKey returns the public key of the validator. // Implements PrivValidator. -func (pv *ThresholdValidator) GetPubKey(chainID string) ([]byte, error) { +func (pv *ThresholdValidator) GetPubKey(_ context.Context, chainID string) ([]byte, error) { pubKey, err := pv.myCosigner.GetPubKey(chainID) if err != nil { return nil, err @@ -233,12 +252,6 @@ func (pv *ThresholdValidator) GetPubKey(chainID string) ([]byte, error) { return pubKey.Bytes(), nil } -// SignVote signs a canonical representation of the vote, along with the -// chainID. Implements PrivValidator. -func (pv *ThresholdValidator) Sign(chainID string, block Block) ([]byte, time.Time, error) { - return pv.SignBlock(chainID, block) -} - type Block struct { Height int64 Round int64 @@ -329,117 +342,6 @@ func newSameBlockError(chainID string, hrs HRSKey) *SameBlockError { } } -func (pv *ThresholdValidator) waitForPeerNonces( - chainID string, - peer Cosigner, - hrst HRSTKey, - wg *sync.WaitGroup, - nonces map[Cosigner][]CosignerNonce, - thresholdPeersMutex *sync.Mutex, -) { - peerStartTime := time.Now() - peerNonces, err := peer.GetNonces(chainID, hrst) - if err != nil { - // Significant missing shares may lead to signature failure - missedNonces.WithLabelValues(peer.GetAddress()).Add(float64(1)) - totalMissedNonces.WithLabelValues(peer.GetAddress()).Inc() - pv.logger.Error("Error getting nonces", "cosigner", peer.GetID(), "err", err) - return - } - // Significant missing shares may lead to signature failure - missedNonces.WithLabelValues(peer.GetAddress()).Set(0) - 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 - thresholdPeersMutex.Lock() - if len(nonces) < pv.threshold-1 { - nonces[peer] = peerNonces.Nonces - defer wg.Done() - } - thresholdPeersMutex.Unlock() -} -func (pv *ThresholdValidator) waitForPeerSetNoncesAndSign( - chainID string, - peer Cosigner, - hrst HRSTKey, - noncesMap map[Cosigner][]CosignerNonce, - signBytes []byte, - shareSignatures *[][]byte, - shareSignaturesMutex *sync.Mutex, - wg *sync.WaitGroup, -) { - peerStartTime := time.Now() - defer wg.Done() - peerNonces := make([]CosignerNonce, 0, pv.threshold-1) - - peerID := peer.GetID() - - for _, nonces := range noncesMap { - for _, nonce := range nonces { - // if share is intended for peer, check to make sure source peer is included in threshold - if nonce.DestinationID != peerID { - continue - } - for thresholdPeer := range noncesMap { - if thresholdPeer.GetID() != nonce.SourceID { - continue - } - // source peer is included in threshold signature, include in sharing - peerNonces = append(peerNonces, nonce) - break - } - break - } - } - - sigRes, err := peer.SetNoncesAndSign(CosignerSetNoncesAndSignRequest{ - ChainID: chainID, - Nonces: peerNonces, - HRST: hrst, - SignBytes: signBytes, - }) - - if err != nil { - pv.logger.Error( - "Cosigner failed to set nonces and sign", - "id", peerID, - "err", err.Error(), - ) - return - } - - timedCosignerSignLag.WithLabelValues(peer.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) - pv.logger.Debug( - "Received signature part", - "cosigner", peerID, - "chain_id", chainID, - "height", hrst.Height, - "round", hrst.Round, - "step", hrst.Step, - ) - - shareSignaturesMutex.Lock() - defer shareSignaturesMutex.Unlock() - - peerIdx := peerID - 1 - (*shareSignatures)[peerIdx] = make([]byte, len(sigRes.Signature)) - copy((*shareSignatures)[peerIdx], sigRes.Signature) -} - -func waitUntilCompleteOrTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { - c := make(chan struct{}) - go func() { - defer close(c) - wg.Wait() - }() - select { - case <-c: - return false // completed normally - case <-time.After(timeout): - return true // timed out - } -} - func (pv *ThresholdValidator) LoadSignStateIfNecessary(chainID string) error { if _, ok := pv.chainState.Load(chainID); ok { return nil @@ -538,7 +440,7 @@ func (pv *ThresholdValidator) compareBlockSignatureAgainstHRS( return newStillWaitingForBlockError(chainID, blockHRS) } -func (pv *ThresholdValidator) SignBlock(chainID string, block Block) ([]byte, time.Time, error) { +func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Block) ([]byte, time.Time, error) { height, round, step, stamp, signBytes := block.Height, block.Round, block.Step, block.Timestamp, block.SignBytes if err := pv.LoadSignStateIfNecessary(chainID); err != nil { @@ -550,7 +452,7 @@ func (pv *ThresholdValidator) SignBlock(chainID string, block Block) ([]byte, ti // Only the leader can execute this function. Followers can handle the requests, // but they just need to proxy the request to the raft leader if !pv.leader.IsLeader() { - pv.logger.Debug("I am not the raft leader. Proxying request to the leader", + pv.logger.Debug("I am not the leader. Proxying request to the leader", "chain_id", chainID, "height", height, "round", round, @@ -576,7 +478,7 @@ func (pv *ThresholdValidator) SignBlock(chainID string, block Block) ([]byte, ti totalRaftLeader.Inc() pv.logger.Debug( - "I am the raft leader. Managing the sign process for this block", + "I am the leader. Managing the sign process for this block", "chain_id", chainID, "height", height, "round", round, @@ -602,69 +504,52 @@ func (pv *ThresholdValidator) SignBlock(chainID string, block Block) ([]byte, ti numPeers := len(pv.peerCosigners) total := uint8(numPeers + 1) - getEphemeralWaitGroup := sync.WaitGroup{} - // Only wait until we have threshold sigs - getEphemeralWaitGroup.Add(pv.threshold - 1) - // Used to track how close we are to threshold - - nonces := make(map[Cosigner][]CosignerNonce) - thresholdPeersMutex := sync.Mutex{} - - for _, c := range pv.peerCosigners { - go pv.waitForPeerNonces(chainID, c, hrst, &getEphemeralWaitGroup, - nonces, &thresholdPeersMutex) - } - - myNonces, err := pv.myCosigner.GetNonces(chainID, hrst) + fastestPeers := pv.cosignerHealth.GetFastest(pv.threshold - 1) + cosignersForThisBlock := append(fastestPeers, pv.myCosigner) + nonceCtx, nonceCancel := context.WithTimeout(ctx, pv.grpcTimeout) + defer nonceCancel() + nonces, err := pv.nonceCache.GetNonces(nonceCtx, cosignersForThisBlock) if err != nil { pv.notifyBlockSignError(chainID, block.HRSKey()) - // Our ephemeral secret parts are required, cannot proceed - return nil, stamp, err + // Nonces are required, cannot proceed + return nil, stamp, fmt.Errorf("failed to get nonces: %w", err) } - // Wait for threshold cosigners to be complete - // A Cosigner will either respond in time, or be cancelled with timeout - if waitUntilCompleteOrTimeout(&getEphemeralWaitGroup, pv.grpcTimeout) { - pv.notifyBlockSignError(chainID, block.HRSKey()) - return nil, stamp, errors.New("timed out waiting for ephemeral shares") - } + cosignersForThisBlockInt := make([]int, len(cosignersForThisBlock)) - thresholdPeersMutex.Lock() - nonces[pv.myCosigner] = myNonces.Nonces - thresholdPeersMutex.Unlock() - - timedSignBlockThresholdLag.Observe(time.Since(timeStartSignBlock).Seconds()) - pv.logger.Debug( - "Have threshold peers", - "chain_id", chainID, - "height", hrst.Height, - "round", hrst.Round, - "step", hrst.Step, - ) - - setEphemeralAndSignWaitGroup := sync.WaitGroup{} - - // Only wait until we have threshold sigs - setEphemeralAndSignWaitGroup.Add(pv.threshold) + for i, cosigner := range cosignersForThisBlock { + cosignersForThisBlockInt[i] = cosigner.GetID() + } // destination for share signatures shareSignatures := make([][]byte, total) - // share sigs is updated by goroutines - shareSignaturesMutex := sync.Mutex{} - - for cosigner := range nonces { - // set peerNonces and sign in single rpc call. - go pv.waitForPeerSetNoncesAndSign(chainID, cosigner, hrst, nonces, - signBytes, &shareSignatures, &shareSignaturesMutex, &setEphemeralAndSignWaitGroup) + var eg errgroup.Group + for _, cosigner := range cosignersForThisBlock { + cosigner := cosigner + eg.Go(func() error { + ctx, cancel := context.WithTimeout(ctx, pv.grpcTimeout) + defer cancel() + + // set peerNonces and sign in single rpc call. + sigRes, err := cosigner.SetNoncesAndSign(ctx, CosignerSetNoncesAndSignRequest{ + ChainID: chainID, + Nonces: nonces.For(cosigner.GetID()), + HRST: hrst, + SignBytes: signBytes, + }) + if err != nil { + return err + } + shareSignatures[cosigner.GetID()-1] = sigRes.Signature + return nil + }) } - // Wait for threshold cosigners to be complete - // A Cosigner will either respond in time, or be cancelled with timeout - if waitUntilCompleteOrTimeout(&setEphemeralAndSignWaitGroup, 4*time.Second) { + if err := eg.Wait(); err != nil { pv.notifyBlockSignError(chainID, block.HRSKey()) - return nil, stamp, errors.New("timed out waiting for peers to sign") + return nil, stamp, err } timedSignBlockCosignerLag.Observe(time.Since(timeStartSignBlock).Seconds()) diff --git a/signer/threshold_validator_test.go b/signer/threshold_validator_test.go index 4e50e5aa..832f734c 100644 --- a/signer/threshold_validator_test.go +++ b/signer/threshold_validator_test.go @@ -2,6 +2,7 @@ package signer import ( "bytes" + "context" "crypto/rand" "fmt" mrand "math/rand" @@ -78,7 +79,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { leader := &MockLeader{id: 1} validator := NewThresholdValidator( - cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)).With("module", "validator"), + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), cosigners[0].config, int(threshold), time.Second, @@ -91,6 +92,10 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { leader.leader = validator + ctx := context.Background() + + validator.nonceCache.LoadN(ctx, 5) + err := validator.LoadSignStateIfNecessary(testChainID) require.NoError(t, err) @@ -102,7 +107,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { block := ProposalToBlock(testChainID, &proposal) - signature, _, err := validator.Sign(testChainID, block) + signature, _, err := validator.Sign(ctx, testChainID, block) require.NoError(t, err) require.True(t, pubKey.VerifySignature(block.SignBytes, signature)) @@ -121,7 +126,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { block = ProposalToBlock(testChainID, &proposal) // should be able to sign same proposal with only differing timestamp - _, _, err = validator.Sign(testChainID, block) + _, _, err = validator.Sign(ctx, testChainID, block) require.NoError(t, err) // construct different block ID for proposal at same height as highest signed @@ -138,7 +143,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { // different than single-signer mode, threshold mode will be successful for this, // but it will return the same signature as before. - signature, _, err = validator.Sign(testChainID, ProposalToBlock(testChainID, &proposal)) + signature, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.NoError(t, err) require.True(t, bytes.Equal(firstSignature, signature)) @@ -146,16 +151,16 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { proposal.Round = 19 // should not be able to sign lower than highest signed - _, _, err = validator.Sign(testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.Error(t, err, "double sign!") // lower LSS should sign for different chain ID - _, _, err = validator.Sign(testChainID2, ProposalToBlock(testChainID2, &proposal)) + _, _, err = validator.Sign(ctx, testChainID2, ProposalToBlock(testChainID2, &proposal)) require.NoError(t, err) // reinitialize validator to make sure new runtime will not allow double sign newValidator := NewThresholdValidator( - cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)).With("module", "validator"), + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), cosigners[0].config, int(threshold), time.Second, @@ -166,7 +171,9 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { ) defer newValidator.Stop() - _, _, err = newValidator.Sign(testChainID, ProposalToBlock(testChainID, &proposal)) + newValidator.nonceCache.LoadN(ctx, 500) + + _, _, err = newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.Error(t, err, "double sign!") proposal = cometproto.Proposal{ @@ -185,15 +192,15 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { var eg errgroup.Group eg.Go(func() error { - _, _, err := newValidator.Sign(testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) return err }) eg.Go(func() error { - _, _, err := newValidator.Sign(testChainID, ProposalToBlock(testChainID, &proposalClone)) + _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposalClone)) return err }) eg.Go(func() error { - _, _, err := newValidator.Sign(testChainID, ProposalToBlock(testChainID, &proposalClone2)) + _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposalClone2)) return err }) // signing higher block now should succeed @@ -216,15 +223,21 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { prevoteClone2.Timestamp = prevote.Timestamp.Add(4 * time.Millisecond) eg.Go(func() error { - _, _, err := newValidator.Sign(testChainID, VoteToBlock(testChainID, &prevote)) + start := time.Now() + _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevote)) + t.Log("Sign time", "duration", time.Since(start)) return err }) eg.Go(func() error { - _, _, err := newValidator.Sign(testChainID, VoteToBlock(testChainID, &prevoteClone)) + start := time.Now() + _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevoteClone)) + t.Log("Sign time", "duration", time.Since(start)) return err }) eg.Go(func() error { - _, _, err := newValidator.Sign(testChainID, VoteToBlock(testChainID, &prevoteClone2)) + start := time.Now() + _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevoteClone2)) + t.Log("Sign time", "duration", time.Since(start)) return err }) @@ -245,15 +258,15 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { precommitClone2.Timestamp = precommit.Timestamp.Add(4 * time.Millisecond) eg.Go(func() error { - _, _, err := newValidator.Sign(testChainID, VoteToBlock(testChainID, &precommit)) + _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &precommit)) return err }) eg.Go(func() error { - _, _, err := newValidator.Sign(testChainID, VoteToBlock(testChainID, &precommitClone)) + _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &precommitClone)) return err }) eg.Go(func() error { - _, _, err := newValidator.Sign(testChainID, VoteToBlock(testChainID, &precommitClone2)) + _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &precommitClone2)) return err }) @@ -307,7 +320,7 @@ func getTestLocalCosigners(t *testing.T, threshold, total uint8) ([]*LocalCosign } cosigner := NewLocalCosigner( - cometlog.NewNopLogger(), + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), cosignerConfig, NewCosignerSecurityECIES( CosignerECIESKey{ @@ -338,6 +351,9 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) thresholdValidators := make([]*ThresholdValidator, 0, total) var leader *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 { @@ -347,7 +363,7 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) } leaders[i] = &MockLeader{id: cosigner.GetID(), leader: leader} tv := NewThresholdValidator( - cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)).With("module", "validator"), + cometlog.NewTMLogger(cometlog.NewSyncWriter(os.Stdout)), cosigner.config, int(threshold), time.Second, @@ -360,6 +376,9 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) leader = tv leaders[i].leader = tv } + + tv.nonceCache.LoadN(ctx, 100) + thresholdValidators = append(thresholdValidators, tv) defer tv.Stop() @@ -418,7 +437,7 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) Type: cometproto.ProposalType, } - signature, _, err := tv.Sign(testChainID, ProposalToBlock(testChainID, &proposal)) + signature, _, err := tv.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) if err != nil { t.Log("Proposal sign failed", "error", err) return @@ -426,7 +445,10 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) signBytes := comet.ProposalSignBytes(testChainID, &proposal) - if !pubKey.VerifySignature(signBytes, signature) { + sig := make([]byte, len(signature)) + copy(sig, signature) + + if !pubKey.VerifySignature(signBytes, sig) { t.Log("Proposal signature verification failed") return } @@ -455,7 +477,7 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) Type: cometproto.PrevoteType, } - signature, _, err := tv.Sign(testChainID, VoteToBlock(testChainID, &preVote)) + signature, _, err := tv.Sign(ctx, testChainID, VoteToBlock(testChainID, &preVote)) if err != nil { t.Log("PreVote sign failed", "error", err) return @@ -463,7 +485,10 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) signBytes := comet.VoteSignBytes(testChainID, &preVote) - if !pubKey.VerifySignature(signBytes, signature) { + sig := make([]byte, len(signature)) + copy(sig, signature) + + if !pubKey.VerifySignature(signBytes, sig) { t.Log("PreVote signature verification failed") return } @@ -492,7 +517,7 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) Type: cometproto.PrecommitType, } - signature, _, err := tv.Sign(testChainID, VoteToBlock(testChainID, &preCommit)) + signature, _, err := tv.Sign(ctx, testChainID, VoteToBlock(testChainID, &preCommit)) if err != nil { t.Log("PreCommit sign failed", "error", err) return @@ -500,7 +525,10 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) signBytes := comet.VoteSignBytes(testChainID, &preCommit) - if !pubKey.VerifySignature(signBytes, signature) { + sig := make([]byte, len(signature)) + copy(sig, signature) + + if !pubKey.VerifySignature(signBytes, sig) { t.Log("PreCommit signature verification failed") return } diff --git a/test/go.sum b/test/go.sum index 0a4268b9..9cc20316 100644 --- a/test/go.sum +++ b/test/go.sum @@ -734,6 +734,7 @@ 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.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/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= @@ -1524,8 +1525,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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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=