From 196531639bf706c2c84152552d9a6ac7b16f1f64 Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Tue, 9 Jan 2024 12:10:03 -0700 Subject: [PATCH 01/10] Support vote extensions --- go.mod | 2 +- go.sum | 4 +- proto/strangelove/horcrux/cosigner.proto | 12 +- scripts/protocgen.sh | 2 +- signer/cosigner.go | 29 +- signer/cosigner_grpc_server.go | 13 +- signer/file.go | 31 +- signer/local_cosigner.go | 75 ++++- signer/proto/cosigner.pb.go | 384 ++++++++++++++++++----- signer/proto/remote_signer.pb.go | 12 +- signer/remote_cosigner.go | 10 +- signer/remote_signer.go | 9 +- signer/remote_signer_grpc_server.go | 26 +- signer/sign_state.go | 22 +- signer/single_signer_validator.go | 4 +- signer/single_signer_validator_test.go | 14 +- signer/threshold_validator.go | 196 ++++++++---- signer/threshold_validator_test.go | 106 +++++-- 18 files changed, 687 insertions(+), 264 deletions(-) diff --git a/go.mod b/go.mod index e2339162..d0d48e3c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Jille/raft-grpc-transport v1.4.0 github.com/Jille/raftadmin v1.2.1 github.com/armon/go-metrics v0.4.1 - github.com/cometbft/cometbft v0.38.0 + github.com/cometbft/cometbft v0.38.2 github.com/cosmos/cosmos-sdk v0.50.1 github.com/cosmos/gogoproto v1.4.11 github.com/ethereum/go-ethereum v1.13.5 diff --git a/go.sum b/go.sum index dce87931..48645992 100644 --- a/go.sum +++ b/go.sum @@ -132,8 +132,8 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/cometbft/cometbft v0.38.0 h1:ogKnpiPX7gxCvqTEF4ly25/wAxUqf181t30P3vqdpdc= -github.com/cometbft/cometbft v0.38.0/go.mod h1:5Jz0Z8YsHSf0ZaAqGvi/ifioSdVFPtEGrm8Y9T/993k= +github.com/cometbft/cometbft v0.38.2 h1:io0JCh5EPxINKN5ZMI5hCdpW3QVZRy+o8qWe3mlJa/8= +github.com/cometbft/cometbft v0.38.2/go.mod h1:PIi48BpzwlHqtV3mzwPyQgOyOnU94BNBimLS2ebBHOg= github.com/cometbft/cometbft-db v0.7.0 h1:uBjbrBx4QzU0zOEnU8KxoDl18dMNgDh+zZRUE0ucsbo= github.com/cometbft/cometbft-db v0.7.0/go.mod h1:yiKJIm2WKrt6x8Cyxtq9YTEcIMPcEe4XPxhgX59Fzf0= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= diff --git a/proto/strangelove/horcrux/cosigner.proto b/proto/strangelove/horcrux/cosigner.proto index 42d886ef..53645408 100644 --- a/proto/strangelove/horcrux/cosigner.proto +++ b/proto/strangelove/horcrux/cosigner.proto @@ -17,7 +17,8 @@ message Block { int64 round = 2; int32 step = 3; bytes signBytes = 4; - int64 timestamp = 5; + bytes voteExtSignBytes = 5; + int64 timestamp = 6; } message SignBlockRequest { @@ -27,7 +28,8 @@ message SignBlockRequest { message SignBlockResponse { bytes signature = 1; - int64 timestamp = 2; + bytes vote_ext_signature = 2; + int64 timestamp = 3; } message Nonce { @@ -59,9 +61,11 @@ message SetNoncesAndSignRequest { } message SetNoncesAndSignResponse { - bytes noncePublic = 1; - int64 timestamp = 2; + int64 timestamp = 1; + bytes noncePublic = 2; bytes signature = 3; + bytes voteExtNoncePublic = 4; + bytes voteExtSignature = 5; } message GetNoncesRequest { diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index c0fb5985..6e0428d0 100644 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -11,5 +11,5 @@ for dir in $proto_dirs; do done done -cp -r github.com/strangelove-ventures/horcrux/signer ./ +cp -r github.com/strangelove-ventures/horcrux/v3/signer ./ rm -rf github.com diff --git a/signer/cosigner.go b/signer/cosigner.go index d11f6ee9..f3db4254 100644 --- a/signer/cosigner.go +++ b/signer/cosigner.go @@ -45,15 +45,19 @@ func (cosigners Cosigners) GetByID(id int) Cosigner { // 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 + ChainID string + SignBytes []byte + UUID uuid.UUID + VoteExtensionSignBytes []byte + VoteExtUUID uuid.UUID } type CosignerSignResponse struct { - NoncePublic []byte - Timestamp time.Time - Signature []byte + Timestamp time.Time + NoncePublic []byte + Signature []byte + VoteExtensionNoncePublic []byte + VoteExtensionSignature []byte } type CosignerNonce struct { @@ -107,7 +111,8 @@ type CosignerSignBlockRequest struct { } type CosignerSignBlockResponse struct { - Signature []byte + Signature []byte + VoteExtensionSignature []byte } type CosignerUUIDNonces struct { UUID uuid.UUID @@ -138,8 +143,10 @@ func (n CosignerUUIDNoncesMultiple) toProto() []*proto.UUIDNonce { } type CosignerSetNoncesAndSignRequest struct { - ChainID string - Nonces *CosignerUUIDNonces - HRST HRSTKey - SignBytes []byte + ChainID string + Nonces *CosignerUUIDNonces + VoteExtensionNonces *CosignerUUIDNonces + HRST HRSTKey + SignBytes []byte + VoteExtensionSignBytes []byte } diff --git a/signer/cosigner_grpc_server.go b/signer/cosigner_grpc_server.go index be62cbed..9cc6433c 100644 --- a/signer/cosigner_grpc_server.go +++ b/signer/cosigner_grpc_server.go @@ -34,12 +34,13 @@ func (rpc *CosignerGRPCServer) SignBlock( ctx context.Context, req *proto.SignBlockRequest, ) (*proto.SignBlockResponse, error) { - res, _, err := rpc.thresholdValidator.Sign(ctx, req.ChainID, BlockFromProto(req.Block)) + sig, voteExtSig, _, err := rpc.thresholdValidator.Sign(ctx, req.ChainID, BlockFromProto(req.Block)) if err != nil { return nil, err } return &proto.SignBlockResponse{ - Signature: res, + Signature: sig, + VoteExtSignature: voteExtSig, }, nil } @@ -75,9 +76,11 @@ func (rpc *CosignerGRPCServer) SetNoncesAndSign( "step", req.Hrst.Step, ) return &proto.SetNoncesAndSignResponse{ - NoncePublic: res.NoncePublic, - Timestamp: res.Timestamp.UnixNano(), - Signature: res.Signature, + NoncePublic: res.NoncePublic, + Timestamp: res.Timestamp.UnixNano(), + Signature: res.Signature, + VoteExtNoncePublic: res.VoteExtensionNoncePublic, + VoteExtSignature: res.VoteExtensionSignature, }, nil } diff --git a/signer/file.go b/signer/file.go index ff2b02d7..1431e973 100644 --- a/signer/file.go +++ b/signer/file.go @@ -200,14 +200,27 @@ func (pv *FilePV) GetPubKey() (crypto.PubKey, error) { return pv.Key.PubKey, nil } -func (pv *FilePV) Sign(block Block) ([]byte, time.Time, error) { - height, round, step, signBytes := block.Height, int32(block.Round), block.Step, block.SignBytes +func (pv *FilePV) Sign(block Block) ([]byte, []byte, time.Time, error) { + height, round, step, signBytes, voteExtensionSignBytes := block.Height, int32(block.Round), block.Step, block.SignBytes, block.VoteExtensionSignBytes lss := pv.LastSignState sameHRS, err := lss.CheckHRS(height, round, step) if err != nil { - return nil, block.Timestamp, err + return nil, nil, block.Timestamp, err + } + + // Vote extensions are non-deterministic, so it is possible that an + // application may have created a different extension. We therefore always + // re-sign the vote extensions of precommits. For prevotes and nil + // precommits, the extension signature will always be empty. + // Even if the signed over data is empty, we still add the signature + var extSig []byte + if block.Step == stepPrecommit && len(voteExtensionSignBytes) > 0 { + extSig, err = pv.Key.PrivKey.Sign(voteExtensionSignBytes) + if err != nil { + return nil, nil, block.Timestamp, err + } } // We might crash before writing to the wal, @@ -218,28 +231,28 @@ func (pv *FilePV) Sign(block Block) ([]byte, time.Time, error) { if sameHRS { switch { case bytes.Equal(signBytes, lss.SignBytes): - return lss.Signature, block.Timestamp, nil + return lss.Signature, nil, block.Timestamp, nil case block.Step == stepPropose: if timestamp, ok := checkProposalsOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { - return lss.Signature, timestamp, nil + return lss.Signature, nil, timestamp, nil } case block.Step == stepPrevote || block.Step == stepPrecommit: if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { - return lss.Signature, timestamp, nil + return lss.Signature, extSig, timestamp, nil } } - return nil, block.Timestamp, fmt.Errorf("conflicting data") + return nil, extSig, block.Timestamp, fmt.Errorf("conflicting data") } // It passed the checks. Sign the vote sig, err := pv.Key.PrivKey.Sign(signBytes) if err != nil { - return nil, block.Timestamp, err + return nil, nil, block.Timestamp, err } pv.saveSigned(height, round, step, signBytes, sig) - return sig, block.Timestamp, nil + return sig, extSig, block.Timestamp, nil } // Save persists the FilePV to disk. diff --git a/signer/local_cosigner.go b/signer/local_cosigner.go index d8955371..10875b88 100644 --- a/signer/local_cosigner.go +++ b/signer/local_cosigner.go @@ -232,6 +232,13 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon return res, nil } + defer func() { + cosigner.noncesMu.Lock() + delete(cosigner.nonces, req.UUID) + delete(cosigner.nonces, req.VoteExtUUID) + cosigner.noncesMu.Unlock() + }() + nonces, err := cosigner.combinedNonces( cosigner.GetID(), uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), @@ -241,17 +248,45 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon return res, err } - sig, err := ccs.signer.Sign(nonces, req.SignBytes) - if err != nil { + var voteExtNonces []Nonce + if len(req.VoteExtensionSignBytes) > 0 { + voteExtNonces, err = cosigner.combinedNonces( + cosigner.GetID(), + uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), + req.VoteExtUUID, + ) + if err != nil { + return res, err + } + } + + var eg errgroup.Group + + var sig, voteExtSig []byte + eg.Go(func() error { + var err error + sig, err = ccs.signer.Sign(nonces, req.SignBytes) + return err + }) + if len(req.VoteExtensionSignBytes) > 0 { + eg.Go(func() error { + var err error + voteExtSig, err = ccs.signer.Sign(voteExtNonces, req.VoteExtensionSignBytes) + return err + }) + } + + if err := eg.Wait(); err != nil { return res, err } err = ccs.lastSignState.Save(SignStateConsensus{ - Height: hrst.Height, - Round: hrst.Round, - Step: hrst.Step, - Signature: sig, - SignBytes: req.SignBytes, + Height: hrst.Height, + Round: hrst.Round, + Step: hrst.Step, + Signature: sig, + SignBytes: req.SignBytes, + VoteExtensionSignature: res.VoteExtensionSignature, }, &cosigner.pendingDiskWG) if err != nil { @@ -260,11 +295,8 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon } } - cosigner.noncesMu.Lock() - delete(cosigner.nonces, req.UUID) - cosigner.noncesMu.Unlock() - res.Signature = sig + res.VoteExtensionSignature = voteExtSig // Note - Function may return before this line so elapsed time for Finish may be multiple block times metricsTimeKeeper.SetPreviousLocalSignFinish(time.Now()) @@ -496,14 +528,31 @@ func (cosigner *LocalCosigner) SetNoncesAndSign( }) } + if req.VoteExtensionNonces != nil { + for _, secretPart := range req.VoteExtensionNonces.Nonces { + secretPart := secretPart + + eg.Go(func() error { + return cosigner.setNonce(req.VoteExtensionNonces.UUID, secretPart) + }) + } + } + if err := eg.Wait(); err != nil { return nil, err } - res, err := cosigner.sign(CosignerSignRequest{ + cosignerReq := CosignerSignRequest{ UUID: req.Nonces.UUID, ChainID: chainID, SignBytes: req.SignBytes, - }) + } + + if len(req.VoteExtensionSignBytes) > 0 { + cosignerReq.VoteExtensionSignBytes = req.VoteExtensionSignBytes + cosignerReq.VoteExtUUID = req.VoteExtensionNonces.UUID + } + + res, err := cosigner.sign(cosignerReq) return &res, err } diff --git a/signer/proto/cosigner.pb.go b/signer/proto/cosigner.pb.go index ff9bf4d6..460e773c 100644 --- a/signer/proto/cosigner.pb.go +++ b/signer/proto/cosigner.pb.go @@ -28,11 +28,12 @@ var _ = math.Inf const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Block 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"` - Step int32 `protobuf:"varint,3,opt,name=step,proto3" json:"step,omitempty"` - SignBytes []byte `protobuf:"bytes,4,opt,name=signBytes,proto3" json:"signBytes,omitempty"` - Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Round int64 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` + Step int32 `protobuf:"varint,3,opt,name=step,proto3" json:"step,omitempty"` + SignBytes []byte `protobuf:"bytes,4,opt,name=signBytes,proto3" json:"signBytes,omitempty"` + VoteExtSignBytes []byte `protobuf:"bytes,5,opt,name=voteExtSignBytes,proto3" json:"voteExtSignBytes,omitempty"` + Timestamp int64 `protobuf:"varint,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (m *Block) Reset() { *m = Block{} } @@ -96,6 +97,13 @@ func (m *Block) GetSignBytes() []byte { return nil } +func (m *Block) GetVoteExtSignBytes() []byte { + if m != nil { + return m.VoteExtSignBytes + } + return nil +} + func (m *Block) GetTimestamp() int64 { if m != nil { return m.Timestamp @@ -156,8 +164,9 @@ func (m *SignBlockRequest) GetBlock() *Block { } type SignBlockResponse struct { - Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + VoteExtSignature []byte `protobuf:"bytes,2,opt,name=vote_ext_signature,json=voteExtSignature,proto3" json:"vote_ext_signature,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (m *SignBlockResponse) Reset() { *m = SignBlockResponse{} } @@ -200,6 +209,13 @@ func (m *SignBlockResponse) GetSignature() []byte { return nil } +func (m *SignBlockResponse) GetVoteExtSignature() []byte { + if m != nil { + return m.VoteExtSignature + } + return nil +} + func (m *SignBlockResponse) GetTimestamp() int64 { if m != nil { return m.Timestamp @@ -480,9 +496,11 @@ func (m *SetNoncesAndSignRequest) GetChainID() string { } type SetNoncesAndSignResponse struct { - NoncePublic []byte `protobuf:"bytes,1,opt,name=noncePublic,proto3" json:"noncePublic,omitempty"` - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + NoncePublic []byte `protobuf:"bytes,2,opt,name=noncePublic,proto3" json:"noncePublic,omitempty"` + Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` + VoteExtNoncePublic []byte `protobuf:"bytes,4,opt,name=voteExtNoncePublic,proto3" json:"voteExtNoncePublic,omitempty"` + VoteExtSignature []byte `protobuf:"bytes,5,opt,name=voteExtSignature,proto3" json:"voteExtSignature,omitempty"` } func (m *SetNoncesAndSignResponse) Reset() { *m = SetNoncesAndSignResponse{} } @@ -518,6 +536,13 @@ func (m *SetNoncesAndSignResponse) XXX_DiscardUnknown() { var xxx_messageInfo_SetNoncesAndSignResponse proto.InternalMessageInfo +func (m *SetNoncesAndSignResponse) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + func (m *SetNoncesAndSignResponse) GetNoncePublic() []byte { if m != nil { return m.NoncePublic @@ -525,16 +550,23 @@ func (m *SetNoncesAndSignResponse) GetNoncePublic() []byte { return nil } -func (m *SetNoncesAndSignResponse) GetTimestamp() int64 { +func (m *SetNoncesAndSignResponse) GetSignature() []byte { if m != nil { - return m.Timestamp + return m.Signature } - return 0 + return nil } -func (m *SetNoncesAndSignResponse) GetSignature() []byte { +func (m *SetNoncesAndSignResponse) GetVoteExtNoncePublic() []byte { if m != nil { - return m.Signature + return m.VoteExtNoncePublic + } + return nil +} + +func (m *SetNoncesAndSignResponse) GetVoteExtSignature() []byte { + if m != nil { + return m.VoteExtSignature } return nil } @@ -899,54 +931,58 @@ func init() { } var fileDescriptor_b7a1f695b94b848a = []byte{ - // 744 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdf, 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, 0x0f, 0xf0, 0xdd, 0x17, 0xff, 0x27, 0x1e, 0x79, - 0xf4, 0x4d, 0x03, 0xff, 0x88, 0xb9, 0xb7, 0xb7, 0x65, 0x2d, 0x1d, 0xf0, 0xc0, 0xd3, 0x7a, 0x4e, - 0xcf, 0x8f, 0xef, 0xfb, 0xf6, 0xdd, 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, 0x51, 0xd3, 0x56, 0x35, 0xf8, 0xa7, 0x06, 0xc6, 0xda, 0x28, 0x74, 0xbf, - 0xa3, 0x25, 0xa8, 0x7a, 0xc4, 0x1f, 0x7a, 0xdc, 0xd4, 0x9a, 0x5a, 0xab, 0x62, 0xab, 0x08, 0x2d, - 0x82, 0x41, 0xc3, 0x28, 0x18, 0x98, 0x65, 0x99, 0x8e, 0x03, 0x84, 0x40, 0x67, 0x9c, 0x8c, 0xcd, - 0x4a, 0x53, 0x6b, 0x19, 0xb6, 0x7c, 0x46, 0x4f, 0xa0, 0x26, 0x16, 0xae, 0x9d, 0x72, 0xc2, 0x4c, - 0xbd, 0xa9, 0xb5, 0x66, 0xed, 0xab, 0x84, 0x78, 0xcb, 0xfd, 0x43, 0xc2, 0xb8, 0x73, 0x38, 0x36, - 0x0d, 0x39, 0xeb, 0x2a, 0x81, 0xf7, 0x61, 0xbe, 0x27, 0x4a, 0x05, 0x14, 0x9b, 0x1c, 0x45, 0x84, - 0x71, 0x64, 0xc2, 0x03, 0xd7, 0x73, 0xfc, 0x60, 0x63, 0x5d, 0x42, 0xaa, 0xd9, 0x49, 0x88, 0x5e, - 0x83, 0xd1, 0x17, 0x95, 0x12, 0x53, 0xbd, 0x6b, 0xb5, 0x0b, 0xa8, 0xb5, 0xe3, 0x59, 0x71, 0x21, - 0xfe, 0x0a, 0x0b, 0x13, 0xf3, 0xd9, 0x38, 0x0c, 0x18, 0x49, 0x00, 0x3b, 0x3c, 0xa2, 0x44, 0xae, - 0x50, 0x80, 0x65, 0x22, 0x0b, 0xb8, 0x9c, 0x07, 0xfc, 0x5b, 0x03, 0x63, 0x2b, 0x0c, 0x5c, 0x82, - 0x2c, 0x98, 0x61, 0x61, 0x44, 0x5d, 0xa2, 0x70, 0x1a, 0x76, 0x1a, 0xa3, 0x67, 0x30, 0x37, 0x20, - 0x8c, 0xfb, 0x81, 0xc3, 0xfd, 0x50, 0x10, 0x29, 0xcb, 0x82, 0x6c, 0x52, 0x48, 0x3f, 0x8e, 0xfa, - 0x9b, 0xe4, 0x54, 0xca, 0x39, 0x6b, 0xab, 0x48, 0x48, 0xcf, 0x3c, 0x87, 0x12, 0x25, 0x66, 0x1c, - 0x64, 0x51, 0x1b, 0x39, 0xd4, 0xb8, 0x07, 0xb5, 0xdd, 0xdd, 0x8d, 0xf5, 0x18, 0x1a, 0x02, 0x3d, - 0x8a, 0xfc, 0x81, 0xe2, 0x26, 0x9f, 0x51, 0x17, 0xaa, 0x81, 0x78, 0xc9, 0xcc, 0x72, 0xb3, 0x32, - 0x55, 0x3c, 0xd9, 0x6f, 0xab, 0x4a, 0x7c, 0x00, 0xfa, 0x27, 0xbb, 0xb7, 0x73, 0x3f, 0x1e, 0xb9, - 0x12, 0x55, 0xcf, 0x8b, 0x7a, 0xa6, 0xc1, 0xe3, 0x1e, 0xe1, 0x72, 0x39, 0x5b, 0x0d, 0x06, 0xe2, - 0x2f, 0x4b, 0xdc, 0x70, 0x4f, 0x5c, 0xd0, 0x0a, 0xe8, 0x1e, 0x65, 0x5c, 0xa2, 0xaa, 0x77, 0x97, - 0x0b, 0x3b, 0x04, 0x59, 0x5b, 0x96, 0xdd, 0x62, 0xea, 0x09, 0x8b, 0x1a, 0x19, 0x8b, 0xe2, 0x13, - 0x30, 0xaf, 0x33, 0x51, 0xbe, 0x6b, 0x42, 0x5d, 0x82, 0xd9, 0x8e, 0xfa, 0x23, 0xdf, 0x55, 0x8c, - 0x26, 0x53, 0x37, 0x7b, 0x2f, 0xeb, 0x80, 0x4a, 0xde, 0x01, 0x2d, 0x98, 0xff, 0x98, 0x6c, 0x4e, - 0xc4, 0x5b, 0x04, 0x43, 0x08, 0xc6, 0x4c, 0xad, 0x59, 0x11, 0x4e, 0x92, 0x01, 0xde, 0x84, 0x85, - 0x89, 0x4a, 0x05, 0xee, 0x5d, 0xaa, 0xa9, 0x26, 0x35, 0x6d, 0x14, 0x2a, 0x94, 0x7a, 0x2c, 0xf5, - 0xc8, 0x7b, 0x58, 0xde, 0xa1, 0x4e, 0xc0, 0x0e, 0x08, 0xfd, 0x4c, 0x9c, 0x01, 0xa1, 0xcc, 0xf3, - 0xc7, 0xc9, 0x7e, 0x0b, 0x66, 0x46, 0x32, 0x99, 0x9e, 0xe5, 0x34, 0xc6, 0xfb, 0x60, 0x15, 0x35, - 0x2a, 0x38, 0x37, 0x74, 0x8a, 0xd3, 0x15, 0x3f, 0xaf, 0x0e, 0x06, 0x94, 0x30, 0x26, 0x95, 0xaa, - 0xd9, 0xd9, 0x24, 0x46, 0x52, 0x8f, 0x78, 0xb4, 0xc2, 0x83, 0x5f, 0x4a, 0xe6, 0x49, 0x4e, 0xad, - 0x5a, 0x82, 0x6a, 0xdc, 0xa9, 0x8e, 0xb1, 0x8a, 0xf0, 0x1c, 0xd4, 0xb7, 0xfd, 0x60, 0x98, 0xf4, - 0x3e, 0x84, 0xd9, 0x38, 0x8c, 0xdb, 0xba, 0x7f, 0x75, 0x98, 0xf9, 0xa0, 0xae, 0x5a, 0xb4, 0x07, - 0xb5, 0xf4, 0x9e, 0x41, 0xcf, 0x0b, 0xa5, 0xcb, 0xdf, 0x73, 0xd6, 0x8b, 0xdb, 0xca, 0xe2, 0x45, - 0xb8, 0x84, 0x8e, 0x60, 0x3e, 0x6f, 0x2a, 0xf4, 0xaa, 0xb8, 0xbb, 0xf8, 0x14, 0x59, 0x2b, 0x77, - 0xac, 0x4e, 0x57, 0xee, 0x41, 0x2d, 0xf5, 0xc8, 0x14, 0x42, 0x79, 0xb7, 0x4d, 0x21, 0x74, 0xcd, - 0x6a, 0xb8, 0x84, 0x7e, 0x00, 0xba, 0xfe, 0xdf, 0xa3, 0x76, 0x61, 0xff, 0x54, 0x77, 0x59, 0x9d, - 0x3b, 0xd7, 0xe7, 0x68, 0xc5, 0xaf, 0xa6, 0xd3, 0xca, 0x98, 0x66, 0x3a, 0xad, 0xac, 0x8f, 0x70, - 0x09, 0x7d, 0x01, 0x5d, 0x58, 0x04, 0x35, 0x0b, 0x3b, 0x26, 0xcc, 0x64, 0x3d, 0xbd, 0xa1, 0x22, - 0x19, 0xb7, 0xb6, 0x75, 0x76, 0xd1, 0xd0, 0xce, 0x2f, 0x1a, 0xda, 0xbf, 0x8b, 0x86, 0xf6, 0xeb, - 0xb2, 0x51, 0x3a, 0xbf, 0x6c, 0x94, 0xfe, 0x5c, 0x36, 0x4a, 0xdf, 0xde, 0x0e, 0x7d, 0xee, 0x45, - 0xfd, 0xb6, 0x1b, 0x1e, 0x76, 0x26, 0x06, 0xad, 0x1c, 0x93, 0x40, 0xdc, 0x05, 0x2c, 0xfd, 0x16, - 0x88, 0xed, 0xd9, 0x91, 0x5f, 0x02, 0xfd, 0xaa, 0xfc, 0x79, 0xf3, 0x3f, 0x00, 0x00, 0xff, 0xff, - 0x97, 0xd9, 0x62, 0x0b, 0x36, 0x08, 0x00, 0x00, + // 810 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcd, 0x6e, 0xeb, 0x44, + 0x14, 0x8e, 0x13, 0x3b, 0x34, 0x27, 0xbd, 0x28, 0x77, 0xb8, 0xba, 0xf8, 0x5a, 0x28, 0x0a, 0x23, + 0x40, 0x11, 0xdc, 0x26, 0x28, 0x95, 0xe8, 0xba, 0xa5, 0x15, 0x54, 0x05, 0x54, 0x9c, 0x76, 0x83, + 0xaa, 0x56, 0x8e, 0x33, 0x8d, 0x2d, 0x52, 0x3b, 0xf5, 0x8c, 0x43, 0xba, 0xe0, 0x1d, 0xd8, 0xf0, + 0x20, 0xbc, 0x45, 0x97, 0x5d, 0xb0, 0x60, 0x07, 0x6a, 0x5f, 0x04, 0xcd, 0x8f, 0x1d, 0xdb, 0x71, + 0xda, 0x2e, 0xba, 0x8a, 0xcf, 0xf1, 0x77, 0x7e, 0xbe, 0x73, 0xbe, 0x99, 0x18, 0x30, 0x65, 0x91, + 0x13, 0x4c, 0xc8, 0x34, 0x9c, 0x93, 0xbe, 0x17, 0x46, 0x6e, 0x14, 0x2f, 0xfa, 0x6e, 0x48, 0xfd, + 0x49, 0x40, 0xa2, 0xde, 0x2c, 0x0a, 0x59, 0x88, 0x3e, 0xca, 0x60, 0x7a, 0x0a, 0x83, 0xff, 0xd2, + 0xc0, 0xd8, 0x9b, 0x86, 0xee, 0xaf, 0xe8, 0x2d, 0xd4, 0x3d, 0xe2, 0x4f, 0x3c, 0x66, 0x6a, 0x1d, + 0xad, 0x5b, 0xb3, 0x95, 0x85, 0xde, 0x80, 0x11, 0x85, 0x71, 0x30, 0x36, 0xab, 0xc2, 0x2d, 0x0d, + 0x84, 0x40, 0xa7, 0x8c, 0xcc, 0xcc, 0x5a, 0x47, 0xeb, 0x1a, 0xb6, 0x78, 0x46, 0x9f, 0x40, 0x83, + 0x17, 0xdc, 0xbb, 0x61, 0x84, 0x9a, 0x7a, 0x47, 0xeb, 0x6e, 0xda, 0x4b, 0x07, 0xfa, 0x12, 0x5a, + 0xf3, 0x90, 0x91, 0x83, 0x05, 0x1b, 0xa6, 0x20, 0x43, 0x80, 0x56, 0xfc, 0x3c, 0x13, 0xf3, 0xaf, + 0x08, 0x65, 0xce, 0xd5, 0xcc, 0xac, 0x8b, 0xba, 0x4b, 0x07, 0x3e, 0x87, 0x96, 0x80, 0xf2, 0xb6, + 0x6d, 0x72, 0x1d, 0x13, 0xca, 0x90, 0x09, 0x1f, 0xb8, 0x9e, 0xe3, 0x07, 0x87, 0xfb, 0xa2, 0xfd, + 0x86, 0x9d, 0x98, 0xe8, 0x6b, 0x30, 0x46, 0x1c, 0x29, 0xfa, 0x6f, 0x0e, 0xac, 0x5e, 0xc9, 0x18, + 0x7a, 0x32, 0x97, 0x04, 0xe2, 0xdf, 0xe1, 0x75, 0x26, 0x3f, 0x9d, 0x85, 0x01, 0x25, 0x09, 0x39, + 0x87, 0xc5, 0x11, 0x11, 0x25, 0x14, 0x39, 0xe1, 0x40, 0xef, 0x01, 0x71, 0x12, 0x17, 0x64, 0xc1, + 0x2e, 0x96, 0xb0, 0xea, 0x0a, 0x3d, 0x89, 0xce, 0xd1, 0xab, 0x15, 0xe9, 0xfd, 0xa9, 0x81, 0xf1, + 0x53, 0x18, 0xb8, 0x04, 0x59, 0xb0, 0x41, 0xc3, 0x38, 0x72, 0x89, 0x62, 0x65, 0xd8, 0xa9, 0x8d, + 0x3e, 0x83, 0x57, 0x63, 0x42, 0x99, 0x1f, 0x38, 0xcc, 0x0f, 0x39, 0xed, 0xaa, 0x00, 0xe4, 0x9d, + 0x7c, 0xa9, 0xb3, 0x78, 0x74, 0x44, 0x6e, 0x44, 0x99, 0x4d, 0x5b, 0x59, 0x7c, 0xa9, 0xd4, 0x73, + 0x22, 0xa2, 0xd6, 0x24, 0x8d, 0x3c, 0x47, 0xa3, 0xc0, 0x11, 0x0f, 0xa1, 0x71, 0x7a, 0x7a, 0xb8, + 0x2f, 0x5b, 0x43, 0xa0, 0xc7, 0xb1, 0x3f, 0x56, 0x93, 0x10, 0xcf, 0x68, 0x00, 0xf5, 0x80, 0xbf, + 0xa4, 0x66, 0xb5, 0x53, 0x5b, 0x3b, 0x6a, 0x11, 0x6f, 0x2b, 0x24, 0xbe, 0x04, 0xfd, 0x7b, 0x7b, + 0x78, 0xf2, 0x32, 0xea, 0x5b, 0x0e, 0x55, 0x2f, 0x0e, 0xf5, 0x56, 0x83, 0x8f, 0x87, 0x84, 0x89, + 0xe2, 0x74, 0x37, 0x18, 0xf3, 0x65, 0x24, 0xda, 0x79, 0x21, 0x2e, 0x68, 0x0b, 0x74, 0x2f, 0xa2, + 0x4c, 0x74, 0xd5, 0x1c, 0xbc, 0x2b, 0x8d, 0xe0, 0x64, 0x6d, 0x01, 0x7b, 0xe2, 0xb8, 0x64, 0x04, + 0x6d, 0xe4, 0x04, 0x8d, 0xff, 0xd6, 0xc0, 0x5c, 0xa5, 0xb2, 0x94, 0xe9, 0x72, 0x0a, 0x5a, 0x61, + 0x0a, 0xa8, 0x03, 0x4d, 0xd1, 0xeb, 0x71, 0x3c, 0x9a, 0xfa, 0xae, 0xd2, 0x67, 0xd6, 0x95, 0x97, + 0x40, 0xad, 0x28, 0xf3, 0x9e, 0x94, 0xf9, 0xc1, 0x42, 0x56, 0x57, 0x69, 0x64, 0xef, 0x25, 0x6f, + 0x0a, 0x67, 0x3e, 0xab, 0xab, 0x15, 0x3f, 0xee, 0x42, 0xeb, 0xbb, 0x84, 0x55, 0xb2, 0x99, 0x37, + 0x60, 0xf0, 0x6d, 0x50, 0x53, 0xeb, 0xd4, 0xb8, 0x4c, 0x85, 0x81, 0x8f, 0xe0, 0x75, 0x06, 0xa9, + 0x88, 0x7f, 0x93, 0x2e, 0x4c, 0x13, 0x0b, 0x6b, 0x97, 0x8e, 0x3f, 0x15, 0x70, 0x2a, 0xc0, 0x1d, + 0x78, 0x77, 0x12, 0x39, 0x01, 0xbd, 0x24, 0xd1, 0x0f, 0xc4, 0x19, 0x93, 0x88, 0x7a, 0xfe, 0x2c, + 0xa9, 0x6f, 0xc1, 0xc6, 0x54, 0x38, 0xd3, 0x6b, 0x25, 0xb5, 0xf1, 0x39, 0x58, 0x65, 0x81, 0xaa, + 0x9d, 0x47, 0x22, 0xf9, 0xd1, 0x95, 0xcf, 0xbb, 0xe3, 0x71, 0x44, 0x28, 0x15, 0x7b, 0x68, 0xd8, + 0x79, 0x27, 0x46, 0x62, 0x1e, 0x32, 0xb5, 0xea, 0x07, 0x7f, 0x25, 0x98, 0x27, 0x3e, 0x55, 0xea, + 0x2d, 0xd4, 0x65, 0xa4, 0xba, 0x23, 0x94, 0x85, 0x5f, 0x41, 0xf3, 0xd8, 0x0f, 0x26, 0x49, 0xec, + 0x87, 0xb0, 0x29, 0x4d, 0x19, 0x36, 0xf8, 0x57, 0x87, 0x8d, 0x6f, 0xd5, 0x3f, 0x04, 0x3a, 0x83, + 0x46, 0x7a, 0xe5, 0xa1, 0xcf, 0x4b, 0x47, 0x57, 0xbc, 0x72, 0xad, 0x2f, 0x9e, 0x82, 0xc9, 0x42, + 0xb8, 0x82, 0xae, 0xa1, 0x55, 0x14, 0x2c, 0x7a, 0x5f, 0x1e, 0x5d, 0x7e, 0x44, 0xad, 0xad, 0x67, + 0xa2, 0xd3, 0x92, 0x67, 0xd0, 0x48, 0x35, 0xb2, 0x86, 0x50, 0x51, 0x6d, 0x6b, 0x08, 0xad, 0x48, + 0x0d, 0x57, 0xd0, 0x6f, 0x80, 0x56, 0x77, 0x8f, 0x7a, 0xa5, 0xf1, 0x6b, 0xd5, 0x65, 0xf5, 0x9f, + 0x8d, 0x2f, 0xd0, 0x92, 0xaf, 0xd6, 0xd3, 0xca, 0x89, 0x66, 0x3d, 0xad, 0xbc, 0x8e, 0x70, 0x05, + 0xfd, 0x08, 0x3a, 0x97, 0x08, 0xea, 0x94, 0x46, 0x64, 0xc4, 0x64, 0x7d, 0xfa, 0x08, 0x22, 0x49, + 0xb7, 0xf7, 0xf3, 0xed, 0x7d, 0x5b, 0xbb, 0xbb, 0x6f, 0x6b, 0xff, 0xdd, 0xb7, 0xb5, 0x3f, 0x1e, + 0xda, 0x95, 0xbb, 0x87, 0x76, 0xe5, 0x9f, 0x87, 0x76, 0xe5, 0x97, 0x9d, 0x89, 0xcf, 0xbc, 0x78, + 0xd4, 0x73, 0xc3, 0xab, 0x7e, 0x26, 0xd1, 0xd6, 0x9c, 0x04, 0xfc, 0x2e, 0xa0, 0xe9, 0x27, 0xcc, + 0x7c, 0xbb, 0x2f, 0x15, 0xda, 0x17, 0xdf, 0x30, 0xa3, 0xba, 0xf8, 0xd9, 0xfe, 0x3f, 0x00, 0x00, + 0xff, 0xff, 0x01, 0xd0, 0x72, 0x3a, 0xf0, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1232,7 +1268,14 @@ func (m *Block) MarshalToSizedBuffer(dAtA []byte) (int, error) { if m.Timestamp != 0 { i = encodeVarintCosigner(dAtA, i, uint64(m.Timestamp)) i-- - dAtA[i] = 0x28 + dAtA[i] = 0x30 + } + if len(m.VoteExtSignBytes) > 0 { + i -= len(m.VoteExtSignBytes) + copy(dAtA[i:], m.VoteExtSignBytes) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtSignBytes))) + i-- + dAtA[i] = 0x2a } if len(m.SignBytes) > 0 { i -= len(m.SignBytes) @@ -1324,7 +1367,14 @@ func (m *SignBlockResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { if m.Timestamp != 0 { i = encodeVarintCosigner(dAtA, i, uint64(m.Timestamp)) i-- - dAtA[i] = 0x10 + dAtA[i] = 0x18 + } + if len(m.VoteExtSignature) > 0 { + i -= len(m.VoteExtSignature) + copy(dAtA[i:], m.VoteExtSignature) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtSignature))) + i-- + dAtA[i] = 0x12 } if len(m.Signature) > 0 { i -= len(m.Signature) @@ -1567,6 +1617,20 @@ func (m *SetNoncesAndSignResponse) MarshalToSizedBuffer(dAtA []byte) (int, error _ = i var l int _ = l + if len(m.VoteExtSignature) > 0 { + i -= len(m.VoteExtSignature) + copy(dAtA[i:], m.VoteExtSignature) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtSignature))) + i-- + dAtA[i] = 0x2a + } + if len(m.VoteExtNoncePublic) > 0 { + i -= len(m.VoteExtNoncePublic) + copy(dAtA[i:], m.VoteExtNoncePublic) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtNoncePublic))) + i-- + dAtA[i] = 0x22 + } if len(m.Signature) > 0 { i -= len(m.Signature) copy(dAtA[i:], m.Signature) @@ -1574,17 +1638,17 @@ func (m *SetNoncesAndSignResponse) MarshalToSizedBuffer(dAtA []byte) (int, error i-- dAtA[i] = 0x1a } - if m.Timestamp != 0 { - i = encodeVarintCosigner(dAtA, i, uint64(m.Timestamp)) - i-- - dAtA[i] = 0x10 - } if len(m.NoncePublic) > 0 { i -= len(m.NoncePublic) copy(dAtA[i:], m.NoncePublic) i = encodeVarintCosigner(dAtA, i, uint64(len(m.NoncePublic))) i-- - dAtA[i] = 0xa + dAtA[i] = 0x12 + } + if m.Timestamp != 0 { + i = encodeVarintCosigner(dAtA, i, uint64(m.Timestamp)) + i-- + dAtA[i] = 0x8 } return len(dAtA) - i, nil } @@ -1852,6 +1916,10 @@ func (m *Block) Size() (n int) { if l > 0 { n += 1 + l + sovCosigner(uint64(l)) } + l = len(m.VoteExtSignBytes) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } if m.Timestamp != 0 { n += 1 + sovCosigner(uint64(m.Timestamp)) } @@ -1885,6 +1953,10 @@ func (m *SignBlockResponse) Size() (n int) { if l > 0 { n += 1 + l + sovCosigner(uint64(l)) } + l = len(m.VoteExtSignature) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } if m.Timestamp != 0 { n += 1 + sovCosigner(uint64(m.Timestamp)) } @@ -1995,17 +2067,25 @@ func (m *SetNoncesAndSignResponse) Size() (n int) { } var l int _ = l + if m.Timestamp != 0 { + n += 1 + sovCosigner(uint64(m.Timestamp)) + } l = len(m.NoncePublic) if l > 0 { n += 1 + l + sovCosigner(uint64(l)) } - if m.Timestamp != 0 { - n += 1 + sovCosigner(uint64(m.Timestamp)) - } l = len(m.Signature) if l > 0 { n += 1 + l + sovCosigner(uint64(l)) } + l = len(m.VoteExtNoncePublic) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + l = len(m.VoteExtSignature) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } return n } @@ -2235,6 +2315,40 @@ func (m *Block) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtSignBytes", 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.VoteExtSignBytes = append(m.VoteExtSignBytes[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtSignBytes == nil { + m.VoteExtSignBytes = []byte{} + } + iNdEx = postIndex + case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) } @@ -2456,6 +2570,40 @@ func (m *SignBlockResponse) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtSignature", 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.VoteExtSignature = append(m.VoteExtSignature[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtSignature == nil { + m.VoteExtSignature = []byte{} + } + iNdEx = postIndex + case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) } @@ -3179,6 +3327,25 @@ func (m *SetNoncesAndSignResponse) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + } + m.Timestamp = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCosigner + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Timestamp |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NoncePublic", wireType) } @@ -3212,11 +3379,11 @@ func (m *SetNoncesAndSignResponse) Unmarshal(dAtA []byte) error { m.NoncePublic = []byte{} } iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) } - m.Timestamp = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowCosigner @@ -3226,14 +3393,29 @@ func (m *SetNoncesAndSignResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Timestamp |= int64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - case 3: + if byteLen < 0 { + return ErrInvalidLengthCosigner + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCosigner + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtNoncePublic", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -3260,9 +3442,43 @@ func (m *SetNoncesAndSignResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) - if m.Signature == nil { - m.Signature = []byte{} + m.VoteExtNoncePublic = append(m.VoteExtNoncePublic[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtNoncePublic == nil { + m.VoteExtNoncePublic = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtSignature", 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.VoteExtSignature = append(m.VoteExtSignature[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtSignature == nil { + m.VoteExtSignature = []byte{} } iNdEx = postIndex default: diff --git a/signer/proto/remote_signer.pb.go b/signer/proto/remote_signer.pb.go index 810048ae..325f3619 100644 --- a/signer/proto/remote_signer.pb.go +++ b/signer/proto/remote_signer.pb.go @@ -125,7 +125,7 @@ func init() { } var fileDescriptor_afd7664cd19b584a = []byte{ - // 276 bytes of a gzipped FileDescriptorProto + // 279 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2f, 0x2e, 0x29, 0x4a, 0xcc, 0x4b, 0x4f, 0xcd, 0xc9, 0x2f, 0x4b, 0xd5, 0xcf, 0xc8, 0x2f, 0x4a, 0x2e, 0x2a, 0xad, 0xd0, 0x2f, 0x4a, 0xcd, 0xcd, 0x2f, 0x49, 0x8d, 0x2f, 0xce, 0x4c, 0xcf, 0x4b, 0x2d, 0xd2, 0x2b, 0x28, @@ -138,12 +138,12 @@ var fileDescriptor_afd7664cd19b584a = []byte{ 0x10, 0xd8, 0x9d, 0xc1, 0x60, 0xdb, 0x84, 0x82, 0xb9, 0xd8, 0x20, 0x7a, 0x85, 0x94, 0xf4, 0xb0, 0xb8, 0x55, 0x0f, 0xc5, 0x11, 0x52, 0xca, 0x78, 0xd5, 0x40, 0x2c, 0x57, 0x62, 0x10, 0x0a, 0xe7, 0x62, 0x01, 0x19, 0x2f, 0xa4, 0x8a, 0x55, 0x39, 0x48, 0xca, 0x29, 0x27, 0x3f, 0x39, 0x1b, 0x66, - 0xaa, 0x1a, 0x21, 0x65, 0x30, 0x83, 0x9d, 0xfc, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, + 0xaa, 0x1a, 0x21, 0x65, 0x30, 0x83, 0x9d, 0x02, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, - 0x8e, 0x21, 0xca, 0x24, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0xc9, - 0x34, 0xdd, 0xb2, 0xd4, 0xbc, 0x92, 0xd2, 0xa2, 0xd4, 0x62, 0x78, 0x38, 0x43, 0x42, 0x59, 0x1f, - 0x1c, 0xca, 0x49, 0x6c, 0x60, 0xca, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x12, 0x7a, 0x1d, 0xbd, - 0xd0, 0x01, 0x00, 0x00, + 0x8e, 0x21, 0xca, 0x3c, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0xc9, + 0x34, 0xdd, 0xb2, 0xd4, 0xbc, 0x92, 0xd2, 0xa2, 0xd4, 0x62, 0x78, 0x38, 0x97, 0x19, 0xeb, 0x43, + 0x02, 0x5a, 0x1f, 0x1c, 0xd0, 0x49, 0x6c, 0x60, 0xca, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x1e, + 0xa5, 0x90, 0x75, 0xd3, 0x01, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/signer/remote_cosigner.go b/signer/remote_cosigner.go index fe5b87f3..d0525d9b 100644 --- a/signer/remote_cosigner.go +++ b/signer/remote_cosigner.go @@ -119,9 +119,10 @@ func (cosigner *RemoteCosigner) SetNoncesAndSign( return nil, err } return &CosignerSignResponse{ - NoncePublic: res.GetNoncePublic(), - Timestamp: time.Unix(0, res.GetTimestamp()), - Signature: res.GetSignature(), + NoncePublic: res.GetNoncePublic(), + Timestamp: time.Unix(0, res.GetTimestamp()), + Signature: res.GetSignature(), + VoteExtensionNoncePublic: res.GetVoteExtNoncePublic(), }, nil } @@ -137,6 +138,7 @@ func (cosigner *RemoteCosigner) Sign( return nil, err } return &CosignerSignBlockResponse{ - Signature: res.GetSignature(), + Signature: res.GetSignature(), + VoteExtensionSignature: res.GetVoteExtSignature(), }, nil } diff --git a/signer/remote_signer.go b/signer/remote_signer.go index 744c7a77..88ce9ff5 100644 --- a/signer/remote_signer.go +++ b/signer/remote_signer.go @@ -22,7 +22,7 @@ const connRetrySec = 2 // PrivValidator is a wrapper for tendermint PrivValidator, // with additional Stop method for safe shutdown. type PrivValidator interface { - Sign(ctx context.Context, chainID string, block Block) ([]byte, time.Time, error) + Sign(ctx context.Context, chainID string, block Block) ([]byte, []byte, time.Time, error) GetPubKey(ctx context.Context, chainID string) ([]byte, error) Stop() } @@ -186,14 +186,15 @@ func (rs *ReconnRemoteSigner) handleSignVoteRequest(chainID string, vote *cometp Error: nil, }} - signature, timestamp, err := signAndTrack(context.TODO(), rs.Logger, rs.privVal, chainID, VoteToBlock(chainID, vote)) + sig, voteExtSig, 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} } msgSum.SignedVoteResponse.Vote.Timestamp = timestamp - msgSum.SignedVoteResponse.Vote.Signature = signature + msgSum.SignedVoteResponse.Vote.Signature = sig + msgSum.SignedVoteResponse.Vote.ExtensionSignature = voteExtSig return cometprotoprivval.Message{Sum: msgSum} } @@ -208,7 +209,7 @@ func (rs *ReconnRemoteSigner) handleSignProposalRequest( }, } - signature, timestamp, err := signAndTrack( + signature, _, timestamp, err := signAndTrack( context.TODO(), rs.Logger, rs.privVal, diff --git a/signer/remote_signer_grpc_server.go b/signer/remote_signer_grpc_server.go index c66ec76d..59c116da 100644 --- a/signer/remote_signer_grpc_server.go +++ b/signer/remote_signer_grpc_server.go @@ -83,14 +83,15 @@ func (s *RemoteSignerGRPCServer) Sign( ) (*proto.SignBlockResponse, error) { chainID, block := req.ChainID, BlockFromProto(req.Block) - signature, timestamp, err := signAndTrack(ctx, s.logger, s.validator, chainID, block) + sig, voteExtSig, timestamp, err := signAndTrack(ctx, s.logger, s.validator, chainID, block) if err != nil { return nil, err } return &proto.SignBlockResponse{ - Signature: signature, - Timestamp: timestamp.UnixNano(), + Signature: sig, + VoteExtSignature: voteExtSig, + Timestamp: timestamp.UnixNano(), }, nil } @@ -100,8 +101,8 @@ func signAndTrack( validator PrivValidator, chainID string, block Block, -) ([]byte, time.Time, error) { - signature, timestamp, err := validator.Sign(ctx, chainID, block) +) ([]byte, []byte, time.Time, error) { + sig, voteExtSig, timestamp, err := validator.Sign(ctx, chainID, block) if err != nil { switch typedErr := err.(type) { case *BeyondBlockError: @@ -125,13 +126,17 @@ func signAndTrack( ) failedSignVote.WithLabelValues(chainID).Inc() } - return nil, block.Timestamp, err + return nil, nil, block.Timestamp, err } // Show signatures provided to each node have the same signature and timestamps sigLen := 6 - if len(signature) < sigLen { - sigLen = len(signature) + if len(sig) < sigLen { + sigLen = len(sig) + } + extSigLen := 6 + if len(voteExtSig) < extSigLen { + extSigLen = len(voteExtSig) } logger.Info( "Signed", @@ -139,7 +144,8 @@ func signAndTrack( "chain_id", chainID, "height", block.Height, "round", block.Round, - "sig", signature[:sigLen], + "sig", sig[:sigLen], + "vote_ext_sig", voteExtSig[:extSigLen], "ts", block.Timestamp, ) @@ -182,5 +188,5 @@ func signAndTrack( totalPrecommitsSigned.WithLabelValues(chainID).Inc() } - return signature, timestamp, nil + return sig, voteExtSig, timestamp, nil } diff --git a/signer/sign_state.go b/signer/sign_state.go index 0ab8d731..819a36b0 100644 --- a/signer/sign_state.go +++ b/signer/sign_state.go @@ -62,11 +62,12 @@ func VoteToStep(vote *cometproto.Vote) int8 { func VoteToBlock(chainID string, vote *cometproto.Vote) Block { return Block{ - Height: vote.Height, - Round: int64(vote.Round), - Step: VoteToStep(vote), - SignBytes: comet.VoteSignBytes(chainID, vote), - Timestamp: vote.Timestamp, + Height: vote.Height, + Round: int64(vote.Round), + Step: VoteToStep(vote), + SignBytes: comet.VoteSignBytes(chainID, vote), + VoteExtensionSignBytes: comet.VoteExtensionSignBytes(chainID, vote), + Timestamp: vote.Timestamp, } } @@ -159,11 +160,12 @@ func (signState *SignState) hrsKeyLocked() HRSKey { } type SignStateConsensus struct { - Height int64 - Round int64 - Step int8 - Signature []byte - SignBytes cometbytes.HexBytes + Height int64 + Round int64 + Step int8 + Signature []byte + VoteExtensionSignature []byte + SignBytes cometbytes.HexBytes } func (signState SignStateConsensus) HRSKey() HRSKey { diff --git a/signer/single_signer_validator.go b/signer/single_signer_validator.go index ee0d40ff..23da5d88 100644 --- a/signer/single_signer_validator.go +++ b/signer/single_signer_validator.go @@ -49,10 +49,10 @@ func (pv *SingleSignerValidator) GetPubKey(_ context.Context, chainID string) ([ } // SignVote implements types.PrivValidator -func (pv *SingleSignerValidator) Sign(_ context.Context, chainID string, block Block) ([]byte, time.Time, error) { +func (pv *SingleSignerValidator) Sign(_ context.Context, chainID string, block Block) ([]byte, []byte, time.Time, error) { chainState, err := pv.loadChainStateIfNecessary(chainID) if err != nil { - return nil, block.Timestamp, err + return nil, nil, block.Timestamp, err } chainState.pvMutex.Lock() defer chainState.pvMutex.Unlock() diff --git a/signer/single_signer_validator_test.go b/signer/single_signer_validator_test.go index 4e23c3a1..aa0e1c06 100644 --- a/signer/single_signer_validator_test.go +++ b/signer/single_signer_validator_test.go @@ -58,7 +58,7 @@ func TestSingleSignerValidator(t *testing.T) { ctx := context.Background() - signature, _, err := validator.Sign(ctx, testChainID, block) + signature, _, _, err := validator.Sign(ctx, testChainID, block) require.NoError(t, err) require.True(t, privateKey.PubKey().VerifySignature(block.SignBytes, signature)) @@ -66,7 +66,7 @@ func TestSingleSignerValidator(t *testing.T) { proposal.Timestamp = time.Now() // should be able to sign same proposal with only differing timestamp - _, _, err = validator.Sign(ctx, 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 @@ -82,28 +82,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(ctx, 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(ctx, 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(ctx, "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(ctx, 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(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.NoError(t, err) } diff --git a/signer/threshold_validator.go b/signer/threshold_validator.go index 2a9d8609..8d954355 100644 --- a/signer/threshold_validator.go +++ b/signer/threshold_validator.go @@ -146,7 +146,7 @@ func (pv *ThresholdValidator) mustLoadChainState(chainID string) ChainSignState // sign process if it is greater than the current high watermark. A mutex is used to avoid concurrent // state updates. The disk write is scheduled in a separate goroutine which will perform an atomic write. // pendingDiskWG is used upon termination in pendingDiskWG to ensure all writes have completed. -func (pv *ThresholdValidator) SaveLastSignedStateInitiated(chainID string, block *Block) ([]byte, time.Time, error) { +func (pv *ThresholdValidator) SaveLastSignedStateInitiated(chainID string, block *Block) ([]byte, []byte, time.Time, error) { css := pv.mustLoadChainState(chainID) height, round, step := block.Height, block.Round, block.Step @@ -154,31 +154,31 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated(chainID string, block err := css.lastSignStateInitiated.Save(NewSignStateConsensus(height, round, step), &pv.pendingDiskWG) if err == nil { // good to sign - return nil, time.Time{}, nil + return nil, nil, time.Time{}, nil } // There was an error saving the last sign state, so check if there is an existing signature for this block. - existingSignature, existingTimestamp, sameBlockErr := pv.getExistingBlockSignature(chainID, block) + existingSignature, existingVoteExtSignature, existingTimestamp, sameBlockErr := pv.getExistingBlockSignature(chainID, block) if _, ok := err.(*SameHRSError); !ok { if sameBlockErr == nil { - return existingSignature, block.Timestamp, nil + return existingSignature, existingVoteExtSignature, block.Timestamp, nil } - return nil, existingTimestamp, pv.newBeyondBlockError(chainID, block.HRSKey()) + return nil, nil, existingTimestamp, pv.newBeyondBlockError(chainID, block.HRSKey()) } if sameBlockErr == nil { if existingSignature != nil { // signature already exists for this block. return it. - return existingSignature, existingTimestamp, nil + return existingSignature, existingVoteExtSignature, existingTimestamp, nil } // good to sign again - return nil, time.Time{}, nil + return nil, nil, time.Time{}, nil } if _, ok := sameBlockErr.(*StillWaitingForBlockError); !ok { // we have an error other than still waiting for block. return error. - return nil, existingTimestamp, fmt.Errorf( + return nil, nil, existingTimestamp, fmt.Errorf( "same block error, but we are not still waiting for signature: %w", sameBlockErr, ) @@ -208,12 +208,12 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated(chainID string, block continue } - existingSignature, existingTimestamp, sameBlockErr = pv.compareBlockSignatureAgainstSSC(chainID, block, &ssc) + existingSignature, existingVoteExtSignature, existingTimestamp, sameBlockErr = pv.compareBlockSignatureAgainstSSC(chainID, block, &ssc) if sameBlockErr == nil { - return existingSignature, existingTimestamp, nil + return existingSignature, existingVoteExtSignature, existingTimestamp, nil } if _, ok := sameBlockErr.(*StillWaitingForBlockError); !ok { - return nil, existingTimestamp, fmt.Errorf( + return nil, nil, existingTimestamp, fmt.Errorf( "same block error in loop, but we are not still waiting for signature: %w", sameBlockErr, ) @@ -232,7 +232,7 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated(chainID string, block ) } - return nil, existingTimestamp, fmt.Errorf( + return nil, nil, existingTimestamp, fmt.Errorf( "exceeded max attempts waiting for block to be signed. height: %d, round: %d, step: %d", height, round, step, ) @@ -278,11 +278,12 @@ func (pv *ThresholdValidator) GetPubKey(_ context.Context, chainID string) ([]by } type Block struct { - Height int64 - Round int64 - Step int8 - SignBytes []byte - Timestamp time.Time + Height int64 + Round int64 + Step int8 + SignBytes []byte + VoteExtensionSignBytes []byte + Timestamp time.Time } func (block Block) HRSKey() HRSKey { @@ -304,21 +305,23 @@ func (block Block) HRSTKey() HRSTKey { func (block Block) ToProto() *proto.Block { return &proto.Block{ - Height: block.Height, - Round: block.Round, - Step: int32(block.Step), - SignBytes: block.SignBytes, - Timestamp: block.Timestamp.UnixNano(), + Height: block.Height, + Round: block.Round, + Step: int32(block.Step), + SignBytes: block.SignBytes, + VoteExtSignBytes: block.VoteExtensionSignBytes, + Timestamp: block.Timestamp.UnixNano(), } } func BlockFromProto(block *proto.Block) Block { return Block{ - Height: block.Height, - Round: block.Round, - Step: int8(block.Step), - SignBytes: block.SignBytes, - Timestamp: time.Unix(0, block.Timestamp), + Height: block.Height, + Round: block.Round, + Step: int8(block.Step), + SignBytes: block.SignBytes, + VoteExtensionSignBytes: block.VoteExtSignBytes, + Timestamp: time.Unix(0, block.Timestamp), } } @@ -394,7 +397,7 @@ func (pv *ThresholdValidator) LoadSignStateIfNecessary(chainID string) error { // getExistingBlockSignature returns the existing block signature and no error if the signature is valid for the block. // It returns nil signature and nil error if there is no signature and it's okay to sign (fresh or again). // It returns an error if we have already signed a greater block, or if we are still waiting for in in-progress sign. -func (pv *ThresholdValidator) getExistingBlockSignature(chainID string, block *Block) ([]byte, time.Time, error) { +func (pv *ThresholdValidator) getExistingBlockSignature(chainID string, block *Block) ([]byte, []byte, time.Time, error) { css := pv.mustLoadChainState(chainID) latestBlock, existingSignature := css.lastSignState.GetFromCache(block.HRSKey()) @@ -404,7 +407,7 @@ func (pv *ThresholdValidator) getExistingBlockSignature(chainID string, block *B } // signature does not exist in cache, so compare against latest signed block. - return nil, block.Timestamp, pv.compareBlockSignatureAgainstHRS(chainID, block, latestBlock) + return nil, nil, block.Timestamp, pv.compareBlockSignatureAgainstHRS(chainID, block, latestBlock) } // compareBlockSignatureAgainstSSC compares a block's HRS against a cached signature. @@ -421,27 +424,27 @@ func (pv *ThresholdValidator) compareBlockSignatureAgainstSSC( chainID string, block *Block, existingSignature *SignStateConsensus, -) ([]byte, time.Time, error) { +) ([]byte, []byte, time.Time, error) { stamp, signBytes := block.Timestamp, block.SignBytes if err := pv.compareBlockSignatureAgainstHRS(chainID, block, existingSignature.HRSKey()); err != nil { if _, ok := err.(*SameBlockError); !ok { - return nil, stamp, err + return nil, nil, stamp, err } } // If a proposal has already been signed for this HRS, or the sign payload is identical, return the existing signature. if block.Step == stepPropose || bytes.Equal(signBytes, existingSignature.SignBytes) { - return existingSignature.Signature, block.Timestamp, nil + return existingSignature.Signature, existingSignature.VoteExtensionSignature, block.Timestamp, nil } // If there is a difference in the existing signature payload other than timestamp, return that error. if err := existingSignature.OnlyDifferByTimestamp(signBytes); err != nil { - return nil, stamp, err + return nil, nil, stamp, err } // only differ by timestamp, okay to sign again - return nil, stamp, nil + return nil, nil, stamp, nil } // compareBlockSignatureAgainstHRS returns a BeyondBlockError if the hrs is greater than the @@ -558,11 +561,11 @@ func (pv *ThresholdValidator) proxyIfNecessary( ctx context.Context, chainID string, block Block, -) (bool, []byte, time.Time, error) { +) (bool, []byte, []byte, time.Time, error) { height, round, step, stamp := block.Height, block.Round, block.Step, block.Timestamp if pv.leader.IsLeader() { - return false, nil, time.Time{}, nil + return false, nil, nil, time.Time{}, nil } leader := pv.leader.GetLeader() @@ -575,11 +578,11 @@ func (pv *ThresholdValidator) proxyIfNecessary( if leader == -1 { totalRaftLeaderElectionTimeout.Inc() - return true, nil, stamp, fmt.Errorf("timed out waiting for raft leader") + return true, nil, nil, stamp, fmt.Errorf("timed out waiting for raft leader") } if leader == pv.myCosigner.GetID() { - return false, nil, time.Time{}, nil + return false, nil, nil, time.Time{}, nil } pv.logger.Debug("I am not the leader. Proxying request to the leader", @@ -592,7 +595,7 @@ func (pv *ThresholdValidator) proxyIfNecessary( cosignerLeader := pv.peerCosigners.GetByID(leader) if cosignerLeader == nil { - return true, nil, stamp, fmt.Errorf("failed to find cosigner with id %d", leader) + return true, nil, nil, stamp, fmt.Errorf("failed to find cosigner with id %d", leader) } signRes, err := cosignerLeader.(*RemoteCosigner).Sign(ctx, CosignerSignBlockRequest{ @@ -604,16 +607,16 @@ func (pv *ThresholdValidator) proxyIfNecessary( rpcErrUnwrapped := err.(*cometrpcjsontypes.RPCError).Data // Need to return BeyondBlockError after proxy since the error type will be lost over RPC if len(rpcErrUnwrapped) > 33 && rpcErrUnwrapped[:33] == "Progress already started on block" { - return true, nil, stamp, &BeyondBlockError{msg: rpcErrUnwrapped} + return true, nil, nil, stamp, &BeyondBlockError{msg: rpcErrUnwrapped} } } - return true, nil, stamp, err + return true, nil, nil, stamp, err } - return true, signRes.Signature, stamp, nil + return true, signRes.Signature, signRes.VoteExtensionSignature, stamp, nil } -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 +func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Block) ([]byte, []byte, time.Time, error) { + height, round, step, stamp, signBytes, voteExtensionSignBytes := block.Height, block.Round, block.Step, block.Timestamp, block.SignBytes, block.VoteExtensionSignBytes log := pv.logger.With( "chain_id", chainID, @@ -623,14 +626,14 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl ) if err := pv.LoadSignStateIfNecessary(chainID); err != nil { - return nil, stamp, err + return nil, nil, stamp, err } // Only the leader can execute this function. Followers can handle the requests, // but they just need to proxy the request to the raft leader - isProxied, proxySig, proxyStamp, err := pv.proxyIfNecessary(ctx, chainID, block) + isProxied, proxySig, proxyVoteExtSig, proxyStamp, err := pv.proxyIfNecessary(ctx, chainID, block) if isProxied { - return proxySig, proxyStamp, err + return proxySig, proxyVoteExtSig, proxyStamp, err } totalRaftLeader.Inc() @@ -647,13 +650,13 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl } // Keep track of the last block that we began the signing process for. Only allow one attempt per block - existingSignature, existingTimestamp, err := pv.SaveLastSignedStateInitiated(chainID, &block) + existingSignature, existingVoteExtSig, existingTimestamp, err := pv.SaveLastSignedStateInitiated(chainID, &block) if err != nil { - return nil, stamp, fmt.Errorf("error saving last sign state initiated: %w", err) + return nil, nil, stamp, fmt.Errorf("error saving last sign state initiated: %w", err) } if existingSignature != nil { log.Debug("Returning existing signature", "signature", fmt.Sprintf("%x", existingSignature)) - return existingSignature, existingTimestamp, nil + return existingSignature, existingVoteExtSig, existingTimestamp, nil } numPeers := len(pv.peerCosigners) @@ -675,13 +678,23 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl nonces, cosignersForThisBlock, fallbackErr = pv.getNoncesFallback(ctx) if fallbackErr != nil { pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) - return nil, stamp, fmt.Errorf("failed to get nonces: %w", errors.Join(err, fallbackErr)) + return nil, nil, stamp, fmt.Errorf("failed to get nonces: %w", errors.Join(err, fallbackErr)) } dontIterateFastestCosigners = true } else { drainedNonceCache.Set(0) } + var voteExtNonces *CosignerUUIDNonces + if step == stepPrecommit && len(voteExtensionSignBytes) > 0 { + voteExtNonces, err = pv.nonceCache.GetNonces(cosignersForThisBlock) + if err != nil { + // TODO how to handle fallback for vote extensions? + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("failed to get nonces for vote extensions: %w", err) + } + } + nextFastestCosignerIndex := pv.threshold - 1 var nextFastestCosignerIndexMu sync.Mutex getNextFastestCosigner := func() Cosigner { @@ -710,6 +723,7 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl // destination for share signatures shareSignatures := make([][]byte, total) + voteExtShareSignatures := make([][]byte, total) var eg errgroup.Group for _, cosigner := range cosignersForThisBlock { @@ -721,13 +735,20 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl peerStartTime := time.Now() - // set peerNonces and sign in single rpc call. - sigRes, err := cosigner.SetNoncesAndSign(signCtx, CosignerSetNoncesAndSignRequest{ + sigReq := CosignerSetNoncesAndSignRequest{ ChainID: chainID, Nonces: nonces.For(cosigner.GetID()), HRST: hrst, SignBytes: signBytes, - }) + } + + if voteExtNonces != nil { + sigReq.VoteExtensionSignBytes = voteExtensionSignBytes + sigReq.VoteExtensionNonces = voteExtNonces.For(cosigner.GetID()) + } + + // set peerNonces and sign in single rpc call. + sigRes, err := cosigner.SetNoncesAndSign(signCtx, sigReq) if err != nil { log.Error( "Cosigner failed to set nonces and sign", @@ -763,6 +784,7 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl timedCosignerSignLag.WithLabelValues(cosigner.GetAddress()).Observe(time.Since(peerStartTime).Seconds()) } shareSignatures[cosigner.GetID()-1] = sigRes.Signature + voteExtShareSignatures[cosigner.GetID()-1] = sigRes.VoteExtensionSignature return nil } @@ -772,7 +794,7 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl if err := eg.Wait(); err != nil { pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) - return nil, stamp, fmt.Errorf("error from cosigner(s): %s", err) + return nil, nil, stamp, fmt.Errorf("error from cosigner(s): %s", err) } timedSignBlockCosignerLag.Observe(time.Since(timeStartSignBlock).Seconds()) @@ -798,14 +820,14 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl if len(shareSigs) < pv.threshold { totalInsufficientCosigners.Inc() pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) - return nil, stamp, errors.New("not enough co-signers") + return nil, nil, stamp, errors.New("not enough co-signers") } // assemble into final signature signature, err := pv.myCosigner.CombineSignatures(chainID, shareSigs) if err != nil { pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) - return nil, stamp, fmt.Errorf("error combining signatures: %w", err) + return nil, nil, stamp, fmt.Errorf("error combining signatures: %w", err) } // verify the combined signature before saving to watermark @@ -813,17 +835,61 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl totalInvalidSignature.Inc() pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) - return nil, stamp, errors.New("combined signature is not valid") + return nil, nil, stamp, errors.New("combined signature is not valid") + } + + var voteExtSig []byte + + if step == stepPrecommit && len(voteExtensionSignBytes) > 0 { + // collect all valid responses into array of partial signatures + voteExtShareSigs := make([]PartialSignature, 0, pv.threshold) + for idx, shareSig := range voteExtShareSignatures { + if len(shareSig) == 0 { + continue + } + + sig := make([]byte, len(shareSig)) + copy(sig, shareSig) + + // we are ok to use the share signatures - complete boolean + // prevents future concurrent access + voteExtShareSigs = append(voteExtShareSigs, PartialSignature{ + ID: idx + 1, + Signature: sig, + }) + } + + if len(voteExtShareSigs) < pv.threshold { + totalInsufficientCosigners.Inc() + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, errors.New("not enough co-signers for vote extension") + } + + // assemble into final signature + voteExtSig, err = pv.myCosigner.CombineSignatures(chainID, voteExtShareSigs) + if err != nil { + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("error combining vote extension signatures: %w", err) + } + + // verify the combined signature before saving to watermark + if !pv.myCosigner.VerifySignature(chainID, voteExtensionSignBytes, voteExtSig) { + totalInvalidSignature.Inc() + + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, errors.New("combined signature for vote extension is not valid") + } } newLss := ChainSignStateConsensus{ ChainID: chainID, SignStateConsensus: SignStateConsensus{ - Height: height, - Round: round, - Step: step, - Signature: signature, - SignBytes: signBytes, + Height: height, + Round: round, + Step: step, + Signature: signature, + SignBytes: signBytes, + VoteExtensionSignature: voteExtSig, }, } @@ -837,7 +903,7 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl if _, isSameHRSError := err.(*SameHRSError); !isSameHRSError { pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) - return nil, stamp, fmt.Errorf("error saving last sign state: %w", err) + return nil, nil, stamp, fmt.Errorf("error saving last sign state: %w", err) } } @@ -858,5 +924,5 @@ func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Bl "duration_ms", float64(timeSignBlock.Microseconds())/1000, ) - return signature, stamp, nil + return signature, voteExtSig, stamp, nil } diff --git a/signer/threshold_validator_test.go b/signer/threshold_validator_test.go index ea5c6060..4e67e24e 100644 --- a/signer/threshold_validator_test.go +++ b/signer/threshold_validator_test.go @@ -105,7 +105,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { block := ProposalToBlock(testChainID, &proposal) - signature, _, err := validator.Sign(ctx, testChainID, block) + signature, _, _, err := validator.Sign(ctx, testChainID, block) require.NoError(t, err) require.True(t, pubKey.VerifySignature(block.SignBytes, signature)) @@ -126,7 +126,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { validator.nonceCache.LoadN(ctx, 1) // should be able to sign same proposal with only differing timestamp - _, _, err = validator.Sign(ctx, testChainID, block) + _, _, _, err = validator.Sign(ctx, testChainID, block) require.NoError(t, err) // construct different block ID for proposal at same height as highest signed @@ -145,7 +145,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(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + signature, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.NoError(t, err) require.True(t, bytes.Equal(firstSignature, signature)) @@ -155,13 +155,13 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { validator.nonceCache.LoadN(ctx, 1) // should not be able to sign lower than highest signed - _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.Error(t, err, "double sign!") validator.nonceCache.LoadN(ctx, 1) // lower LSS should sign for different chain ID - _, _, err = validator.Sign(ctx, 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 @@ -179,7 +179,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { newValidator.nonceCache.LoadN(ctx, 1) - _, _, err = newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, _, err = newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.Error(t, err, "double sign!") proposal = cometproto.Proposal{ @@ -200,15 +200,15 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { newValidator.nonceCache.LoadN(ctx, 3) eg.Go(func() error { - _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + _, _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) return err }) eg.Go(func() error { - _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposalClone)) + _, _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposalClone)) return err }) eg.Go(func() error { - _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposalClone2)) + _, _, _, err := newValidator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposalClone2)) return err }) // signing higher block now should succeed @@ -234,19 +234,19 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { eg.Go(func() error { start := time.Now() - _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevote)) + _, _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevote)) t.Log("Sign time", "duration", time.Since(start)) return err }) eg.Go(func() error { start := time.Now() - _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevoteClone)) + _, _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevoteClone)) t.Log("Sign time", "duration", time.Since(start)) return err }) eg.Go(func() error { start := time.Now() - _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevoteClone2)) + _, _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &prevoteClone2)) t.Log("Sign time", "duration", time.Since(start)) return err }) @@ -259,6 +259,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { Round: 0, Type: cometproto.PrecommitType, Timestamp: time.Now(), + Extension: []byte("test"), } precommitClone := precommit @@ -267,25 +268,64 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { precommitClone2 := precommit precommitClone2.Timestamp = precommit.Timestamp.Add(4 * time.Millisecond) - newValidator.nonceCache.LoadN(ctx, 3) + newValidator.nonceCache.LoadN(ctx, 6) eg.Go(func() error { start := time.Now() t.Log("Sign time", "duration", time.Since(start)) - _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &precommit)) - return err + block := VoteToBlock(testChainID, &precommit) + sig, voteExtSig, _, err := newValidator.Sign(ctx, testChainID, block) + if err != nil { + return err + } + + if !pubKey.VerifySignature(block.SignBytes, sig) { + return fmt.Errorf("vote extension signature verification failed") + } + + if !pubKey.VerifySignature(block.VoteExtensionSignBytes, voteExtSig) { + return fmt.Errorf("vote extension signature verification failed") + } + + return nil }) eg.Go(func() error { start := time.Now() t.Log("Sign time", "duration", time.Since(start)) - _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &precommitClone)) - return err + block := VoteToBlock(testChainID, &precommitClone) + sig, voteExtSig, _, err := newValidator.Sign(ctx, testChainID, block) + if err != nil { + return err + } + + if !pubKey.VerifySignature(block.SignBytes, sig) { + return fmt.Errorf("vote extension signature verification failed") + } + + if !pubKey.VerifySignature(block.VoteExtensionSignBytes, voteExtSig) { + return fmt.Errorf("vote extension signature verification failed") + } + + return nil }) eg.Go(func() error { start := time.Now() - _, _, err := newValidator.Sign(ctx, testChainID, VoteToBlock(testChainID, &precommitClone2)) + block := VoteToBlock(testChainID, &precommitClone2) + sig, voteExtSig, _, err := newValidator.Sign(ctx, testChainID, block) t.Log("Sign time", "duration", time.Since(start)) - return err + if err != nil { + return err + } + + if !pubKey.VerifySignature(block.SignBytes, sig) { + return fmt.Errorf("vote extension signature verification failed") + } + + if !pubKey.VerifySignature(block.VoteExtensionSignBytes, voteExtSig) { + return fmt.Errorf("vote extension signature verification failed") + } + + return nil }) err = eg.Wait() @@ -457,7 +497,7 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) Type: cometproto.ProposalType, } - signature, _, err := tv.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) + signature, _, _, err := tv.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) if err != nil { t.Log("Proposal sign failed", "error", err) return @@ -499,7 +539,7 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) Type: cometproto.PrevoteType, } - signature, _, err := tv.Sign(ctx, testChainID, VoteToBlock(testChainID, &preVote)) + signature, _, _, err := tv.Sign(ctx, testChainID, VoteToBlock(testChainID, &preVote)) if err != nil { t.Log("PreVote sign failed", "error", err) return @@ -528,20 +568,23 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) for _, tv := range thresholdValidators { tv := tv - tv.nonceCache.LoadN(ctx, 1) + tv.nonceCache.LoadN(ctx, 2) go func() { defer wg.Done() // stagger signing requests with random sleep time.Sleep(time.Duration(mrand.Intn(50)+100) * time.Millisecond) //nolint:gosec + var extension = []byte{0x1, 0x2, 0x3} + preCommit := cometproto.Vote{ - Height: 1 + int64(i), - Round: 1, - Type: cometproto.PrecommitType, + Height: 1 + int64(i), + Round: 1, + Type: cometproto.PrecommitType, + Extension: extension, } - signature, _, err := tv.Sign(ctx, testChainID, VoteToBlock(testChainID, &preCommit)) + signature, voteExtSignature, _, err := tv.Sign(ctx, testChainID, VoteToBlock(testChainID, &preCommit)) if err != nil { t.Log("PreCommit sign failed", "error", err) return @@ -557,6 +600,17 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) return } + voteExtSignBytes := comet.VoteExtensionSignBytes(testChainID, &preCommit) + voteExtSig := make([]byte, len(voteExtSignature)) + copy(voteExtSig, voteExtSignature) + + if !pubKey.VerifySignature(voteExtSignBytes, voteExtSig) { + t.Log("PreCommit vote extension signature verification failed") + return + } else { + t.Log("PreCommit vote extension signature verification succeeded") + } + mu.Lock() defer mu.Unlock() success = true From 71b00456414feddbf065d924c4fb1606bdb32d55 Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Tue, 9 Jan 2024 12:14:00 -0700 Subject: [PATCH 02/10] Update single signer test --- signer/single_signer_validator_test.go | 17 +++++++++++++++++ signer/threshold_validator_test.go | 6 +++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/signer/single_signer_validator_test.go b/signer/single_signer_validator_test.go index aa0e1c06..4ef737b5 100644 --- a/signer/single_signer_validator_test.go +++ b/signer/single_signer_validator_test.go @@ -106,4 +106,21 @@ func TestSingleSignerValidator(t *testing.T) { // signing higher block now should succeed _, _, _, err = validator.Sign(ctx, testChainID, ProposalToBlock(testChainID, &proposal)) require.NoError(t, err) + + precommit := cometproto.Vote{ + Height: 2, + Round: 0, + Type: cometproto.PrecommitType, + Timestamp: time.Now(), + Extension: []byte("test"), + } + + block = VoteToBlock(testChainID, &precommit) + sig, voteExtSig, _, err := validator.Sign(ctx, testChainID, block) + require.NoError(t, err) + + require.True(t, privateKey.PubKey().VerifySignature(block.SignBytes, sig), "signature verification failed") + + require.True(t, privateKey.PubKey().VerifySignature(block.VoteExtensionSignBytes, voteExtSig), "vote extension signature verification failed") + } diff --git a/signer/threshold_validator_test.go b/signer/threshold_validator_test.go index 4e67e24e..7befae6f 100644 --- a/signer/threshold_validator_test.go +++ b/signer/threshold_validator_test.go @@ -280,7 +280,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { } if !pubKey.VerifySignature(block.SignBytes, sig) { - return fmt.Errorf("vote extension signature verification failed") + return fmt.Errorf("signature verification failed") } if !pubKey.VerifySignature(block.VoteExtensionSignBytes, voteExtSig) { @@ -299,7 +299,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { } if !pubKey.VerifySignature(block.SignBytes, sig) { - return fmt.Errorf("vote extension signature verification failed") + return fmt.Errorf("signature verification failed") } if !pubKey.VerifySignature(block.VoteExtensionSignBytes, voteExtSig) { @@ -318,7 +318,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { } if !pubKey.VerifySignature(block.SignBytes, sig) { - return fmt.Errorf("vote extension signature verification failed") + return fmt.Errorf("signature verification failed") } if !pubKey.VerifySignature(block.VoteExtensionSignBytes, voteExtSig) { From f8a45c6f5026163ef015e389581cd482f2619240 Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Tue, 9 Jan 2024 12:21:24 -0700 Subject: [PATCH 03/10] Lint --- signer/file.go | 3 ++- signer/remote_signer.go | 8 +++++++- signer/single_signer_validator.go | 10 +++++++++- signer/single_signer_validator_test.go | 3 ++- signer/threshold_validator.go | 25 +++++++++++++++++++------ signer/threshold_validator_test.go | 2 -- 6 files changed, 39 insertions(+), 12 deletions(-) diff --git a/signer/file.go b/signer/file.go index 1431e973..b0eae6f6 100644 --- a/signer/file.go +++ b/signer/file.go @@ -201,7 +201,8 @@ func (pv *FilePV) GetPubKey() (crypto.PubKey, error) { } func (pv *FilePV) Sign(block Block) ([]byte, []byte, time.Time, error) { - height, round, step, signBytes, voteExtensionSignBytes := block.Height, int32(block.Round), block.Step, block.SignBytes, block.VoteExtensionSignBytes + height, round, step := block.Height, int32(block.Round), block.Step + signBytes, voteExtensionSignBytes := block.SignBytes, block.VoteExtensionSignBytes lss := pv.LastSignState diff --git a/signer/remote_signer.go b/signer/remote_signer.go index 88ce9ff5..1a675e30 100644 --- a/signer/remote_signer.go +++ b/signer/remote_signer.go @@ -186,7 +186,13 @@ func (rs *ReconnRemoteSigner) handleSignVoteRequest(chainID string, vote *cometp Error: nil, }} - sig, voteExtSig, timestamp, err := signAndTrack(context.TODO(), rs.Logger, rs.privVal, chainID, VoteToBlock(chainID, vote)) + sig, voteExtSig, 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} diff --git a/signer/single_signer_validator.go b/signer/single_signer_validator.go index 23da5d88..33d78de0 100644 --- a/signer/single_signer_validator.go +++ b/signer/single_signer_validator.go @@ -49,7 +49,15 @@ func (pv *SingleSignerValidator) GetPubKey(_ context.Context, chainID string) ([ } // SignVote implements types.PrivValidator -func (pv *SingleSignerValidator) Sign(_ context.Context, chainID string, block Block) ([]byte, []byte, time.Time, error) { +func (pv *SingleSignerValidator) Sign( + _ context.Context, + chainID string, + block Block) ( + []byte, + []byte, + time.Time, + error, +) { chainState, err := pv.loadChainStateIfNecessary(chainID) if err != nil { return nil, nil, block.Timestamp, err diff --git a/signer/single_signer_validator_test.go b/signer/single_signer_validator_test.go index 4ef737b5..721ec234 100644 --- a/signer/single_signer_validator_test.go +++ b/signer/single_signer_validator_test.go @@ -121,6 +121,7 @@ func TestSingleSignerValidator(t *testing.T) { require.True(t, privateKey.PubKey().VerifySignature(block.SignBytes, sig), "signature verification failed") - require.True(t, privateKey.PubKey().VerifySignature(block.VoteExtensionSignBytes, voteExtSig), "vote extension signature verification failed") + require.True(t, privateKey.PubKey().VerifySignature(block.VoteExtensionSignBytes, voteExtSig), + "vote extension signature verification failed") } diff --git a/signer/threshold_validator.go b/signer/threshold_validator.go index 8d954355..ba33dbb1 100644 --- a/signer/threshold_validator.go +++ b/signer/threshold_validator.go @@ -146,7 +146,10 @@ func (pv *ThresholdValidator) mustLoadChainState(chainID string) ChainSignState // sign process if it is greater than the current high watermark. A mutex is used to avoid concurrent // state updates. The disk write is scheduled in a separate goroutine which will perform an atomic write. // pendingDiskWG is used upon termination in pendingDiskWG to ensure all writes have completed. -func (pv *ThresholdValidator) SaveLastSignedStateInitiated(chainID string, block *Block) ([]byte, []byte, time.Time, error) { +func (pv *ThresholdValidator) SaveLastSignedStateInitiated( + chainID string, + block *Block, +) ([]byte, []byte, time.Time, error) { css := pv.mustLoadChainState(chainID) height, round, step := block.Height, block.Round, block.Step @@ -158,7 +161,8 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated(chainID string, block } // There was an error saving the last sign state, so check if there is an existing signature for this block. - existingSignature, existingVoteExtSignature, existingTimestamp, sameBlockErr := pv.getExistingBlockSignature(chainID, block) + existingSignature, existingVoteExtSignature, + existingTimestamp, sameBlockErr := pv.getExistingBlockSignature(chainID, block) if _, ok := err.(*SameHRSError); !ok { if sameBlockErr == nil { @@ -208,7 +212,8 @@ func (pv *ThresholdValidator) SaveLastSignedStateInitiated(chainID string, block continue } - existingSignature, existingVoteExtSignature, existingTimestamp, sameBlockErr = pv.compareBlockSignatureAgainstSSC(chainID, block, &ssc) + existingSignature, existingVoteExtSignature, + existingTimestamp, sameBlockErr = pv.compareBlockSignatureAgainstSSC(chainID, block, &ssc) if sameBlockErr == nil { return existingSignature, existingVoteExtSignature, existingTimestamp, nil } @@ -397,7 +402,10 @@ func (pv *ThresholdValidator) LoadSignStateIfNecessary(chainID string) error { // getExistingBlockSignature returns the existing block signature and no error if the signature is valid for the block. // It returns nil signature and nil error if there is no signature and it's okay to sign (fresh or again). // It returns an error if we have already signed a greater block, or if we are still waiting for in in-progress sign. -func (pv *ThresholdValidator) getExistingBlockSignature(chainID string, block *Block) ([]byte, []byte, time.Time, error) { +func (pv *ThresholdValidator) getExistingBlockSignature( + chainID string, + block *Block, +) ([]byte, []byte, time.Time, error) { css := pv.mustLoadChainState(chainID) latestBlock, existingSignature := css.lastSignState.GetFromCache(block.HRSKey()) @@ -615,8 +623,13 @@ func (pv *ThresholdValidator) proxyIfNecessary( return true, signRes.Signature, signRes.VoteExtensionSignature, stamp, nil } -func (pv *ThresholdValidator) Sign(ctx context.Context, chainID string, block Block) ([]byte, []byte, time.Time, error) { - height, round, step, stamp, signBytes, voteExtensionSignBytes := block.Height, block.Round, block.Step, block.Timestamp, block.SignBytes, block.VoteExtensionSignBytes +func (pv *ThresholdValidator) Sign( + ctx context.Context, + chainID string, + block Block, +) ([]byte, []byte, time.Time, error) { + height, round, step, stamp := block.Height, block.Round, block.Step, block.Timestamp + signBytes, voteExtensionSignBytes := block.SignBytes, block.VoteExtensionSignBytes log := pv.logger.With( "chain_id", chainID, diff --git a/signer/threshold_validator_test.go b/signer/threshold_validator_test.go index 7befae6f..05d499d5 100644 --- a/signer/threshold_validator_test.go +++ b/signer/threshold_validator_test.go @@ -607,8 +607,6 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) if !pubKey.VerifySignature(voteExtSignBytes, voteExtSig) { t.Log("PreCommit vote extension signature verification failed") return - } else { - t.Log("PreCommit vote extension signature verification succeeded") } mu.Lock() From e68ce7f7666eb318e5c562edb60788458180b891 Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Tue, 9 Jan 2024 13:46:24 -0700 Subject: [PATCH 04/10] add additional checks --- signer/cosigner.go | 52 +++++++++++++++++++++++++++ signer/file.go | 9 +++-- signer/io.go | 23 ++++++++++++ signer/local_cosigner.go | 12 +++---- signer/serialization.go | 44 ----------------------- signer/serialization_test.go | 57 ------------------------------ signer/sign_state.go | 40 +++++++++++---------- signer/single_signer_validator.go | 2 +- signer/threshold_validator.go | 11 ++++-- signer/threshold_validator_test.go | 5 +++ 10 files changed, 125 insertions(+), 130 deletions(-) create mode 100644 signer/io.go delete mode 100644 signer/serialization.go delete mode 100644 signer/serialization_test.go diff --git a/signer/cosigner.go b/signer/cosigner.go index f3db4254..b1a9f116 100644 --- a/signer/cosigner.go +++ b/signer/cosigner.go @@ -2,9 +2,13 @@ package signer import ( "context" + "errors" + "fmt" "time" cometcrypto "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/libs/protoio" + cometproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/google/uuid" "github.com/strangelove-ventures/horcrux/v3/signer/proto" ) @@ -150,3 +154,51 @@ type CosignerSetNoncesAndSignRequest struct { SignBytes []byte VoteExtensionSignBytes []byte } + +func verifySignPayload(chainID string, signBytes, voteExtensionSignBytes []byte) (HRSTKey, bool, error) { + var vote cometproto.CanonicalVote + voteErr := protoio.UnmarshalDelimited(signBytes, &vote) + if voteErr == nil && (vote.Type == cometproto.PrevoteType || vote.Type == cometproto.PrecommitType) { + hrstKey := HRSTKey{ + Height: vote.Height, + Round: vote.Round, + Step: CanonicalVoteToStep(&vote), + Timestamp: vote.Timestamp.UnixNano(), + } + + if hrstKey.Step == stepPrecommit && len(voteExtensionSignBytes) > 0 && vote.BlockID != nil { + var voteExt cometproto.CanonicalVoteExtension + if err := protoio.UnmarshalDelimited(voteExtensionSignBytes, &voteExt); err != nil { + return hrstKey, false, fmt.Errorf("failed to unmarshal vote extension: %w", err) + } + if voteExt.ChainId != chainID { + return hrstKey, false, fmt.Errorf("vote extension chain ID %s does not match chain ID %s", voteExt.ChainId, chainID) + } + if voteExt.Height != hrstKey.Height { + return hrstKey, false, + fmt.Errorf("vote extension height %d does not match block height %d", voteExt.Height, hrstKey.Height) + } + if voteExt.Round != hrstKey.Round { + return hrstKey, false, + fmt.Errorf("vote extension round %d does not match block round %d", voteExt.Round, hrstKey.Round) + } + return hrstKey, true, nil + } + + return hrstKey, false, nil + } + + var proposal cometproto.CanonicalProposal + proposalErr := protoio.UnmarshalDelimited(signBytes, &proposal) + if proposalErr == nil { + return HRSTKey{ + Height: proposal.Height, + Round: proposal.Round, + Step: stepPropose, + Timestamp: proposal.Timestamp.UnixNano(), + }, false, nil + } + + return HRSTKey{}, false, + fmt.Errorf("failed to unmarshal sign bytes into vote or proposal: %w", errors.Join(voteErr, proposalErr)) +} diff --git a/signer/file.go b/signer/file.go index b0eae6f6..d4379dbc 100644 --- a/signer/file.go +++ b/signer/file.go @@ -200,7 +200,7 @@ func (pv *FilePV) GetPubKey() (crypto.PubKey, error) { return pv.Key.PubKey, nil } -func (pv *FilePV) Sign(block Block) ([]byte, []byte, time.Time, error) { +func (pv *FilePV) Sign(chainID string, block Block) ([]byte, []byte, time.Time, error) { height, round, step := block.Height, int32(block.Round), block.Step signBytes, voteExtensionSignBytes := block.SignBytes, block.VoteExtensionSignBytes @@ -211,13 +211,18 @@ func (pv *FilePV) Sign(block Block) ([]byte, []byte, time.Time, error) { return nil, nil, block.Timestamp, err } + _, hasVoteExtensions, err := verifySignPayload(chainID, signBytes, voteExtensionSignBytes) + if err != nil { + return nil, nil, block.Timestamp, err + } + // Vote extensions are non-deterministic, so it is possible that an // application may have created a different extension. We therefore always // re-sign the vote extensions of precommits. For prevotes and nil // precommits, the extension signature will always be empty. // Even if the signed over data is empty, we still add the signature var extSig []byte - if block.Step == stepPrecommit && len(voteExtensionSignBytes) > 0 { + if hasVoteExtensions { extSig, err = pv.Key.PrivKey.Sign(voteExtensionSignBytes) if err != nil { return nil, nil, block.Timestamp, err diff --git a/signer/io.go b/signer/io.go new file mode 100644 index 00000000..45237a8a --- /dev/null +++ b/signer/io.go @@ -0,0 +1,23 @@ +package signer + +import ( + "io" + + "github.com/cometbft/cometbft/libs/protoio" + cometprotoprivval "github.com/cometbft/cometbft/proto/tendermint/privval" +) + +// ReadMsg reads a message from an io.Reader +func ReadMsg(reader io.Reader) (msg cometprotoprivval.Message, err error) { + const maxRemoteSignerMsgSize = 1024 * 10 + protoReader := protoio.NewDelimitedReader(reader, maxRemoteSignerMsgSize) + _, err = protoReader.ReadMsg(&msg) + return msg, err +} + +// WriteMsg writes a message to an io.Writer +func WriteMsg(writer io.Writer, msg cometprotoprivval.Message) (err error) { + protoWriter := protoio.NewDelimitedWriter(writer) + _, err = protoWriter.WriteMsg(&msg) + return err +} diff --git a/signer/local_cosigner.go b/signer/local_cosigner.go index 10875b88..4559974f 100644 --- a/signer/local_cosigner.go +++ b/signer/local_cosigner.go @@ -214,14 +214,14 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon return res, err } - // This function has multiple exit points. Only start time can be guaranteed - metricsTimeKeeper.SetPreviousLocalSignStart(time.Now()) - - hrst, err := UnpackHRST(req.SignBytes) + hrst, hasVoteExtensions, err := verifySignPayload(chainID, req.SignBytes, req.VoteExtensionSignBytes) if err != nil { return res, err } + // This function has multiple exit points. Only start time can be guaranteed + metricsTimeKeeper.SetPreviousLocalSignStart(time.Now()) + existingSignature, err := ccs.lastSignState.existingSignatureOrErrorIfRegression(hrst, req.SignBytes) if err != nil { return res, err @@ -249,7 +249,7 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon } var voteExtNonces []Nonce - if len(req.VoteExtensionSignBytes) > 0 { + if hasVoteExtensions { voteExtNonces, err = cosigner.combinedNonces( cosigner.GetID(), uint8(cosigner.config.Config.ThresholdModeConfig.Threshold), @@ -268,7 +268,7 @@ func (cosigner *LocalCosigner) sign(req CosignerSignRequest) (CosignerSignRespon sig, err = ccs.signer.Sign(nonces, req.SignBytes) return err }) - if len(req.VoteExtensionSignBytes) > 0 { + if hasVoteExtensions { eg.Go(func() error { var err error voteExtSig, err = ccs.signer.Sign(voteExtNonces, req.VoteExtensionSignBytes) diff --git a/signer/serialization.go b/signer/serialization.go deleted file mode 100644 index ee6b5ab4..00000000 --- a/signer/serialization.go +++ /dev/null @@ -1,44 +0,0 @@ -package signer - -import ( - "errors" - "io" - - "github.com/cometbft/cometbft/libs/protoio" - cometprotoprivval "github.com/cometbft/cometbft/proto/tendermint/privval" - cometproto "github.com/cometbft/cometbft/proto/tendermint/types" -) - -// ReadMsg reads a message from an io.Reader -func ReadMsg(reader io.Reader) (msg cometprotoprivval.Message, err error) { - const maxRemoteSignerMsgSize = 1024 * 10 - protoReader := protoio.NewDelimitedReader(reader, maxRemoteSignerMsgSize) - _, err = protoReader.ReadMsg(&msg) - return msg, err -} - -// WriteMsg writes a message to an io.Writer -func WriteMsg(writer io.Writer, msg cometprotoprivval.Message) (err error) { - protoWriter := protoio.NewDelimitedWriter(writer) - _, err = protoWriter.WriteMsg(&msg) - return err -} - -// UnpackHRS deserializes sign bytes and gets the height, round, and step -func UnpackHRST(signBytes []byte) (HRSTKey, error) { - { - var proposal cometproto.CanonicalProposal - if err := protoio.UnmarshalDelimited(signBytes, &proposal); err == nil { - return HRSTKey{proposal.Height, proposal.Round, stepPropose, proposal.Timestamp.UnixNano()}, nil - } - } - - { - var vote cometproto.CanonicalVote - if err := protoio.UnmarshalDelimited(signBytes, &vote); err == nil { - return HRSTKey{vote.Height, vote.Round, CanonicalVoteToStep(&vote), vote.Timestamp.UnixNano()}, nil - } - } - - return HRSTKey{0, 0, 0, 0}, errors.New("could not UnpackHRS from sign bytes") -} diff --git a/signer/serialization_test.go b/signer/serialization_test.go deleted file mode 100644 index d51a678c..00000000 --- a/signer/serialization_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package signer - -import ( - "testing" - - cometproto "github.com/cometbft/cometbft/proto/tendermint/types" - comet "github.com/cometbft/cometbft/types" - "github.com/stretchr/testify/require" -) - -func TestUnpackHRSPrevote(t *testing.T) { - vote := cometproto.Vote{ - Height: 1, - Round: 2, - Type: cometproto.PrevoteType, - } - - signBytes := comet.VoteSignBytes("chain-id", &vote) - - hrs, err := UnpackHRST(signBytes) - require.NoError(t, err) - require.Equal(t, int64(1), hrs.Height) - require.Equal(t, int64(2), hrs.Round) - require.Equal(t, int8(2), hrs.Step) -} - -func TestUnpackHRSPrecommit(t *testing.T) { - vote := cometproto.Vote{ - Height: 3, - Round: 2, - Type: cometproto.PrecommitType, - } - - signBytes := comet.VoteSignBytes("chain-id", &vote) - - hrs, err := UnpackHRST(signBytes) - require.NoError(t, err) - require.Equal(t, int64(3), hrs.Height) - require.Equal(t, int64(2), hrs.Round) - require.Equal(t, int8(3), hrs.Step) -} - -func TestUnpackHRSProposal(t *testing.T) { - proposal := cometproto.Proposal{ - Height: 1, - Round: 2, - Type: cometproto.ProposalType, - } - - signBytes := comet.ProposalSignBytes("chain-id", &proposal) - - hrs, err := UnpackHRST(signBytes) - require.NoError(t, err) - require.Equal(t, int64(1), hrs.Height) - require.Equal(t, int64(2), hrs.Round) - require.Equal(t, int8(1), hrs.Step) -} diff --git a/signer/sign_state.go b/signer/sign_state.go index 819a36b0..f6c55eb0 100644 --- a/signer/sign_state.go +++ b/signer/sign_state.go @@ -100,12 +100,13 @@ func StepToType(step int8) cometproto.SignedMsgType { // SignState stores signing information for high level watermark management. type SignState struct { - Height int64 `json:"height"` - Round int64 `json:"round"` - Step int8 `json:"step"` - NoncePublic []byte `json:"nonce_public"` - Signature []byte `json:"signature,omitempty"` - SignBytes cometbytes.HexBytes `json:"signbytes,omitempty"` + Height int64 `json:"height"` + Round int64 `json:"round"` + Step int8 `json:"step"` + NoncePublic []byte `json:"nonce_public"` + Signature []byte `json:"signature,omitempty"` + SignBytes cometbytes.HexBytes `json:"signbytes,omitempty"` + VoteExtensionSignature []byte `json:"vote_ext_signature,omitempty"` filePath string @@ -232,6 +233,7 @@ func (signState *SignState) cacheAndMarshal(ssc SignStateConsensus) []byte { signState.Step = ssc.Step signState.Signature = ssc.Signature signState.SignBytes = ssc.SignBytes + signState.VoteExtensionSignature = ssc.VoteExtensionSignature jsonBytes, err := cometjson.MarshalIndent(signState, "", " ") if err != nil { @@ -416,13 +418,14 @@ func (signState *SignState) GetErrorIfLessOrEqual(height int64, round int64, ste // including the most recent sign state. func (signState *SignState) FreshCache() *SignState { newSignState := &SignState{ - Height: signState.Height, - Round: signState.Round, - Step: signState.Step, - NoncePublic: signState.NoncePublic, - Signature: signState.Signature, - SignBytes: signState.SignBytes, - cache: make(map[HRSKey]SignStateConsensus), + Height: signState.Height, + Round: signState.Round, + Step: signState.Step, + NoncePublic: signState.NoncePublic, + Signature: signState.Signature, + SignBytes: signState.SignBytes, + VoteExtensionSignature: signState.VoteExtensionSignature, + cache: make(map[HRSKey]SignStateConsensus), filePath: signState.filePath, } @@ -434,11 +437,12 @@ func (signState *SignState) FreshCache() *SignState { Round: signState.Round, Step: signState.Step, }] = SignStateConsensus{ - Height: signState.Height, - Round: signState.Round, - Step: signState.Step, - Signature: signState.Signature, - SignBytes: signState.SignBytes, + Height: signState.Height, + Round: signState.Round, + Step: signState.Step, + Signature: signState.Signature, + SignBytes: signState.SignBytes, + VoteExtensionSignature: signState.VoteExtensionSignature, } return newSignState diff --git a/signer/single_signer_validator.go b/signer/single_signer_validator.go index 33d78de0..846e283d 100644 --- a/signer/single_signer_validator.go +++ b/signer/single_signer_validator.go @@ -65,7 +65,7 @@ func (pv *SingleSignerValidator) Sign( chainState.pvMutex.Lock() defer chainState.pvMutex.Unlock() - return chainState.filePV.Sign(block) + return chainState.filePV.Sign(chainID, block) } func (pv *SingleSignerValidator) loadChainStateIfNecessary(chainID string) (*SingleSignerChainState, error) { diff --git a/signer/threshold_validator.go b/signer/threshold_validator.go index ba33dbb1..7ca5f87b 100644 --- a/signer/threshold_validator.go +++ b/signer/threshold_validator.go @@ -699,7 +699,14 @@ func (pv *ThresholdValidator) Sign( } var voteExtNonces *CosignerUUIDNonces - if step == stepPrecommit && len(voteExtensionSignBytes) > 0 { + + _, hasVoteExtensions, err := verifySignPayload(chainID, signBytes, voteExtensionSignBytes) + if err != nil { + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("failed to verify payload: %w", err) + } + + if hasVoteExtensions { voteExtNonces, err = pv.nonceCache.GetNonces(cosignersForThisBlock) if err != nil { // TODO how to handle fallback for vote extensions? @@ -853,7 +860,7 @@ func (pv *ThresholdValidator) Sign( var voteExtSig []byte - if step == stepPrecommit && len(voteExtensionSignBytes) > 0 { + if hasVoteExtensions { // collect all valid responses into array of partial signatures voteExtShareSigs := make([]PartialSignature, 0, pv.threshold) for idx, shareSig := range voteExtShareSignatures { diff --git a/signer/threshold_validator_test.go b/signer/threshold_validator_test.go index 05d499d5..ca016cdc 100644 --- a/signer/threshold_validator_test.go +++ b/signer/threshold_validator_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/rand" + "crypto/sha256" "fmt" mrand "math/rand" "path/filepath" @@ -254,9 +255,13 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { err = eg.Wait() require.NoError(t, err) + blockIDHash := sha256.New() + blockIDHash.Write([]byte("something")) + precommit := cometproto.Vote{ Height: int64(i), Round: 0, + BlockID: cometproto.BlockID{Hash: blockIDHash.Sum(nil)}, Type: cometproto.PrecommitType, Timestamp: time.Now(), Extension: []byte("test"), From c1a24e64a4405d13da8d738249a7fdd24e5c7ed0 Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Tue, 9 Jan 2024 13:53:36 -0700 Subject: [PATCH 05/10] Fix leader election test --- signer/threshold_validator_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/signer/threshold_validator_test.go b/signer/threshold_validator_test.go index ca016cdc..f5c27c3e 100644 --- a/signer/threshold_validator_test.go +++ b/signer/threshold_validator_test.go @@ -582,9 +582,13 @@ func testThresholdValidatorLeaderElection(t *testing.T, threshold, total uint8) var extension = []byte{0x1, 0x2, 0x3} + blockIDHash := sha256.New() + blockIDHash.Write([]byte("something")) + preCommit := cometproto.Vote{ Height: 1 + int64(i), Round: 1, + BlockID: cometproto.BlockID{Hash: blockIDHash.Sum(nil)}, Type: cometproto.PrecommitType, Extension: extension, } From bd20c607d0f7a13304f5e776b31c2c988ca6172a Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Tue, 9 Jan 2024 17:52:31 -0700 Subject: [PATCH 06/10] fix e2e --- proto/strangelove/horcrux/cosigner.proto | 5 +- signer/cosigner.go | 10 +- signer/cosigner_grpc_server.go | 22 +- signer/proto/cosigner.pb.go | 284 ++++++++++++++++++----- signer/remote_cosigner.go | 25 +- signer/threshold_validator.go | 128 ++++++---- signer/threshold_validator_test.go | 2 +- 7 files changed, 358 insertions(+), 118 deletions(-) diff --git a/proto/strangelove/horcrux/cosigner.proto b/proto/strangelove/horcrux/cosigner.proto index 53645408..5e0c0cef 100644 --- a/proto/strangelove/horcrux/cosigner.proto +++ b/proto/strangelove/horcrux/cosigner.proto @@ -57,7 +57,10 @@ message SetNoncesAndSignRequest { repeated Nonce nonces = 2; HRST hrst = 3; bytes signBytes = 4; - string chainID = 5; + bytes voteExtUuid = 5; + repeated Nonce voteExtNonces = 6; + bytes voteExtSignBytes = 7; + string chainID = 8; } message SetNoncesAndSignResponse { diff --git a/signer/cosigner.go b/signer/cosigner.go index b1a9f116..216e7e59 100644 --- a/signer/cosigner.go +++ b/signer/cosigner.go @@ -147,11 +147,13 @@ func (n CosignerUUIDNoncesMultiple) toProto() []*proto.UUIDNonce { } type CosignerSetNoncesAndSignRequest struct { - ChainID string - Nonces *CosignerUUIDNonces + ChainID string + HRST HRSTKey + + Nonces *CosignerUUIDNonces + SignBytes []byte + VoteExtensionNonces *CosignerUUIDNonces - HRST HRSTKey - SignBytes []byte VoteExtensionSignBytes []byte } diff --git a/signer/cosigner_grpc_server.go b/signer/cosigner_grpc_server.go index 9cc6433c..1522ae15 100644 --- a/signer/cosigner_grpc_server.go +++ b/signer/cosigner_grpc_server.go @@ -48,15 +48,27 @@ func (rpc *CosignerGRPCServer) SetNoncesAndSign( ctx context.Context, req *proto.SetNoncesAndSignRequest, ) (*proto.SetNoncesAndSignResponse, error) { - res, err := rpc.cosigner.SetNoncesAndSign(ctx, CosignerSetNoncesAndSignRequest{ + cosignerReq := CosignerSetNoncesAndSignRequest{ ChainID: req.ChainID, + + HRST: HRSTKeyFromProto(req.Hrst), + Nonces: &CosignerUUIDNonces{ UUID: uuid.UUID(req.Uuid), - Nonces: CosignerNoncesFromProto(req.GetNonces()), + Nonces: CosignerNoncesFromProto(req.Nonces), }, - HRST: HRSTKeyFromProto(req.GetHrst()), - SignBytes: req.GetSignBytes(), - }) + SignBytes: req.SignBytes, + } + + if len(req.VoteExtSignBytes) > 0 && len(req.VoteExtUuid) == 16 { + cosignerReq.VoteExtensionNonces = &CosignerUUIDNonces{ + UUID: uuid.UUID(req.VoteExtUuid), + Nonces: CosignerNoncesFromProto(req.VoteExtNonces), + } + cosignerReq.VoteExtensionSignBytes = req.VoteExtSignBytes + } + + res, err := rpc.cosigner.SetNoncesAndSign(ctx, cosignerReq) if err != nil { rpc.raftStore.logger.Error( "Failed to sign with shard", diff --git a/signer/proto/cosigner.pb.go b/signer/proto/cosigner.pb.go index 460e773c..1aed51a3 100644 --- a/signer/proto/cosigner.pb.go +++ b/signer/proto/cosigner.pb.go @@ -420,11 +420,14 @@ func (m *HRST) GetTimestamp() int64 { } type SetNoncesAndSignRequest 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"` - 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"` + 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"` + VoteExtUuid []byte `protobuf:"bytes,5,opt,name=voteExtUuid,proto3" json:"voteExtUuid,omitempty"` + VoteExtNonces []*Nonce `protobuf:"bytes,6,rep,name=voteExtNonces,proto3" json:"voteExtNonces,omitempty"` + VoteExtSignBytes []byte `protobuf:"bytes,7,opt,name=voteExtSignBytes,proto3" json:"voteExtSignBytes,omitempty"` + ChainID string `protobuf:"bytes,8,opt,name=chainID,proto3" json:"chainID,omitempty"` } func (m *SetNoncesAndSignRequest) Reset() { *m = SetNoncesAndSignRequest{} } @@ -488,6 +491,27 @@ func (m *SetNoncesAndSignRequest) GetSignBytes() []byte { return nil } +func (m *SetNoncesAndSignRequest) GetVoteExtUuid() []byte { + if m != nil { + return m.VoteExtUuid + } + return nil +} + +func (m *SetNoncesAndSignRequest) GetVoteExtNonces() []*Nonce { + if m != nil { + return m.VoteExtNonces + } + return nil +} + +func (m *SetNoncesAndSignRequest) GetVoteExtSignBytes() []byte { + if m != nil { + return m.VoteExtSignBytes + } + return nil +} + func (m *SetNoncesAndSignRequest) GetChainID() string { if m != nil { return m.ChainID @@ -931,58 +955,60 @@ func init() { } var fileDescriptor_b7a1f695b94b848a = []byte{ - // 810 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcd, 0x6e, 0xeb, 0x44, - 0x14, 0x8e, 0x13, 0x3b, 0x34, 0x27, 0xbd, 0x28, 0x77, 0xb8, 0xba, 0xf8, 0x5a, 0x28, 0x0a, 0x23, - 0x40, 0x11, 0xdc, 0x26, 0x28, 0x95, 0xe8, 0xba, 0xa5, 0x15, 0x54, 0x05, 0x54, 0x9c, 0x76, 0x83, - 0xaa, 0x56, 0x8e, 0x33, 0x8d, 0x2d, 0x52, 0x3b, 0xf5, 0x8c, 0x43, 0xba, 0xe0, 0x1d, 0xd8, 0xf0, - 0x20, 0xbc, 0x45, 0x97, 0x5d, 0xb0, 0x60, 0x07, 0x6a, 0x5f, 0x04, 0xcd, 0x8f, 0x1d, 0xdb, 0x71, - 0xda, 0x2e, 0xba, 0x8a, 0xcf, 0xf1, 0x77, 0x7e, 0xbe, 0x73, 0xbe, 0x99, 0x18, 0x30, 0x65, 0x91, - 0x13, 0x4c, 0xc8, 0x34, 0x9c, 0x93, 0xbe, 0x17, 0x46, 0x6e, 0x14, 0x2f, 0xfa, 0x6e, 0x48, 0xfd, - 0x49, 0x40, 0xa2, 0xde, 0x2c, 0x0a, 0x59, 0x88, 0x3e, 0xca, 0x60, 0x7a, 0x0a, 0x83, 0xff, 0xd2, - 0xc0, 0xd8, 0x9b, 0x86, 0xee, 0xaf, 0xe8, 0x2d, 0xd4, 0x3d, 0xe2, 0x4f, 0x3c, 0x66, 0x6a, 0x1d, - 0xad, 0x5b, 0xb3, 0x95, 0x85, 0xde, 0x80, 0x11, 0x85, 0x71, 0x30, 0x36, 0xab, 0xc2, 0x2d, 0x0d, - 0x84, 0x40, 0xa7, 0x8c, 0xcc, 0xcc, 0x5a, 0x47, 0xeb, 0x1a, 0xb6, 0x78, 0x46, 0x9f, 0x40, 0x83, - 0x17, 0xdc, 0xbb, 0x61, 0x84, 0x9a, 0x7a, 0x47, 0xeb, 0x6e, 0xda, 0x4b, 0x07, 0xfa, 0x12, 0x5a, - 0xf3, 0x90, 0x91, 0x83, 0x05, 0x1b, 0xa6, 0x20, 0x43, 0x80, 0x56, 0xfc, 0x3c, 0x13, 0xf3, 0xaf, - 0x08, 0x65, 0xce, 0xd5, 0xcc, 0xac, 0x8b, 0xba, 0x4b, 0x07, 0x3e, 0x87, 0x96, 0x80, 0xf2, 0xb6, - 0x6d, 0x72, 0x1d, 0x13, 0xca, 0x90, 0x09, 0x1f, 0xb8, 0x9e, 0xe3, 0x07, 0x87, 0xfb, 0xa2, 0xfd, - 0x86, 0x9d, 0x98, 0xe8, 0x6b, 0x30, 0x46, 0x1c, 0x29, 0xfa, 0x6f, 0x0e, 0xac, 0x5e, 0xc9, 0x18, - 0x7a, 0x32, 0x97, 0x04, 0xe2, 0xdf, 0xe1, 0x75, 0x26, 0x3f, 0x9d, 0x85, 0x01, 0x25, 0x09, 0x39, - 0x87, 0xc5, 0x11, 0x11, 0x25, 0x14, 0x39, 0xe1, 0x40, 0xef, 0x01, 0x71, 0x12, 0x17, 0x64, 0xc1, - 0x2e, 0x96, 0xb0, 0xea, 0x0a, 0x3d, 0x89, 0xce, 0xd1, 0xab, 0x15, 0xe9, 0xfd, 0xa9, 0x81, 0xf1, - 0x53, 0x18, 0xb8, 0x04, 0x59, 0xb0, 0x41, 0xc3, 0x38, 0x72, 0x89, 0x62, 0x65, 0xd8, 0xa9, 0x8d, - 0x3e, 0x83, 0x57, 0x63, 0x42, 0x99, 0x1f, 0x38, 0xcc, 0x0f, 0x39, 0xed, 0xaa, 0x00, 0xe4, 0x9d, - 0x7c, 0xa9, 0xb3, 0x78, 0x74, 0x44, 0x6e, 0x44, 0x99, 0x4d, 0x5b, 0x59, 0x7c, 0xa9, 0xd4, 0x73, - 0x22, 0xa2, 0xd6, 0x24, 0x8d, 0x3c, 0x47, 0xa3, 0xc0, 0x11, 0x0f, 0xa1, 0x71, 0x7a, 0x7a, 0xb8, - 0x2f, 0x5b, 0x43, 0xa0, 0xc7, 0xb1, 0x3f, 0x56, 0x93, 0x10, 0xcf, 0x68, 0x00, 0xf5, 0x80, 0xbf, - 0xa4, 0x66, 0xb5, 0x53, 0x5b, 0x3b, 0x6a, 0x11, 0x6f, 0x2b, 0x24, 0xbe, 0x04, 0xfd, 0x7b, 0x7b, - 0x78, 0xf2, 0x32, 0xea, 0x5b, 0x0e, 0x55, 0x2f, 0x0e, 0xf5, 0x56, 0x83, 0x8f, 0x87, 0x84, 0x89, - 0xe2, 0x74, 0x37, 0x18, 0xf3, 0x65, 0x24, 0xda, 0x79, 0x21, 0x2e, 0x68, 0x0b, 0x74, 0x2f, 0xa2, - 0x4c, 0x74, 0xd5, 0x1c, 0xbc, 0x2b, 0x8d, 0xe0, 0x64, 0x6d, 0x01, 0x7b, 0xe2, 0xb8, 0x64, 0x04, - 0x6d, 0xe4, 0x04, 0x8d, 0xff, 0xd6, 0xc0, 0x5c, 0xa5, 0xb2, 0x94, 0xe9, 0x72, 0x0a, 0x5a, 0x61, - 0x0a, 0xa8, 0x03, 0x4d, 0xd1, 0xeb, 0x71, 0x3c, 0x9a, 0xfa, 0xae, 0xd2, 0x67, 0xd6, 0x95, 0x97, - 0x40, 0xad, 0x28, 0xf3, 0x9e, 0x94, 0xf9, 0xc1, 0x42, 0x56, 0x57, 0x69, 0x64, 0xef, 0x25, 0x6f, - 0x0a, 0x67, 0x3e, 0xab, 0xab, 0x15, 0x3f, 0xee, 0x42, 0xeb, 0xbb, 0x84, 0x55, 0xb2, 0x99, 0x37, - 0x60, 0xf0, 0x6d, 0x50, 0x53, 0xeb, 0xd4, 0xb8, 0x4c, 0x85, 0x81, 0x8f, 0xe0, 0x75, 0x06, 0xa9, - 0x88, 0x7f, 0x93, 0x2e, 0x4c, 0x13, 0x0b, 0x6b, 0x97, 0x8e, 0x3f, 0x15, 0x70, 0x2a, 0xc0, 0x1d, - 0x78, 0x77, 0x12, 0x39, 0x01, 0xbd, 0x24, 0xd1, 0x0f, 0xc4, 0x19, 0x93, 0x88, 0x7a, 0xfe, 0x2c, - 0xa9, 0x6f, 0xc1, 0xc6, 0x54, 0x38, 0xd3, 0x6b, 0x25, 0xb5, 0xf1, 0x39, 0x58, 0x65, 0x81, 0xaa, - 0x9d, 0x47, 0x22, 0xf9, 0xd1, 0x95, 0xcf, 0xbb, 0xe3, 0x71, 0x44, 0x28, 0x15, 0x7b, 0x68, 0xd8, - 0x79, 0x27, 0x46, 0x62, 0x1e, 0x32, 0xb5, 0xea, 0x07, 0x7f, 0x25, 0x98, 0x27, 0x3e, 0x55, 0xea, - 0x2d, 0xd4, 0x65, 0xa4, 0xba, 0x23, 0x94, 0x85, 0x5f, 0x41, 0xf3, 0xd8, 0x0f, 0x26, 0x49, 0xec, - 0x87, 0xb0, 0x29, 0x4d, 0x19, 0x36, 0xf8, 0x57, 0x87, 0x8d, 0x6f, 0xd5, 0x3f, 0x04, 0x3a, 0x83, - 0x46, 0x7a, 0xe5, 0xa1, 0xcf, 0x4b, 0x47, 0x57, 0xbc, 0x72, 0xad, 0x2f, 0x9e, 0x82, 0xc9, 0x42, - 0xb8, 0x82, 0xae, 0xa1, 0x55, 0x14, 0x2c, 0x7a, 0x5f, 0x1e, 0x5d, 0x7e, 0x44, 0xad, 0xad, 0x67, - 0xa2, 0xd3, 0x92, 0x67, 0xd0, 0x48, 0x35, 0xb2, 0x86, 0x50, 0x51, 0x6d, 0x6b, 0x08, 0xad, 0x48, - 0x0d, 0x57, 0xd0, 0x6f, 0x80, 0x56, 0x77, 0x8f, 0x7a, 0xa5, 0xf1, 0x6b, 0xd5, 0x65, 0xf5, 0x9f, - 0x8d, 0x2f, 0xd0, 0x92, 0xaf, 0xd6, 0xd3, 0xca, 0x89, 0x66, 0x3d, 0xad, 0xbc, 0x8e, 0x70, 0x05, - 0xfd, 0x08, 0x3a, 0x97, 0x08, 0xea, 0x94, 0x46, 0x64, 0xc4, 0x64, 0x7d, 0xfa, 0x08, 0x22, 0x49, - 0xb7, 0xf7, 0xf3, 0xed, 0x7d, 0x5b, 0xbb, 0xbb, 0x6f, 0x6b, 0xff, 0xdd, 0xb7, 0xb5, 0x3f, 0x1e, - 0xda, 0x95, 0xbb, 0x87, 0x76, 0xe5, 0x9f, 0x87, 0x76, 0xe5, 0x97, 0x9d, 0x89, 0xcf, 0xbc, 0x78, - 0xd4, 0x73, 0xc3, 0xab, 0x7e, 0x26, 0xd1, 0xd6, 0x9c, 0x04, 0xfc, 0x2e, 0xa0, 0xe9, 0x27, 0xcc, - 0x7c, 0xbb, 0x2f, 0x15, 0xda, 0x17, 0xdf, 0x30, 0xa3, 0xba, 0xf8, 0xd9, 0xfe, 0x3f, 0x00, 0x00, - 0xff, 0xff, 0x01, 0xd0, 0x72, 0x3a, 0xf0, 0x08, 0x00, 0x00, + // 841 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x6f, 0xe3, 0x54, + 0x10, 0x8f, 0x13, 0x3b, 0x9b, 0x4c, 0x5a, 0x94, 0x7d, 0xac, 0x16, 0xaf, 0x85, 0xa2, 0xf0, 0x04, + 0x28, 0x82, 0x6d, 0x82, 0xb2, 0x12, 0xbd, 0xd2, 0x52, 0x04, 0x55, 0x01, 0x15, 0xa7, 0xbd, 0xa0, + 0xaa, 0x95, 0xe3, 0xbc, 0xc6, 0x16, 0xa9, 0x9d, 0xfa, 0x3d, 0x87, 0xf6, 0xc0, 0x77, 0xe0, 0xc2, + 0x07, 0xe1, 0x5b, 0x70, 0xec, 0x81, 0x43, 0x6f, 0xa0, 0xf6, 0x8b, 0xac, 0xde, 0x1f, 0x3b, 0xb6, + 0xe3, 0xb4, 0x3d, 0xf4, 0x14, 0xcf, 0xf8, 0x37, 0x6f, 0xe6, 0x37, 0xf3, 0x9b, 0x17, 0x03, 0xa6, + 0x2c, 0x72, 0x82, 0x29, 0x99, 0x85, 0x0b, 0x32, 0xf0, 0xc2, 0xc8, 0x8d, 0xe2, 0xab, 0x81, 0x1b, + 0x52, 0x7f, 0x1a, 0x90, 0xa8, 0x3f, 0x8f, 0x42, 0x16, 0xa2, 0x0f, 0x33, 0x98, 0xbe, 0xc2, 0xe0, + 0xbf, 0x35, 0x30, 0x76, 0x67, 0xa1, 0xfb, 0x1b, 0x7a, 0x0d, 0x75, 0x8f, 0xf8, 0x53, 0x8f, 0x99, + 0x5a, 0x57, 0xeb, 0xd5, 0x6c, 0x65, 0xa1, 0x57, 0x60, 0x44, 0x61, 0x1c, 0x4c, 0xcc, 0xaa, 0x70, + 0x4b, 0x03, 0x21, 0xd0, 0x29, 0x23, 0x73, 0xb3, 0xd6, 0xd5, 0x7a, 0x86, 0x2d, 0x9e, 0xd1, 0xc7, + 0xd0, 0xe4, 0x09, 0x77, 0xaf, 0x19, 0xa1, 0xa6, 0xde, 0xd5, 0x7a, 0x1b, 0xf6, 0xd2, 0x81, 0xbe, + 0x80, 0xf6, 0x22, 0x64, 0xe4, 0xbb, 0x2b, 0x36, 0x4a, 0x41, 0x86, 0x00, 0xad, 0xf8, 0xf9, 0x49, + 0xcc, 0xbf, 0x20, 0x94, 0x39, 0x17, 0x73, 0xb3, 0x2e, 0xf2, 0x2e, 0x1d, 0xf8, 0x14, 0xda, 0x02, + 0xca, 0xcb, 0xb6, 0xc9, 0x65, 0x4c, 0x28, 0x43, 0x26, 0xbc, 0x70, 0x3d, 0xc7, 0x0f, 0xf6, 0xf7, + 0x44, 0xf9, 0x4d, 0x3b, 0x31, 0xd1, 0x57, 0x60, 0x8c, 0x39, 0x52, 0xd4, 0xdf, 0x1a, 0x5a, 0xfd, + 0x92, 0x36, 0xf4, 0xe5, 0x59, 0x12, 0x88, 0xff, 0x80, 0x97, 0x99, 0xf3, 0xe9, 0x3c, 0x0c, 0x28, + 0x49, 0xc8, 0x39, 0x2c, 0x8e, 0x88, 0x48, 0xa1, 0xc8, 0x09, 0x07, 0x7a, 0x0b, 0x88, 0x93, 0x38, + 0x23, 0x57, 0xec, 0x6c, 0x09, 0xab, 0xae, 0xd0, 0x93, 0xe8, 0x1c, 0xbd, 0x5a, 0x91, 0xde, 0x5f, + 0x1a, 0x18, 0x3f, 0x87, 0x81, 0x4b, 0x90, 0x05, 0x0d, 0x1a, 0xc6, 0x91, 0x4b, 0x14, 0x2b, 0xc3, + 0x4e, 0x6d, 0xf4, 0x29, 0x6c, 0x4e, 0x08, 0x65, 0x7e, 0xe0, 0x30, 0x3f, 0xe4, 0xb4, 0xab, 0x02, + 0x90, 0x77, 0xf2, 0xa1, 0xce, 0xe3, 0xf1, 0x01, 0xb9, 0x16, 0x69, 0x36, 0x6c, 0x65, 0xf1, 0xa1, + 0x52, 0xcf, 0x89, 0x88, 0x1a, 0x93, 0x34, 0xf2, 0x1c, 0x8d, 0x02, 0x47, 0x3c, 0x82, 0xe6, 0xf1, + 0xf1, 0xfe, 0x9e, 0x2c, 0x0d, 0x81, 0x1e, 0xc7, 0xfe, 0x44, 0x75, 0x42, 0x3c, 0xa3, 0x21, 0xd4, + 0x03, 0xfe, 0x92, 0x9a, 0xd5, 0x6e, 0x6d, 0x6d, 0xab, 0x45, 0xbc, 0xad, 0x90, 0xf8, 0x1c, 0xf4, + 0x1f, 0xec, 0xd1, 0xd1, 0xf3, 0xa8, 0x6f, 0xd9, 0x54, 0xbd, 0xd8, 0xd4, 0xdb, 0x2a, 0x7c, 0x34, + 0x22, 0x4c, 0x24, 0xa7, 0x3b, 0xc1, 0x84, 0x0f, 0x23, 0xd1, 0xce, 0x33, 0x71, 0x41, 0x5b, 0xa0, + 0x7b, 0x11, 0x65, 0xa2, 0xaa, 0xd6, 0xf0, 0x4d, 0x69, 0x04, 0x27, 0x6b, 0x0b, 0xd8, 0x23, 0xeb, + 0xd2, 0x85, 0x96, 0xd2, 0xcd, 0x31, 0xaf, 0x4d, 0x4e, 0x23, 0xeb, 0x42, 0xdf, 0xc0, 0xa6, 0x32, + 0x25, 0x2b, 0xb3, 0xfe, 0x68, 0xa5, 0xf9, 0x80, 0xd2, 0x95, 0x7c, 0xb1, 0x66, 0x25, 0x33, 0x0b, + 0xd6, 0xc8, 0x2d, 0x18, 0xfe, 0x57, 0x03, 0x73, 0xb5, 0xb5, 0xcb, 0xb5, 0x59, 0x4e, 0x45, 0x2b, + 0x4c, 0x85, 0x93, 0x14, 0xbd, 0x3b, 0x8c, 0xc7, 0x33, 0xdf, 0x55, 0xfb, 0x92, 0x75, 0xe5, 0x25, + 0x59, 0x2b, 0xae, 0x5d, 0x5f, 0xae, 0x5d, 0xc2, 0x48, 0x1d, 0x23, 0x7b, 0x59, 0xf2, 0xa6, 0x40, + 0x38, 0xab, 0xf3, 0x15, 0x3f, 0xee, 0x41, 0xfb, 0xfb, 0x84, 0x55, 0xa2, 0x94, 0x57, 0x60, 0x70, + 0x75, 0x50, 0x53, 0xeb, 0xd6, 0xf8, 0xda, 0x08, 0x03, 0x1f, 0xc0, 0xcb, 0x0c, 0x52, 0x11, 0xff, + 0x3a, 0x15, 0x90, 0x26, 0xc6, 0xd2, 0x29, 0x1d, 0x4b, 0xba, 0x50, 0xe9, 0x42, 0x6c, 0xc3, 0x9b, + 0xa3, 0xc8, 0x09, 0xe8, 0x39, 0x89, 0x7e, 0x24, 0xce, 0x84, 0x44, 0xd4, 0xf3, 0xe7, 0x49, 0x7e, + 0x0b, 0x1a, 0x33, 0xe1, 0x4c, 0xaf, 0xb9, 0xd4, 0xc6, 0xa7, 0x60, 0x95, 0x05, 0xaa, 0x72, 0x1e, + 0x88, 0xe4, 0x57, 0x89, 0x7c, 0xde, 0x99, 0x4c, 0x22, 0x42, 0xa9, 0x98, 0x43, 0xd3, 0xce, 0x3b, + 0x31, 0x12, 0xfd, 0x90, 0x47, 0xab, 0x7a, 0xf0, 0x97, 0x82, 0x79, 0xe2, 0x53, 0xa9, 0x5e, 0x43, + 0x5d, 0x46, 0xaa, 0x3b, 0x4b, 0x59, 0x78, 0x13, 0x5a, 0x87, 0x7e, 0x30, 0x4d, 0x62, 0x3f, 0x80, + 0x0d, 0x69, 0xca, 0xb0, 0xe1, 0x7f, 0x3a, 0x34, 0xbe, 0x55, 0xff, 0x58, 0xe8, 0x04, 0x9a, 0xe9, + 0x15, 0x8c, 0x3e, 0x2b, 0x6d, 0x5d, 0xf1, 0x2f, 0xc0, 0xfa, 0xfc, 0x31, 0x98, 0x4c, 0x84, 0x2b, + 0xe8, 0x12, 0xda, 0x45, 0xc1, 0xa2, 0xb7, 0xe5, 0xd1, 0xe5, 0x57, 0x86, 0xb5, 0xf5, 0x44, 0x74, + 0x9a, 0xf2, 0x04, 0x9a, 0xa9, 0x46, 0xd6, 0x10, 0x2a, 0xaa, 0x6d, 0x0d, 0xa1, 0x15, 0xa9, 0xe1, + 0x0a, 0xfa, 0x1d, 0xd0, 0xea, 0xec, 0x51, 0xbf, 0x34, 0x7e, 0xad, 0xba, 0xac, 0xc1, 0x93, 0xf1, + 0x05, 0x5a, 0xf2, 0xd5, 0x7a, 0x5a, 0x39, 0xd1, 0xac, 0xa7, 0x95, 0xd7, 0x11, 0xae, 0xa0, 0x9f, + 0x40, 0xe7, 0x12, 0x41, 0xdd, 0xd2, 0x88, 0x8c, 0x98, 0xac, 0x4f, 0x1e, 0x40, 0x24, 0xc7, 0xed, + 0xfe, 0xf2, 0xcf, 0x5d, 0x47, 0xbb, 0xb9, 0xeb, 0x68, 0xff, 0xdf, 0x75, 0xb4, 0x3f, 0xef, 0x3b, + 0x95, 0x9b, 0xfb, 0x4e, 0xe5, 0xf6, 0xbe, 0x53, 0xf9, 0x75, 0x7b, 0xea, 0x33, 0x2f, 0x1e, 0xf7, + 0xdd, 0xf0, 0x62, 0x90, 0x39, 0x68, 0x6b, 0x41, 0x02, 0x7e, 0x17, 0xd0, 0xf4, 0x93, 0x6a, 0xf1, + 0x6e, 0x20, 0x15, 0x3a, 0x10, 0xdf, 0x54, 0xe3, 0xba, 0xf8, 0x79, 0xf7, 0x3e, 0x00, 0x00, 0xff, + 0xff, 0x61, 0xe1, 0x3e, 0xbf, 0x80, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1552,6 +1578,34 @@ func (m *SetNoncesAndSignRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) copy(dAtA[i:], m.ChainID) i = encodeVarintCosigner(dAtA, i, uint64(len(m.ChainID))) i-- + dAtA[i] = 0x42 + } + if len(m.VoteExtSignBytes) > 0 { + i -= len(m.VoteExtSignBytes) + copy(dAtA[i:], m.VoteExtSignBytes) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtSignBytes))) + i-- + dAtA[i] = 0x3a + } + if len(m.VoteExtNonces) > 0 { + for iNdEx := len(m.VoteExtNonces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.VoteExtNonces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCosigner(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + } + if len(m.VoteExtUuid) > 0 { + i -= len(m.VoteExtUuid) + copy(dAtA[i:], m.VoteExtUuid) + i = encodeVarintCosigner(dAtA, i, uint64(len(m.VoteExtUuid))) + i-- dAtA[i] = 0x2a } if len(m.SignBytes) > 0 { @@ -2054,6 +2108,20 @@ func (m *SetNoncesAndSignRequest) Size() (n int) { if l > 0 { n += 1 + l + sovCosigner(uint64(l)) } + l = len(m.VoteExtUuid) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } + if len(m.VoteExtNonces) > 0 { + for _, e := range m.VoteExtNonces { + l = e.Size() + n += 1 + l + sovCosigner(uint64(l)) + } + } + l = len(m.VoteExtSignBytes) + if l > 0 { + n += 1 + l + sovCosigner(uint64(l)) + } l = len(m.ChainID) if l > 0 { n += 1 + l + sovCosigner(uint64(l)) @@ -3245,6 +3313,108 @@ func (m *SetNoncesAndSignRequest) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtUuid", 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.VoteExtUuid = append(m.VoteExtUuid[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtUuid == nil { + m.VoteExtUuid = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtNonces", 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.VoteExtNonces = append(m.VoteExtNonces, &Nonce{}) + if err := m.VoteExtNonces[len(m.VoteExtNonces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtSignBytes", 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.VoteExtSignBytes = append(m.VoteExtSignBytes[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtSignBytes == nil { + m.VoteExtSignBytes = []byte{} + } + iNdEx = postIndex + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) } diff --git a/signer/remote_cosigner.go b/signer/remote_cosigner.go index d0525d9b..dfb12350 100644 --- a/signer/remote_cosigner.go +++ b/signer/remote_cosigner.go @@ -108,21 +108,30 @@ func (cosigner *RemoteCosigner) GetNonces( func (cosigner *RemoteCosigner) SetNoncesAndSign( ctx context.Context, req CosignerSetNoncesAndSignRequest) (*CosignerSignResponse, error) { - res, err := cosigner.client.SetNoncesAndSign(ctx, &proto.SetNoncesAndSignRequest{ + cosignerReq := &proto.SetNoncesAndSignRequest{ Uuid: req.Nonces.UUID[:], ChainID: req.ChainID, Nonces: req.Nonces.Nonces.toProto(), Hrst: req.HRST.toProto(), SignBytes: req.SignBytes, - }) + } + + if req.VoteExtensionNonces != nil && len(req.VoteExtensionSignBytes) > 0 { + cosignerReq.VoteExtUuid = req.VoteExtensionNonces.UUID[:] + cosignerReq.VoteExtNonces = req.VoteExtensionNonces.Nonces.toProto() + cosignerReq.VoteExtSignBytes = req.VoteExtensionSignBytes + } + + res, err := cosigner.client.SetNoncesAndSign(ctx, cosignerReq) if err != nil { return nil, err } return &CosignerSignResponse{ - NoncePublic: res.GetNoncePublic(), - Timestamp: time.Unix(0, res.GetTimestamp()), - Signature: res.GetSignature(), - VoteExtensionNoncePublic: res.GetVoteExtNoncePublic(), + NoncePublic: res.NoncePublic, + Timestamp: time.Unix(0, res.Timestamp), + Signature: res.Signature, + VoteExtensionSignature: res.VoteExtSignature, + VoteExtensionNoncePublic: res.VoteExtNoncePublic, }, nil } @@ -138,7 +147,7 @@ func (cosigner *RemoteCosigner) Sign( return nil, err } return &CosignerSignBlockResponse{ - Signature: res.GetSignature(), - VoteExtensionSignature: res.GetVoteExtSignature(), + Signature: res.Signature, + VoteExtensionSignature: res.VoteExtSignature, }, nil } diff --git a/signer/threshold_validator.go b/signer/threshold_validator.go index 7ca5f87b..67422d35 100644 --- a/signer/threshold_validator.go +++ b/signer/threshold_validator.go @@ -478,9 +478,8 @@ func (pv *ThresholdValidator) compareBlockSignatureAgainstHRS( func (pv *ThresholdValidator) getNoncesFallback( ctx context.Context, -) (*CosignerUUIDNonces, []Cosigner, error) { - nonces := make(map[Cosigner]CosignerNonces) - + count int, +) (*CosignersAndNonces, error) { drainedNonceCache.Inc() totalDrainedNonceCache.Inc() @@ -489,36 +488,28 @@ func (pv *ThresholdValidator) getNoncesFallback( var mu sync.Mutex - u := uuid.New() + uuids := make([]uuid.UUID, count) + for i := 0; i < count; i++ { + uuids[i] = uuid.New() + } allCosigners := make([]Cosigner, len(pv.peerCosigners)+1) allCosigners[0] = pv.myCosigner copy(allCosigners[1:], pv.peerCosigners) + var thresholdNonces CosignersAndNonces + for _, c := range allCosigners { - go pv.waitForPeerNonces(ctx, u, c, &wg, nonces, &mu) + go pv.waitForPeerNonces(ctx, uuids, c, &wg, &thresholdNonces, &mu) } // Wait for threshold cosigners to be complete // A Cosigner will either respond in time, or be cancelled with timeout if waitUntilCompleteOrTimeout(&wg, pv.grpcTimeout) { - return nil, nil, errors.New("timed out waiting for ephemeral shares") - } - - var thresholdNonces CosignerNonces - thresholdCosigners := make([]Cosigner, len(nonces)) - i := 0 - for c, n := range nonces { - thresholdCosigners[i] = c - i++ - - thresholdNonces = append(thresholdNonces, n...) + return nil, errors.New("timed out waiting for ephemeral shares") } - return &CosignerUUIDNonces{ - UUID: u, - Nonces: thresholdNonces, - }, thresholdCosigners, nil + return &thresholdNonces, nil } func waitUntilCompleteOrTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { @@ -535,16 +526,22 @@ func waitUntilCompleteOrTimeout(wg *sync.WaitGroup, timeout time.Duration) bool } } +type CosignersAndNonces struct { + Cosigners Cosigners + Nonces CosignerUUIDNoncesMultiple +} + func (pv *ThresholdValidator) waitForPeerNonces( ctx context.Context, - u uuid.UUID, + uuids []uuid.UUID, peer Cosigner, wg *sync.WaitGroup, - nonces map[Cosigner]CosignerNonces, + thresholdNonces *CosignersAndNonces, mu sync.Locker, ) { peerStartTime := time.Now() - peerNonces, err := peer.GetNonces(ctx, []uuid.UUID{u}) + + peerNonces, err := peer.GetNonces(ctx, uuids) if err != nil { missedNonces.WithLabelValues(peer.GetAddress()).Inc() totalMissedNonces.WithLabelValues(peer.GetAddress()).Inc() @@ -558,8 +555,21 @@ func (pv *ThresholdValidator) waitForPeerNonces( // Check so that wg.Done is not called more than (threshold - 1) times which causes hardlock mu.Lock() - if len(nonces) < pv.threshold { - nonces[peer] = peerNonces[0].Nonces + if len(thresholdNonces.Cosigners) < pv.threshold { + thresholdNonces.Cosigners = append(thresholdNonces.Cosigners, peer) + for _, n := range peerNonces { + var found bool + for _, nn := range thresholdNonces.Nonces { + if n.UUID == nn.UUID { + nn.Nonces = append(nn.Nonces, n.Nonces...) + found = true + break + } + } + if !found { + thresholdNonces.Nonces = append(thresholdNonces.Nonces, n) + } + } defer wg.Done() } mu.Unlock() @@ -682,36 +692,70 @@ func (pv *ThresholdValidator) Sign( cosignersForThisBlock[0] = pv.myCosigner copy(cosignersForThisBlock[1:], cosignersOrderedByFastest[:pv.threshold-1]) - nonces, err := pv.nonceCache.GetNonces(cosignersForThisBlock) - var dontIterateFastestCosigners bool + _, hasVoteExtensions, err := verifySignPayload(chainID, signBytes, voteExtensionSignBytes) + if err != nil { + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("failed to verify payload: %w", err) + } + + count := 1 + if hasVoteExtensions { + count = 2 + } + + var voteExtNonces *CosignerUUIDNonces + nonces, err := pv.nonceCache.GetNonces(cosignersForThisBlock) if err != nil { + var fallbackRes *CosignersAndNonces var fallbackErr error - nonces, cosignersForThisBlock, fallbackErr = pv.getNoncesFallback(ctx) + + fallbackRes, fallbackErr = pv.getNoncesFallback(ctx, count) if fallbackErr != nil { pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) return nil, nil, stamp, fmt.Errorf("failed to get nonces: %w", errors.Join(err, fallbackErr)) } + + cosignersForThisBlock = fallbackRes.Cosigners + nonces = fallbackRes.Nonces[0] + if hasVoteExtensions { + voteExtNonces = fallbackRes.Nonces[1] + } dontIterateFastestCosigners = true } else { drainedNonceCache.Set(0) } - var voteExtNonces *CosignerUUIDNonces - - _, hasVoteExtensions, err := verifySignPayload(chainID, signBytes, voteExtensionSignBytes) - if err != nil { - pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) - return nil, nil, stamp, fmt.Errorf("failed to verify payload: %w", err) - } - - if hasVoteExtensions { + if err == nil && hasVoteExtensions { voteExtNonces, err = pv.nonceCache.GetNonces(cosignersForThisBlock) if err != nil { - // TODO how to handle fallback for vote extensions? - pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) - return nil, nil, stamp, fmt.Errorf("failed to get nonces for vote extensions: %w", err) + + u := uuid.New() + var eg errgroup.Group + var mu sync.Mutex + for _, c := range cosignersForThisBlock { + c := c + eg.Go(func() error { + nonces, err := c.GetNonces(ctx, []uuid.UUID{u}) + if err != nil { + return err + } + mu.Lock() + defer mu.Unlock() + if voteExtNonces == nil { + voteExtNonces = nonces[0] + } else { + voteExtNonces.Nonces = append(voteExtNonces.Nonces, nonces[0].Nonces...) + } + return nil + }) + } + + if fallbackErr := eg.Wait(); fallbackErr != nil { + pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) + return nil, nil, stamp, fmt.Errorf("failed to get nonces for vote extensions: %w", errors.Join(err, fallbackErr)) + } } } @@ -840,7 +884,7 @@ func (pv *ThresholdValidator) Sign( if len(shareSigs) < pv.threshold { totalInsufficientCosigners.Inc() pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) - return nil, nil, stamp, errors.New("not enough co-signers") + return nil, nil, stamp, errors.New("not enough cosigners") } // assemble into final signature @@ -882,7 +926,7 @@ func (pv *ThresholdValidator) Sign( if len(voteExtShareSigs) < pv.threshold { totalInsufficientCosigners.Inc() pv.notifyBlockSignError(chainID, block.HRSKey(), signBytes) - return nil, nil, stamp, errors.New("not enough co-signers for vote extension") + return nil, nil, stamp, errors.New("not enough cosigners for vote extension") } // assemble into final signature diff --git a/signer/threshold_validator_test.go b/signer/threshold_validator_test.go index f5c27c3e..e182bba0 100644 --- a/signer/threshold_validator_test.go +++ b/signer/threshold_validator_test.go @@ -273,7 +273,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { precommitClone2 := precommit precommitClone2.Timestamp = precommit.Timestamp.Add(4 * time.Millisecond) - newValidator.nonceCache.LoadN(ctx, 6) + newValidator.nonceCache.LoadN(ctx, mrand.Intn(7)) eg.Go(func() error { start := time.Now() From 18842583de37300d10f1af5d7c14f654e48b365f Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Tue, 9 Jan 2024 17:53:06 -0700 Subject: [PATCH 07/10] lint --- signer/threshold_validator_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/signer/threshold_validator_test.go b/signer/threshold_validator_test.go index e182bba0..22688fb1 100644 --- a/signer/threshold_validator_test.go +++ b/signer/threshold_validator_test.go @@ -273,7 +273,7 @@ func testThresholdValidator(t *testing.T, threshold, total uint8) { precommitClone2 := precommit precommitClone2.Timestamp = precommit.Timestamp.Add(4 * time.Millisecond) - newValidator.nonceCache.LoadN(ctx, mrand.Intn(7)) + newValidator.nonceCache.LoadN(ctx, mrand.Intn(7)) //nolint:gosec eg.Go(func() error { start := time.Now() From ef0cc5361b4b66ff99da56cd03f6dde2a69c337a Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Tue, 12 Dec 2023 16:32:34 -0700 Subject: [PATCH 08/10] use grpc timeout in nonce overallocation --- signer/cosigner_nonce_cache.go | 9 +++++---- signer/threshold_validator.go | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/signer/cosigner_nonce_cache.go b/signer/cosigner_nonce_cache.go index f9b34b89..3b8ad25c 100644 --- a/signer/cosigner_nonce_cache.go +++ b/signer/cosigner_nonce_cache.go @@ -15,6 +15,7 @@ const ( defaultGetNoncesInterval = 3 * time.Second defaultGetNoncesTimeout = 4 * time.Second defaultNonceExpiration = 10 * time.Second // half of the local cosigner cache expiration + nonceOverallocation = 1.5 ) type CosignerNonceCache struct { @@ -198,7 +199,7 @@ func (cnc *CosignerNonceCache) getUuids(n int) []uuid.UUID { } func (cnc *CosignerNonceCache) target(noncesPerMinute float64) int { - t := int((noncesPerMinute / 60) * ((cnc.getNoncesInterval.Seconds() * 1.2) + 0.5)) + t := int((noncesPerMinute / 60) * ((cnc.getNoncesInterval.Seconds() * nonceOverallocation) + float64(cnc.getNoncesTimeout.Seconds()))) if t <= 0 { return 1 // always target at least one nonce ready } @@ -332,12 +333,12 @@ func (cnc *CosignerNonceCache) Start(ctx context.Context) { cnc.lastReconcileNonces.Store(uint64(cnc.cache.Size())) cnc.lastReconcileTime = time.Now() - ticker := time.NewTimer(cnc.getNoncesInterval) + timer := time.NewTimer(cnc.getNoncesInterval) for { select { case <-ctx.Done(): return - case <-ticker.C: + case <-timer.C: case <-cnc.empty: // clear out channel for len(cnc.empty) > 0 { @@ -345,7 +346,7 @@ func (cnc *CosignerNonceCache) Start(ctx context.Context) { } } cnc.reconcile(ctx) - ticker.Reset(cnc.getNoncesInterval) + timer.Reset(cnc.getNoncesInterval) } } diff --git a/signer/threshold_validator.go b/signer/threshold_validator.go index 67422d35..e2120036 100644 --- a/signer/threshold_validator.go +++ b/signer/threshold_validator.go @@ -84,7 +84,7 @@ func NewThresholdValidator( allCosigners, leader, defaultGetNoncesInterval, - defaultGetNoncesTimeout, + grpcTimeout, defaultNonceExpiration, uint8(threshold), nil, From 64e1e6faa0e526a85bdec02c35ffa661b46e9dd0 Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Tue, 9 Jan 2024 17:55:09 -0700 Subject: [PATCH 09/10] lint --- signer/cosigner_nonce_cache.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/signer/cosigner_nonce_cache.go b/signer/cosigner_nonce_cache.go index 3b8ad25c..327bd00b 100644 --- a/signer/cosigner_nonce_cache.go +++ b/signer/cosigner_nonce_cache.go @@ -199,7 +199,8 @@ func (cnc *CosignerNonceCache) getUuids(n int) []uuid.UUID { } func (cnc *CosignerNonceCache) target(noncesPerMinute float64) int { - t := int((noncesPerMinute / 60) * ((cnc.getNoncesInterval.Seconds() * nonceOverallocation) + float64(cnc.getNoncesTimeout.Seconds()))) + t := int((noncesPerMinute / 60) * + ((cnc.getNoncesInterval.Seconds() * nonceOverallocation) + cnc.getNoncesTimeout.Seconds())) if t <= 0 { return 1 // always target at least one nonce ready } From 684510a9a98daa70b02090ae8621c27342f78af7 Mon Sep 17 00:00:00 2001 From: Andrew Gouin Date: Tue, 9 Jan 2024 21:55:21 -0700 Subject: [PATCH 10/10] Bump horcrux-proxy --- test/validator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/validator.go b/test/validator.go index 487eecf0..66b1e01a 100644 --- a/test/validator.go +++ b/test/validator.go @@ -45,7 +45,7 @@ const ( signerImageHomeDir = "/home/horcrux" horcruxProxyRegistry = "ghcr.io/strangelove-ventures/horcrux-proxy" - horcruxProxyTag = "andrew-horcrux_remote_signer_grpc" + horcruxProxyTag = "andrew-vote_extensions" ) // chainWrapper holds the initial configuration for a chain to start from genesis.