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