Skip to content

Commit

Permalink
Pre-share nonces (#218)
Browse files Browse the repository at this point in the history
* Pre-share nonces

* lint

* next cosigner retry

* improve

* fix test

* lint

* noplogger

* Add fallback to old nonces fetch mechanism

* lint

* use new raft get leader ID

* lint

* lint

* add back leader election timeout metric. Use existing grpc clients for ping

* error types

* Expired nonce pruning

* should use nonceExpiration

* make local cosigner nonce cache expiration twice the leader nonce cache
  • Loading branch information
agouin authored Nov 16, 2023
1 parent 1a3490f commit 73350e2
Show file tree
Hide file tree
Showing 33 changed files with 2,140 additions and 792 deletions.
4 changes: 2 additions & 2 deletions cmd/horcrux/cmd/leader_election.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ horcrux elect 2 # elect specific leader`,
return err
}

fmt.Printf("Leader election successful. New leader: %s\n", res.Leader)
fmt.Printf("Leader election successful. New leader: %d\n", res.Leader)

return nil
},
Expand Down Expand Up @@ -173,7 +173,7 @@ func getLeaderCmd() *cobra.Command {
return err
}

fmt.Printf("Current leader: %s\n", res.Leader)
fmt.Printf("Current leader: %d\n", res.Leader)

return nil
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/horcrux/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
12 changes: 11 additions & 1 deletion cmd/horcrux/cmd/threshold.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"context"
"fmt"
"os"
"path/filepath"
Expand All @@ -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 {
Expand All @@ -39,9 +41,13 @@ func NewThresholdValidator(

for _, c := range thresholdCfg.Cosigners {
if c.ShardID != security.GetID() {
rc, err := signer.NewRemoteCosigner(c.ShardID, c.P2PAddr)
if err != nil {
return nil, nil, fmt.Errorf("failed to initialize remote cosigner: %w", err)
}
remoteCosigners = append(
remoteCosigners,
signer.NewRemoteCosigner(c.ShardID, c.P2PAddr),
rc,
)
} else {
p2pListen = c.P2PAddr
Expand Down Expand Up @@ -92,5 +98,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
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
25 changes: 17 additions & 8 deletions proto/strangelove/horcrux/cosigner.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -78,5 +84,8 @@ message TransferLeadershipResponse {
message GetLeaderRequest {}

message GetLeaderResponse {
string leader = 1;
int32 leader = 1;
}

message PingRequest {}
message PingResponse {}
59 changes: 42 additions & 17 deletions signer/cosigner.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand All @@ -23,17 +25,29 @@ 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)
}

type Cosigners []Cosigner

func (cosigners Cosigners) GetByID(id int) Cosigner {
for _, cosigner := range cosigners {
if cosigner.GetID() == id {
return cosigner
}
}
return nil
}

// CosignerSignRequest is sent to a co-signer to obtain their signature for the SignBytes
// The SignBytes should be a serialized block
type CosignerSignRequest struct {
ChainID string
SignBytes []byte
UUID uuid.UUID
}

type CosignerSignResponse struct {
Expand Down Expand Up @@ -87,18 +101,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
Expand All @@ -107,14 +109,37 @@ type CosignerSignBlockRequest struct {
type CosignerSignBlockResponse struct {
Signature []byte
}
type CosignerUUIDNonces struct {
UUID uuid.UUID
Nonces CosignerNonces
}

func (n *CosignerUUIDNonces) For(id int) *CosignerUUIDNonces {
res := &CosignerUUIDNonces{UUID: n.UUID}
for _, nonce := range n.Nonces {
if nonce.DestinationID == id {
res.Nonces = append(res.Nonces, nonce)
}
}
return res
}

type CosignerNoncesResponse struct {
Nonces []CosignerNonce
type CosignerUUIDNoncesMultiple []*CosignerUUIDNonces

func (n CosignerUUIDNoncesMultiple) toProto() []*proto.UUIDNonce {
out := make([]*proto.UUIDNonce, len(n))
for i, nonces := range n {
out[i] = &proto.UUIDNonce{
Uuid: nonces.UUID[:],
Nonces: nonces.Nonces.toProto(),
}
}
return out
}

type CosignerSetNoncesAndSignRequest struct {
ChainID string
Nonces []CosignerNonce
Nonces *CosignerUUIDNonces
HRST HRSTKey
SignBytes []byte
}
35 changes: 24 additions & 11 deletions signer/cosigner_grpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"

"github.com/google/uuid"
"github.com/hashicorp/raft"
"github.com/strangelove-ventures/horcrux/signer/proto"
)
Expand All @@ -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
}
Expand All @@ -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(),
})
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -122,5 +131,9 @@ func (rpc *CosignerGRPCServer) GetLeader(
*proto.GetLeaderRequest,
) (*proto.GetLeaderResponse, error) {
leader := rpc.raftStore.GetLeader()
return &proto.GetLeaderResponse{Leader: string(leader)}, nil
return &proto.GetLeaderResponse{Leader: int32(leader)}, nil
}

func (rpc *CosignerGRPCServer) Ping(context.Context, *proto.PingRequest) (*proto.PingResponse, error) {
return &proto.PingResponse{}, nil
}
Loading

0 comments on commit 73350e2

Please sign in to comment.