diff --git a/.golangci.yml b/.golangci.yml index 5ef06171e83..e9030773237 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -50,6 +50,7 @@ linters: - github.com/stretchr/testify/require - github.com/syndtr/goleveldb - github.com/decred/dcrd/dcrec/secp256k1/v4 + - github.com/supranational/blst/bindings/go - google.golang.org/grpc - google.golang.org/protobuf/proto - golang.org/x/sync @@ -81,6 +82,7 @@ linters: - github.com/spf13 - github.com/stretchr/testify - github.com/decred/dcrd/dcrec/secp256k1/v4 + - github.com/supranational/blst/bindings/go - google.golang.org/grpc - google.golang.org/protobuf/proto - google.golang.org/protobuf/types/known/timestampp diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f3ea602067..a829a768327 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,37 @@ ### API-BREAKING +## Berachain fork (unreleased) + +_Berachain-specific changes layered on top of upstream CometBFT, based on upstream cometbft +v0.39.3 (`49b82838f`). These ports keep consensus sign/state bytes byte-compatible with the +bera-v1.x line, so an existing bera-v1.x network can restart on this binary (consensus-compatible +with bera-v1.x networks). Upstream backports are tracked in the sections above. At release, this +section becomes `## v0.39.3-bera.N`._ + +### FEATURES + +- `[consensus,types,state]` Port Proposer-Based Timestamps (PBTS) from the bera-v1.x line. + Block timestamps are set by the proposer and validated against synchrony parameters instead of + the BFT-median of vote times; per-vote timestamps are removed. PBTS is always enabled on this fork. + ([\#51](https://github.com/berachain/cometbft/pull/51)) +- `[consensus,crypto,types]` Port BLS12-381 signature aggregation. For all-BLS validator sets, + precommit signatures are aggregated into a single commit that is gossiped whole and used for + fast catch-up of lagging nodes. + ([\#51](https://github.com/berachain/cometbft/pull/51)) +- `[state,abci]` Add `NextBlockDelay` (ADR-115): the application controls the delay before the next + height starts, replacing the static `timeout_commit`. Also adds `next_proposer_address` to + `ProcessProposal`. + ([\#51](https://github.com/berachain/cometbft/pull/51)) + +### STATE-BREAKING + +- `[types,state]` New consensus parameters (`SynchronyParams`, `FeatureParams`) and the removal of + per-vote timestamps change consensus sign bytes and persisted-state encoding. These match the + bera-v1.x line byte-for-byte, so an existing bera-v1.x network can restart on this binary; the + encoding is not compatible with pristine upstream v0.39.x state. + ([\#51](https://github.com/berachain/cometbft/pull/51)) + ## v0.39.3 *May 5, 2026* diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index f4314f82f3e..7e4f85313dd 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -13,6 +13,7 @@ import ( proto "github.com/cosmos/gogoproto/proto" _ "github.com/cosmos/gogoproto/types" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + _ "github.com/golang/protobuf/ptypes/duration" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -1325,6 +1326,10 @@ type RequestProcessProposal struct { NextValidatorsHash []byte `protobuf:"bytes,7,opt,name=next_validators_hash,json=nextValidatorsHash,proto3" json:"next_validators_hash,omitempty"` // address of the public key of the original proposer of the block. ProposerAddress []byte `protobuf:"bytes,8,opt,name=proposer_address,json=proposerAddress,proto3" json:"proposer_address,omitempty"` + // address of the public key of the validator that will propose the block at the + // next height (also the proposer at round 1 of the current height if consensus + // goes into multiple rounds). + NextProposerAddress []byte `protobuf:"bytes,9,opt,name=next_proposer_address,json=nextProposerAddress,proto3" json:"next_proposer_address,omitempty"` } func (m *RequestProcessProposal) Reset() { *m = RequestProcessProposal{} } @@ -1416,6 +1421,13 @@ func (m *RequestProcessProposal) GetProposerAddress() []byte { return nil } +func (m *RequestProcessProposal) GetNextProposerAddress() []byte { + if m != nil { + return m.NextProposerAddress + } + return nil +} + // Extends a vote with application-injected data type RequestExtendVote struct { // the hash of the block that this vote may be referring to @@ -2984,6 +2996,9 @@ type ResponseFinalizeBlock struct { ConsensusParamUpdates *types1.ConsensusParams `protobuf:"bytes,4,opt,name=consensus_param_updates,json=consensusParamUpdates,proto3" json:"consensus_param_updates,omitempty"` // app_hash is the hash of the applications' state which is used to confirm that execution of the transactions was deterministic. It is up to the application to decide which algorithm to use. AppHash []byte `protobuf:"bytes,5,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` + // delay between the time when this block is committed and the next height is started. + // previously `timeout_commit` in config.toml + NextBlockDelay time.Duration `protobuf:"bytes,6,opt,name=next_block_delay,json=nextBlockDelay,proto3,stdduration" json:"next_block_delay"` } func (m *ResponseFinalizeBlock) Reset() { *m = ResponseFinalizeBlock{} } @@ -3054,6 +3069,13 @@ func (m *ResponseFinalizeBlock) GetAppHash() []byte { return nil } +func (m *ResponseFinalizeBlock) GetNextBlockDelay() time.Duration { + if m != nil { + return m.NextBlockDelay + } + return 0 +} + type CommitInfo struct { Round int32 `protobuf:"varint,1,opt,name=round,proto3" json:"round,omitempty"` Votes []VoteInfo `protobuf:"bytes,2,rep,name=votes,proto3" json:"votes"` @@ -3904,215 +3926,219 @@ func init() { func init() { proto.RegisterFile("tendermint/abci/types.proto", fileDescriptor_252557cfdd89a31a) } var fileDescriptor_252557cfdd89a31a = []byte{ - // 3321 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xc9, 0x73, 0x1b, 0xc7, - 0xd5, 0xc7, 0x60, 0xc7, 0xc3, 0xc2, 0x61, 0x93, 0xa2, 0x20, 0x48, 0x26, 0xa9, 0x51, 0xd9, 0x96, - 0x65, 0x9b, 0xf4, 0x27, 0x7d, 0xf2, 0x52, 0xb2, 0xbf, 0x2f, 0x20, 0x04, 0x1a, 0xa4, 0x64, 0x92, - 0x1e, 0x42, 0x72, 0x39, 0x8b, 0xc7, 0x43, 0xa0, 0x41, 0x8c, 0x05, 0x60, 0xc6, 0x33, 0x03, 0x1a, - 0xf4, 0x29, 0x15, 0x27, 0x55, 0x29, 0x9f, 0x5c, 0x95, 0x4a, 0x95, 0x0f, 0xf1, 0x21, 0x87, 0x5c, - 0xf2, 0x17, 0xe4, 0x90, 0xf2, 0x29, 0x07, 0x1f, 0x72, 0xf0, 0x31, 0x27, 0x27, 0x65, 0xdf, 0x7c, - 0xcd, 0x21, 0xd7, 0x54, 0x2f, 0xb3, 0x01, 0x33, 0x58, 0x64, 0xe7, 0x90, 0x4a, 0x6e, 0xbd, 0xbc, - 0xf7, 0xba, 0xfb, 0x75, 0xf7, 0x5b, 0x7e, 0xdd, 0x70, 0xd9, 0xc6, 0x83, 0x36, 0x36, 0xfb, 0xda, - 0xc0, 0xde, 0x56, 0x4f, 0x5a, 0xda, 0xb6, 0x7d, 0x6e, 0x60, 0x6b, 0xcb, 0x30, 0x75, 0x5b, 0x47, - 0x4b, 0x5e, 0xe7, 0x16, 0xe9, 0xac, 0xac, 0x9e, 0xea, 0xa7, 0x3a, 0xed, 0xdb, 0x26, 0x25, 0x46, - 0x56, 0xd9, 0x38, 0xd5, 0xf5, 0xd3, 0x1e, 0xde, 0xa6, 0xb5, 0x93, 0x61, 0x67, 0xdb, 0xd6, 0xfa, - 0xd8, 0xb2, 0xd5, 0xbe, 0xc1, 0x09, 0xae, 0xf8, 0x06, 0x69, 0x99, 0xe7, 0x86, 0xad, 0x6f, 0x3f, - 0xc2, 0xe7, 0x7c, 0x94, 0xca, 0x13, 0x93, 0xbd, 0x86, 0xa9, 0xeb, 0x9d, 0x90, 0x6e, 0x3a, 0xb9, - 0x6d, 0x43, 0x35, 0xd5, 0xbe, 0xc3, 0xbd, 0x39, 0xd1, 0x7d, 0xa6, 0xf6, 0xb4, 0xb6, 0x6a, 0xeb, - 0x26, 0xa3, 0x90, 0x3e, 0x07, 0xc8, 0xc8, 0xf8, 0xfd, 0x21, 0xb6, 0x6c, 0x74, 0x13, 0x92, 0xb8, - 0xd5, 0xd5, 0xcb, 0xc2, 0xa6, 0x70, 0x3d, 0x7f, 0xf3, 0xca, 0xd6, 0xd8, 0x02, 0xb7, 0x38, 0x5d, - 0xbd, 0xd5, 0xd5, 0x1b, 0x31, 0x99, 0xd2, 0xa2, 0xdb, 0x90, 0xea, 0xf4, 0x86, 0x56, 0xb7, 0x1c, - 0xa7, 0x4c, 0x4f, 0x44, 0x31, 0xed, 0x12, 0xa2, 0x46, 0x4c, 0x66, 0xd4, 0x64, 0x28, 0x6d, 0xd0, - 0xd1, 0xcb, 0x89, 0xe9, 0x43, 0xed, 0x0d, 0x3a, 0x74, 0x28, 0x42, 0x8b, 0x76, 0x00, 0xb4, 0x81, - 0x66, 0x2b, 0xad, 0xae, 0xaa, 0x0d, 0xca, 0x29, 0xca, 0x79, 0x35, 0x9a, 0x53, 0xb3, 0x6b, 0x84, - 0xb0, 0x11, 0x93, 0x73, 0x9a, 0x53, 0x21, 0xd3, 0x7d, 0x7f, 0x88, 0xcd, 0xf3, 0x72, 0x7a, 0xfa, - 0x74, 0xdf, 0x24, 0x44, 0x64, 0xba, 0x94, 0x1a, 0xbd, 0x0a, 0xd9, 0x56, 0x17, 0xb7, 0x1e, 0x29, - 0xf6, 0xa8, 0x9c, 0xa5, 0x9c, 0x1b, 0x51, 0x9c, 0x35, 0x42, 0xd7, 0x1c, 0x35, 0x62, 0x72, 0xa6, - 0xc5, 0x8a, 0xe8, 0x65, 0x48, 0xb7, 0xf4, 0x7e, 0x5f, 0xb3, 0xcb, 0x79, 0xca, 0xbb, 0x1e, 0xc9, - 0x4b, 0xa9, 0x1a, 0x31, 0x99, 0xd3, 0xa3, 0x03, 0x28, 0xf5, 0x34, 0xcb, 0x56, 0xac, 0x81, 0x6a, - 0x58, 0x5d, 0xdd, 0xb6, 0xca, 0x05, 0x2a, 0xe1, 0xc9, 0x28, 0x09, 0xf7, 0x35, 0xcb, 0x3e, 0x76, - 0x88, 0x1b, 0x31, 0xb9, 0xd8, 0xf3, 0x37, 0x10, 0x79, 0x7a, 0xa7, 0x83, 0x4d, 0x57, 0x60, 0xb9, - 0x38, 0x5d, 0xde, 0x21, 0xa1, 0x76, 0xf8, 0x89, 0x3c, 0xdd, 0xdf, 0x80, 0x7e, 0x04, 0x2b, 0x3d, - 0x5d, 0x6d, 0xbb, 0xe2, 0x94, 0x56, 0x77, 0x38, 0x78, 0x54, 0x2e, 0x51, 0xa1, 0xcf, 0x44, 0x4e, - 0x52, 0x57, 0xdb, 0x8e, 0x88, 0x1a, 0x61, 0x68, 0xc4, 0xe4, 0xe5, 0xde, 0x78, 0x23, 0x7a, 0x07, - 0x56, 0x55, 0xc3, 0xe8, 0x9d, 0x8f, 0x4b, 0x5f, 0xa2, 0xd2, 0x6f, 0x44, 0x49, 0xaf, 0x12, 0x9e, - 0x71, 0xf1, 0x48, 0x9d, 0x68, 0x45, 0x4d, 0x10, 0x0d, 0x13, 0x1b, 0xaa, 0x89, 0x15, 0xc3, 0xd4, - 0x0d, 0xdd, 0x52, 0x7b, 0x65, 0x91, 0xca, 0x7e, 0x3a, 0x4a, 0xf6, 0x11, 0xa3, 0x3f, 0xe2, 0xe4, - 0x8d, 0x98, 0xbc, 0x64, 0x04, 0x9b, 0x98, 0x54, 0xbd, 0x85, 0x2d, 0xcb, 0x93, 0xba, 0x3c, 0x4b, - 0x2a, 0xa5, 0x0f, 0x4a, 0x0d, 0x34, 0xa1, 0x3a, 0xe4, 0xf1, 0x88, 0xb0, 0x2b, 0x67, 0xba, 0x8d, - 0xcb, 0x88, 0x0a, 0x94, 0x22, 0x6f, 0x28, 0x25, 0x7d, 0xa8, 0xdb, 0xb8, 0x11, 0x93, 0x01, 0xbb, - 0x35, 0xa4, 0xc2, 0x85, 0x33, 0x6c, 0x6a, 0x9d, 0x73, 0x2a, 0x46, 0xa1, 0x3d, 0x96, 0xa6, 0x0f, - 0xca, 0x2b, 0x54, 0xe0, 0xb3, 0x51, 0x02, 0x1f, 0x52, 0x26, 0x22, 0xa2, 0xee, 0xb0, 0x34, 0x62, - 0xf2, 0xca, 0xd9, 0x64, 0x33, 0x39, 0x62, 0x1d, 0x6d, 0xa0, 0xf6, 0xb4, 0x0f, 0xb1, 0x72, 0xd2, - 0xd3, 0x5b, 0x8f, 0xca, 0xab, 0xd3, 0x8f, 0xd8, 0x2e, 0xa7, 0xde, 0x21, 0xc4, 0xe4, 0x88, 0x75, - 0xfc, 0x0d, 0xe8, 0xff, 0x21, 0xa7, 0x0d, 0x2c, 0x6c, 0xda, 0xe4, 0xee, 0x5d, 0xa0, 0xa2, 0x36, - 0xa3, 0x2f, 0x3d, 0x21, 0xa4, 0x97, 0x2f, 0xab, 0xf1, 0x32, 0xb9, 0xbb, 0x26, 0x56, 0x0d, 0xc5, - 0x1e, 0x59, 0xe5, 0xb5, 0xe9, 0x77, 0x57, 0xc6, 0xaa, 0xd1, 0x1c, 0x91, 0x7b, 0x93, 0x31, 0x59, - 0x71, 0x27, 0x03, 0xa9, 0x33, 0xb5, 0x37, 0xc4, 0xfb, 0xc9, 0x6c, 0x52, 0x4c, 0xed, 0x27, 0xb3, - 0x19, 0x31, 0xbb, 0x9f, 0xcc, 0xe6, 0x44, 0xd8, 0x4f, 0x66, 0x41, 0xcc, 0x4b, 0x4f, 0x43, 0xde, - 0x67, 0x17, 0x51, 0x19, 0x32, 0x7d, 0x6c, 0x59, 0xea, 0x29, 0xa6, 0x66, 0x34, 0x27, 0x3b, 0x55, - 0xa9, 0x04, 0x05, 0xbf, 0x2d, 0x94, 0x3e, 0x11, 0x5c, 0x4e, 0x62, 0xe6, 0x08, 0xe7, 0x19, 0x36, - 0xe9, 0x6e, 0x70, 0x4e, 0x5e, 0x45, 0xd7, 0xa0, 0x48, 0x35, 0xa9, 0x38, 0xfd, 0xc4, 0xd6, 0x26, - 0xe5, 0x02, 0x6d, 0x7c, 0xc8, 0x89, 0x36, 0x20, 0x6f, 0xdc, 0x34, 0x5c, 0x92, 0x04, 0x25, 0x01, - 0xe3, 0xa6, 0xe1, 0x10, 0x5c, 0x85, 0x02, 0x59, 0xab, 0x4b, 0x91, 0xa4, 0x83, 0xe4, 0x49, 0x1b, - 0x27, 0x91, 0xfe, 0x1c, 0x07, 0x71, 0xdc, 0x7e, 0xa2, 0x97, 0x21, 0x49, 0x5c, 0x16, 0xf7, 0x0a, - 0x95, 0x2d, 0xe6, 0xcf, 0xb6, 0x1c, 0x7f, 0xb6, 0xd5, 0x74, 0xfc, 0xd9, 0x4e, 0xf6, 0x8b, 0xaf, - 0x36, 0x62, 0x9f, 0xfc, 0x75, 0x43, 0x90, 0x29, 0x07, 0xba, 0x44, 0xac, 0xa6, 0xaa, 0x0d, 0x14, - 0xad, 0x4d, 0xa7, 0x9c, 0x23, 0x26, 0x51, 0xd5, 0x06, 0x7b, 0x6d, 0x74, 0x1f, 0xc4, 0x96, 0x3e, - 0xb0, 0xf0, 0xc0, 0x1a, 0x5a, 0x0a, 0x73, 0x59, 0xdc, 0x17, 0x04, 0x2c, 0x3a, 0xf3, 0xb7, 0x35, - 0x87, 0xf2, 0x88, 0x12, 0xca, 0x4b, 0xad, 0x60, 0x03, 0xda, 0x05, 0x70, 0xfd, 0x9a, 0x55, 0x4e, - 0x6e, 0x26, 0x42, 0x0f, 0xc9, 0x43, 0x87, 0xe4, 0x81, 0xd1, 0x56, 0x6d, 0xbc, 0x93, 0x24, 0xd3, - 0x95, 0x7d, 0x9c, 0xe8, 0x29, 0x58, 0x52, 0x0d, 0x43, 0xb1, 0x6c, 0xd5, 0xc6, 0xca, 0xc9, 0xb9, - 0x8d, 0x2d, 0xea, 0x66, 0x0a, 0x72, 0x51, 0x35, 0x8c, 0x63, 0xd2, 0xba, 0x43, 0x1a, 0xd1, 0x93, - 0x50, 0x22, 0x2e, 0x45, 0x53, 0x7b, 0x4a, 0x17, 0x6b, 0xa7, 0x5d, 0x9b, 0xba, 0x93, 0x84, 0x5c, - 0xe4, 0xad, 0x0d, 0xda, 0x28, 0xb5, 0xdd, 0x1d, 0xa7, 0xee, 0x04, 0x21, 0x48, 0xb6, 0x55, 0x5b, - 0xa5, 0x9a, 0x2c, 0xc8, 0xb4, 0x4c, 0xda, 0x0c, 0xd5, 0xee, 0x72, 0xfd, 0xd0, 0x32, 0x5a, 0x83, - 0x34, 0x17, 0x9b, 0xa0, 0x62, 0x79, 0x0d, 0xad, 0x42, 0xca, 0x30, 0xf5, 0x33, 0x4c, 0xb7, 0x2e, - 0x2b, 0xb3, 0x8a, 0x24, 0x43, 0x29, 0xe8, 0x7a, 0x50, 0x09, 0xe2, 0xf6, 0x88, 0x8f, 0x12, 0xb7, - 0x47, 0xe8, 0x05, 0x48, 0x12, 0x45, 0xd2, 0x31, 0x4a, 0x21, 0xce, 0x96, 0xf3, 0x35, 0xcf, 0x0d, - 0x2c, 0x53, 0x4a, 0xe9, 0x2a, 0x2c, 0x8d, 0x5d, 0xa9, 0x71, 0xa1, 0xd2, 0xae, 0x3b, 0x2c, 0xbf, - 0x35, 0xe8, 0x32, 0xe4, 0xfa, 0xea, 0x88, 0xeb, 0x4d, 0xa0, 0xe7, 0x2f, 0xdb, 0x57, 0x47, 0x4c, - 0x65, 0x17, 0x21, 0x43, 0x3a, 0x4f, 0x55, 0x8b, 0x9f, 0xde, 0x74, 0x5f, 0x1d, 0xbd, 0xae, 0x5a, - 0xd2, 0x12, 0x14, 0x03, 0xde, 0x4f, 0x5a, 0x83, 0xd5, 0x30, 0x67, 0x26, 0x75, 0xdd, 0xf6, 0x80, - 0x53, 0x42, 0xb7, 0x21, 0xeb, 0x7a, 0x33, 0x76, 0x46, 0x2f, 0x4d, 0xac, 0xd0, 0x21, 0x96, 0x5d, - 0x52, 0x72, 0x38, 0xc9, 0x5e, 0x77, 0x55, 0x1e, 0xbb, 0x14, 0xe4, 0x8c, 0x6a, 0x18, 0x0d, 0xd5, - 0xea, 0x4a, 0xef, 0x42, 0x39, 0xca, 0x53, 0xf9, 0xf6, 0x86, 0xad, 0xd0, 0xd9, 0x9b, 0x35, 0x48, - 0x77, 0x74, 0xb3, 0xaf, 0xda, 0x54, 0x58, 0x51, 0xe6, 0x35, 0xb2, 0x67, 0xcc, 0x6b, 0x25, 0x68, - 0x33, 0xab, 0x48, 0x0a, 0x5c, 0x8a, 0xf4, 0x56, 0x84, 0x45, 0x1b, 0xb4, 0x31, 0x53, 0x76, 0x51, - 0x66, 0x15, 0x4f, 0x10, 0x9b, 0x2c, 0xab, 0x90, 0x61, 0x2d, 0xba, 0x56, 0x2a, 0x3f, 0x27, 0xf3, - 0x9a, 0xf4, 0x69, 0x02, 0xd6, 0xc2, 0x7d, 0x16, 0xda, 0x84, 0x02, 0xd9, 0x09, 0xdb, 0xbf, 0x53, - 0x09, 0x19, 0xfa, 0xea, 0xa8, 0xc9, 0xf7, 0x4a, 0x84, 0x04, 0x31, 0x96, 0xf1, 0xcd, 0xc4, 0xf5, - 0x82, 0x4c, 0x8a, 0xe8, 0x01, 0x2c, 0xf7, 0xf4, 0x96, 0xda, 0x53, 0x7a, 0xaa, 0x65, 0x2b, 0x3c, - 0x98, 0x61, 0xf7, 0xf5, 0xda, 0x84, 0xb2, 0x99, 0xf7, 0xc1, 0x6d, 0xb6, 0x9f, 0xc4, 0xb6, 0xf1, - 0xab, 0xb6, 0x44, 0x65, 0xdc, 0x57, 0x9d, 0xad, 0x46, 0x77, 0x21, 0xdf, 0xd7, 0xac, 0x13, 0xdc, - 0x55, 0xcf, 0x34, 0xdd, 0xe4, 0x17, 0x77, 0xf2, 0x7c, 0xbe, 0xe1, 0xd1, 0x70, 0x49, 0x7e, 0x36, - 0xdf, 0x96, 0xa4, 0x02, 0xd7, 0xc5, 0x31, 0x5c, 0xe9, 0x85, 0x0d, 0xd7, 0x0b, 0xb0, 0x3a, 0xc0, - 0x23, 0x5b, 0xf1, 0x4c, 0x03, 0x3b, 0x27, 0x19, 0xaa, 0x7a, 0x44, 0xfa, 0x5c, 0x63, 0x62, 0x91, - 0x23, 0x83, 0x9e, 0xa1, 0x5e, 0xdf, 0xd0, 0x2d, 0x6c, 0x2a, 0x6a, 0xbb, 0x6d, 0x62, 0xcb, 0xa2, - 0x81, 0x62, 0x81, 0xba, 0x72, 0xda, 0x5e, 0x65, 0xcd, 0xd2, 0x2f, 0xfd, 0x5b, 0x13, 0xf4, 0xf2, - 0x5c, 0xf1, 0x82, 0xa7, 0xf8, 0x63, 0x58, 0xe5, 0xfc, 0xed, 0x80, 0xee, 0x59, 0xb4, 0x7d, 0x79, - 0xf2, 0x2a, 0x8f, 0xeb, 0x1c, 0x39, 0xec, 0xd1, 0x6a, 0x4f, 0x3c, 0x9e, 0xda, 0x11, 0x24, 0xa9, - 0x52, 0x92, 0xcc, 0x9a, 0x91, 0xf2, 0xbf, 0xdb, 0x56, 0x7c, 0x94, 0x80, 0xe5, 0x89, 0x90, 0xc9, - 0x5d, 0x98, 0x10, 0xba, 0xb0, 0x78, 0xe8, 0xc2, 0x12, 0x0b, 0x2f, 0x8c, 0xef, 0x75, 0x72, 0xf6, - 0x5e, 0xa7, 0xbe, 0xc7, 0xbd, 0x4e, 0x3f, 0xde, 0x5e, 0xff, 0x4b, 0x77, 0xe1, 0x37, 0x02, 0x54, - 0xa2, 0xe3, 0xcc, 0xd0, 0xed, 0x78, 0x16, 0x96, 0xdd, 0xa9, 0xb8, 0xe2, 0x99, 0x61, 0x14, 0xdd, - 0x0e, 0x2e, 0x3f, 0xd2, 0x9d, 0x3e, 0x09, 0xa5, 0xb1, 0x28, 0x98, 0x1d, 0xe5, 0xe2, 0x99, 0x7f, - 0x7c, 0xe9, 0xe7, 0x09, 0xd7, 0xf1, 0x04, 0x42, 0xd5, 0x90, 0xdb, 0xfa, 0x26, 0xac, 0xb4, 0x71, - 0x4b, 0x6b, 0x3f, 0xee, 0x65, 0x5d, 0xe6, 0xdc, 0xff, 0xbd, 0xab, 0x93, 0xa7, 0xe4, 0xd7, 0x79, - 0xc8, 0xca, 0xd8, 0x32, 0x48, 0xe8, 0x87, 0x76, 0x20, 0x87, 0x47, 0x2d, 0x6c, 0xd8, 0x4e, 0xb4, - 0x1c, 0x9e, 0x0c, 0x31, 0xea, 0xba, 0x43, 0xd9, 0x88, 0xc9, 0x1e, 0x1b, 0xba, 0xc5, 0xd1, 0x8e, - 0x68, 0xe0, 0x82, 0xb3, 0xfb, 0xe1, 0x8e, 0x17, 0x1d, 0xb8, 0x23, 0x11, 0x99, 0xc9, 0x33, 0xae, - 0x31, 0xbc, 0xe3, 0x16, 0xc7, 0x3b, 0x92, 0x33, 0x06, 0x0b, 0x00, 0x1e, 0xb5, 0x00, 0xe0, 0x91, - 0x9e, 0xb1, 0xcc, 0x08, 0xc4, 0xe3, 0x45, 0x07, 0xf1, 0xc8, 0xcc, 0x98, 0xf1, 0x18, 0xe4, 0xf1, - 0x9a, 0x0f, 0xf2, 0xc8, 0x45, 0xa6, 0x5d, 0x8c, 0x35, 0x04, 0xf3, 0x78, 0xc5, 0xc5, 0x3c, 0x0a, - 0x91, 0x39, 0x17, 0x67, 0x1e, 0x07, 0x3d, 0x0e, 0x27, 0x40, 0x0f, 0x06, 0x52, 0x3c, 0x15, 0x29, - 0x62, 0x06, 0xea, 0x71, 0x38, 0x81, 0x7a, 0x94, 0x66, 0x08, 0x9c, 0x01, 0x7b, 0xfc, 0x38, 0x1c, - 0xf6, 0x88, 0x06, 0x26, 0xf8, 0x34, 0xe7, 0xc3, 0x3d, 0x94, 0x08, 0xdc, 0x43, 0x8c, 0xcc, 0xd1, - 0x99, 0xf8, 0xb9, 0x81, 0x8f, 0x07, 0x21, 0xc0, 0x07, 0x83, 0x28, 0xae, 0x47, 0x0a, 0x9f, 0x03, - 0xf9, 0x78, 0x10, 0x82, 0x7c, 0xa0, 0x99, 0x62, 0x67, 0x42, 0x1f, 0xbb, 0x41, 0xe8, 0x63, 0x25, - 0x22, 0xea, 0xf4, 0x6e, 0x7b, 0x04, 0xf6, 0x71, 0x12, 0x85, 0x7d, 0x30, 0x7c, 0xe2, 0xb9, 0x48, - 0x89, 0x0b, 0x80, 0x1f, 0x87, 0x13, 0xe0, 0xc7, 0x85, 0x19, 0x27, 0x6d, 0x06, 0xfa, 0xf1, 0x03, - 0x3f, 0xfa, 0xb1, 0x16, 0x09, 0x79, 0x3a, 0x16, 0x20, 0x04, 0xfe, 0x78, 0xcd, 0x07, 0x7f, 0x5c, - 0x9c, 0x71, 0x8f, 0xa7, 0xe3, 0x1f, 0x29, 0x31, 0xbd, 0x9f, 0xcc, 0x66, 0xc5, 0x1c, 0x43, 0x3e, - 0xf6, 0x93, 0xd9, 0xbc, 0x58, 0x90, 0x9e, 0x21, 0x21, 0xd4, 0x98, 0xa1, 0x25, 0xc9, 0x0a, 0x36, - 0x4d, 0xdd, 0xe4, 0x48, 0x06, 0xab, 0x48, 0xd7, 0x49, 0x3e, 0xec, 0x19, 0xd5, 0x29, 0x58, 0x09, - 0x4d, 0x0a, 0x7d, 0x86, 0x54, 0xfa, 0x83, 0xe0, 0xf1, 0x52, 0xb4, 0xc4, 0x9f, 0x4b, 0xe7, 0x78, - 0x2e, 0xed, 0x43, 0x50, 0xe2, 0x41, 0x04, 0x65, 0x03, 0xf2, 0x24, 0xd9, 0x1b, 0x03, 0x47, 0x54, - 0xc3, 0x05, 0x47, 0x6e, 0xc0, 0x32, 0xf5, 0xd8, 0x0c, 0x67, 0xe1, 0x7e, 0x31, 0x49, 0xfd, 0xe2, - 0x12, 0xe9, 0x60, 0xdb, 0xc3, 0x1c, 0xe4, 0xf3, 0xb0, 0xe2, 0xa3, 0x75, 0x93, 0x48, 0x86, 0x14, - 0x88, 0x2e, 0x75, 0x95, 0x67, 0x93, 0x7f, 0x12, 0x3c, 0x0d, 0x79, 0xa8, 0x4a, 0x18, 0x00, 0x22, - 0x7c, 0x4f, 0x00, 0x48, 0xfc, 0xb1, 0x01, 0x10, 0x7f, 0x52, 0x9c, 0x08, 0x26, 0xc5, 0xff, 0x10, - 0xbc, 0x3d, 0x71, 0xe1, 0x8c, 0x96, 0xde, 0xc6, 0x3c, 0x4d, 0xa5, 0x65, 0x12, 0x13, 0xf5, 0xf4, - 0x53, 0x9e, 0x8c, 0x92, 0x22, 0xa1, 0x72, 0x3d, 0x5f, 0x8e, 0x3b, 0x36, 0x37, 0xc3, 0x65, 0x91, - 0x07, 0xcf, 0x70, 0x45, 0x48, 0x3c, 0xc2, 0x0c, 0x99, 0x2f, 0xc8, 0xa4, 0x48, 0xe8, 0xe8, 0xe1, - 0xe3, 0x11, 0x04, 0xab, 0xa0, 0x97, 0x21, 0x47, 0x9f, 0x40, 0x14, 0xdd, 0xb0, 0x38, 0x1a, 0x1f, - 0x88, 0xad, 0xd8, 0x33, 0xc9, 0xd6, 0x11, 0xa1, 0x39, 0x34, 0x2c, 0x39, 0x6b, 0xf0, 0x92, 0x2f, - 0xe4, 0xc9, 0x05, 0x42, 0x9e, 0x2b, 0x90, 0x23, 0xb3, 0xb7, 0x0c, 0xb5, 0x85, 0xcb, 0x40, 0x27, - 0xea, 0x35, 0x48, 0xbf, 0x8f, 0xc3, 0xd2, 0x98, 0xa7, 0x0b, 0x5d, 0xbb, 0x73, 0x24, 0xe3, 0x3e, - 0x78, 0x67, 0x3e, 0x7d, 0xac, 0x03, 0x9c, 0xaa, 0x96, 0xf2, 0x81, 0x3a, 0xb0, 0x71, 0x9b, 0x2b, - 0xc5, 0xd7, 0x82, 0x2a, 0x90, 0x25, 0xb5, 0xa1, 0x85, 0xdb, 0x1c, 0x69, 0x72, 0xeb, 0xa8, 0x01, - 0x69, 0x7c, 0x86, 0x07, 0xb6, 0x55, 0xce, 0xd0, 0x6d, 0x5f, 0x9b, 0xcc, 0xc7, 0x49, 0xf7, 0x4e, - 0x99, 0x6c, 0xf6, 0xb7, 0x5f, 0x6d, 0x88, 0x8c, 0xfa, 0x39, 0xbd, 0xaf, 0xd9, 0xb8, 0x6f, 0xd8, - 0xe7, 0x32, 0xe7, 0x0f, 0x6a, 0x21, 0x3b, 0xa6, 0x05, 0x8a, 0x79, 0x16, 0x1c, 0x7c, 0x81, 0xe8, - 0x54, 0xd3, 0x4d, 0xcd, 0x3e, 0x97, 0x8b, 0x7d, 0xdc, 0x37, 0x74, 0xbd, 0xa7, 0xb0, 0x3b, 0xfe, - 0x14, 0x88, 0xe3, 0xe6, 0x28, 0x4c, 0x59, 0xd2, 0x35, 0x4f, 0xa7, 0x0e, 0x7e, 0x34, 0x11, 0x4f, - 0x4b, 0x55, 0x28, 0x05, 0xa3, 0x04, 0x74, 0x0d, 0x8a, 0x26, 0xb6, 0x55, 0x6d, 0xa0, 0x04, 0x42, - 0xfa, 0x02, 0x6b, 0x64, 0x17, 0x74, 0x3f, 0x99, 0x15, 0xc4, 0xf8, 0x7e, 0x32, 0x1b, 0x17, 0x13, - 0xd2, 0x11, 0x5c, 0x08, 0x8d, 0x12, 0xd0, 0x4b, 0x90, 0xf3, 0x02, 0x0c, 0x81, 0xaa, 0x6e, 0x0a, - 0x6e, 0xe4, 0xd1, 0x4a, 0x9f, 0x0b, 0x9e, 0xc8, 0x20, 0x12, 0x55, 0x87, 0xb4, 0x89, 0xad, 0x61, - 0x8f, 0x61, 0x43, 0xa5, 0x9b, 0xcf, 0xcf, 0x17, 0x5f, 0x90, 0xd6, 0x61, 0xcf, 0x96, 0x39, 0xb3, - 0xf4, 0x0e, 0xa4, 0x59, 0x0b, 0xca, 0x43, 0xe6, 0xc1, 0xc1, 0xbd, 0x83, 0xc3, 0xb7, 0x0e, 0xc4, - 0x18, 0x02, 0x48, 0x57, 0x6b, 0xb5, 0xfa, 0x51, 0x53, 0x14, 0x50, 0x0e, 0x52, 0xd5, 0x9d, 0x43, - 0xb9, 0x29, 0xc6, 0x49, 0xb3, 0x5c, 0xdf, 0xaf, 0xd7, 0x9a, 0x62, 0x02, 0x2d, 0x43, 0x91, 0x95, - 0x95, 0xdd, 0x43, 0xf9, 0x8d, 0x6a, 0x53, 0x4c, 0xfa, 0x9a, 0x8e, 0xeb, 0x07, 0x77, 0xeb, 0xb2, - 0x98, 0x92, 0xfe, 0x07, 0x2e, 0x45, 0x46, 0x24, 0x1e, 0xcc, 0x24, 0xf8, 0x60, 0x26, 0xe9, 0xd3, - 0x38, 0x49, 0xd1, 0xa2, 0xc2, 0x0c, 0xb4, 0x3f, 0xb6, 0xf0, 0x9b, 0x0b, 0xc4, 0x28, 0x63, 0xab, - 0x27, 0x59, 0x99, 0x89, 0x3b, 0xd8, 0x6e, 0x75, 0x59, 0xd8, 0xc3, 0xcc, 0x59, 0x51, 0x2e, 0xf2, - 0x56, 0xca, 0x64, 0x31, 0xb2, 0xf7, 0x70, 0xcb, 0x56, 0xd8, 0x89, 0xb4, 0x68, 0x6a, 0x94, 0x23, - 0x64, 0xa4, 0xf5, 0x98, 0x35, 0x4a, 0xef, 0x2e, 0xa4, 0xcb, 0x1c, 0xa4, 0xe4, 0x7a, 0x53, 0x7e, - 0x5b, 0x4c, 0x20, 0x04, 0x25, 0x5a, 0x54, 0x8e, 0x0f, 0xaa, 0x47, 0xc7, 0x8d, 0x43, 0xa2, 0xcb, - 0x15, 0x58, 0x72, 0x74, 0xe9, 0x34, 0xa6, 0xa4, 0x67, 0xe1, 0x62, 0x44, 0x8c, 0x14, 0x72, 0xa0, - 0x7f, 0x2b, 0xf8, 0xa9, 0x83, 0x71, 0xce, 0x21, 0xa4, 0x2d, 0x5b, 0xb5, 0x87, 0x16, 0x57, 0xe2, - 0x4b, 0xf3, 0x06, 0x4d, 0x5b, 0x4e, 0xe1, 0x98, 0xb2, 0xcb, 0x5c, 0x8c, 0x74, 0x1b, 0x4a, 0xc1, - 0x9e, 0x68, 0x1d, 0x78, 0x87, 0x28, 0x2e, 0xdd, 0x01, 0x34, 0x19, 0x4b, 0x85, 0x24, 0xcb, 0x42, - 0x58, 0xb2, 0xfc, 0x3b, 0x01, 0x2e, 0x4f, 0x89, 0x9b, 0xd0, 0x9b, 0x63, 0x8b, 0x7c, 0x65, 0x91, - 0xa8, 0x6b, 0x8b, 0xb5, 0x8d, 0x2d, 0xf3, 0x16, 0x14, 0xfc, 0xed, 0xf3, 0x2d, 0xf2, 0xdb, 0xb8, - 0x77, 0x89, 0x83, 0x59, 0xbd, 0x67, 0x4f, 0x85, 0xef, 0x68, 0x4f, 0x5f, 0x05, 0xb0, 0x47, 0x0a, - 0x3b, 0xd6, 0x8e, 0x53, 0x7e, 0x22, 0x04, 0x2d, 0xc5, 0xad, 0xe6, 0x88, 0x5f, 0x82, 0x9c, 0xcd, - 0x4b, 0x16, 0x3a, 0xf6, 0x43, 0x1c, 0x43, 0xea, 0xb0, 0x2d, 0x9e, 0xfe, 0xcf, 0xeb, 0xd9, 0x3d, - 0x28, 0x84, 0x35, 0x5b, 0xe8, 0x6d, 0xb8, 0x38, 0x16, 0x75, 0xb8, 0xa2, 0x93, 0xf3, 0x06, 0x1f, - 0x17, 0x82, 0xc1, 0x87, 0x23, 0xda, 0x1f, 0x3a, 0xa4, 0x82, 0xa1, 0xc3, 0xdb, 0x00, 0x1e, 0xd4, - 0x41, 0x2c, 0x8c, 0xa9, 0x0f, 0x07, 0x6d, 0x7a, 0x02, 0x52, 0x32, 0xab, 0xa0, 0xdb, 0x90, 0x22, - 0x27, 0xc9, 0xd1, 0xd3, 0xa4, 0x29, 0x26, 0x27, 0xc1, 0x07, 0x95, 0x30, 0x6a, 0x49, 0x03, 0x34, - 0x09, 0x37, 0x47, 0x0c, 0xf1, 0x5a, 0x70, 0x88, 0xab, 0x91, 0xc0, 0x75, 0xf8, 0x50, 0x1f, 0x42, - 0x8a, 0xee, 0x3c, 0x71, 0x67, 0xf4, 0x39, 0x85, 0x87, 0x9e, 0xa4, 0x8c, 0x7e, 0x02, 0xa0, 0xda, - 0xb6, 0xa9, 0x9d, 0x0c, 0xbd, 0x01, 0x36, 0xc2, 0x4f, 0x4e, 0xd5, 0xa1, 0xdb, 0xb9, 0xc2, 0x8f, - 0xd0, 0xaa, 0xc7, 0xea, 0x3b, 0x46, 0x3e, 0x81, 0xd2, 0x01, 0x94, 0x82, 0xbc, 0x4e, 0xb0, 0xc4, - 0xe6, 0x10, 0x0c, 0x96, 0x58, 0xec, 0xcb, 0x83, 0x25, 0x37, 0xd4, 0x4a, 0xb0, 0x37, 0x23, 0x5a, - 0x91, 0x7e, 0x1a, 0x87, 0x82, 0xff, 0xe0, 0xfd, 0xe7, 0xc5, 0x33, 0xd2, 0x2f, 0x04, 0xc8, 0xba, - 0xcb, 0x0f, 0xbe, 0xea, 0x04, 0x5e, 0xdc, 0x98, 0xf6, 0xe2, 0xfe, 0xa7, 0x18, 0xf6, 0x14, 0x96, - 0x70, 0xdf, 0xd7, 0xee, 0xb8, 0xee, 0x2f, 0x0a, 0xde, 0xf1, 0xeb, 0x9a, 0x9f, 0x2a, 0xc7, 0xdb, - 0xdf, 0x81, 0x9c, 0x7b, 0x7b, 0x49, 0x06, 0xe3, 0xc0, 0x60, 0x02, 0xbf, 0x43, 0x1c, 0xc4, 0x5c, - 0x85, 0x94, 0xa1, 0x7f, 0xc0, 0xdf, 0x79, 0x12, 0x32, 0xab, 0x48, 0x6d, 0x58, 0x1a, 0xbb, 0xfa, - 0xe8, 0x0e, 0x64, 0x8c, 0xe1, 0x89, 0xe2, 0x1c, 0x8e, 0x31, 0xb0, 0xd0, 0x89, 0x8d, 0x87, 0x27, - 0x3d, 0xad, 0x75, 0x0f, 0x9f, 0x3b, 0x93, 0x31, 0x86, 0x27, 0xf7, 0xd8, 0x19, 0x62, 0xa3, 0xc4, - 0xfd, 0xa3, 0xfc, 0x4a, 0x80, 0xac, 0x73, 0x27, 0xd0, 0xff, 0x41, 0xce, 0x35, 0x2b, 0xee, 0x9b, - 0x70, 0xa4, 0x3d, 0xe2, 0xf2, 0x3d, 0x16, 0x54, 0x75, 0x1e, 0xb3, 0xb5, 0xb6, 0xd2, 0xe9, 0xa9, - 0xec, 0x2c, 0x95, 0x82, 0x3a, 0x63, 0x86, 0x87, 0xda, 0xe3, 0xbd, 0xbb, 0xbb, 0x3d, 0xf5, 0x54, - 0xce, 0x53, 0x9e, 0xbd, 0x36, 0xa9, 0xf0, 0xc8, 0xee, 0xef, 0x02, 0x88, 0xe3, 0x37, 0xf6, 0x3b, - 0xcf, 0x6e, 0xd2, 0xcd, 0x25, 0x42, 0xdc, 0x1c, 0xda, 0x86, 0x15, 0x97, 0x42, 0xb1, 0xb4, 0xd3, - 0x81, 0x6a, 0x0f, 0x4d, 0xcc, 0xe1, 0x55, 0xe4, 0x76, 0x1d, 0x3b, 0x3d, 0x93, 0xab, 0x4e, 0x3d, - 0xe6, 0xaa, 0x3f, 0x8a, 0x43, 0xde, 0x07, 0xf6, 0xa2, 0xff, 0xf5, 0x19, 0xa3, 0x52, 0x88, 0x67, - 0xf0, 0xd1, 0x7a, 0xef, 0xbb, 0x41, 0x35, 0xc5, 0x17, 0x57, 0x53, 0x14, 0xa4, 0xee, 0x60, 0xc7, - 0xc9, 0x85, 0xb1, 0xe3, 0xe7, 0x00, 0xd9, 0xba, 0xad, 0xf6, 0x94, 0x33, 0xdd, 0xd6, 0x06, 0xa7, - 0x0a, 0x3b, 0x86, 0xcc, 0x74, 0x88, 0xb4, 0xe7, 0x21, 0xed, 0x38, 0xa2, 0x27, 0xf2, 0x67, 0x02, - 0x64, 0xdd, 0xb0, 0x7b, 0xd1, 0x27, 0xd9, 0x35, 0x48, 0xf3, 0xc8, 0x92, 0xbd, 0xc9, 0xf2, 0x5a, - 0x28, 0x48, 0x5e, 0x81, 0x6c, 0x1f, 0xdb, 0x2a, 0xb5, 0x83, 0xcc, 0xab, 0xb9, 0xf5, 0x1b, 0xaf, - 0x40, 0xde, 0xf7, 0x72, 0x4e, 0x4c, 0xe3, 0x41, 0xfd, 0x2d, 0x31, 0x56, 0xc9, 0x7c, 0xfc, 0xd9, - 0x66, 0xe2, 0x00, 0x7f, 0x40, 0x6e, 0xb3, 0x5c, 0xaf, 0x35, 0xea, 0xb5, 0x7b, 0xa2, 0x50, 0xc9, - 0x7f, 0xfc, 0xd9, 0x66, 0x46, 0xc6, 0x14, 0x1f, 0xbd, 0x71, 0x0f, 0x96, 0xc6, 0x36, 0x26, 0x18, - 0xb6, 0x20, 0x28, 0xdd, 0x7d, 0x70, 0x74, 0x7f, 0xaf, 0x56, 0x6d, 0xd6, 0x95, 0x87, 0x87, 0xcd, - 0xba, 0x28, 0xa0, 0x8b, 0xb0, 0x72, 0x7f, 0xef, 0xf5, 0x46, 0x53, 0xa9, 0xdd, 0xdf, 0xab, 0x1f, - 0x34, 0x95, 0x6a, 0xb3, 0x59, 0xad, 0xdd, 0x13, 0xe3, 0x37, 0xff, 0x58, 0x80, 0x64, 0x75, 0xa7, - 0xb6, 0x87, 0x6a, 0x90, 0xa4, 0xb8, 0xca, 0xd4, 0x9f, 0x7b, 0x95, 0xe9, 0x48, 0x37, 0xda, 0x85, - 0x14, 0x85, 0x5c, 0xd0, 0xf4, 0xaf, 0x7c, 0x95, 0x19, 0xd0, 0x37, 0x99, 0x0c, 0xbd, 0x91, 0x53, - 0xff, 0xf6, 0x55, 0xa6, 0x23, 0xe1, 0xe8, 0x3e, 0x64, 0x9c, 0x8c, 0x7b, 0xd6, 0x87, 0xbb, 0xca, - 0x4c, 0x78, 0x1a, 0x1d, 0x42, 0xd6, 0xcd, 0x49, 0x67, 0xfe, 0x21, 0xaa, 0xcc, 0xc6, 0xd9, 0xc8, - 0xf4, 0x9c, 0xe4, 0x75, 0xd6, 0x9f, 0xa2, 0xca, 0x4c, 0xd4, 0x8d, 0x68, 0x9e, 0x01, 0x2b, 0xd3, - 0x7f, 0x25, 0x56, 0x66, 0x40, 0xf8, 0x68, 0x0f, 0xd2, 0x3c, 0x5b, 0x9e, 0xf1, 0xd1, 0xb0, 0x32, - 0x0b, 0x94, 0x47, 0x32, 0xe4, 0x3c, 0xc8, 0x6a, 0xf6, 0x5f, 0xcb, 0xca, 0x1c, 0xaf, 0x13, 0xe8, - 0x1d, 0x28, 0x06, 0x33, 0xf1, 0xf9, 0x3e, 0x33, 0x56, 0xe6, 0x84, 0xff, 0x89, 0xfc, 0x60, 0x5a, - 0x3e, 0xdf, 0xe7, 0xc6, 0xca, 0x9c, 0xaf, 0x01, 0xe8, 0x3d, 0x58, 0x9e, 0x4c, 0x9b, 0xe7, 0xff, - 0xeb, 0x58, 0x59, 0xe0, 0x7d, 0x00, 0xf5, 0x01, 0x85, 0xa4, 0xdb, 0x0b, 0x7c, 0x7d, 0xac, 0x2c, - 0xf2, 0x5c, 0x80, 0xda, 0xb0, 0x34, 0x9e, 0xc3, 0xce, 0xfb, 0x15, 0xb2, 0x32, 0xf7, 0xd3, 0x01, - 0x1b, 0x25, 0x98, 0xfb, 0xce, 0xfb, 0x35, 0xb2, 0x32, 0xf7, 0x4b, 0x02, 0x7a, 0x00, 0xe0, 0x4b, - 0x5f, 0xe7, 0xf8, 0x2a, 0x59, 0x99, 0xe7, 0x4d, 0x01, 0x19, 0xb0, 0x12, 0x96, 0xd7, 0x2e, 0xf2, - 0x73, 0xb2, 0xb2, 0xd0, 0x53, 0x03, 0x39, 0xcf, 0xc1, 0x0c, 0x75, 0xbe, 0x9f, 0x94, 0x95, 0x39, - 0xdf, 0x1c, 0x76, 0xaa, 0x5f, 0x7c, 0xbd, 0x2e, 0x7c, 0xf9, 0xf5, 0xba, 0xf0, 0xb7, 0xaf, 0xd7, - 0x85, 0x4f, 0xbe, 0x59, 0x8f, 0x7d, 0xf9, 0xcd, 0x7a, 0xec, 0x2f, 0xdf, 0xac, 0xc7, 0x7e, 0xf8, - 0xf4, 0xa9, 0x66, 0x77, 0x87, 0x27, 0x5b, 0x2d, 0xbd, 0xbf, 0xdd, 0xd2, 0xfb, 0xd8, 0x3e, 0xe9, - 0xd8, 0x5e, 0xc1, 0xfb, 0x10, 0x7f, 0x92, 0xa6, 0x0e, 0xfe, 0xd6, 0x3f, 0x03, 0x00, 0x00, 0xff, - 0xff, 0x0d, 0x83, 0x0c, 0x75, 0x30, 0x2f, 0x00, 0x00, + // 3388 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcb, 0x73, 0x23, 0x45, + 0x9a, 0x57, 0xe9, 0xad, 0x4f, 0xb2, 0x5c, 0x4e, 0xbb, 0xdd, 0x6a, 0x75, 0x63, 0xbb, 0xab, 0x03, + 0x68, 0x1a, 0xb0, 0x59, 0xf7, 0x36, 0x8f, 0x68, 0xd8, 0x5d, 0x5b, 0x2d, 0x23, 0xbb, 0x1b, 0xdb, + 0x94, 0xd5, 0x4d, 0xb0, 0x0f, 0x8a, 0xb4, 0x94, 0xb6, 0x8a, 0x96, 0x54, 0x45, 0x55, 0xc9, 0xc8, + 0x9c, 0x36, 0x96, 0xdd, 0x0b, 0x27, 0x22, 0x36, 0x26, 0x82, 0xc3, 0x70, 0x98, 0xc3, 0x5c, 0xe6, + 0x2f, 0x98, 0x89, 0x20, 0x38, 0xcd, 0x81, 0xc3, 0x1c, 0x38, 0xce, 0x09, 0x26, 0xe0, 0x36, 0xd7, + 0x39, 0xcc, 0x75, 0x22, 0x1f, 0xf5, 0x92, 0xaa, 0x24, 0xb9, 0x61, 0x0e, 0x13, 0x33, 0xb7, 0x7c, + 0x7c, 0xdf, 0x57, 0x59, 0x5f, 0x7e, 0xf9, 0x3d, 0x7e, 0x99, 0x70, 0xd5, 0x21, 0xfd, 0x36, 0xb1, + 0x7a, 0x7a, 0xdf, 0xd9, 0xc0, 0xc7, 0x2d, 0x7d, 0xc3, 0x39, 0x37, 0x89, 0xbd, 0x6e, 0x5a, 0x86, + 0x63, 0xa0, 0x79, 0x7f, 0x72, 0x9d, 0x4e, 0x56, 0x97, 0x4e, 0x8d, 0x53, 0x83, 0xcd, 0x6d, 0xd0, + 0x16, 0x27, 0xab, 0xae, 0x9c, 0x1a, 0xc6, 0x69, 0x97, 0x6c, 0xb0, 0xde, 0xf1, 0xe0, 0x64, 0xa3, + 0x3d, 0xb0, 0xb0, 0xa3, 0x1b, 0x7d, 0x31, 0xbf, 0x3a, 0x3a, 0xef, 0xe8, 0x3d, 0x62, 0x3b, 0xb8, + 0x67, 0x0a, 0x82, 0x6b, 0x81, 0x45, 0xb4, 0xac, 0x73, 0xd3, 0x31, 0x36, 0x1e, 0x93, 0x73, 0xb1, + 0x8a, 0xea, 0x53, 0xe3, 0xb3, 0xa6, 0x65, 0x18, 0x27, 0x11, 0xd3, 0x6c, 0xf1, 0x1b, 0x26, 0xb6, + 0x70, 0xcf, 0xe5, 0x5e, 0x1b, 0x9b, 0x3e, 0xc3, 0x5d, 0xbd, 0x8d, 0x1d, 0xc3, 0xe2, 0x14, 0xca, + 0x57, 0x00, 0x39, 0x95, 0x7c, 0x38, 0x20, 0xb6, 0x83, 0x36, 0x21, 0x4d, 0x5a, 0x1d, 0xa3, 0x22, + 0xad, 0x49, 0x37, 0x8b, 0x9b, 0xd7, 0xd6, 0x47, 0x14, 0xb0, 0x2e, 0xe8, 0xea, 0xad, 0x8e, 0xd1, + 0x48, 0xa8, 0x8c, 0x16, 0xdd, 0x81, 0xcc, 0x49, 0x77, 0x60, 0x77, 0x2a, 0x49, 0xc6, 0xf4, 0x54, + 0x1c, 0xd3, 0x0e, 0x25, 0x6a, 0x24, 0x54, 0x4e, 0x4d, 0x3f, 0xa5, 0xf7, 0x4f, 0x8c, 0x4a, 0x6a, + 0xf2, 0xa7, 0x76, 0xfb, 0x27, 0xec, 0x53, 0x94, 0x16, 0x6d, 0x03, 0xe8, 0x7d, 0xdd, 0xd1, 0x5a, + 0x1d, 0xac, 0xf7, 0x2b, 0x19, 0xc6, 0x79, 0x3d, 0x9e, 0x53, 0x77, 0x6a, 0x94, 0xb0, 0x91, 0x50, + 0x0b, 0xba, 0xdb, 0xa1, 0xcb, 0xfd, 0x70, 0x40, 0xac, 0xf3, 0x4a, 0x76, 0xf2, 0x72, 0xdf, 0xa6, + 0x44, 0x74, 0xb9, 0x8c, 0x1a, 0xbd, 0x0e, 0xf9, 0x56, 0x87, 0xb4, 0x1e, 0x6b, 0xce, 0xb0, 0x92, + 0x67, 0x9c, 0xab, 0x71, 0x9c, 0x35, 0x4a, 0xd7, 0x1c, 0x36, 0x12, 0x6a, 0xae, 0xc5, 0x9b, 0xe8, + 0x55, 0xc8, 0xb6, 0x8c, 0x5e, 0x4f, 0x77, 0x2a, 0x45, 0xc6, 0xbb, 0x12, 0xcb, 0xcb, 0xa8, 0x1a, + 0x09, 0x55, 0xd0, 0xa3, 0x7d, 0x28, 0x77, 0x75, 0xdb, 0xd1, 0xec, 0x3e, 0x36, 0xed, 0x8e, 0xe1, + 0xd8, 0x95, 0x12, 0x93, 0xf0, 0x74, 0x9c, 0x84, 0x07, 0xba, 0xed, 0x1c, 0xb9, 0xc4, 0x8d, 0x84, + 0x3a, 0xd7, 0x0d, 0x0e, 0x50, 0x79, 0xc6, 0xc9, 0x09, 0xb1, 0x3c, 0x81, 0x95, 0xb9, 0xc9, 0xf2, + 0x0e, 0x28, 0xb5, 0xcb, 0x4f, 0xe5, 0x19, 0xc1, 0x01, 0xf4, 0x1f, 0xb0, 0xd8, 0x35, 0x70, 0xdb, + 0x13, 0xa7, 0xb5, 0x3a, 0x83, 0xfe, 0xe3, 0x4a, 0x99, 0x09, 0x7d, 0x2e, 0x76, 0x91, 0x06, 0x6e, + 0xbb, 0x22, 0x6a, 0x94, 0xa1, 0x91, 0x50, 0x17, 0xba, 0xa3, 0x83, 0xe8, 0x3d, 0x58, 0xc2, 0xa6, + 0xd9, 0x3d, 0x1f, 0x95, 0x3e, 0xcf, 0xa4, 0xdf, 0x8a, 0x93, 0xbe, 0x45, 0x79, 0x46, 0xc5, 0x23, + 0x3c, 0x36, 0x8a, 0x9a, 0x20, 0x9b, 0x16, 0x31, 0xb1, 0x45, 0x34, 0xd3, 0x32, 0x4c, 0xc3, 0xc6, + 0xdd, 0x8a, 0xcc, 0x64, 0x3f, 0x1b, 0x27, 0xfb, 0x90, 0xd3, 0x1f, 0x0a, 0xf2, 0x46, 0x42, 0x9d, + 0x37, 0xc3, 0x43, 0x5c, 0xaa, 0xd1, 0x22, 0xb6, 0xed, 0x4b, 0x5d, 0x98, 0x26, 0x95, 0xd1, 0x87, + 0xa5, 0x86, 0x86, 0x50, 0x1d, 0x8a, 0x64, 0x48, 0xd9, 0xb5, 0x33, 0xc3, 0x21, 0x15, 0xc4, 0x04, + 0x2a, 0xb1, 0x27, 0x94, 0x91, 0x3e, 0x32, 0x1c, 0xd2, 0x48, 0xa8, 0x40, 0xbc, 0x1e, 0xc2, 0x70, + 0xe9, 0x8c, 0x58, 0xfa, 0xc9, 0x39, 0x13, 0xa3, 0xb1, 0x19, 0x5b, 0x37, 0xfa, 0x95, 0x45, 0x26, + 0xf0, 0xf9, 0x38, 0x81, 0x8f, 0x18, 0x13, 0x15, 0x51, 0x77, 0x59, 0x1a, 0x09, 0x75, 0xf1, 0x6c, + 0x7c, 0x98, 0x9a, 0xd8, 0x89, 0xde, 0xc7, 0x5d, 0xfd, 0x63, 0xa2, 0x1d, 0x77, 0x8d, 0xd6, 0xe3, + 0xca, 0xd2, 0x64, 0x13, 0xdb, 0x11, 0xd4, 0xdb, 0x94, 0x98, 0x9a, 0xd8, 0x49, 0x70, 0x00, 0xfd, + 0x2b, 0x14, 0xf4, 0xbe, 0x4d, 0x2c, 0x87, 0x9e, 0xbd, 0x4b, 0x4c, 0xd4, 0x5a, 0xfc, 0xa1, 0xa7, + 0x84, 0xec, 0xf0, 0xe5, 0x75, 0xd1, 0xa6, 0x67, 0xd7, 0x22, 0xd8, 0xd4, 0x9c, 0xa1, 0x5d, 0x59, + 0x9e, 0x7c, 0x76, 0x55, 0x82, 0xcd, 0xe6, 0x90, 0x9e, 0x9b, 0x9c, 0xc5, 0x9b, 0xdb, 0x39, 0xc8, + 0x9c, 0xe1, 0xee, 0x80, 0xec, 0xa5, 0xf3, 0x69, 0x39, 0xb3, 0x97, 0xce, 0xe7, 0xe4, 0xfc, 0x5e, + 0x3a, 0x5f, 0x90, 0x61, 0x2f, 0x9d, 0x07, 0xb9, 0xa8, 0x3c, 0x0b, 0xc5, 0x80, 0x5f, 0x44, 0x15, + 0xc8, 0xf5, 0x88, 0x6d, 0xe3, 0x53, 0xc2, 0xdc, 0x68, 0x41, 0x75, 0xbb, 0x4a, 0x19, 0x4a, 0x41, + 0x5f, 0xa8, 0x7c, 0x26, 0x79, 0x9c, 0xd4, 0xcd, 0x51, 0xce, 0x33, 0x62, 0xb1, 0xdd, 0x10, 0x9c, + 0xa2, 0x8b, 0x6e, 0xc0, 0x1c, 0xd3, 0xa4, 0xe6, 0xce, 0x53, 0x5f, 0x9b, 0x56, 0x4b, 0x6c, 0xf0, + 0x91, 0x20, 0x5a, 0x85, 0xa2, 0xb9, 0x69, 0x7a, 0x24, 0x29, 0x46, 0x02, 0xe6, 0xa6, 0xe9, 0x12, + 0x5c, 0x87, 0x12, 0xfd, 0x57, 0x8f, 0x22, 0xcd, 0x3e, 0x52, 0xa4, 0x63, 0x82, 0x44, 0xf9, 0x5d, + 0x12, 0xe4, 0x51, 0xff, 0x89, 0x5e, 0x85, 0x34, 0x0d, 0x59, 0x22, 0x2a, 0x54, 0xd7, 0x79, 0x3c, + 0x5b, 0x77, 0xe3, 0xd9, 0x7a, 0xd3, 0x8d, 0x67, 0xdb, 0xf9, 0xaf, 0xbf, 0x5d, 0x4d, 0x7c, 0xf6, + 0xdd, 0xaa, 0xa4, 0x32, 0x0e, 0x74, 0x85, 0x7a, 0x4d, 0xac, 0xf7, 0x35, 0xbd, 0xcd, 0x96, 0x5c, + 0xa0, 0x2e, 0x11, 0xeb, 0xfd, 0xdd, 0x36, 0x7a, 0x00, 0x72, 0xcb, 0xe8, 0xdb, 0xa4, 0x6f, 0x0f, + 0x6c, 0x8d, 0x87, 0x2c, 0x11, 0x0b, 0x42, 0x1e, 0x9d, 0xc7, 0xe3, 0x9a, 0x4b, 0x79, 0xc8, 0x08, + 0xd5, 0xf9, 0x56, 0x78, 0x00, 0xed, 0x00, 0x78, 0x71, 0xcd, 0xae, 0xa4, 0xd7, 0x52, 0x91, 0x46, + 0xf2, 0xc8, 0x25, 0x79, 0x68, 0xb6, 0xb1, 0x43, 0xb6, 0xd3, 0x74, 0xb9, 0x6a, 0x80, 0x13, 0x3d, + 0x03, 0xf3, 0xd8, 0x34, 0x35, 0xdb, 0xc1, 0x0e, 0xd1, 0x8e, 0xcf, 0x1d, 0x62, 0xb3, 0x30, 0x53, + 0x52, 0xe7, 0xb0, 0x69, 0x1e, 0xd1, 0xd1, 0x6d, 0x3a, 0x88, 0x9e, 0x86, 0x32, 0x0d, 0x29, 0x3a, + 0xee, 0x6a, 0x1d, 0xa2, 0x9f, 0x76, 0x1c, 0x16, 0x4e, 0x52, 0xea, 0x9c, 0x18, 0x6d, 0xb0, 0x41, + 0xa5, 0xed, 0xed, 0x38, 0x0b, 0x27, 0x08, 0x41, 0xba, 0x8d, 0x1d, 0xcc, 0x34, 0x59, 0x52, 0x59, + 0x9b, 0x8e, 0x99, 0xd8, 0xe9, 0x08, 0xfd, 0xb0, 0x36, 0x5a, 0x86, 0xac, 0x10, 0x9b, 0x62, 0x62, + 0x45, 0x0f, 0x2d, 0x41, 0xc6, 0xb4, 0x8c, 0x33, 0xc2, 0xb6, 0x2e, 0xaf, 0xf2, 0x8e, 0xa2, 0x42, + 0x39, 0x1c, 0x7a, 0x50, 0x19, 0x92, 0xce, 0x50, 0x7c, 0x25, 0xe9, 0x0c, 0xd1, 0x4b, 0x90, 0xa6, + 0x8a, 0x64, 0xdf, 0x28, 0x47, 0x04, 0x5b, 0xc1, 0xd7, 0x3c, 0x37, 0x89, 0xca, 0x28, 0x95, 0xeb, + 0x30, 0x3f, 0x72, 0xa4, 0x46, 0x85, 0x2a, 0x3b, 0xde, 0x67, 0xc5, 0xa9, 0x41, 0x57, 0xa1, 0xd0, + 0xc3, 0x43, 0xa1, 0x37, 0x89, 0xd9, 0x5f, 0xbe, 0x87, 0x87, 0x5c, 0x65, 0x97, 0x21, 0x47, 0x27, + 0x4f, 0xb1, 0x2d, 0xac, 0x37, 0xdb, 0xc3, 0xc3, 0x37, 0xb1, 0xad, 0xcc, 0xc3, 0x5c, 0x28, 0xfa, + 0x29, 0xcb, 0xb0, 0x14, 0x15, 0xcc, 0x94, 0x8e, 0x37, 0x1e, 0x0a, 0x4a, 0xe8, 0x0e, 0xe4, 0xbd, + 0x68, 0xc6, 0x6d, 0xf4, 0xca, 0xd8, 0x1f, 0xba, 0xc4, 0xaa, 0x47, 0x4a, 0x8d, 0x93, 0xee, 0x75, + 0x07, 0x8b, 0xdc, 0xa5, 0xa4, 0xe6, 0xb0, 0x69, 0x36, 0xb0, 0xdd, 0x51, 0xde, 0x87, 0x4a, 0x5c, + 0xa4, 0x0a, 0xec, 0x0d, 0xff, 0x43, 0x77, 0x6f, 0x96, 0x21, 0x7b, 0x62, 0x58, 0x3d, 0xec, 0x30, + 0x61, 0x73, 0xaa, 0xe8, 0xd1, 0x3d, 0xe3, 0x51, 0x2b, 0xc5, 0x86, 0x79, 0x47, 0xd1, 0xe0, 0x4a, + 0x6c, 0xb4, 0xa2, 0x2c, 0x7a, 0xbf, 0x4d, 0xb8, 0xb2, 0xe7, 0x54, 0xde, 0xf1, 0x05, 0xf1, 0xc5, + 0xf2, 0x0e, 0xfd, 0xac, 0xcd, 0xfe, 0x95, 0xc9, 0x2f, 0xa8, 0xa2, 0xa7, 0x7c, 0x9e, 0x82, 0xe5, + 0xe8, 0x98, 0x85, 0xd6, 0xa0, 0x44, 0x77, 0xc2, 0x09, 0xee, 0x54, 0x4a, 0x85, 0x1e, 0x1e, 0x36, + 0xc5, 0x5e, 0xc9, 0x90, 0xa2, 0xce, 0x32, 0xb9, 0x96, 0xba, 0x59, 0x52, 0x69, 0x13, 0x3d, 0x84, + 0x85, 0xae, 0xd1, 0xc2, 0x5d, 0xad, 0x8b, 0x6d, 0x47, 0x13, 0xc9, 0x0c, 0x3f, 0xaf, 0x37, 0xc6, + 0x94, 0xcd, 0xa3, 0x0f, 0x69, 0xf3, 0xfd, 0xa4, 0xbe, 0x4d, 0x1c, 0xb5, 0x79, 0x26, 0xe3, 0x01, + 0x76, 0xb7, 0x1a, 0xdd, 0x83, 0x62, 0x4f, 0xb7, 0x8f, 0x49, 0x07, 0x9f, 0xe9, 0x86, 0x25, 0x0e, + 0xee, 0xb8, 0x7d, 0xbe, 0xe5, 0xd3, 0x08, 0x49, 0x41, 0xb6, 0xc0, 0x96, 0x64, 0x42, 0xc7, 0xc5, + 0x75, 0x5c, 0xd9, 0x0b, 0x3b, 0xae, 0x97, 0x60, 0xa9, 0x4f, 0x86, 0x8e, 0xe6, 0xbb, 0x06, 0x6e, + 0x27, 0x39, 0xa6, 0x7a, 0x44, 0xe7, 0x3c, 0x67, 0x62, 0x53, 0x93, 0x41, 0xcf, 0xb1, 0xa8, 0x6f, + 0x1a, 0x36, 0xb1, 0x34, 0xdc, 0x6e, 0x5b, 0xc4, 0xb6, 0x59, 0xa2, 0x58, 0x62, 0xa1, 0x9c, 0x8d, + 0x6f, 0xf1, 0x61, 0xe5, 0xcb, 0xe0, 0xd6, 0x84, 0xa3, 0xbc, 0x50, 0xbc, 0xe4, 0x2b, 0xfe, 0x08, + 0x96, 0x04, 0x7f, 0x3b, 0xa4, 0x7b, 0x9e, 0x6d, 0x5f, 0x1d, 0x3f, 0xca, 0xa3, 0x3a, 0x47, 0x2e, + 0x7b, 0xbc, 0xda, 0x53, 0x4f, 0xa6, 0x76, 0x04, 0x69, 0xa6, 0x94, 0x34, 0xf7, 0x66, 0xb4, 0xfd, + 0x37, 0xb6, 0x15, 0x68, 0x13, 0x2e, 0x31, 0xe1, 0x63, 0xf4, 0x05, 0x46, 0xbf, 0x48, 0x27, 0x0f, + 0x47, 0xb6, 0xef, 0x93, 0x14, 0x2c, 0x8c, 0xa5, 0x59, 0x9e, 0x32, 0xa4, 0x48, 0x65, 0x24, 0x23, + 0x95, 0x91, 0xba, 0xb0, 0x32, 0x84, 0x7d, 0xa4, 0xa7, 0xdb, 0x47, 0xe6, 0x27, 0xb4, 0x8f, 0xec, + 0x93, 0xd9, 0xc7, 0x5f, 0xf5, 0x10, 0xfd, 0x5c, 0x82, 0x6a, 0x7c, 0x6e, 0x1a, 0xb9, 0x1d, 0xcf, + 0xc3, 0x82, 0xb7, 0x14, 0x4f, 0x3c, 0x77, 0xa6, 0xb2, 0x37, 0xe1, 0x5a, 0x46, 0x5c, 0x08, 0x7e, + 0x1a, 0xca, 0x23, 0x99, 0x33, 0x37, 0xff, 0xb9, 0xb3, 0xe0, 0xf7, 0x95, 0xff, 0x4d, 0x79, 0xc1, + 0x2a, 0x94, 0xde, 0x46, 0x9c, 0xf0, 0xb7, 0x61, 0xb1, 0x4d, 0x5a, 0x7a, 0xfb, 0x49, 0x0f, 0xf8, + 0x82, 0xe0, 0xfe, 0xc7, 0xf9, 0x1e, 0xb7, 0x92, 0x9f, 0x15, 0x21, 0xaf, 0x12, 0xdb, 0xa4, 0xe9, + 0x22, 0xda, 0x86, 0x02, 0x19, 0xb6, 0x88, 0xe9, 0xb8, 0x19, 0x76, 0x74, 0x01, 0xc5, 0xa9, 0xeb, + 0x2e, 0x65, 0x23, 0xa1, 0xfa, 0x6c, 0xe8, 0xb6, 0x40, 0x48, 0xe2, 0xc1, 0x0e, 0xc1, 0x1e, 0x84, + 0x48, 0x5e, 0x76, 0x21, 0x92, 0x54, 0x6c, 0xf5, 0xcf, 0xb9, 0x46, 0x30, 0x92, 0xdb, 0x02, 0x23, + 0x49, 0x4f, 0xf9, 0x58, 0x08, 0x24, 0xa9, 0x85, 0x40, 0x92, 0xec, 0x94, 0xdf, 0x8c, 0x41, 0x49, + 0x5e, 0x76, 0x51, 0x92, 0xdc, 0x94, 0x15, 0x8f, 0xc0, 0x24, 0x6f, 0x04, 0x60, 0x92, 0x42, 0x6c, + 0xa9, 0xc6, 0x59, 0x23, 0x70, 0x92, 0xd7, 0x3c, 0x9c, 0xa4, 0x14, 0x5b, 0xa7, 0x09, 0xe6, 0x51, + 0xa0, 0xe4, 0x60, 0x0c, 0x28, 0xe1, 0xc0, 0xc6, 0x33, 0xb1, 0x22, 0xa6, 0x20, 0x25, 0x07, 0x63, + 0x48, 0x49, 0x79, 0x8a, 0xc0, 0x29, 0x50, 0xc9, 0x7f, 0x46, 0x43, 0x25, 0xf1, 0x60, 0x86, 0x58, + 0xe6, 0x6c, 0x58, 0x89, 0x16, 0x83, 0x95, 0xc8, 0xb1, 0x75, 0x3d, 0x17, 0x3f, 0x33, 0x58, 0xf2, + 0x30, 0x02, 0x2c, 0xe1, 0xb0, 0xc6, 0xcd, 0x58, 0xe1, 0x33, 0xa0, 0x25, 0x0f, 0x23, 0xd0, 0x12, + 0x34, 0x55, 0xec, 0x54, 0xb8, 0x64, 0x27, 0x0c, 0x97, 0x2c, 0xc6, 0x64, 0xaa, 0xfe, 0x69, 0x8f, + 0xc1, 0x4b, 0x8e, 0xe3, 0xf0, 0x12, 0x8e, 0x69, 0xbc, 0x10, 0x2b, 0xf1, 0x02, 0x80, 0xc9, 0xc1, + 0x18, 0x60, 0x72, 0x69, 0x8a, 0xa5, 0x4d, 0x41, 0x4c, 0xfe, 0x2d, 0x88, 0x98, 0x2c, 0xc7, 0xc2, + 0xa4, 0xae, 0x07, 0x88, 0x80, 0x4c, 0xde, 0x08, 0x40, 0x26, 0x97, 0xa7, 0x9c, 0xe3, 0xc9, 0x98, + 0x49, 0x46, 0xce, 0xee, 0xa5, 0xf3, 0x79, 0xb9, 0xc0, 0xd1, 0x92, 0xbd, 0x74, 0xbe, 0x28, 0x97, + 0x94, 0xe7, 0x68, 0x0a, 0x35, 0xe2, 0x68, 0x69, 0x81, 0x43, 0x2c, 0xcb, 0xb0, 0x04, 0xfa, 0xc1, + 0x3b, 0xca, 0x4d, 0x5a, 0x43, 0xfb, 0x4e, 0x75, 0x02, 0xbe, 0xc2, 0x0a, 0xc9, 0x80, 0x23, 0x55, + 0x7e, 0x2d, 0xf9, 0xbc, 0x0c, 0x61, 0x09, 0xd6, 0xdf, 0x05, 0x51, 0x7f, 0x07, 0x50, 0x97, 0x64, + 0x18, 0x75, 0x59, 0x85, 0x22, 0x2d, 0x10, 0x47, 0x00, 0x15, 0x6c, 0x7a, 0x80, 0xca, 0x2d, 0x58, + 0x60, 0x11, 0x9b, 0x63, 0x33, 0x22, 0x2e, 0xa6, 0x59, 0x5c, 0x9c, 0xa7, 0x13, 0x7c, 0x7b, 0x78, + 0x80, 0x7c, 0x11, 0x16, 0x03, 0xb4, 0x5e, 0xe1, 0xc9, 0xd1, 0x05, 0xd9, 0xa3, 0xde, 0x12, 0x15, + 0xe8, 0x6f, 0x25, 0x5f, 0x43, 0x3e, 0x12, 0x13, 0x05, 0x9a, 0x48, 0x3f, 0x11, 0x68, 0x92, 0x7c, + 0x62, 0xd0, 0x24, 0x58, 0x48, 0xa7, 0xc2, 0x85, 0xf4, 0x9f, 0x25, 0x7f, 0x4f, 0x3c, 0x08, 0xa4, + 0x65, 0xb4, 0x89, 0x28, 0x6d, 0x59, 0x9b, 0xe6, 0x44, 0x5d, 0xe3, 0x54, 0x14, 0xb0, 0xb4, 0x49, + 0xa9, 0xbc, 0xc8, 0x57, 0x10, 0x81, 0xcd, 0xab, 0x8a, 0x79, 0xe6, 0x21, 0xaa, 0x62, 0x19, 0x52, + 0x8f, 0x09, 0x47, 0xf3, 0x4b, 0x2a, 0x6d, 0x52, 0x3a, 0x66, 0x7c, 0x22, 0x83, 0xe0, 0x1d, 0xf4, + 0x2a, 0x14, 0xd8, 0xb5, 0x89, 0x66, 0x98, 0xb6, 0x40, 0xf0, 0x43, 0xb9, 0x15, 0xbf, 0x5a, 0x59, + 0x3f, 0xa4, 0x34, 0x07, 0xa6, 0xad, 0xe6, 0x4d, 0xd1, 0x0a, 0xa4, 0x3c, 0x85, 0x50, 0xca, 0x73, + 0x0d, 0x0a, 0x74, 0xf5, 0xb6, 0x89, 0x5b, 0xa4, 0x02, 0x6c, 0xa1, 0xfe, 0x80, 0xf2, 0xab, 0x24, + 0xcc, 0x8f, 0x44, 0xba, 0xc8, 0x7f, 0x77, 0x4d, 0x32, 0x19, 0x80, 0x84, 0x66, 0xd3, 0xc7, 0x0a, + 0xc0, 0x29, 0xb6, 0xb5, 0x8f, 0x70, 0xdf, 0x21, 0x6d, 0xa1, 0x94, 0xc0, 0x08, 0xaa, 0x42, 0x9e, + 0xf6, 0x06, 0x36, 0x69, 0x0b, 0x74, 0xca, 0xeb, 0xa3, 0x06, 0x64, 0xc9, 0x19, 0xe9, 0x3b, 0x76, + 0x25, 0xc7, 0xb6, 0x7d, 0x79, 0xbc, 0x86, 0xa7, 0xd3, 0xdb, 0x15, 0xba, 0xd9, 0x7f, 0xfc, 0x76, + 0x55, 0xe6, 0xd4, 0x2f, 0x18, 0x3d, 0xdd, 0x21, 0x3d, 0xd3, 0x39, 0x57, 0x05, 0x7f, 0x58, 0x0b, + 0xf9, 0x11, 0x2d, 0x30, 0x9c, 0xb4, 0xe4, 0x62, 0x12, 0x54, 0xa7, 0xba, 0x61, 0xe9, 0xce, 0xb9, + 0x3a, 0xd7, 0x23, 0x3d, 0xd3, 0x30, 0xba, 0x1a, 0x3f, 0xe3, 0xcf, 0x80, 0x3c, 0xea, 0x8e, 0xa2, + 0x94, 0xa5, 0xdc, 0xf0, 0x75, 0xea, 0x62, 0x4e, 0x63, 0xf9, 0xb4, 0xb2, 0x05, 0xe5, 0x70, 0x96, + 0x80, 0x6e, 0xc0, 0x9c, 0x45, 0x1c, 0xac, 0xf7, 0xb5, 0x50, 0x4a, 0x5f, 0xe2, 0x83, 0xfc, 0x80, + 0xee, 0xa5, 0xf3, 0x92, 0x9c, 0xdc, 0x4b, 0xe7, 0x93, 0x72, 0x4a, 0x39, 0x84, 0x4b, 0x91, 0x59, + 0x02, 0x7a, 0x05, 0x0a, 0x7e, 0x82, 0x21, 0x31, 0xd5, 0x4d, 0xc0, 0x9a, 0x7c, 0x5a, 0xe5, 0x2b, + 0xc9, 0x17, 0x19, 0x46, 0xaf, 0xea, 0x90, 0xb5, 0x88, 0x3d, 0xe8, 0x72, 0x3c, 0xa9, 0xbc, 0xf9, + 0xe2, 0x6c, 0xf9, 0x05, 0x1d, 0x1d, 0x74, 0x1d, 0x55, 0x30, 0x2b, 0xef, 0x41, 0x96, 0x8f, 0xa0, + 0x22, 0xe4, 0x1e, 0xee, 0xdf, 0xdf, 0x3f, 0x78, 0x67, 0x5f, 0x4e, 0x20, 0x80, 0xec, 0x56, 0xad, + 0x56, 0x3f, 0x6c, 0xca, 0x12, 0x2a, 0x40, 0x66, 0x6b, 0xfb, 0x40, 0x6d, 0xca, 0x49, 0x3a, 0xac, + 0xd6, 0xf7, 0xea, 0xb5, 0xa6, 0x9c, 0x42, 0x0b, 0x30, 0xc7, 0xdb, 0xda, 0xce, 0x81, 0xfa, 0xd6, + 0x56, 0x53, 0x4e, 0x07, 0x86, 0x8e, 0xea, 0xfb, 0xf7, 0xea, 0xaa, 0x9c, 0x51, 0xfe, 0x09, 0xae, + 0xc4, 0x66, 0x24, 0x3e, 0x34, 0x25, 0x05, 0xa0, 0x29, 0xe5, 0xf3, 0x24, 0x2d, 0xd1, 0xe2, 0xd2, + 0x0c, 0xb4, 0x37, 0xf2, 0xe3, 0x9b, 0x17, 0xc8, 0x51, 0x46, 0xfe, 0x9e, 0x56, 0x65, 0x16, 0x39, + 0x21, 0x4e, 0xab, 0xc3, 0xd3, 0x1e, 0xee, 0xce, 0xe6, 0xd4, 0x39, 0x31, 0xca, 0x98, 0x6c, 0x4e, + 0xf6, 0x01, 0x69, 0x39, 0x1a, 0xb7, 0x48, 0x9b, 0x95, 0x46, 0x05, 0x4a, 0x46, 0x47, 0x8f, 0xf8, + 0xa0, 0xf2, 0xfe, 0x85, 0x74, 0x59, 0x80, 0x8c, 0x5a, 0x6f, 0xaa, 0xef, 0xca, 0x29, 0x84, 0xa0, + 0xcc, 0x9a, 0xda, 0xd1, 0xfe, 0xd6, 0xe1, 0x51, 0xe3, 0x80, 0xea, 0x72, 0x11, 0xe6, 0x5d, 0x5d, + 0xba, 0x83, 0x19, 0xe5, 0x79, 0xb8, 0x1c, 0x93, 0x23, 0x45, 0x18, 0xf4, 0x2f, 0xa4, 0x20, 0x75, + 0x38, 0xcf, 0x39, 0x80, 0xac, 0xed, 0x60, 0x67, 0x60, 0x0b, 0x25, 0xbe, 0x32, 0x6b, 0xd2, 0xb4, + 0xee, 0x36, 0x8e, 0x18, 0xbb, 0x2a, 0xc4, 0x28, 0x77, 0xa0, 0x1c, 0x9e, 0x89, 0xd7, 0x81, 0x6f, + 0x44, 0x49, 0xe5, 0x2e, 0xa0, 0xf1, 0x5c, 0x2a, 0xa2, 0x58, 0x96, 0xa2, 0x8a, 0xe5, 0x5f, 0x4a, + 0x70, 0x75, 0x42, 0xde, 0x84, 0xde, 0x1e, 0xf9, 0xc9, 0xd7, 0x2e, 0x92, 0x75, 0xad, 0xf3, 0xb1, + 0x91, 0xdf, 0xbc, 0x0d, 0xa5, 0xe0, 0xf8, 0x6c, 0x3f, 0xf9, 0x9b, 0x94, 0x7f, 0x88, 0xc3, 0x55, + 0xbd, 0xef, 0x4f, 0xa5, 0x1f, 0xe9, 0x4f, 0x5f, 0x07, 0x70, 0x86, 0x1a, 0x37, 0x6b, 0x37, 0x28, + 0x3f, 0x15, 0x81, 0xb0, 0x92, 0x56, 0x73, 0x28, 0x0e, 0x41, 0xc1, 0x11, 0x2d, 0x1b, 0x1d, 0x05, + 0x21, 0x8e, 0x01, 0x0b, 0xd8, 0xb6, 0x28, 0xff, 0x67, 0x8d, 0xec, 0x3e, 0x14, 0xc2, 0x87, 0x6d, + 0xf4, 0x2e, 0x5c, 0x1e, 0xc9, 0x3a, 0x3c, 0xd1, 0xe9, 0x59, 0x93, 0x8f, 0x4b, 0xe1, 0xe4, 0xc3, + 0x15, 0x1d, 0x4c, 0x1d, 0x32, 0xa1, 0xd4, 0x01, 0xbd, 0x05, 0x32, 0xc3, 0x05, 0x78, 0xc2, 0xd4, + 0x26, 0x5d, 0xec, 0xde, 0xd9, 0x5f, 0x19, 0x43, 0x17, 0xee, 0x89, 0x17, 0x17, 0x1c, 0x5c, 0xf8, + 0xfc, 0xbb, 0x55, 0x49, 0x2d, 0x53, 0x66, 0xb6, 0x39, 0xf7, 0x28, 0xab, 0xf2, 0x2e, 0x80, 0x8f, + 0x9c, 0x50, 0x87, 0x65, 0x19, 0x83, 0x7e, 0x9b, 0x19, 0x54, 0x46, 0xe5, 0x1d, 0x74, 0x07, 0x32, + 0xd4, 0x30, 0x5d, 0xb5, 0x8f, 0x7b, 0x76, 0x6a, 0x58, 0x01, 0xe4, 0x85, 0x53, 0x2b, 0x3a, 0xa0, + 0x71, 0xc4, 0x3b, 0xe6, 0x13, 0x6f, 0x84, 0x3f, 0x71, 0x3d, 0x16, 0x3b, 0x8f, 0xfe, 0xd4, 0xc7, + 0x90, 0x61, 0x86, 0x44, 0xa3, 0x23, 0xbb, 0xd1, 0x11, 0x99, 0x2c, 0x6d, 0xa3, 0xff, 0x02, 0xc0, + 0x8e, 0x63, 0xe9, 0xc7, 0x03, 0xff, 0x03, 0xab, 0xd1, 0x86, 0xb8, 0xe5, 0xd2, 0x6d, 0x5f, 0x13, + 0x16, 0xb9, 0xe4, 0xb3, 0x06, 0xac, 0x32, 0x20, 0x50, 0xd9, 0x87, 0x72, 0x98, 0xd7, 0xcd, 0xbd, + 0xf8, 0x1a, 0xc2, 0xb9, 0x17, 0x4f, 0xa5, 0x45, 0xee, 0xe5, 0x65, 0x6e, 0x29, 0x7e, 0x6d, 0xc5, + 0x3a, 0xca, 0x7f, 0x27, 0xa1, 0x14, 0xb4, 0xe3, 0xbf, 0xbf, 0xf4, 0x48, 0xf9, 0x3f, 0x09, 0xf2, + 0xde, 0xef, 0x87, 0x2f, 0x96, 0x42, 0x97, 0x7e, 0x5c, 0x7b, 0xc9, 0xe0, 0x6d, 0x10, 0xbf, 0x8d, + 0x4b, 0x79, 0x57, 0x7c, 0x77, 0xbd, 0x68, 0x1a, 0x87, 0x16, 0x05, 0x75, 0x2d, 0xac, 0xca, 0x4d, + 0x1e, 0xee, 0x42, 0xc1, 0x73, 0x06, 0xb4, 0x20, 0x72, 0x51, 0x35, 0x49, 0x1c, 0x49, 0x81, 0x89, + 0x2e, 0x41, 0xc6, 0x34, 0x3e, 0x12, 0x57, 0x4d, 0x29, 0x95, 0x77, 0x94, 0x36, 0xcc, 0x8f, 0x78, + 0x12, 0x74, 0x17, 0x72, 0xe6, 0xe0, 0x58, 0x73, 0x8d, 0x63, 0x04, 0x7b, 0x74, 0x53, 0xed, 0xc1, + 0x71, 0x57, 0x6f, 0xdd, 0x27, 0xe7, 0xee, 0x62, 0xcc, 0xc1, 0xf1, 0x7d, 0x6e, 0x43, 0xfc, 0x2b, + 0xc9, 0xe0, 0x57, 0xfe, 0x5f, 0x82, 0xbc, 0x7b, 0x26, 0xd0, 0xbf, 0x40, 0xc1, 0xf3, 0x52, 0xde, + 0xb5, 0x74, 0xac, 0x7b, 0x13, 0xf2, 0x7d, 0x16, 0xb4, 0xe5, 0xde, 0xa7, 0xeb, 0x6d, 0xed, 0xa4, + 0x8b, 0xb9, 0x2d, 0x95, 0xc3, 0x3a, 0xe3, 0x7e, 0x8c, 0x79, 0x90, 0xdd, 0x7b, 0x3b, 0x5d, 0x7c, + 0xaa, 0x16, 0x19, 0xcf, 0x6e, 0x9b, 0x76, 0x44, 0xa2, 0xf8, 0x27, 0x09, 0xe4, 0xd1, 0x13, 0xfb, + 0xa3, 0x57, 0x37, 0x1e, 0x35, 0x53, 0x11, 0x51, 0x13, 0x6d, 0xc0, 0xa2, 0x47, 0xa1, 0xd9, 0xfa, + 0x69, 0x1f, 0x3b, 0x03, 0x8b, 0x08, 0xb4, 0x16, 0x79, 0x53, 0x47, 0xee, 0xcc, 0xf8, 0x5f, 0x67, + 0x9e, 0xf0, 0xaf, 0x3f, 0x49, 0x42, 0x31, 0x80, 0x1d, 0xa3, 0x7f, 0x0e, 0x38, 0xa3, 0x72, 0x44, + 0xa0, 0x09, 0xd0, 0xfa, 0x57, 0xcc, 0x61, 0x35, 0x25, 0x2f, 0xae, 0xa6, 0x38, 0x84, 0xde, 0x85, + 0xa2, 0xd3, 0x17, 0x86, 0xa2, 0x5f, 0x00, 0xe4, 0x18, 0x0e, 0xee, 0x6a, 0x67, 0x86, 0xa3, 0xf7, + 0x4f, 0x35, 0x6e, 0x86, 0xdc, 0x75, 0xc8, 0x6c, 0xe6, 0x11, 0x9b, 0x38, 0x64, 0x16, 0xf9, 0x3f, + 0x12, 0xe4, 0xbd, 0x2c, 0xfe, 0xa2, 0xb7, 0xc2, 0xcb, 0x90, 0x15, 0x89, 0x2a, 0xbf, 0x16, 0x16, + 0xbd, 0x48, 0xcc, 0xbd, 0x0a, 0xf9, 0x1e, 0x71, 0x30, 0xf3, 0x83, 0x3c, 0x48, 0x7a, 0xfd, 0x5b, + 0xaf, 0x41, 0x31, 0x70, 0x79, 0x4f, 0x5d, 0xe3, 0x7e, 0xfd, 0x1d, 0x39, 0x51, 0xcd, 0x7d, 0xfa, + 0xc5, 0x5a, 0x6a, 0x9f, 0x7c, 0x44, 0x4f, 0xb3, 0x5a, 0xaf, 0x35, 0xea, 0xb5, 0xfb, 0xb2, 0x54, + 0x2d, 0x7e, 0xfa, 0xc5, 0x5a, 0x4e, 0x25, 0x0c, 0x6e, 0xbd, 0x75, 0x1f, 0xe6, 0x47, 0x36, 0x26, + 0x9c, 0x05, 0x21, 0x28, 0xdf, 0x7b, 0x78, 0xf8, 0x60, 0xb7, 0xb6, 0xd5, 0xac, 0x6b, 0x8f, 0x0e, + 0x9a, 0x75, 0x59, 0x42, 0x97, 0x61, 0xf1, 0xc1, 0xee, 0x9b, 0x8d, 0xa6, 0x56, 0x7b, 0xb0, 0x5b, + 0xdf, 0x6f, 0x6a, 0x5b, 0xcd, 0xe6, 0x56, 0xed, 0xbe, 0x9c, 0xdc, 0xfc, 0xb2, 0x04, 0xe9, 0xad, + 0xed, 0xda, 0x2e, 0xaa, 0x41, 0x9a, 0xc1, 0x34, 0x13, 0x1f, 0x0f, 0x56, 0x27, 0x03, 0xe7, 0x68, + 0x07, 0x32, 0x0c, 0xc1, 0x41, 0x93, 0x5f, 0x13, 0x56, 0xa7, 0x20, 0xe9, 0x74, 0x31, 0xec, 0x44, + 0x4e, 0x7c, 0x5e, 0x58, 0x9d, 0x0c, 0xac, 0xa3, 0x07, 0x90, 0x73, 0x0b, 0xf8, 0x69, 0x6f, 0xfe, + 0xaa, 0x53, 0xd1, 0x6e, 0x74, 0x00, 0x79, 0xaf, 0xc4, 0x9d, 0xfa, 0x8c, 0xa9, 0x3a, 0x1d, 0xb6, + 0xa3, 0xcb, 0x73, 0x6b, 0xe1, 0x69, 0xcf, 0x9a, 0xaa, 0x53, 0x41, 0x3c, 0xaa, 0x79, 0x8e, 0xd3, + 0x4c, 0x7e, 0x18, 0x59, 0x9d, 0x72, 0x23, 0x80, 0x76, 0x21, 0x2b, 0x8a, 0xef, 0x29, 0x6f, 0x1d, + 0xab, 0xd3, 0x30, 0x7e, 0xa4, 0x42, 0xc1, 0x47, 0xc0, 0xa6, 0x3f, 0xf7, 0xac, 0xce, 0x70, 0xd9, + 0x81, 0xde, 0x83, 0xb9, 0x70, 0x61, 0x3f, 0xdb, 0x7b, 0xca, 0xea, 0x8c, 0xb7, 0x09, 0x54, 0x7e, + 0xb8, 0xca, 0x9f, 0xed, 0x7d, 0x65, 0x75, 0xc6, 0xcb, 0x05, 0xf4, 0x01, 0x2c, 0x8c, 0x57, 0xe1, + 0xb3, 0x3f, 0xb7, 0xac, 0x5e, 0xe0, 0xba, 0x01, 0xf5, 0x00, 0x45, 0x54, 0xef, 0x17, 0x78, 0x7d, + 0x59, 0xbd, 0xc8, 0xed, 0x03, 0x6a, 0xc3, 0xfc, 0x68, 0x49, 0x3c, 0xeb, 0x6b, 0xcc, 0xea, 0xcc, + 0x37, 0x11, 0xfc, 0x2b, 0xe1, 0x52, 0x7a, 0xd6, 0xd7, 0x99, 0xd5, 0x99, 0x2f, 0x26, 0xd0, 0x43, + 0x80, 0x40, 0x35, 0x3c, 0xc3, 0x6b, 0xcd, 0xea, 0x2c, 0x57, 0x14, 0xc8, 0x84, 0xc5, 0xa8, 0x32, + 0xf9, 0x22, 0x8f, 0x37, 0xab, 0x17, 0xba, 0xb9, 0xa0, 0xf6, 0x1c, 0x2e, 0x78, 0x67, 0x7b, 0xcc, + 0x59, 0x9d, 0xf1, 0x0a, 0x63, 0x7b, 0xeb, 0xeb, 0xef, 0x57, 0xa4, 0x6f, 0xbe, 0x5f, 0x91, 0xfe, + 0xf0, 0xfd, 0x8a, 0xf4, 0xd9, 0x0f, 0x2b, 0x89, 0x6f, 0x7e, 0x58, 0x49, 0xfc, 0xfe, 0x87, 0x95, + 0xc4, 0xbf, 0x3f, 0x7b, 0xaa, 0x3b, 0x9d, 0xc1, 0xf1, 0x7a, 0xcb, 0xe8, 0x6d, 0xb4, 0x8c, 0x1e, + 0x71, 0x8e, 0x4f, 0x1c, 0xbf, 0xe1, 0xbf, 0xd9, 0x3f, 0xce, 0xb2, 0x00, 0x7f, 0xfb, 0x2f, 0x01, + 0x00, 0x00, 0xff, 0xff, 0x95, 0x4a, 0x3f, 0xe6, 0xd3, 0x2f, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -5834,6 +5860,13 @@ func (m *RequestProcessProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if len(m.NextProposerAddress) > 0 { + i -= len(m.NextProposerAddress) + copy(dAtA[i:], m.NextProposerAddress) + i = encodeVarintTypes(dAtA, i, uint64(len(m.NextProposerAddress))) + i-- + dAtA[i] = 0x4a + } if len(m.ProposerAddress) > 0 { i -= len(m.ProposerAddress) copy(dAtA[i:], m.ProposerAddress) @@ -7314,6 +7347,14 @@ func (m *ResponseFinalizeBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + n53, err53 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.NextBlockDelay, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.NextBlockDelay):]) + if err53 != nil { + return 0, err53 + } + i -= n53 + i = encodeVarintTypes(dAtA, i, uint64(n53)) + i-- + dAtA[i] = 0x32 if len(m.AppHash) > 0 { i -= len(m.AppHash) copy(dAtA[i:], m.AppHash) @@ -7871,12 +7912,12 @@ func (m *Misbehavior) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x28 } - n58, err58 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err58 != nil { - return 0, err58 + n59, err59 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) + if err59 != nil { + return 0, err59 } - i -= n58 - i = encodeVarintTypes(dAtA, i, uint64(n58)) + i -= n59 + i = encodeVarintTypes(dAtA, i, uint64(n59)) i-- dAtA[i] = 0x22 if m.Height != 0 { @@ -8486,6 +8527,10 @@ func (m *RequestProcessProposal) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + l = len(m.NextProposerAddress) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -9188,6 +9233,8 @@ func (m *ResponseFinalizeBlock) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.NextBlockDelay) + n += 1 + l + sovTypes(uint64(l)) return n } @@ -12111,6 +12158,40 @@ func (m *RequestProcessProposal) Unmarshal(dAtA []byte) error { m.ProposerAddress = []byte{} } iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextProposerAddress", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NextProposerAddress = append(m.NextProposerAddress[:0], dAtA[iNdEx:postIndex]...) + if m.NextProposerAddress == nil { + m.NextProposerAddress = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -15895,6 +15976,39 @@ func (m *ResponseFinalizeBlock) Unmarshal(dAtA []byte) error { m.AppHash = []byte{} } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextBlockDelay", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.NextBlockDelay, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/blocksync/reactor.go b/blocksync/reactor.go index 9060b569b72..1c5a890a561 100644 --- a/blocksync/reactor.go +++ b/blocksync/reactor.go @@ -297,7 +297,7 @@ func (r *Reactor) respondToPeer(msg *bcproto.BlockRequest, src p2p.Peer) { } var extCommit *types.ExtendedCommit - if state.ConsensusParams.ABCI.VoteExtensionsEnabled(msg.Height) { + if state.ConsensusParams.Feature.VoteExtensionsEnabled(msg.Height) { extCommit = r.store.LoadBlockExtendedCommit(msg.Height) if extCommit == nil { r.Logger.Error("Found block in store with no extended commit", "block", block) @@ -456,7 +456,7 @@ FOR_LOOP: // missingExtension := true if state.LastBlockHeight == 0 || - !state.ConsensusParams.ABCI.VoteExtensionsEnabled(state.LastBlockHeight) || + !state.ConsensusParams.Feature.VoteExtensionsEnabled(state.LastBlockHeight) || blocksSynced > 0 || initialCommitHasExtensions { missingExtension = false @@ -551,7 +551,7 @@ FOR_LOOP: // vote extension validations presentExtCommit := extCommit != nil - extensionsEnabled := state.ConsensusParams.ABCI.VoteExtensionsEnabled(first.Height) + extensionsEnabled := state.ConsensusParams.Feature.VoteExtensionsEnabled(first.Height) if presentExtCommit != extensionsEnabled { err = fmt.Errorf("non-nil extended commit must be received iff vote extensions are enabled for its height "+ diff --git a/blocksync/reactor_test.go b/blocksync/reactor_test.go index 355c9857a82..81fba64edf4 100644 --- a/blocksync/reactor_test.go +++ b/blocksync/reactor_test.go @@ -50,7 +50,7 @@ func genesisDocWithValsPowers(powers []int64) (*types.GenesisDoc, []types.PrivVa sort.Sort(types.PrivValidatorsByAddress(privValidators)) consPar := types.DefaultConsensusParams() - consPar.ABCI.VoteExtensionsEnableHeight = 1 + consPar.Feature.VoteExtensionsEnableHeight = 1 return &types.GenesisDoc{ GenesisTime: cmttime.Now(), ChainID: test.DefaultTestChainID, @@ -161,12 +161,18 @@ func newReactor( // let's add some blocks in for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { - voteExtensionIsEnabled := genDoc.ConsensusParams.ABCI.VoteExtensionsEnabled(blockHeight) + voteExtensionIsEnabled := genDoc.ConsensusParams.Feature.VoteExtensionsEnabled(blockHeight) lastExtCommit := seenExtCommit.Clone() thisBlock, err := state.MakeBlock(blockHeight, nil, lastExtCommit.ToCommit(), nil, state.Validators.Proposer.Address) require.NoError(t, err) + if options.deterministicVoteTimes { + // Under PBTS the proposer stamps the block with its wall clock; + // pin it so independently constructed test chains with the same + // genesis produce identical block IDs and LastBlockID links. + thisBlock.Time = genDoc.GenesisTime.Add(time.Duration(blockHeight) * time.Second) + } thisParts, err := thisBlock.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) @@ -432,7 +438,7 @@ func ExtendedCommitNetworkHelper(t *testing.T, maxBlockHeight int64, enableVoteE config = test.ResetTestRoot("blocksync_reactor_test") defer os.RemoveAll(config.RootDir) genDoc, privVals := genesisDocWithValsPowers(valPowers) - genDoc.ConsensusParams.ABCI.VoteExtensionsEnableHeight = enableVoteExtensionAt + genDoc.ConsensusParams.Feature.VoteExtensionsEnableHeight = enableVoteExtensionAt reactorPairs := make([]ReactorPair, 1, 2) reactorPairs[0] = newReactor(t, log.TestingLogger(), genDoc, privVals, 0) @@ -554,7 +560,7 @@ func (bcR *ByzantineReactor) respondToPeer(msg *bcproto.BlockRequest, src p2p.Pe return false } var extCommit *types.ExtendedCommit - voteExtensionEnabled := state.ConsensusParams.ABCI.VoteExtensionsEnabled(msg.Height) + voteExtensionEnabled := state.ConsensusParams.Feature.VoteExtensionsEnabled(msg.Height) incorrectBlock := bcR.corruptedBlock == msg.Height if voteExtensionEnabled && !incorrectBlock || !voteExtensionEnabled && incorrectBlock { extCommit = bcR.store.LoadBlockExtendedCommit(msg.Height) diff --git a/buf.gen.yaml b/buf.gen.yaml index a36032410ce..a05ed1c0895 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -5,5 +5,6 @@ plugins: opt: - Mgoogle/protobuf/timestamp.proto=github.com/cosmos/gogoproto/types - Mgoogle/protobuf/duration.proto=github.com/golang/protobuf/ptypes/duration + - Mgoogle/protobuf/wrappers.proto=github.com/cosmos/gogoproto/types - plugins=grpc - paths=source_relative diff --git a/consensus/aggregation_catchup_test.go b/consensus/aggregation_catchup_test.go new file mode 100644 index 00000000000..4556ace6c99 --- /dev/null +++ b/consensus/aggregation_catchup_test.go @@ -0,0 +1,328 @@ +//go:build bls12381 + +package consensus + +import ( + "context" + "fmt" + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + + dbm "github.com/cometbft/cometbft-db" + + abci "github.com/cometbft/cometbft/abci/types" + cstypes "github.com/cometbft/cometbft/consensus/types" + "github.com/cometbft/cometbft/crypto/bls12381" + "github.com/cometbft/cometbft/internal/test" + "github.com/cometbft/cometbft/libs/log" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" +) + +// blsConsensusNetFromGenesis is like blsConsensusNet but builds the network from +// a CALLER-SUPPLIED genesis doc + priv validators, so a separate late-joiner +// node can be bootstrapped from the very same genesis and catch up. +func blsConsensusNetFromGenesis( + t *testing.T, + genDoc *types.GenesisDoc, + privVals []types.PrivValidator, + testName string, +) ([]*State, cleanupFunc) { + t.Helper() + n := len(privVals) + css := make([]*State, n) + logger := consensusLogger() + configRootDirs := make([]string, 0, n) + for i := 0; i < n; i++ { + stateDB := dbm.NewMemDB() + stateStore := sm.NewStore(stateDB, sm.StoreOptions{DiscardABCIResponses: false}) + state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc) + require.NoError(t, err) + + thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) + configRootDirs = append(configRootDirs, thisConfig.RootDir) + ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0o700) + + app := newKVStore() + vals := types.TM2PB.ValidatorUpdates(state.Validators) + _, err = app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) + + css[i] = newStateWithConfigAndBlockStore(thisConfig, state, privVals[i], app, stateDB) + css[i].SetTimeoutTicker(newMockTickerFunc(false)()) + css[i].SetLogger(logger.With("validator", i, "module", "consensus")) + } + return css, func() { + for _, dir := range configRootDirs { + os.RemoveAll(dir) + } + } +} + +// TestAggregationCatchUpViaAddCommit proves the late-joiner / catch-up path for +// aggregated commits: a node that is behind ingests a whole aggregated Commit +// (the CommitMessage gossip payload) via AddCommit, fetches the committed block +// via block-part gossip, and finalizes the height with that aggregated commit in +// its store — all of which then verifies. +// +// It drives the exact load-bearing state-machine path the reactor's +// CommitMessage handler invokes (AddCommit -> enterCommit -> block parts -> +// tryFinalizeCommit -> finalizeCommit), feeding the messages a lagging node would +// receive over the wire. +func TestAggregationCatchUpViaAddCommit(t *testing.T) { + const N = 4 + + // One shared genesis for both the producers and the late joiner. + genDoc, privVals := blsGenesisDoc(N, 10, blsConsensusParams()) + require.Equal(t, test.DefaultTestChainID, genDoc.ChainID) + + // ---- Producer network: produce a real aggregated commit. ---- + prodCss, cleanup := blsConsensusNetFromGenesis(t, genDoc, privVals, "consensus_agg_catchup_prod") + defer cleanup() + require.True(t, prodCss[0].state.Validators.AllKeysHaveSameType()) + require.Equal(t, bls12381.KeyType, prodCss[0].state.Validators.Validators[0].PubKey.Type()) + + reactors, blocksSubs, eventBuses := startConsensusNet(t, prodCss, N) + for j := 0; j < N; j++ { + go func(j int) { + for range blocksSubs[j].Out() { + } + }(j) + } + + const producedHeight = int64(3) + deadline := time.Now().Add(60 * time.Second) + for prodCss[0].blockStore.Height() < producedHeight { + if time.Now().After(deadline) { + stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + t.Fatalf("producer network did not reach height %d", producedHeight) + } + time.Sleep(50 * time.Millisecond) + } + + const catchUpHeight = int64(1) + pbs := prodCss[0].blockStore + block := pbs.LoadBlock(catchUpHeight) + require.NotNil(t, block) + blockParts, err := block.MakePartSet(types.BlockPartSizeBytes) + require.NoError(t, err) + commit := pbs.LoadBlockCommit(catchUpHeight) + require.NotNil(t, commit) + require.True(t, commit.HasAggregatedSignature(), + "producer commit at height %d is not aggregated", catchUpHeight) + + stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + + // GetState copies under the lock, avoiding a race with any state updates + // still in flight on the receive routine. + prodState := prodCss[0].GetState() + chainID := prodState.ChainID + genValSet := prodState.Validators.Copy() + + // ---- Late joiner: a fresh node bootstrapped from the same genesis. ---- + lateDB := dbm.NewMemDB() + lateStateStore := sm.NewStore(lateDB, sm.StoreOptions{DiscardABCIResponses: false}) + lateState, err := lateStateStore.LoadFromDBOrGenesisDoc(genDoc) + require.NoError(t, err) + require.EqualValues(t, 0, lateState.LastBlockHeight, "late joiner must start at genesis") + + lateConfig := ResetConfig("consensus_agg_catchup_late") + t.Cleanup(func() { _ = os.RemoveAll(lateConfig.RootDir) }) + ensureDir(filepath.Dir(lateConfig.Consensus.WalFile()), 0o700) + + app := newKVStore() + vals := types.TM2PB.ValidatorUpdates(lateState.Validators) + _, err = app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) + + // The late joiner uses a fresh non-validator BLS key: it is catching up, not + // participating, so it must never propose (which would race the catch-up and + // try to finalize its own block). + nonValKey, err := bls12381.GenPrivKey() + require.NoError(t, err) + latePV := types.NewMockPVWithParams(nonValKey, false, false) + late := newStateWithConfigAndBlockStore(lateConfig, lateState, latePV, app, lateDB) + late.SetTimeoutTicker(newMockTickerFunc(true)()) + late.SetLogger(log.TestingLogger().With("module", "consensus", "node", "late")) + + require.NoError(t, late.Start()) + defer func() { _ = late.Stop() }() + + // Give the state machine a moment to settle at height 1. + require.Eventually(t, func() bool { + late.mtx.RLock() + defer late.mtx.RUnlock() + return late.Height == catchUpHeight + }, 5*time.Second, 20*time.Millisecond, "late joiner never reached height 1") + + // Before feeding the real commit, exercise AddCommit's rejection paths. A + // lagging node adopts a block on the strength of this check alone, so the + // gate must reject any commit it cannot verify. + + // Forged aggregated signature: flip a byte in the aggregate so BLS + // verification fails. Expect an error and no adoption. + forged := commit.Clone() + for i, s := range forged.Signatures { + if len(s.Signature) > 0 { + bad := make([]byte, len(s.Signature)) + copy(bad, s.Signature) + bad[0] ^= 0xff + forged.Signatures[i].Signature = bad + break + } + } + late.mtx.Lock() + rejAdded, rejErr := late.AddCommit(forged, "byzantine-peer") + late.mtx.Unlock() + require.Error(t, rejErr, "AddCommit accepted a forged aggregated signature") + require.False(t, rejAdded, "forged-signature commit must not be added") + + // Wrong block: the aggregate was signed over a different BlockID, so + // verification must fail. + wrongBlock := commit.Clone() + badHash := make([]byte, len(commit.BlockID.Hash)) + copy(badHash, commit.BlockID.Hash) + badHash[0] ^= 0xff + wrongBlock.BlockID.Hash = badHash + late.mtx.Lock() + rejAdded, rejErr = late.AddCommit(wrongBlock, "byzantine-peer") + late.mtx.Unlock() + require.Error(t, rejErr, "AddCommit accepted a commit for the wrong block") + require.False(t, rejAdded, "wrong-block commit must not be added") + + // Non-aggregated commit: this fork only adopts aggregated commits via catch-up, + // so a non-aggregated one is ignored (dropped, but not treated as an error). + nonAgg := &types.Commit{Height: catchUpHeight, Round: commit.Round, BlockID: commit.BlockID} + require.False(t, nonAgg.HasAggregatedSignature(), "test setup: nonAgg must not be aggregated") + late.mtx.Lock() + rejAdded, rejErr = late.AddCommit(nonAgg, "byzantine-peer") + late.mtx.Unlock() + require.NoError(t, rejErr, "non-aggregated commit should be ignored, not error") + require.False(t, rejAdded, "non-aggregated commit must not be added") + + // None of the rejected commits may have advanced the node or polluted round state. + late.mtx.RLock() + require.Nil(t, late.Votes.GetCommit(commit.Round), + "a rejected commit must not be stored in the HeightVoteSet") + require.Equal(t, catchUpHeight, late.Height, + "late joiner must still be at the catch-up height after rejections") + late.mtx.RUnlock() + + // Feed the aggregated commit through the real ingestion path, a + // CommitMessage on the peer message queue handled by the receive routine + // (handleMsg -> AddCommit). AddCommit is the load-bearing catch-up entry + // point: it + // (a) rejects non-aggregated commits, + // (b) VERIFIES the aggregated commit against the local validator set + // (routing through verifyAggregatedCommit), and + // (c) stores it in the HeightVoteSet and enters the commit step. + // A lagging validator uses this to adopt a block it never voted on. + // State-mutating calls must run on the receive routine's goroutine (the + // unlocked RoundState read in receiveRoutine assumes a single writer), so + // the test must not call AddCommit directly here. + late.peerMsgQueue <- msgInfo{&CommitMessage{commit}, "producer-peer", cmttime.Now()} + require.Eventually(t, func() bool { + late.mtx.RLock() + defer late.mtx.RUnlock() + return late.Votes.GetCommit(commit.Round) != nil + }, 5*time.Second, 20*time.Millisecond, + "aggregated commit was not added by the late joiner") + + // The aggregated commit is now adopted by the late joiner: it is stored in the + // round's HeightVoteSet, is aggregated, and verifies against the validator set. + late.mtx.RLock() + stored := late.Votes.GetCommit(commit.Round) + step := late.Step + late.mtx.RUnlock() + require.NotNil(t, stored, "late joiner did not adopt the aggregated commit via AddCommit") + require.True(t, stored.HasAggregatedSignature(), + "adopted commit is not aggregated: %v", stored) + require.Equal(t, commit.BlockID, stored.BlockID, "adopted commit has wrong BlockID") + require.NoError(t, + genValSet.VerifyCommit(chainID, stored.BlockID, catchUpHeight, stored), + "late joiner's adopted aggregated commit failed VerifyCommit") + + // AddCommit drove the node into the commit step for the caught-up height, + // waiting only for the committed block (delivered via block-part gossip). + require.GreaterOrEqual(t, step, cstypes.RoundStepCommit, + "AddCommit did not advance the late joiner into the commit step (step=%v)", step) + + t.Logf("late joiner adopted aggregated commit for height %d via AddCommit "+ + "(verified, commit step reached); blockParts available=%d", + catchUpHeight, blockParts.Total()) + + // ---- Deliver the committed block via block-part gossip and finalize. ---- + // AddCommit -> enterCommit set up cs.ProposalBlockParts from the commit's + // part-set header (the node has the commit but not yet the block). Feeding the + // parts completes the block, which fires handleCompleteProposal -> + // tryFinalizeCommit -> finalizeCommit. finalizeCommit's GetCommit branch saves + // the aggregated seen-commit, and updateToState sets LastCommit = *Commit + // (the aggregated commit; individual precommit votes cannot be reconstructed) + // and advances the node to the next height. This is the catch-up *advance* + // the VoteSetReader/LastCommit change unblocks. + // Deliver the parts on the peer message queue. The message loop runs + // addProposalBlockPart, then (once complete) handleCompleteProposal, which + // in the commit step calls tryFinalizeCommit -> finalizeCommit. + for i := 0; i < int(blockParts.Total()); i++ { + part := blockParts.GetPart(i) + late.peerMsgQueue <- msgInfo{ + &BlockPartMessage{Height: catchUpHeight, Round: commit.Round, Part: part}, + "producer-peer", + cmttime.Now(), + } + } + + // The caught-up node now finalizes the height and ADVANCES past it. This is + // the gap the inventory's round 4 left open: with LastCommit as a concrete + // *VoteSet the node could adopt the aggregated commit (reach commit step) but + // not advance, since votesFromSeenCommit cannot reconstruct the individual + // precommits of an aggregated commit. With LastCommit as a VoteSetReader and + // *Commit implementing it, updateToState keeps the *Commit and advances. + const nextHeight = catchUpHeight + 1 + require.Eventually(t, func() bool { + late.mtx.RLock() + defer late.mtx.RUnlock() + return late.Height == nextHeight + }, 10*time.Second, 20*time.Millisecond, + "caught-up node did not advance past the adopted aggregated commit "+ + "(still at height %d, expected %d)", func() int64 { + late.mtx.RLock() + defer late.mtx.RUnlock() + return late.Height + }(), nextHeight) + + // The committed block is now in the late joiner's own block store, and the + // stored seen-commit for the caught-up height is the same aggregated commit. + // (LoadBlockCommit(h) lives in block h+1's LastCommit, which does not exist + // yet; the locally-justified seen commit is what finalizeCommit saved.) + storedCommit := late.blockStore.LoadSeenCommit(catchUpHeight) + require.NotNil(t, storedCommit, "caught-up node did not store the committed block") + require.True(t, storedCommit.HasAggregatedSignature(), + "stored commit at caught-up height is not aggregated") + require.NoError(t, + genValSet.VerifyCommit(chainID, storedCommit.BlockID, catchUpHeight, storedCommit), + "caught-up node's stored aggregated commit failed VerifyCommit") + require.Equal(t, catchUpHeight, late.blockStore.Height(), + "caught-up node block store height mismatch") + + // And LastCommit is the adopted aggregated *Commit (the VoteSetReader path), + // not a reconstructed VoteSet. + late.mtx.RLock() + lastCommit := late.LastCommit + late.mtx.RUnlock() + asCommit, ok := lastCommit.(*types.Commit) + require.True(t, ok, + "LastCommit after aggregated catch-up should be a *types.Commit, got %T", lastCommit) + require.True(t, asCommit.HasAggregatedSignature(), + "LastCommit *Commit is not aggregated") + require.Equal(t, commit.BlockID, asCommit.BlockID, + "LastCommit *Commit has wrong BlockID") + + t.Logf("late joiner finalized height %d via aggregated commit and advanced to height %d "+ + "(LastCommit is the adopted aggregated *Commit)", catchUpHeight, nextHeight) +} diff --git a/consensus/aggregation_pbts_test.go b/consensus/aggregation_pbts_test.go new file mode 100644 index 00000000000..097e54e68d0 --- /dev/null +++ b/consensus/aggregation_pbts_test.go @@ -0,0 +1,188 @@ +//go:build bls12381 + +package consensus + +import ( + "context" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cometbft/cometbft/abci/example/kvstore" + "github.com/cometbft/cometbft/crypto/bls12381" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" +) + +// blsConsensusParamsWithSynchrony returns BLS/PBTS consensus params with tight +// synchrony bounds, so the timeliness window is small enough that an +// intentionally-skewed proposal timestamp lands clearly out of window. +func blsConsensusParamsWithSynchrony(precision, msgDelay time.Duration) *types.ConsensusParams { + c := blsConsensusParams() + c.Synchrony = types.SynchronyParams{ + Precision: precision, + MessageDelay: msgDelay, + } + return c +} + +// blsConsensusState builds a single observed consensus State plus validator +// stubs, all using bls12_381 keys with PBTS enabled. The observed State is +// privVals[0]; the returned stubs cover ALL validators (index 0 is the observed +// validator itself), mirroring randState's contract so callers can drive the +// network by hand. +func blsConsensusState(t *testing.T, nValidators int, consensusParams *types.ConsensusParams) (*State, []*validatorStub) { + t.Helper() + genDoc, privVals := blsGenesisDoc(nValidators, 10, consensusParams) + state, err := sm.MakeGenesisState(genDoc) + require.NoError(t, err) + + thisConfig := ResetConfig("blsConsensusState") + t.Cleanup(func() { _ = os.RemoveAll(thisConfig.RootDir) }) + + app := kvstore.NewInMemoryApplication() + cs := newStateWithConfig(thisConfig, state, privVals[0], app) + cs.SetTimeoutTicker(newMockTickerFunc(true)()) + + vss := make([]*validatorStub, nValidators) + for i := 0; i < nValidators; i++ { + vss[i] = newValidatorStub(privVals[i], int32(i)) + } + return cs, vss +} + +// proposerStubForRound returns the validator stub whose privval is the proposer +// for the given round in cs's validator set, along with its index. It mirrors +// the increment that enterNewRound applies (IncrementProposerPriority by +// round-cs.Round on the live round-state valset) so the chosen proposer matches +// the one cs will verify the proposal signature against. +func proposerStubForRound(cs *State, vss []*validatorStub, round int32) (*validatorStub, int) { + base := cs.Validators.Copy() + if cs.Round < round { + base.IncrementProposerPriority(round - cs.Round) + } + propAddr := base.GetProposer().Address + for i, vs := range vss { + pub, err := vs.GetPubKey() + if err != nil { + continue + } + if pub.Address().String() == propAddr.String() { + return vs, i + } + } + return nil, -1 +} + +// TestAggregationPBTSRejectsUntimelyProposal proves the PBTS timeliness gate +// actually rejects: in a BLS+PBTS network, when the proposer delivers a block +// whose timestamp is far outside the synchrony window relative to the receiver's +// clock, the observed validator prevotes NIL rather than for the block. A +// control case with an in-window timestamp prevotes for the block, proving the +// gate is selective and not simply always-nil. +func TestAggregationPBTSRejectsUntimelyProposal(t *testing.T) { + testCases := []struct { + name string + // offset applied to BOTH the proposed block time and the proposal + // timestamp, relative to "now" (the receiver's clock at prevote time). + // Positive = future, negative = past. + tsOffset time.Duration + expectNilVote bool + }{ + { + name: "timely_proposal_prevotes_for_block", + tsOffset: 0, // proposal time == now, clearly inside window + expectNilVote: false, + }, + { + name: "future_proposal_prevotes_nil", + tsOffset: 1 * time.Hour, // way beyond MessageDelay+Precision + expectNilVote: true, + }, + { + name: "past_proposal_prevotes_nil", + tsOffset: -1 * time.Hour, // way before now-Precision + expectNilVote: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + // Tight synchrony: 10ms precision, 50ms message delay. The 1h skew in + // the negative cases dwarfs this window; the 0 offset sits inside it. + cp := blsConsensusParamsWithSynchrony(10*time.Millisecond, 50*time.Millisecond) + cs, vss := blsConsensusState(t, 4, cp) + + // Sanity: the aggregation path is in play (all BLS keys). + require.True(t, cs.state.Validators.AllKeysHaveSameType()) + pk0, err := vss[0].GetPubKey() + require.NoError(t, err) + require.Equal(t, bls12381.KeyType, pk0.Type()) + + height, round := cs.Height, cs.Round + + proposalCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal) + voteCh := subscribe(cs.eventBus, types.EventQueryVote) + + // Build the proposal block from the observed state, then have a + // DIFFERENT validator (a stub) act as proposer so the observed + // validator must evaluate timeliness on a received proposal rather + // than proposing itself. + cs.mtx.Lock() + propBlock, err := cs.createProposalBlock(ctx) + cs.mtx.Unlock() + require.NoError(t, err) + + // Make a stub the proposer for round 1 by advancing the round. + round++ + incrementRound(vss[1:]...) + prop, propIdx := proposerStubForRound(cs, vss, round) + require.NotNil(t, prop, "could not find proposer stub for round %d", round) + require.NotEqual(t, 0, propIdx, "observed validator must not be the proposer") + + // Stamp the block time and proposal timestamp with the skew, relative + // to the genesis time. At InitialHeight, ValidateBlock requires the + // block time to equal genesis time, so the timely (offset 0) control + // uses genesis time exactly. The timeliness gate runs BEFORE + // ValidateBlock, so the out-of-window cases are rejected by PBTS first. + // Both the block time and the proposal timestamp must be equal (the + // gate first requires Proposal.Timestamp == block.Time), so we set both + // to the same skewed value and exercise the timeliness check. + genesisTime := cs.state.LastBlockTime // == genesis time at InitialHeight + skewed := cmttime.Canonical(genesisTime.Add(tc.tsOffset)) + propBlock.Time = skewed + + // Set the proposer address in the header to the chosen proposer. + ppk, err := prop.GetPubKey() + require.NoError(t, err) + propBlock.ProposerAddress = ppk.Address() + + propBlockParts, err := propBlock.MakePartSet(types.BlockPartSizeBytes) + require.NoError(t, err) + blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} + + proposal := types.NewProposal(height, round, -1, blockID, propBlock.Time) + pp := proposal.ToProto() + require.NoError(t, prop.SignProposal(cs.state.ChainID, pp)) + proposal.Signature = pp.Signature + + require.NoError(t, cs.SetProposalAndBlock(proposal, propBlock, propBlockParts, "peerID")) + + startTestRound(cs, height, round) + + ensureNewProposal(proposalCh, height, round) + ensurePrevote(voteCh, height, round) + + if tc.expectNilVote { + validatePrevote(t, cs, round, vss[0], nil) + } else { + validatePrevote(t, cs, round, vss[0], propBlock.Hash()) + } + }) + } +} diff --git a/consensus/aggregation_reactor_test.go b/consensus/aggregation_reactor_test.go new file mode 100644 index 00000000000..1aac88699bd --- /dev/null +++ b/consensus/aggregation_reactor_test.go @@ -0,0 +1,205 @@ +//go:build bls12381 + +package consensus + +import ( + "context" + "fmt" + "os" + "path/filepath" + "sort" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" + + dbm "github.com/cometbft/cometbft-db" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/crypto/bls12381" + "github.com/cometbft/cometbft/internal/test" + "github.com/cometbft/cometbft/libs/log" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" +) + +// blsConsensusParams returns consensus params suitable for BLS aggregation: +// PBTS enabled from height 1 (required, since aggregation strips the per-vote +// timestamp so every validator signs identical bytes) and vote extensions +// disabled (the aggregated-commit path does not support extensions). +func blsConsensusParams() *types.ConsensusParams { + c := types.DefaultConsensusParams() + c.Feature.VoteExtensionsEnableHeight = 0 // disabled + c.Feature.VoteExtensionsEnableHeight = 0 + c.Feature.PbtsEnableHeight = 1 // PBTS on from genesis + return c +} + +// blsGenesisDoc builds a genesis doc whose validators all use bls12_381 keys, +// returning the matching MockPV priv validators (sorted by address, as the +// harness expects). +func blsGenesisDoc(numValidators int, votingPower int64, consensusParams *types.ConsensusParams) (*types.GenesisDoc, []types.PrivValidator) { + validators := make([]types.GenesisValidator, numValidators) + privValidators := make([]types.PrivValidator, numValidators) + for i := 0; i < numValidators; i++ { + privKey, err := bls12381.GenPrivKey() + if err != nil { + panic(fmt.Errorf("could not generate BLS priv key: %w", err)) + } + privVal := types.NewMockPVWithParams(privKey, false, false) + pubKey, err := privVal.GetPubKey() + if err != nil { + panic(err) + } + validators[i] = types.GenesisValidator{ + PubKey: pubKey, + Power: votingPower, + } + privValidators[i] = privVal + } + sort.Sort(types.PrivValidatorsByAddress(privValidators)) + + return &types.GenesisDoc{ + GenesisTime: cmttime.Now(), + InitialHeight: 1, + ChainID: test.DefaultTestChainID, + Validators: validators, + ConsensusParams: consensusParams, + }, privValidators +} + +// blsConsensusNet spins up nValidators in-process consensus States, all using +// BLS keys, with PBTS enabled. Mirrors randConsensusNet but with a BLS genesis. +func blsConsensusNet(t *testing.T, nValidators int, testName string) ([]*State, cleanupFunc) { + t.Helper() + genDoc, privVals := blsGenesisDoc(nValidators, 10, blsConsensusParams()) + css := make([]*State, nValidators) + logger := consensusLogger() + configRootDirs := make([]string, 0, nValidators) + for i := 0; i < nValidators; i++ { + stateDB := dbm.NewMemDB() + stateStore := sm.NewStore(stateDB, sm.StoreOptions{DiscardABCIResponses: false}) + state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc) + require.NoError(t, err) + thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) + configRootDirs = append(configRootDirs, thisConfig.RootDir) + ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0o700) + app := newKVStore() + vals := types.TM2PB.ValidatorUpdates(state.Validators) + _, err = app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) + + css[i] = newStateWithConfigAndBlockStore(thisConfig, state, privVals[i], app, stateDB) + css[i].SetTimeoutTicker(newMockTickerFunc(false)()) + css[i].SetLogger(logger.With("validator", i, "module", "consensus")) + } + return css, func() { + for _, dir := range configRootDirs { + os.RemoveAll(dir) + } + } +} + +// TestReactorAggregatedCommits is the win-condition test: a multi-validator +// network using BLS keys + PBTS produces aggregated commits (BlockIDFlagAgg*) +// over the real network/consensus path, makes progress across several heights +// (liveness), and the aggregated commits verify via VerifyCommit (which routes +// through verifyAggregatedCommit). +func TestReactorAggregatedCommits(t *testing.T) { + N := 4 + css, cleanup := blsConsensusNet(t, N, "consensus_agg_reactor_test") + defer cleanup() + + // Sanity: all validators carry BLS keys, so the aggregation path is taken. + require.True(t, css[0].state.Validators.AllKeysHaveSameType()) + require.Equal(t, bls12381.KeyType, css[0].state.Validators.Validators[0].PubKey.Type(), + "validators must use bls12381 keys") + + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N) + defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + + // Wait until every validator commits several blocks (liveness). We drain the + // new-block subscriptions continuously in the background so the event bus + // never back-pressures and stalls consensus, while tracking the max height + // each validator has reached. + const targetHeight = 4 + heights := make([]int64, N) + var hmtx sync.Mutex + for j := 0; j < N; j++ { + go func(j int) { + for msg := range blocksSubs[j].Out() { + if nb, ok := msg.Data().(types.EventDataNewBlock); ok { + hmtx.Lock() + if nb.Block.Height > heights[j] { + heights[j] = nb.Block.Height + } + hmtx.Unlock() + } + } + }(j) + } + + deadline := time.Now().Add(60 * time.Second) + for { + hmtx.Lock() + min := int64(1 << 62) + for _, h := range heights { + if h < min { + min = h + } + } + hmtx.Unlock() + if min >= targetHeight { + break + } + if time.Now().After(deadline) { + t.Fatalf("liveness stall: heights=%v, did not all reach %d", heights, targetHeight) + } + time.Sleep(50 * time.Millisecond) + } + + // Give the last commit a moment to be persisted everywhere. + time.Sleep(500 * time.Millisecond) + + // Assert the real properties on every validator's block store. + // The network is still running, so read consensus state via GetState(), + // which copies under the lock. + chainID := css[0].GetState().ChainID + checkedAgg := 0 + for i := 0; i < N; i++ { + bs := css[i].blockStore + valSet := css[i].GetState().Validators // genesis valset (unchanged across these heights) + require.GreaterOrEqual(t, bs.Height(), int64(targetHeight), + "validator %d only reached height %d", i, bs.Height()) + + // For each committed height, the seen commit must be aggregated and verify. + for h := int64(1); h <= bs.Height()-1; h++ { + block := bs.LoadBlock(h) + require.NotNil(t, block, "validator %d missing block %d", i, h) + + // The commit stored at h+1's LastCommit / seen commit for h. + commit := bs.LoadBlockCommit(h) + if commit == nil { + commit = bs.LoadSeenCommit(h) + } + require.NotNil(t, commit, "validator %d missing commit for height %d", i, h) + + require.True(t, commit.HasAggregatedSignature(), + "validator %d commit at height %d is NOT aggregated: %v", i, h, commit) + + // The commit's BlockID must match the block it commits. + require.Equal(t, block.Hash().Bytes(), commit.BlockID.Hash.Bytes(), + "validator %d commit blockID mismatch at height %d", i, h) + + err := valSet.VerifyCommit(chainID, commit.BlockID, h, commit) + require.NoError(t, err, + "validator %d aggregated commit at height %d failed VerifyCommit", i, h) + checkedAgg++ + } + } + require.Positive(t, checkedAgg, "no aggregated commits were checked") + t.Logf("verified %d aggregated commits across %d validators up to height %d", + checkedAgg, N, css[0].blockStore.Height()) +} diff --git a/consensus/aggregation_replay_test.go b/consensus/aggregation_replay_test.go new file mode 100644 index 00000000000..fcff3bd2a3f --- /dev/null +++ b/consensus/aggregation_replay_test.go @@ -0,0 +1,176 @@ +//go:build bls12381 + +package consensus + +import ( + "context" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + + dbm "github.com/cometbft/cometbft-db" + + abci "github.com/cometbft/cometbft/abci/types" + cfg "github.com/cometbft/cometbft/config" + "github.com/cometbft/cometbft/crypto/bls12381" + "github.com/cometbft/cometbft/libs/log" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/store" + "github.com/cometbft/cometbft/types" +) + +// buildBLSState constructs a single consensus State backed by the supplied +// (shared, hence restart-surviving) block/state DB and application, using a BLS +// genesis with PBTS enabled. Returning the genesis doc lets a second invocation +// rebuild an identical State over the same persistent stores, simulating a node +// restart. +func buildBLSState( + t *testing.T, + thisConfig *cfg.Config, + genDoc *types.GenesisDoc, + privVal types.PrivValidator, + app abci.Application, + blockDB dbm.DB, +) *State { + t.Helper() + + stateDB := blockDB + stateStore := sm.NewStore(stateDB, sm.StoreOptions{DiscardABCIResponses: false}) + + state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc) + require.NoError(t, err) + + // InitChain so the app knows the validator set (idempotent for the kvstore). + vals := types.TM2PB.ValidatorUpdates(state.Validators) + _, err = app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) + + cs := newStateWithConfigAndBlockStore(thisConfig, state, privVal, app, blockDB) + cs.SetLogger(log.TestingLogger().With("module", "consensus")) + return cs +} + +// TestAggregationWALReplayRestart proves a BLS+PBTS node can be restarted and +// replay its WAL cleanly, ending at the same height with the SAME aggregated +// commits in its store, all of which still verify. +// +// 1. A single BLS validator commits several heights to a persistent block/state +// DB and a real on-disk WAL. +// 2. The State is stopped. +// 3. A brand-new State is constructed over the very same DB, app and WAL file, +// and started. On start it replays the WAL (catchupReplay). +// 4. We assert the post-restart height matches and that every stored commit is +// aggregated (BlockIDFlagAgg*) and verifies via VerifyCommit. +func TestAggregationWALReplayRestart(t *testing.T) { + // Shared, restart-surviving resources. + cp := blsConsensusParams() + genDoc, privVals := blsGenesisDoc(1, 10, cp) + privVal := privVals[0] + + pk, err := privVal.GetPubKey() + require.NoError(t, err) + require.Equal(t, bls12381.KeyType, pk.Type(), "validator must use a bls12381 key") + + thisConfig := ResetConfig("aggregation_wal_replay") + t.Cleanup(func() { _ = thisConfig }) + ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0o700) + + blockDB := dbm.NewMemDB() // a single handle, shared across both State lifetimes + appPath := filepath.Join(thisConfig.DBDir(), "agg_replay_app") + app := newPersistentKVStoreWithPath(appPath) + + const targetHeight = int64(4) + + // ---- Phase 1: run to targetHeight, persisting to DB + WAL. ---- + func() { + cs := buildBLSState(t, thisConfig, genDoc, privVal, app, blockDB) + cs.SetTimeoutTicker(newMockTickerFunc(true)()) + + newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock) + + require.NoError(t, cs.Start()) + defer func() { _ = cs.Stop() }() + + deadline := time.Now().Add(30 * time.Second) + for { + select { + case msg := <-newBlockCh: + nb := msg.Data().(types.EventDataNewBlock) + if nb.Block.Height >= targetHeight { + goto done + } + case <-time.After(time.Until(deadline)): + t.Fatalf("phase 1 liveness stall before height %d", targetHeight) + } + } + done: + // Let the commit for the final height settle into the store/WAL. + time.Sleep(300 * time.Millisecond) + require.GreaterOrEqual(t, cs.blockStore.Height(), targetHeight, + "phase 1 did not reach target height") + }() + + // Capture what the store holds pre-restart for comparison. + preStore := store.NewBlockStore(blockDB) + preHeight := preStore.Height() + require.GreaterOrEqual(t, preHeight, targetHeight) + + chainID := genDoc.ChainID + valSet := types.NewValidatorSet(nil) + { + // Reconstruct the genesis validator set for verification. + s, err := sm.MakeGenesisState(genDoc) + require.NoError(t, err) + valSet = s.Validators + } + + assertAggregatedCommits := func(t *testing.T, bs sm.BlockStore, label string) { + t.Helper() + checked := 0 + for h := int64(1); h <= bs.Height()-1; h++ { + commit := bs.LoadBlockCommit(h) + if commit == nil { + commit = bs.LoadSeenCommit(h) + } + require.NotNil(t, commit, "%s: missing commit for height %d", label, h) + require.True(t, commit.HasAggregatedSignature(), + "%s: commit at height %d is NOT aggregated: %v", label, h, commit) + require.NoError(t, valSet.VerifyCommit(chainID, commit.BlockID, h, commit), + "%s: aggregated commit at height %d failed VerifyCommit", label, h) + checked++ + } + require.Positive(t, checked, "%s: no aggregated commits checked", label) + t.Logf("%s: verified %d aggregated commits up to height %d", label, checked, bs.Height()) + } + + // Pre-restart sanity: the persisted commits are aggregated and verify. + assertAggregatedCommits(t, preStore, "pre-restart") + + // ---- Phase 2: restart over the SAME DB + WAL and replay. ---- + cs2 := buildBLSState(t, thisConfig, genDoc, privVal, app, blockDB) + cs2.SetTimeoutTicker(newMockTickerFunc(true)()) + + // Starting the State triggers WAL load + catchupReplay over the persisted WAL. + require.NoError(t, cs2.Start()) + defer func() { _ = cs2.Stop() }() + + // Give replay a moment to run. + time.Sleep(500 * time.Millisecond) + + // The restarted node must be at least at the pre-restart height (replay must + // not lose committed state) and its store must still hold the aggregated + // commits, all verifying. + require.GreaterOrEqual(t, cs2.blockStore.Height(), preHeight, + "restarted node regressed below pre-restart height %d (got %d)", + preHeight, cs2.blockStore.Height()) + + require.GreaterOrEqual(t, cs2.Height, preHeight, + "restarted consensus height %d below pre-restart height %d", cs2.Height, preHeight) + + assertAggregatedCommits(t, cs2.blockStore, "post-restart") + + t.Logf("restart replay OK: pre-restart height=%d, post-restart store height=%d, cs height=%d", + preHeight, cs2.blockStore.Height(), cs2.Height) +} diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index a12d1c29294..4e43a8d31e5 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -22,6 +22,7 @@ import ( cmtsync "github.com/cometbft/cometbft/libs/sync" mempl "github.com/cometbft/cometbft/mempool" "github.com/cometbft/cometbft/proxy" + cmttime "github.com/cometbft/cometbft/types/time" "github.com/cometbft/cometbft/p2p" cmtcons "github.com/cometbft/cometbft/proto/tendermint/consensus" @@ -184,18 +185,20 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { } var extCommit *types.ExtendedCommit - switch { - case lazyProposer.Height == lazyProposer.state.InitialHeight: + switch lazyProposer.Height { + case lazyProposer.state.InitialHeight: // We're creating a proposal for the first block. // The commit is empty, but not nil. extCommit = &types.ExtendedCommit{} - case lazyProposer.LastCommit.HasTwoThirdsMajority(): + default: + lastCommitAsVs, ok := lazyProposer.LastCommit.(*types.VoteSet) + if !ok || !lastCommitAsVs.HasTwoThirdsMajority() { + lazyProposer.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block") + return + } // Make the commit from LastCommit - veHeightParam := types.ABCIParams{VoteExtensionsEnableHeight: height} - extCommit = lazyProposer.LastCommit.MakeExtendedCommit(veHeightParam) - default: // This shouldn't happen. - lazyProposer.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block") - return + veHeightParam := types.FeatureParams{VoteExtensionsEnableHeight: height} + extCommit = lastCommitAsVs.MakeExtendedCommit(veHeightParam) } // omit the last signature in the commit @@ -223,16 +226,16 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { // Make proposal propBlockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} - proposal := types.NewProposal(height, round, lazyProposer.ValidRound, propBlockID) + proposal := types.NewProposal(height, round, lazyProposer.ValidRound, propBlockID, block.Time) p := proposal.ToProto() if err := lazyProposer.privValidator.SignProposal(lazyProposer.state.ChainID, p); err == nil { proposal.Signature = p.Signature // send proposal and block parts on internal msg queue - lazyProposer.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, ""}) + lazyProposer.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, "", cmttime.Now()}) for i := 0; i < int(blockParts.Total()); i++ { part := blockParts.GetPart(i) - lazyProposer.sendInternalMessage(msgInfo{&BlockPartMessage{lazyProposer.Height, lazyProposer.Round, part}, ""}) + lazyProposer.sendInternalMessage(msgInfo{&BlockPartMessage{lazyProposer.Height, lazyProposer.Round, part}, "", time.Time{}}) } lazyProposer.Logger.Info("Signed proposal", "height", height, "round", round, "proposal", proposal) lazyProposer.Logger.Debug(fmt.Sprintf("Signed proposal block: %v", block)) @@ -464,7 +467,7 @@ func byzantineDecideProposalFunc(ctx context.Context, t *testing.T, height int64 blockParts1, err := block1.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) polRound, propBlockID := cs.ValidRound, types.BlockID{Hash: block1.Hash(), PartSetHeader: blockParts1.Header()} - proposal1 := types.NewProposal(height, round, polRound, propBlockID) + proposal1 := types.NewProposal(height, round, polRound, propBlockID, block1.Time) p1 := proposal1.ToProto() if err := cs.privValidator.SignProposal(cs.state.ChainID, p1); err != nil { t.Error(err) @@ -481,7 +484,7 @@ func byzantineDecideProposalFunc(ctx context.Context, t *testing.T, height int64 blockParts2, err := block2.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) polRound, propBlockID = cs.ValidRound, types.BlockID{Hash: block2.Hash(), PartSetHeader: blockParts2.Header()} - proposal2 := types.NewProposal(height, round, polRound, propBlockID) + proposal2 := types.NewProposal(height, round, polRound, propBlockID, block2.Time) p2 := proposal2.ToProto() if err := cs.privValidator.SignProposal(cs.state.ChainID, p2); err != nil { t.Error(err) @@ -645,7 +648,7 @@ func TestRejectOversizedProposals(t *testing.T) { propBlockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} propBlockID.PartSetHeader.Total = 4294967295 - proposal := types.NewProposal(height, round, -1, propBlockID) + proposal := types.NewProposal(height, round, -1, propBlockID, block.Time) p := proposal.ToProto() if err := cs.privValidator.SignProposal(cs.state.ChainID, p); err != nil { t.Error(err) diff --git a/consensus/common_test.go b/consensus/common_test.go index f4f626a71e8..093fba63ee2 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -240,7 +240,7 @@ func decideProposal( // Make proposal polRound, propBlockID := validRound, types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} - proposal := types.NewProposal(height, round, polRound, propBlockID) + proposal := types.NewProposal(height, round, polRound, propBlockID, block.Time) p := proposal.ToProto() if err := vs.SignProposal(chainID, p); err != nil { panic(err) @@ -295,11 +295,13 @@ func validateLastPrecommit(t *testing.T, cs *State, privVal *validatorStub, bloc require.NoError(t, err) address := pv.Address() var vote *types.Vote - if vote = votes.GetByAddress(address); vote == nil { - panic("Failed to find precommit from validator") - } - if !bytes.Equal(vote.BlockID.Hash, blockHash) { - panic(fmt.Sprintf("Expected precommit to be for %X, got %X", blockHash, vote.BlockID.Hash)) + if vs, ok := votes.(*types.VoteSet); ok { + if vote = vs.GetByAddress(address); vote == nil { + panic("Failed to find precommit from validator") + } + if !bytes.Equal(vote.BlockID.Hash, blockHash) { + panic(fmt.Sprintf("Expected precommit to be for %X, got %X", blockHash, vote.BlockID.Hash)) + } } } @@ -464,7 +466,7 @@ func randStateWithAppWithHeight( height int64, ) (*State, []*validatorStub) { c := test.ConsensusParams() - c.ABCI.VoteExtensionsEnableHeight = height + c.Feature.VoteExtensionsEnableHeight = height return randStateWithAppImpl(nValidators, app, c) } diff --git a/consensus/invalid_test.go b/consensus/invalid_test.go index c028afb3868..46d3a7ae97e 100644 --- a/consensus/invalid_test.go +++ b/consensus/invalid_test.go @@ -85,7 +85,6 @@ func invalidDoPrevoteFunc(t *testing.T, cs *State, sw p2p.Switcher, pv types.Pri ValidatorIndex: valIndex, Height: cs.Height, Round: cs.Round, - Timestamp: cs.voteTime(), Type: cmtproto.PrecommitType, BlockID: types.BlockID{ Hash: blockHash, diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index 405ba194b67..0f077980f16 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -81,14 +81,14 @@ func TestMempoolProgressInHigherRound(t *testing.T) { newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock) newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound) timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose) - cs.setProposal = func(proposal *types.Proposal) error { + cs.setProposal = func(proposal *types.Proposal, recvTime time.Time) error { if cs.Height == 2 && cs.Round == 0 { // dont set the proposal in round 0 so we timeout and // go to next round cs.Logger.Info("Ignoring set proposal at height 2, round 0") return nil } - return cs.defaultSetProposal(proposal) + return cs.defaultSetProposal(proposal, recvTime) } startTestRound(cs, height, round) diff --git a/consensus/metrics.gen.go b/consensus/metrics.gen.go index aa05a97bdd0..6696ef010a2 100644 --- a/consensus/metrics.gen.go +++ b/consensus/metrics.gen.go @@ -228,6 +228,20 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { Name: "round_increment_total", Help: "RoundIncrementTotal is the number of times that the consensus reactor has incremented above the initial round in a step.", }, append(labels, "step")).With(labelsAndValues...), + NextBlockDelay: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "next_block_delay", + Help: "NextBlockDelay is the time to wait before proposing the next block, as instructed by the application via FinalizeBlockResponse (ADR-115).", + }, labels).With(labelsAndValues...), + ProposalTimestampDifference: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "proposal_timestamp_difference", + Help: "Difference in seconds between the local time when a proposal message is received and the timestamp in the proposal message.", + + Buckets: []float64{-1.5, -1.0, -0.5, -0.2, 0, 0.2, 0.5, 1.0, 1.5, 2.0, 2.5, 4.0, 8.0}, + }, append(labels, "is_timely")).With(labelsAndValues...), } } @@ -268,5 +282,7 @@ func NopMetrics() *Metrics { LateVotes: discard.NewCounter(), PeerHeight: discard.NewGauge(), RoundIncrementTotal: discard.NewCounter(), + NextBlockDelay: discard.NewHistogram(), + ProposalTimestampDifference: discard.NewHistogram(), } } diff --git a/consensus/metrics.go b/consensus/metrics.go index e1840062657..746d57f6bff 100644 --- a/consensus/metrics.go +++ b/consensus/metrics.go @@ -152,6 +152,25 @@ type Metrics struct { // RoundIncrementTotal is the number of times that the consensus reactor // has incremented above the initial round in a step. RoundIncrementTotal metrics.Counter `metrics_labels:"step"` + + // NextBlockDelay is the time to wait before proposing the next block, + // as instructed by the application via FinalizeBlockResponse (ADR-115). + NextBlockDelay metrics.Histogram + + // ProposalTimestampDifference is the difference between the local time + // of the validator at the time it receives a proposal message, and the + // timestamp of the received proposal message. + // + // The value of this metric is not expected to be negative, as it would + // mean that the proposal's timestamp is in the future. This indicates + // that the proposer's and this node's clocks are desynchronized. + // + // A positive value of this metric reflects the message delay from the + // proposer to this node, for the delivery of a proposal message. This + // metric thus should drive the definition of values for the consensus + // parameter SynchronyParams.MessageDelay, used by the PBTS algorithm. + //metrics:Difference in seconds between the local time when a proposal message is received and the timestamp in the proposal message. + ProposalTimestampDifference metrics.Histogram `metrics_bucketsizes:"-1.5, -1.0, -0.5, -0.2, 0, 0.2, 0.5, 1.0, 1.5, 2.0, 2.5, 4.0, 8.0" metrics_labels:"is_timely"` } func (m *Metrics) MarkRoundIncremented(step cstypes.RoundStepType) { diff --git a/consensus/msgs.go b/consensus/msgs.go index 9d7583444be..0aedfe9ade0 100644 --- a/consensus/msgs.go +++ b/consensus/msgs.go @@ -76,6 +76,12 @@ func MsgToProto(msg Message) (proto.Message, error) { Vote: vote, } + case *CommitMessage: + commit := msg.Commit.ToProto() + pb = &cmtcons.Commit{ + Commit: commit, + } + case *HasVoteMessage: pb = &cmtcons.HasVote{ Height: msg.Height, @@ -84,6 +90,13 @@ func MsgToProto(msg Message) (proto.Message, error) { Index: msg.Index, } + case *HasProposalBlockPartMessage: + pb = &cmtcons.HasProposalBlockPart{ + Height: msg.Height, + Round: msg.Round, + Index: msg.Index, + } + case *VoteSetMaj23Message: bi := msg.BlockID.ToProto() pb = &cmtcons.VoteSetMaj23{ @@ -181,6 +194,15 @@ func MsgFromProto(p proto.Message) (Message, error) { Round: msg.Round, Part: parts, } + case *cmtcons.Commit: + commit, err := types.CommitFromProto(msg.Commit) + if err != nil { + return nil, cmterrors.ErrMsgToProto{MessageName: "Commit", Err: err} + } + + pb = &CommitMessage{ + Commit: commit, + } case *cmtcons.Vote: // Vote validation will be handled in the vote message ValidateBasic // call below. @@ -199,6 +221,12 @@ func MsgFromProto(p proto.Message) (Message, error) { Type: msg.Type, Index: msg.Index, } + case *cmtcons.HasProposalBlockPart: + pb = &HasProposalBlockPartMessage{ + Height: msg.Height, + Round: msg.Round, + Index: msg.Index, + } case *cmtcons.VoteSetMaj23: bi, err := types.BlockIDFromProto(&msg.BlockID) if err != nil { @@ -252,6 +280,9 @@ func WALToProto(msg WALMessage) (*cmtcons.WALMessage, error) { }, } case msgInfo: + // NOTE: ReceiveTime is intentionally dropped (bera-v1.x persists it), so a + // WAL-replayed proposal has a zero time. Only affects PBTS timeliness in a + // rare crash window and is safe. consMsg, err := MsgToProto(msg.Msg) if err != nil { return nil, err @@ -317,6 +348,7 @@ func WALFromProto(msg *cmtcons.WALMessage) (WALMessage, error) { if err != nil { return nil, cmterrors.ErrMsgFromProto{MessageName: "MsgInfo", Err: err} } + // ReceiveTime is not persisted, so it is zero here (see WALToProto). pb = msgInfo{ Msg: walMsg, PeerID: p2p.ID(msg.MsgInfo.PeerID), diff --git a/consensus/msgs_test.go b/consensus/msgs_test.go index d55b55d00ef..d4bac4689ba 100644 --- a/consensus/msgs_test.go +++ b/consensus/msgs_test.go @@ -17,6 +17,7 @@ import ( cmtcons "github.com/cometbft/cometbft/proto/tendermint/consensus" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" ) func TestMsgToProto(t *testing.T) { @@ -49,10 +50,10 @@ func TestMsgToProto(t *testing.T) { proposal := types.Proposal{ Type: cmtproto.ProposalType, Height: 1, - Round: 1, + Round: 2, POLRound: 1, BlockID: bi, - Timestamp: time.Now(), + Timestamp: cmttime.Now(), Signature: cmtrand.Bytes(20), } pbProposal := proposal.ToProto() @@ -437,6 +438,12 @@ func TestConsMsgsVectors(t *testing.T) { }}, "3a1808ffffffffffffffff7f10ffffffff07180120ffffffff07", }, + { + "HasProposalBlockPart", &cmtcons.Message{Sum: &cmtcons.Message_HasProposalBlockPart{ + HasProposalBlockPart: &cmtcons.HasProposalBlockPart{Height: 1, Round: 1, Index: 1}, + }}, + "5206080110011801", + }, { "VoteSetMaj23", &cmtcons.Message{Sum: &cmtcons.Message_VoteSetMaj23{ VoteSetMaj23: &cmtcons.VoteSetMaj23{Height: 1, Round: 1, Type: cmtproto.PrevoteType, BlockID: pbBi}, @@ -460,3 +467,28 @@ func TestConsMsgsVectors(t *testing.T) { }) } } + +// TestHasProposalBlockPartWireCompat pins the decode path for the +// has_proposal_block_part gossip hint (oneof field 10) exactly as the +// bera-v1.x line encodes it. Mixed-version gossip during a rolling upgrade +// depends on these bytes unwrapping instead of failing as an unknown sum. +func TestHasProposalBlockPartWireCompat(t *testing.T) { + oldLineBytes, err := hex.DecodeString("5206080510011802") + require.NoError(t, err) + + var m cmtcons.Message + require.NoError(t, proto.Unmarshal(oldLineBytes, &m)) + + unwrapped, err := m.Unwrap() + require.NoError(t, err) + + msg, err := MsgFromProto(unwrapped) + require.NoError(t, err) + require.NoError(t, msg.ValidateBasic()) + + hpbp, ok := msg.(*HasProposalBlockPartMessage) + require.True(t, ok) + require.Equal(t, int64(5), hpbp.Height) + require.Equal(t, int32(1), hpbp.Round) + require.Equal(t, int32(2), hpbp.Index) +} diff --git a/consensus/pbts_test.go b/consensus/pbts_test.go new file mode 100644 index 00000000000..e386cf84105 --- /dev/null +++ b/consensus/pbts_test.go @@ -0,0 +1,136 @@ +package consensus + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cometbft/cometbft/types" + cmttimemocks "github.com/cometbft/cometbft/types/time/mocks" +) + +// TestPBTSProposerWaitTime verifies that proposerWaitTime returns the correct +// amount of time a proposer must wait for its local clock to catch up to the +// previous block time under PBTS. +func TestPBTSProposerWaitTime(t *testing.T) { + genesisTime, err := time.Parse(time.RFC3339, "2019-03-13T23:00:00Z") + require.NoError(t, err) + testCases := []struct { + name string + previousBlockTime time.Time + localTime time.Time + expectedWait time.Duration + }{ + { + name: "block time greater than local time", + previousBlockTime: genesisTime.Add(5 * time.Nanosecond), + localTime: genesisTime.Add(1 * time.Nanosecond), + expectedWait: 4 * time.Nanosecond, + }, + { + name: "local time greater than block time", + previousBlockTime: genesisTime.Add(1 * time.Nanosecond), + localTime: genesisTime.Add(5 * time.Nanosecond), + expectedWait: 0, + }, + { + name: "both times equal", + previousBlockTime: genesisTime.Add(5 * time.Nanosecond), + localTime: genesisTime.Add(5 * time.Nanosecond), + expectedWait: 0, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + mockSource := new(cmttimemocks.Source) + mockSource.On("Now").Return(testCase.localTime) + + ti := proposerWaitTime(mockSource, testCase.previousBlockTime) + assert.Equal(t, testCase.expectedWait, ti) + }) + } +} + +// TestPBTSProposalIsTimely verifies the PBTS timeliness window: a proposal is +// timely when the receiver's local clock falls within +// [proposalTime - Precision, proposalTime + MessageDelay + Precision]. +func TestPBTSProposalIsTimely(t *testing.T) { + genesisTime, err := time.Parse(time.RFC3339, "2019-03-13T23:00:00Z") + require.NoError(t, err) + + sp := types.SynchronyParams{ + Precision: 10 * time.Millisecond, + MessageDelay: 140 * time.Millisecond, + } + + testCases := []struct { + name string + proposalTime time.Time + recvTime time.Time + expectedTimely bool + }{ + { + name: "timely: recv equals proposal time", + proposalTime: genesisTime, + recvTime: genesisTime, + expectedTimely: true, + }, + { + name: "timely: recv at lower bound (proposal - precision)", + proposalTime: genesisTime, + recvTime: genesisTime.Add(-sp.Precision), + expectedTimely: true, + }, + { + name: "timely: recv at upper bound (proposal + delay + precision)", + proposalTime: genesisTime, + recvTime: genesisTime.Add(sp.MessageDelay + sp.Precision), + expectedTimely: true, + }, + { + name: "not timely: too far in the past (recv after upper bound)", + proposalTime: genesisTime, + recvTime: genesisTime.Add(sp.MessageDelay + sp.Precision + time.Millisecond), + expectedTimely: false, + }, + { + name: "not timely: too far in the future (recv before lower bound)", + proposalTime: genesisTime, + recvTime: genesisTime.Add(-sp.Precision - time.Millisecond), + expectedTimely: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := &types.Proposal{Timestamp: tc.proposalTime} + assert.Equal(t, tc.expectedTimely, p.IsTimely(tc.recvTime, sp)) + }) + } +} + +// TestPBTSSynchronyParamsInRound verifies that the synchrony message delay +// applies an exponential back-off as the round increases, while precision stays +// constant. This is the adaptive component of PBTS. +func TestPBTSSynchronyParamsInRound(t *testing.T) { + sp := types.SynchronyParams{ + Precision: 10 * time.Millisecond, + MessageDelay: 100 * time.Millisecond, + } + + round0 := sp.InRound(0) + assert.Equal(t, sp.Precision, round0.Precision) + assert.Equal(t, sp.MessageDelay, round0.MessageDelay) + + // Message delay grows with the round; precision is unchanged. + round1 := sp.InRound(1) + assert.Equal(t, sp.Precision, round1.Precision) + assert.True(t, round1.MessageDelay > sp.MessageDelay, + "message delay should grow after round 0: got %v, base %v", round1.MessageDelay, sp.MessageDelay) + + round5 := sp.InRound(5) + assert.True(t, round5.MessageDelay >= round1.MessageDelay, + "message delay should be non-decreasing in the round") +} diff --git a/consensus/reactor.go b/consensus/reactor.go index d0973bb84f9..3d41147712e 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -281,11 +281,13 @@ func (conR *Reactor) Receive(e p2p.Envelope) { return } ps.ApplyNewRoundStepMessage(msg) - conR.conS.statsMsgQueue <- msgInfo{msg, e.Src.ID()} + conR.conS.statsMsgQueue <- msgInfo{msg, e.Src.ID(), time.Time{}} case *NewValidBlockMessage: ps.ApplyNewValidBlockMessage(msg) case *HasVoteMessage: ps.ApplyHasVoteMessage(msg) + case *HasProposalBlockPartMessage: + ps.ApplyHasProposalBlockPartMessage(msg) case *VoteSetMaj23Message: rs := conR.getRoundState() height, votes := rs.Height, rs.Votes @@ -342,13 +344,13 @@ func (conR *Reactor) Receive(e p2p.Envelope) { } ps.SetHasProposal(msg.Proposal) - conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID()} + conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID(), cmttime.Now()} case *ProposalPOLMessage: ps.ApplyProposalPOLMessage(msg) case *BlockPartMessage: ps.SetHasProposalBlockPart(msg.Height, msg.Round, int(msg.Part.Index)) conR.Metrics.BlockParts.With("peer_id", string(e.Src.ID())).Add(1) - conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID()} + conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID(), time.Time{}} default: conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg))) } @@ -365,7 +367,10 @@ func (conR *Reactor) Receive(e p2p.Envelope) { height, valSize, lastCommitSize := rs.Height, rs.Validators.Size(), rs.LastCommit.Size() ps.SetHasVoteFromPeer(msg.Vote, height, valSize, lastCommitSize) - conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID()} + conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID(), time.Time{}} + + case *CommitMessage: + conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID(), time.Time{}} default: // don't punish (leave room for soft upgrades) @@ -480,6 +485,24 @@ func (conR *Reactor) subscribeToBroadcastEvents() { conR.Logger.Error("Error adding listener for Vote events", "err", err) } + err = conR.conS.evsw.AddListenerForEvent( + subscriber, + types.EventProposalBlockPart, + func(data cmtevents.EventData) { + conR.broadcastHasProposalBlockPartMessage(data.(*BlockPartMessage)) + + // update reactor's view of round state + // NOTE this is safe to do without locking cs because the eventBus is + // synchronous. If it were not, we could pass rs in this event + // instead + rs := conR.conS.getRoundState() + conR.updateRoundState(&rs) + }, + ) + if err != nil { + conR.Logger.Error("Error adding listener for ProposalBlockPart events", "err", err) + } + err = conR.conS.evsw.AddListenerForEvent( subscriber, types.EventNewConsensusParams, @@ -579,6 +602,21 @@ func (conR *Reactor) broadcastHasVoteMessage(vote *types.Vote) { */ } +// Broadcasts HasProposalBlockPartMessage to peers that care. +func (conR *Reactor) broadcastHasProposalBlockPartMessage(partMsg *BlockPartMessage) { + msg := &cmtcons.HasProposalBlockPart{ + Height: partMsg.Height, + Round: partMsg.Round, + Index: int32(partMsg.Part.Index), + } + go func() { + conR.Switch.TryBroadcast(p2p.Envelope{ + ChannelID: StateChannel, + Message: msg, + }) + }() +} + func makeRoundStepMessage(rs *cstypes.RoundState) (nrsMsg *cmtcons.NewRoundStep) { nrsMsg = &cmtcons.NewRoundStep{ Height: rs.Height, @@ -684,6 +722,20 @@ OUTER_LOOP: "height", prs.Height, "vote", vote, ) + } else if !ps.HasCatchupCommit() { + // With BLS aggregation individual votes cannot be extracted from + // a stored commit, so a lagging peer is sent the whole commit. + c := getEntireCommitToSend(logger, conR.conS, &rs, ps, prs) + if commit, ok := (c).(*types.Commit); ok { + if ps.sendCommit(commit) { + continue OUTER_LOOP + } + logger.Error("Failed to send commit to peer", + "height", prs.Height, + "commit", commit) + } else if c != nil { + logger.Error("Commit should have been returned, instead unknown type.", "type", fmt.Sprintf("%T", c)) + } } switch sleeping { @@ -898,9 +950,14 @@ func pickVoteToSend( // Special catchup logic. // If peer is lagging by height 1, send LastCommit. if prs.Height != 0 && rs.Height == prs.Height+1 { - if vote := ps.PickVoteToSend(rs.LastCommit); vote != nil { - logger.Debug("Picked rs.LastCommit to send", "height", prs.Height) - return vote + // Individual votes cannot be extracted from a whole (aggregated) + // *types.Commit; the peer will catch up via block sync or the next + // proposal instead. + if _, isWholeCommit := rs.LastCommit.(*types.Commit); !isWholeCommit { + if vote := ps.PickVoteToSend(rs.LastCommit); vote != nil { + logger.Debug("Picked rs.LastCommit to send", "height", prs.Height) + return vote + } } } @@ -915,7 +972,7 @@ func pickVoteToSend( func() { conS.mtx.RLock() defer conS.mtx.RUnlock() - veEnabled = conS.state.ConsensusParams.ABCI.VoteExtensionsEnabled(prs.Height) + veEnabled = conS.state.ConsensusParams.Feature.VoteExtensionsEnabled(prs.Height) }() if veEnabled { ec = conS.blockStore.LoadBlockExtendedCommit(prs.Height) @@ -924,6 +981,12 @@ func pickVoteToSend( if c == nil { return nil } + if c.HasAggregatedSignature() { + // Individual votes cannot be extracted from an aggregated + // commit; the whole commit is gossiped instead (see + // getEntireCommitToSend). + return nil + } ec = c.WrappedExtendedCommit() } if ec == nil { @@ -937,6 +1000,49 @@ func pickVoteToSend( return nil } +// getEntireCommitToSend returns the whole stored commit for the peer's +// height when the peer is lagging by more than one height. It is used when +// commits are aggregated, since individual votes cannot be extracted from an +// aggregated commit. +func getEntireCommitToSend(_ log.Logger, + conS *State, + rs *cstypes.RoundState, + _ *PeerState, + prs *cstypes.PeerRoundState, +) types.VoteSetReader { + // Catchup logic + // If peer is lagging by more than 1, send Commit. + blockStoreBase := conS.blockStore.Base() + if blockStoreBase > 0 && prs.Height != 0 && rs.Height >= prs.Height+2 && prs.Height >= blockStoreBase { + // Load the block's commit for prs.Height, + // which contains precommit signatures for prs.Height. + var commit types.VoteSetReader + var veEnabled bool + func() { + conS.mtx.RLock() + defer conS.mtx.RUnlock() + veEnabled = conS.state.ConsensusParams.Feature.VoteExtensionsEnabled(prs.Height) + }() + if veEnabled { + ec := conS.blockStore.LoadBlockExtendedCommit(prs.Height) + if ec == nil { + return nil + } + commit = ec + } else { + c := conS.blockStore.LoadBlockCommit(prs.Height) + if c == nil || !c.HasAggregatedSignature() { + // Non-aggregated commits are gossiped vote by vote in + // pickVoteToSend. + return nil + } + commit = c + } + return commit + } + return nil +} + func pickVoteCurrentHeight( logger log.Logger, rs *cstypes.RoundState, @@ -1192,6 +1298,17 @@ func (ps *PeerState) SetHasProposalBlockPart(height int64, round int32, index in ps.mtx.Lock() defer ps.mtx.Unlock() + ps.setHasProposalBlockPart(height, round, index) +} + +func (ps *PeerState) setHasProposalBlockPart(height int64, round int32, index int) { + ps.logger.Debug("setHasProposalBlockPart", + "peerH/R", + log.NewLazySprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), + "H/R", + log.NewLazySprintf("%d/%d", height, round), + "index", index) + if ps.PRS.Height != height || ps.PRS.Round != round { return } @@ -1281,6 +1398,47 @@ func (ps *PeerState) SendProposalSetHasProposal( } } +// sendCommit sends the aggregated commit to the peer. +func (ps *PeerState) sendCommit(commit *types.Commit) bool { + ps.logger.Debug("Sending commit message", "ps", ps, "commit", commit) + if ps.peer.Send(p2p.Envelope{ + ChannelID: VoteChannel, + Message: &cmtcons.Commit{ + Commit: commit.ToProto(), + }, + }) { + ps.SetHasCatchupCommit(commit) + return true + } + return false +} + +// SetHasCatchupCommit sets the given commit as known by the peer. +func (ps *PeerState) SetHasCatchupCommit(commit *types.Commit) { + ps.mtx.Lock() + defer ps.mtx.Unlock() + + ps.setHasCatchupCommit(commit.Height, commit.Round) +} + +// CONTRACT: Caller must hold the mutex. +func (ps *PeerState) setHasCatchupCommit(height int64, round int32) { + ps.logger.Debug("setHasCatchupCommit", + "peerH/R", + log.NewLazySprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), + "H/R", + log.NewLazySprintf("%d/%d", height, round)) + + ps.PRS.HasCatchupCommit = true +} + +func (ps *PeerState) HasCatchupCommit() bool { + ps.mtx.Lock() + defer ps.mtx.Unlock() + + return ps.PRS.HasCatchupCommit +} + // sendVoteSetHasVote sends the vote to the peer. // Returns true and marks the peer as having the vote if the vote was sent. func (ps *PeerState) sendVoteSetHasVote(vote *types.Vote) bool { @@ -1559,6 +1717,8 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) { // We'll update the BitArray capacity later. ps.PRS.CatchupCommitRound = -1 ps.PRS.CatchupCommit = nil + + ps.PRS.HasCatchupCommit = false } } @@ -1608,6 +1768,18 @@ func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) { ps.setHasVote(msg.Height, msg.Round, msg.Type, msg.Index) } +// ApplyHasProposalBlockPartMessage updates the peer state for the new block part. +func (ps *PeerState) ApplyHasProposalBlockPartMessage(msg *HasProposalBlockPartMessage) { + ps.mtx.Lock() + defer ps.mtx.Unlock() + + if ps.PRS.Height != msg.Height { + return + } + + ps.setHasProposalBlockPart(msg.Height, msg.Round, int(msg.Index)) +} + // ApplyVoteSetBitsMessage updates the peer state for the bit-array of votes // it claims to have for the corresponding BlockID. // `ourVotes` is a BitArray of votes we have for msg.BlockID @@ -1664,7 +1836,9 @@ func init() { cmtjson.RegisterType(&ProposalPOLMessage{}, "tendermint/ProposalPOL") cmtjson.RegisterType(&BlockPartMessage{}, "tendermint/BlockPart") cmtjson.RegisterType(&VoteMessage{}, "tendermint/Vote") + cmtjson.RegisterType(&CommitMessage{}, "tendermint/Commit") cmtjson.RegisterType(&HasVoteMessage{}, "tendermint/HasVote") + cmtjson.RegisterType(&HasProposalBlockPartMessage{}, "tendermint/HasProposalBlockPart") cmtjson.RegisterType(&VoteSetMaj23Message{}, "tendermint/VoteSetMaj23") cmtjson.RegisterType(&VoteSetBitsMessage{}, "tendermint/VoteSetBits") } @@ -1887,6 +2061,24 @@ func (m *VoteMessage) String() string { //------------------------------------- +// CommitMessage is sent instead of individual votes once the precommit +// signatures have been aggregated into a single commit. +type CommitMessage struct { + Commit *types.Commit +} + +// ValidateBasic checks whether the commit within the message is well-formed. +func (m *CommitMessage) ValidateBasic() error { + return m.Commit.ValidateBasic() +} + +// String returns a string representation. +func (m *CommitMessage) String() string { + return fmt.Sprintf("[Commit with aggregated signatures %v]", m.Commit) +} + +//------------------------------------- + // HasVoteMessage is sent to indicate that a particular vote has been received. type HasVoteMessage struct { Height int64 diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 4114c309881..692c51b7987 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -360,7 +360,7 @@ func TestSwitchToConsensusVoteExtensions(t *testing.T) { cs.state.LastBlockHeight = testCase.storedHeight cs.state.LastValidators = cs.state.Validators.Copy() - cs.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.initialRequiredHeight + cs.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = testCase.initialRequiredHeight propBlock, err := cs.createProposalBlock(ctx) require.NoError(t, err) @@ -392,7 +392,7 @@ func TestSwitchToConsensusVoteExtensions(t *testing.T) { require.NoError(t, err) require.True(t, added) - veHeightParam := types.ABCIParams{VoteExtensionsEnableHeight: veHeight} + veHeightParam := types.FeatureParams{VoteExtensionsEnableHeight: veHeight} if testCase.includeExtensions { cs.blockStore.SaveBlockWithExtendedCommit(propBlock, blockParts, voteSet.MakeExtendedCommit(veHeightParam)) } else { @@ -1136,7 +1136,8 @@ func TestMarshalJSONPeerState(t *testing.T) { "last_commit_round": -1, "last_commit": null, "catchup_commit_round": -1, - "catchup_commit": null + "catchup_commit": null, + "has_catchup_commit": false }, "stats":{ "votes":"0", diff --git a/consensus/replay.go b/consensus/replay.go index 60ad3340eb5..75a7804a416 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -70,13 +70,16 @@ func (cs *State) readReplayMessage(msg *TimedWALMessage, newStepSub types.Subscr case *ProposalMessage: p := msg.Proposal cs.Logger.Info("Replay: Proposal", "height", p.Height, "round", p.Round, "header", - p.BlockID.PartSetHeader, "pol", p.POLRound, "peer", peerID) + p.BlockID.PartSetHeader, "pol", p.POLRound, "peer", peerID, "receive_time", m.ReceiveTime) case *BlockPartMessage: cs.Logger.Info("Replay: BlockPart", "height", msg.Height, "round", msg.Round, "peer", peerID) case *VoteMessage: v := msg.Vote cs.Logger.Info("Replay: Vote", "height", v.Height, "round", v.Round, "type", v.Type, "blockID", v.BlockID, "peer", peerID, "extensionLen", len(v.Extension), "extSigLen", len(v.ExtensionSignature)) + case *CommitMessage: + c := msg.Commit + cs.Logger.Info("Replay: Commit", "height", c.Height, "round", c.Round, "peer", peerID) } cs.handleMsg(m) diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 4eaa4e4d7ed..cf01cd25263 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -34,6 +34,7 @@ import ( sm "github.com/cometbft/cometbft/state" smmocks "github.com/cometbft/cometbft/state/mocks" "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" ) func TestMain(m *testing.M) { @@ -463,7 +464,7 @@ func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (* require.NoError(t, err) blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} - proposal := types.NewProposal(vss[1].Height, round, -1, blockID) + proposal := types.NewProposal(vss[1].Height, round, -1, blockID, propBlock.Time) p := proposal.ToProto() if err := vss[1].SignProposal(test.DefaultTestChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -495,7 +496,7 @@ func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (* require.NoError(t, err) blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} - proposal = types.NewProposal(vss[2].Height, round, -1, blockID) + proposal = types.NewProposal(vss[2].Height, round, -1, blockID, propBlock.Time) p = proposal.ToProto() if err := vss[2].SignProposal(test.DefaultTestChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -554,7 +555,7 @@ func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (* selfIndex := valIndexFn(0) - proposal = types.NewProposal(vss[3].Height, round, -1, blockID) + proposal = types.NewProposal(vss[3].Height, round, -1, blockID, cmttime.Now()) p = proposal.ToProto() if err := vss[3].SignProposal(test.DefaultTestChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -615,7 +616,7 @@ func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (* sort.Sort(ValidatorStubsByPower(newVss)) selfIndex = valIndexFn(0) - proposal = types.NewProposal(vss[1].Height, round, -1, blockID) + proposal = types.NewProposal(vss[1].Height, round, -1, blockID, propBlock.Time) p = proposal.ToProto() if err := vss[1].SignProposal(test.DefaultTestChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) diff --git a/consensus/state.go b/consensus/state.go index c16c3570ba7..7d4476a1fa0 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -6,9 +6,11 @@ import ( "errors" "fmt" "io" + "math" "os" "runtime/debug" "sort" + "strconv" "time" "github.com/cosmos/gogoproto/proto" @@ -16,6 +18,7 @@ import ( cfg "github.com/cometbft/cometbft/config" cstypes "github.com/cometbft/cometbft/consensus/types" "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/bls12381" cmtevents "github.com/cometbft/cometbft/libs/events" "github.com/cometbft/cometbft/libs/fail" cmtjson "github.com/cometbft/cometbft/libs/json" @@ -36,8 +39,9 @@ var msgQueueSize = 1000 // msgs from the reactor which may update the state type msgInfo struct { - Msg Message `json:"msg"` - PeerID p2p.ID `json:"peer_key"` + Msg Message `json:"msg"` + PeerID p2p.ID `json:"peer_key"` + ReceiveTime time.Time `json:"receive_time"` } // internally generated messages which may update the state @@ -121,7 +125,7 @@ type State struct { // some functions can be overwritten for testing decideProposal func(height int64, round int32) doPrevote func(height int64, round int32) - setProposal func(proposal *types.Proposal) error + setProposal func(proposal *types.Proposal, recvTime time.Time) error // closed when we finish shutting down done chan struct{} @@ -474,21 +478,80 @@ func (cs *State) OpenWAL(walFile string) (WAL, error) { // AddVote inputs a vote. func (cs *State) AddVote(vote *types.Vote, peerID p2p.ID) (added bool, err error) { if peerID == "" { - cs.internalMsgQueue <- msgInfo{&VoteMessage{vote}, ""} + cs.internalMsgQueue <- msgInfo{&VoteMessage{vote}, "", time.Time{}} } else { - cs.peerMsgQueue <- msgInfo{&VoteMessage{vote}, peerID} + cs.peerMsgQueue <- msgInfo{&VoteMessage{vote}, peerID, time.Time{}} } // TODO: wait for event?! return false, nil } +// AddCommit ingests a whole aggregated commit received from a peer (gossiped +// instead of individual votes once signatures have been aggregated). It is +// used by nodes that fall behind to catch up to the rest of the network. +func (cs *State) AddCommit(commit *types.Commit, peerID p2p.ID) (bool, error) { + cs.Logger.Debug( + "Adding whole commit", + "commit_height", commit.Height, + "commit_round", commit.Round, + "commit_blockId", commit.BlockID, + "cs_height", cs.Height, + "cs_round", cs.Round, + ) + + // Height mismatch is ignored. + // Not necessarily a bad peer, but not favorable behavior. + if commit.Height != cs.Height { + cs.Logger.Debug("Commit ignored and not added", + "commit_height", commit.Height, + "cs_height", cs.Height, + "peer", peerID) + return false, nil + } + + // Check to see if the chain is configured to extend votes. + extEnabled := cs.isVoteExtensionsEnabled(commit.Height) + if extEnabled { + // We don't support receiving commits with vote extensions enabled ATM. + cs.Logger.Error("Received commit with vote extensions enabled", "height", commit.Height, "peer_ID", peerID) + return false, nil + } + + if cs.Votes.GetCommit(commit.Round) != nil { + cs.Logger.Debug("Received commit, but we already have one", "height", commit.Height, "peer_ID", peerID) + return false, nil + } + if !commit.HasAggregatedSignature() { + // Only accept aggregated commits + cs.Logger.Error("Received non aggregated commit", "commit", commit.Height, "peer_ID", peerID, "commit", commit) + return false, nil + } + // No need to check blockID. If the commit is valid, 2/3 of the voting power has signed it. + if err := cs.Validators.VerifyCommit(cs.state.ChainID, commit.BlockID, cs.Height, commit); err != nil { + cs.Logger.Error("Failed to verify commit from peer", "err", err, "height", cs.Height, "round", commit.Round, "peer_ID", peerID) + return false, err + } + height := cs.Height + cs.Votes.SetCommit(commit) + + // Executed as Commit could be from a higher round + cs.enterNewRound(height, commit.Round) + cs.enterPrecommit(height, commit.Round) + + cs.enterCommit(height, commit.Round) + // We skip timeoutCommit as this function is hit only when the node is late + cs.enterNewRound(cs.Height, 0) + + return true, nil +} + // SetProposal inputs a proposal. func (cs *State) SetProposal(proposal *types.Proposal, peerID p2p.ID) error { if peerID == "" { - cs.internalMsgQueue <- msgInfo{&ProposalMessage{proposal}, ""} + cs.internalMsgQueue <- msgInfo{&ProposalMessage{proposal}, "", cmttime.Now()} } else { - cs.peerMsgQueue <- msgInfo{&ProposalMessage{proposal}, peerID} + cs.peerMsgQueue <- msgInfo{&ProposalMessage{proposal}, peerID, cmttime.Now()} } // TODO: wait for event?! @@ -498,9 +561,9 @@ func (cs *State) SetProposal(proposal *types.Proposal, peerID p2p.ID) error { // AddProposalBlockPart inputs a part of the proposal block. func (cs *State) AddProposalBlockPart(height int64, round int32, part *types.Part, peerID p2p.ID) error { if peerID == "" { - cs.internalMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, ""} + cs.internalMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, "", time.Time{}} } else { - cs.peerMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, peerID} + cs.peerMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, peerID, time.Time{}} } // TODO: wait for event?! @@ -593,7 +656,7 @@ func (cs *State) reconstructSeenCommit(state sm.State) { // the method will panic on an absent ExtendedCommit or an ExtendedCommit without // extension data. func (cs *State) reconstructLastCommit(state sm.State) { - extensionsEnabled := state.ConsensusParams.ABCI.VoteExtensionsEnabled(state.LastBlockHeight) + extensionsEnabled := state.ConsensusParams.Feature.VoteExtensionsEnabled(state.LastBlockHeight) if !extensionsEnabled { cs.reconstructSeenCommit(state) return @@ -621,7 +684,7 @@ func (cs *State) votesFromExtendedCommit(state sm.State) (*types.VoteSet, error) return vs, nil } -func (cs *State) votesFromSeenCommit(state sm.State) (*types.VoteSet, error) { +func (cs *State) votesFromSeenCommit(state sm.State) (types.VoteSetReader, error) { commit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight) if commit == nil { commit = cs.blockStore.LoadBlockCommit(state.LastBlockHeight) @@ -633,11 +696,10 @@ func (cs *State) votesFromSeenCommit(state sm.State) (*types.VoteSet, error) { return nil, fmt.Errorf("heights don't match in votesFromSeenCommit %v!=%v", commit.Height, state.LastBlockHeight) } - vs := commit.ToVoteSet(state.ChainID, state.LastValidators) - if !vs.HasTwoThirdsMajority() { - return nil, ErrCommitQuorumNotMet - } - return vs, nil + // With BLS aggregation the individual precommit votes cannot be + // reconstructed from an aggregated seen commit, so the whole *Commit + // stands in for the vote set. + return commit, nil } // Updates State and increments height to match that of state. @@ -689,14 +751,18 @@ func (cs *State) updateToState(state sm.State) { case state.LastBlockHeight == 0: // Very first commit should be empty. cs.LastCommit = (*types.VoteSet)(nil) case cs.CommitRound > -1 && cs.Votes != nil: // Otherwise, use cs.Votes - if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() { - panic(fmt.Sprintf( - "wanted to form a commit, but precommits (H/R: %d/%d) didn't have 2/3+: %v", - state.LastBlockHeight, cs.CommitRound, cs.Votes.Precommits(cs.CommitRound), - )) - } + if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil { + cs.LastCommit = commit + } else { + if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() { + panic(fmt.Sprintf( + "wanted to form a commit, but precommits (H/R: %d/%d) didn't have 2/3+: %v", + state.LastBlockHeight, cs.CommitRound, cs.Votes.Precommits(cs.CommitRound), + )) + } - cs.LastCommit = cs.Votes.Precommits(cs.CommitRound) + cs.LastCommit = cs.Votes.Precommits(cs.CommitRound) + } case cs.LastCommit == nil: // NOTE: when consensus starts, it has no votes. reconstructLastCommit @@ -717,19 +783,28 @@ func (cs *State) updateToState(state sm.State) { cs.updateHeight(height) cs.updateRoundStep(0, cstypes.RoundStepNewHeight) + // The app can set a delay between the time when this block is committed + // and the next height is started (ADR-115, previously `timeout_commit` + // in config.toml). + timeoutCommit := state.NextBlockDelay + // If the ABCI app didn't set a delay, use the deprecated config value. + if timeoutCommit == 0 { + timeoutCommit = cs.config.TimeoutCommit + } if cs.CommitTime.IsZero() { // "Now" makes it easier to sync up dev nodes. // We add timeoutCommit to allow transactions // to be gathered for the first block. // And alternative solution that relies on clocks: // cs.StartTime = state.LastBlockTime.Add(timeoutCommit) - cs.StartTime = cs.config.Commit(cmttime.Now()) + cs.StartTime = cmttime.Now().Add(timeoutCommit) } else { - cs.StartTime = cs.config.Commit(cs.CommitTime) + cs.StartTime = cs.CommitTime.Add(timeoutCommit) } cs.Validators = validators cs.Proposal = nil + cs.ProposalReceiveTime = time.Time{} cs.ProposalBlock = nil cs.ProposalBlockParts = nil cs.LockedRound = -1 @@ -738,7 +813,7 @@ func (cs *State) updateToState(state sm.State) { cs.ValidRound = -1 cs.ValidBlock = nil cs.ValidBlockParts = nil - if state.ConsensusParams.ABCI.VoteExtensionsEnabled(height) { + if state.ConsensusParams.Feature.VoteExtensionsEnabled(height) { cs.Votes = cstypes.NewExtendedHeightVoteSet(state.ChainID, height, validators) } else { cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators) @@ -922,7 +997,7 @@ func (cs *State) handleMsg(mi msgInfo) { case *ProposalMessage: // will not cause transition. // once proposal is set, we can receive block parts - err = cs.setProposal(msg.Proposal) + err = cs.setProposal(msg.Proposal, mi.ReceiveTime) case *BlockPartMessage: // if the proposal is complete, we'll enterPrevote or tryFinalizeCommit @@ -982,6 +1057,16 @@ func (cs *State) handleMsg(mi msgInfo) { // the peer is sending us CatchupCommit precommits. // We could make note of this and help filter in broadcastHasVoteMessage(). + case *CommitMessage: + _, err := cs.AddCommit(msg.Commit, peerID) + // XXX The function above returns `added` + // If this boolean is true we should implement + // stats for commit messages + // cs.statsMsgQueue <- mi + if err != nil { + cs.Logger.Error("Failed to add commit ", "commit", msg.Commit, "err", err) + } + case *ingestVerifiedBlockRequest: cs.handleIngestVerifiedBlockRequest(msg) default: @@ -1190,6 +1275,16 @@ func (cs *State) enterPropose(height int64, round int32) { return } + // If this validator is the proposer of this round, and the previous block time is later than + // our local clock time, wait to propose until our local clock time has passed the block time. + if cs.isPBTSEnabled(height) && cs.privValidatorPubKey != nil && cs.isProposer(cs.privValidatorPubKey.Address()) { + proposerWaitTime := proposerWaitTime(cmttime.DefaultSource{}, cs.state.LastBlockTime) + if proposerWaitTime > 0 { + cs.scheduleTimeout(proposerWaitTime, height, round, cstypes.RoundStepNewRound) + return + } + } + logger.Debug("entering propose step", "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) defer func() { @@ -1277,17 +1372,17 @@ func (cs *State) defaultDecideProposal(height int64, round int32) { // Make proposal propBlockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} - proposal := types.NewProposal(height, round, cs.ValidRound, propBlockID) + proposal := types.NewProposal(height, round, cs.ValidRound, propBlockID, block.Time) p := proposal.ToProto() if err := cs.privValidator.SignProposal(cs.state.ChainID, p); err == nil { proposal.Signature = p.Signature // send proposal and block parts on internal msg queue - cs.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, ""}) + cs.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, "", cmttime.Now()}) for i := 0; i < int(blockParts.Total()); i++ { part := blockParts.GetPart(i) - cs.sendInternalMessage(msgInfo{&BlockPartMessage{cs.Height, cs.Round, part}, ""}) + cs.sendInternalMessage(msgInfo{&BlockPartMessage{cs.Height, cs.Round, part}, "", time.Time{}}) } cs.Logger.Debug("signed proposal", "height", height, "round", round, "proposal", proposal) @@ -1325,15 +1420,34 @@ func (cs *State) createProposalBlock(ctx context.Context) (*types.Block, error) // TODO(sergio): wouldn't it be easier if CreateProposalBlock accepted cs.LastCommit directly? var lastExtCommit *types.ExtendedCommit + lastCommitAsVs, ok := cs.LastCommit.(*types.VoteSet) switch { case cs.Height == cs.state.InitialHeight: // We're creating a proposal for the first block. // The commit is empty, but not nil. lastExtCommit = &types.ExtendedCommit{} - case cs.LastCommit.HasTwoThirdsMajority(): - // Make the commit from LastCommit - lastExtCommit = cs.LastCommit.MakeExtendedCommit(cs.state.ConsensusParams.ABCI) + case ok && lastCommitAsVs.HasTwoThirdsMajority(): + // Make the commit from LastCommit. + _, blsKey := cs.privValidatorPubKey.(*bls12381.PubKey) + _, blsKey2 := cs.privValidatorPubKey.(bls12381.PubKey) + canBeAggregated := (blsKey || blsKey2) && + cs.state.Validators.AllKeysHaveSameType() + if canBeAggregated { + if !cs.isPBTSEnabled(cs.Height) { + panic("Wanted to aggregate LastCommit, but PBTS is not enabled for height " + strconv.FormatInt(cs.Height, 10)) + } + lastExtCommit = lastCommitAsVs.MakeBLSCommit() + } else { + lastExtCommit = lastCommitAsVs.MakeExtendedCommit(cs.state.ConsensusParams.Feature) + } + case !ok: + lastCommitAsCommit, ok := cs.LastCommit.(*types.Commit) + if !ok { + return nil, errors.New("last commit is neither a VoteSet nor a Commit") + } + // XXX This will need to be extended to support vote extensions. + lastExtCommit = lastCommitAsCommit.WrappedExtendedCommit() default: // This shouldn't happen. return nil, ErrProposalWithoutPreviousCommit @@ -1384,6 +1498,74 @@ func (cs *State) enterPrevote(height int64, round int32) { // (so we have more time to try and collect +2/3 prevotes for a single block) } +func (cs *State) timelyProposalMargins() (time.Duration, time.Duration) { + sp := cs.state.ConsensusParams.Synchrony.InRound(cs.Round) + + // cs.ProposalReceiveTime - cs.Proposal.Timestamp >= -1 * Precision + // cs.ProposalReceiveTime - cs.Proposal.Timestamp <= MessageDelay + Precision + return -sp.Precision, sp.MessageDelay + sp.Precision +} + +func (cs *State) proposalIsTimely() bool { + if cs.Proposal.Round > 10 { + return true + } + + sp := cs.state.ConsensusParams.Synchrony.InRound(cs.Proposal.Round) + + return cs.Proposal.IsTimely(cs.ProposalReceiveTime, sp) +} + +// isPBTSEnabled returns true if PBTS is enabled at the given height. +func (cs *State) isPBTSEnabled(height int64) bool { + return cs.state.ConsensusParams.Feature.PbtsEnabled(height) +} + +func (cs *State) isVoteExtensionsEnabled(height int64) bool { + return cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(height) +} + +// canAggregateCommits returns true if the validator set uses BLS12-381 keys +// exclusively, in which case precommit signatures are aggregated into a +// single commit signature (mirrors the routing in types.isAggregatedCommit). +func (cs *State) canAggregateCommits() bool { + proposerKey := cs.Validators.GetProposer().PubKey + _, blsKey := proposerKey.(*bls12381.PubKey) + _, blsKey2 := proposerKey.(bls12381.PubKey) + return (blsKey || blsKey2) && cs.Validators.AllKeysHaveSameType() +} + +// proposerWaitTime determines how long the proposer should wait to propose its next block. +// If the result is zero, a block can be proposed immediately. +// +// Block times must be monotonically increasing, so if the block time of the previous +// block is larger than the proposer's current time, then the proposer will sleep +// until its local clock exceeds the previous block time. +func proposerWaitTime(lt cmttime.Source, bt time.Time) time.Duration { + t := lt.Now() + if bt.After(t) { + return bt.Sub(t) + } + return 0 +} + +func (cs *State) calculateProposalTimestampDifferenceMetric() { + if cs.Proposal != nil && cs.Proposal.POLRound == -1 { + sp := cs.state.ConsensusParams.Synchrony.InRound(cs.Proposal.Round) + + isTimely := cs.Proposal.IsTimely(cs.ProposalReceiveTime, sp) + diff := cs.ProposalReceiveTime.Sub(cs.Proposal.Timestamp) + // Clamp overflowed durations to avoid recording garbage metric values. + // time.Sub sub/overflows when the difference exceeds ~±292 years. + if diff == time.Duration(math.MinInt64) || diff == time.Duration(math.MaxInt64) { + return + } + cs.metrics.ProposalTimestampDifference. + With("is_timely", strconv.FormatBool(isTimely)). + Observe(diff.Seconds()) + } +} + func (cs *State) defaultDoPrevote(height int64, round int32) { logger := cs.Logger.With("height", height, "round", round) @@ -1401,6 +1583,46 @@ func (cs *State) defaultDoPrevote(height int64, round int32) { return } + // We did not receive a valid proposal for this round (and thus executing + // this from a timeout). + if cs.Proposal == nil { + logger.Debug("prevote step: did not receive a valid Proposal; prevoting nil") + cs.signAddVote(cmtproto.PrevoteType, nil, types.PartSetHeader{}, nil) + return + } + + // Timestamp validation using Proposer-Based TimeStamp (PBTS) algorithm. + // See: https://github.com/cometbft/cometbft/blob/main/spec/consensus/proposer-based-timestamp/ + if cs.isPBTSEnabled(height) { + if !cs.Proposal.Timestamp.Equal(cs.ProposalBlock.Time) { + logger.Debug("prevote step: proposal timestamp not equal; prevoting nil") + cs.signAddVote(cmtproto.PrevoteType, nil, types.PartSetHeader{}, nil) + return + } + + if cs.Proposal.POLRound == -1 && !cs.proposalIsTimely() { + lowerBound, upperBound := cs.timelyProposalMargins() + // Use .String() to avoid passing a raw time.Duration to the logger, + // which can panic in some logger implementations + // when time.Sub overflows to math.MinInt64 for far-future timestamps. + logger.Info("prevote step: Proposal is not timely; prevoting nil", + "timestamp", cs.Proposal.Timestamp.Format(time.RFC3339Nano), + "receive_time", cs.ProposalReceiveTime.Format(time.RFC3339Nano), + "timestamp_difference", cs.ProposalReceiveTime.Sub(cs.Proposal.Timestamp).String(), + "lower_bound", lowerBound, + "upper_bound", upperBound) + cs.signAddVote(cmtproto.PrevoteType, nil, types.PartSetHeader{}, nil) + return + } + + if cs.Proposal.POLRound == -1 { + logger.Debug("prevote step: Proposal is timely", + "timestamp", cs.Proposal.Timestamp.Format(time.RFC3339Nano), + "receive_time", cs.ProposalReceiveTime.Format(time.RFC3339Nano), + "timestamp_difference", cs.ProposalReceiveTime.Sub(cs.Proposal.Timestamp).String()) + } + } + // Validate proposal block, from consensus' perspective err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock) if err != nil { @@ -1660,9 +1882,15 @@ func (cs *State) enterCommit(height int64, commitRound int32) { cs.tryFinalizeCommit(height) }() - blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority() - if !ok { - panic("RunActionCommit() expects +2/3 precommits") + var blockID types.BlockID + if commit := cs.Votes.GetCommit(commitRound); commit != nil { + blockID = commit.BlockID + } else { + var ok bool + blockID, ok = cs.Votes.Precommits(commitRound).TwoThirdsMajority() + if !ok || blockID.IsNil() { + panic("RunActionCommit() expects +2/3 precommits") + } } // The Locked* fields no longer matter. @@ -1705,10 +1933,16 @@ func (cs *State) tryFinalizeCommit(height int64) { panic(fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height)) } - blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() - if !ok || len(blockID.Hash) == 0 { - logger.Error("failed attempt to finalize commit; there was no +2/3 majority or +2/3 was for nil") - return + var blockID types.BlockID + if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil { + blockID = commit.BlockID + } else { + var ok bool + blockID, ok = cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() + if !ok || len(blockID.Hash) == 0 { + logger.Error("failed attempt to finalize commit; there was no +2/3 majority or +2/3 was for nil") + return + } } if !cs.ProposalBlock.HashesTo(blockID.Hash) { @@ -1737,14 +1971,20 @@ func (cs *State) finalizeCommit(height int64) { return } - cs.calculatePrevoteMessageDelayMetrics() + var blockID types.BlockID + if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil { + blockID = commit.BlockID + } else { + cs.calculatePrevoteMessageDelayMetrics() - blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() + var ok bool + blockID, ok = cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() + if !ok { + panic("cannot finalize commit; commit does not have 2/3 majority") + } + } block, blockParts := cs.ProposalBlock, cs.ProposalBlockParts - if !ok { - panic("cannot finalize commit; commit does not have 2/3 majority") - } if !blockParts.HasHeader(blockID.PartSetHeader) { panic("expected ProposalBlockParts header to be commit header") } @@ -1770,10 +2010,17 @@ func (cs *State) finalizeCommit(height int64) { // Save to blockStore. if cs.blockStore.Height() < block.Height { - // NOTE: the seenCommit is local justification to commit this block, - // but may differ from the LastCommit included in the next block - seenExtendedCommit := cs.Votes.Precommits(cs.CommitRound).MakeExtendedCommit(cs.state.ConsensusParams.ABCI) - if cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(block.Height) { + var seenExtendedCommit *types.ExtendedCommit + if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil { + seenExtendedCommit = commit.WrappedExtendedCommit() + } else if cs.canAggregateCommits() { + // NOTE: the seenCommit is local justification to commit this block, + // but may differ from the LastCommit included in the next block + seenExtendedCommit = cs.Votes.Precommits(cs.CommitRound).MakeBLSCommit() + } else { + seenExtendedCommit = cs.Votes.Precommits(cs.CommitRound).MakeExtendedCommit(cs.state.ConsensusParams.Feature) + } + if cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(block.Height) { cs.blockStore.SaveBlockWithExtendedCommit(block, blockParts, seenExtendedCommit) } else { cs.blockStore.SaveBlock(block, blockParts, seenExtendedCommit.ToCommit()) @@ -1830,6 +2077,9 @@ func (cs *State) finalizeCommit(height int64) { // must be called before we update state cs.recordMetrics(height, block) + cs.metrics.NextBlockDelay.Observe( + stateCopy.NextBlockDelay.Seconds(), + ) // NewHeightStep! cs.updateToState(stateCopy) @@ -1942,10 +2192,10 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { //----------------------------------------------------------------------------- -func (cs *State) defaultSetProposal(proposal *types.Proposal) error { +func (cs *State) defaultSetProposal(proposal *types.Proposal, recvTime time.Time) error { // Already have one // TODO: possibly catch double proposals - if cs.Proposal != nil { + if cs.Proposal != nil || proposal == nil { return nil } @@ -1980,6 +2230,8 @@ func (cs *State) defaultSetProposal(proposal *types.Proposal) error { proposal.Signature = p.Signature cs.Proposal = proposal + cs.ProposalReceiveTime = recvTime + cs.calculateProposalTimestampDifferenceMetric() // We don't update cs.ProposalBlockParts if it is already set. // This happens if we're already in cstypes.RoundStepCommit or if there is a valid block in the current round. // TODO: We can check if Proposal is for a different block as this is a sign of misbehavior! @@ -2032,6 +2284,8 @@ func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID p2p.ID) (add // NOTE: we are disregarding possible duplicates above where heights dont match or we're not expecting block parts yet // but between the matches_current = true and false, we have all the info. cs.metrics.DuplicateBlockPart.Add(1) + } else { + cs.evsw.FireEvent(types.EventProposalBlockPart, msg) } maxBytes := cs.state.ConsensusParams.Block.MaxBytes @@ -2197,7 +2451,14 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error return added, err } - added, err = cs.LastCommit.AddVote(vote) + lastCommitAsVs, ok := cs.LastCommit.(*types.VoteSet) + if !ok { + // LastCommit is a whole aggregated *types.Commit; individual late + // precommits cannot be added to it. + cs.Logger.Debug("Cannot add precommit vote to a commit; precommit ignored", "vote", vote) + return added, err + } + added, err = lastCommitAsVs.AddVote(vote) if !added { // If the vote wasn't added but there's no error, it's a duplicate vote if err == nil { @@ -2206,7 +2467,7 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error return added, err } - cs.Logger.Debug("added vote to last precommits", "last_commit", cs.LastCommit.StringShort()) + cs.Logger.Debug("added vote to last precommits", "last_commit", lastCommitAsVs.StringShort()) if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil { return added, err } @@ -2214,7 +2475,7 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error cs.evsw.FireEvent(types.EventVote, vote) // if we can skip timeoutCommit and have all the votes now, - if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() { + if cs.config.SkipTimeoutCommit && lastCommitAsVs.HasAll() { // go straight to new round (skip timeout commit) // cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight) cs.enterNewRound(cs.Height, 0) @@ -2231,7 +2492,7 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error } // Check to see if the chain is configured to extend votes. - extEnabled := cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(vote.Height) + extEnabled := cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(vote.Height) if extEnabled { // The chain is configured to extend votes, check that the vote is // not for a nil block and verify the extensions signature against the @@ -2443,12 +2704,15 @@ func (cs *State) signVote( ValidatorIndex: valIdx, Height: cs.Height, Round: cs.Round, - Timestamp: cs.voteTime(), Type: msgType, BlockID: types.BlockID{Hash: hash, PartSetHeader: header}, + // Vote timestamps are pinned to zero: they are excluded from the + // signed bytes so that BLS signatures can be aggregated, and the + // block time comes from the proposer (PBTS) instead of BFT Time. + Timestamp: time.Time{}, } - extEnabled := cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(vote.Height) + extEnabled := cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(vote.Height) if msgType == cmtproto.PrecommitType && !vote.BlockID.IsZero() { // if the signedMessage type is for a non-nil precommit, add // VoteExtension @@ -2469,27 +2733,6 @@ func (cs *State) signVote( return vote, err } -func (cs *State) voteTime() time.Time { - now := cmttime.Now() - minVoteTime := now - // Minimum time increment between blocks - const timeIota = time.Millisecond - // TODO: We should remove next line in case we don't vote for v in case cs.ProposalBlock == nil, - // even if cs.LockedBlock != nil. See https://github.com/cometbft/cometbft/tree/v0.38.x/spec/. - if cs.LockedBlock != nil { - // See the BFT time spec - // https://github.com/cometbft/cometbft/blob/v0.38.x/spec/consensus/bft-time.md - minVoteTime = cs.LockedBlock.Time.Add(timeIota) - } else if cs.ProposalBlock != nil { - minVoteTime = cs.ProposalBlock.Time.Add(timeIota) - } - - if now.After(minVoteTime) { - return now - } - return minVoteTime -} - // sign the vote and publish on internalMsgQueue // block information is only used to extend votes (precommit only); should be nil in all other cases func (cs *State) signAddVote( @@ -2520,12 +2763,12 @@ func (cs *State) signAddVote( return } hasExt := len(vote.ExtensionSignature) > 0 - extEnabled := cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(vote.Height) + extEnabled := cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(vote.Height) if vote.Type == cmtproto.PrecommitType && !vote.BlockID.IsZero() && hasExt != extEnabled { panic(fmt.Errorf("vote extension absence/presence does not match extensions enabled %t!=%t, height %d, type %v", hasExt, extEnabled, vote.Height, vote.Type)) } - cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""}) + cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, "", time.Time{}}) cs.Logger.Debug("signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote) } diff --git a/consensus/state_ingest.go b/consensus/state_ingest.go index 423d85ed677..7b45d4369c3 100644 --- a/consensus/state_ingest.go +++ b/consensus/state_ingest.go @@ -127,7 +127,7 @@ func (ic *IngestCandidate) Verify(state state.State) error { height = ic.Height() blockID = ic.BlockID() chainID = state.ChainID - extensionsPresent = state.ConsensusParams.ABCI.VoteExtensionsEnabled(height) + extensionsPresent = state.ConsensusParams.Feature.VoteExtensionsEnabled(height) ) // ensure invariant diff --git a/consensus/state_ingest_test.go b/consensus/state_ingest_test.go index f6af5bf08d9..2a2fd8c732a 100644 --- a/consensus/state_ingest_test.go +++ b/consensus/state_ingest_test.go @@ -34,7 +34,7 @@ func TestStateIngestVerifiedBlock(t *testing.T) { t.Run("ingestedBlockWithVoteExtensions", func(t *testing.T) { // ARRANGE ts := newIngestTestSuite(t) - ts.cs.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = 1 + ts.cs.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = 1 // Given a verified block with extended commit ic := ts.MakeIngestCandidate() @@ -237,7 +237,7 @@ func TestIngestCandidate(t *testing.T) { name: "extensions invariant mismatch", voteExtensions: true, mutate: func(t *testing.T, ic *IngestCandidate, st *sm.State) { - st.ConsensusParams.ABCI.VoteExtensionsEnableHeight = 0 + st.ConsensusParams.Feature.VoteExtensionsEnableHeight = 0 }, errContains: "invalid ext commit state", }, @@ -337,9 +337,9 @@ func TestIngestCandidate(t *testing.T) { ts := newIngestTestSuite(t) if tt.voteExtensions { - ts.cs.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = 1 + ts.cs.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = 1 } else { - ts.cs.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = 0 + ts.cs.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = 0 } if tt.arrange != nil { @@ -429,7 +429,7 @@ func (ts *ingestTestSuite) MakeIngestCandidateUnverified() IngestCandidate { } var ( - extensionsEnabled = ts.cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(block.Height) + extensionsEnabled = ts.cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(block.Height) chainID = ts.cs.state.ChainID blockHeight = block.Height blockID = types.BlockID{ @@ -454,7 +454,7 @@ func (ts *ingestTestSuite) MakeIngestCandidateUnverified() IngestCandidate { require.True(ts.t, added) } - extCommit := voteSet.MakeExtendedCommit(ts.cs.state.ConsensusParams.ABCI) + extCommit := voteSet.MakeExtendedCommit(ts.cs.state.ConsensusParams.Feature) commit := extCommit.ToCommit() if !extensionsEnabled { extCommit = nil diff --git a/consensus/state_test.go b/consensus/state_test.go index 75e7c1a8a43..23d9e3ed183 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -26,6 +26,7 @@ import ( p2pmock "github.com/cometbft/cometbft/p2p/mock" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" ) /* @@ -216,7 +217,7 @@ func TestStateBadProposal(t *testing.T) { propBlockParts, err := propBlock.MakePartSet(partSize) require.NoError(t, err) blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} - proposal := types.NewProposal(vs2.Height, round, -1, blockID) + proposal := types.NewProposal(vs2.Height, round, -1, blockID, propBlock.Time) p := proposal.ToProto() if err := vs2.SignProposal(cs1.state.ChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -289,7 +290,7 @@ func TestStateOversizedBlock(t *testing.T) { incrementRound(vss[1:]...) blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} - proposal := types.NewProposal(height, round, -1, blockID) + proposal := types.NewProposal(height, round, -1, blockID, propBlock.Time) p := proposal.ToProto() if err := vs2.SignProposal(cs1.state.ChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -1175,7 +1176,7 @@ func TestStateLockPOLSafety2(t *testing.T) { round++ // moving to the next round // in round 2 we see the polkad block from round 0 - newProp := types.NewProposal(height, round, 0, propBlockID0) + newProp := types.NewProposal(height, round, 0, propBlockID0, cmttime.Now()) p := newProp.ToProto() if err := vs3.SignProposal(cs1.state.ChainID, p); err != nil { t.Fatal(err) @@ -1585,7 +1586,7 @@ func TestVerifyVoteExtensionNotCalledOnAbsentPrecommit(t *testing.T) { m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe() cs1, vss := randStateWithApp(4, m) height, round := cs1.Height, cs1.Round - cs1.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = cs1.Height + cs1.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = cs1.Height proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) @@ -1887,7 +1888,7 @@ func TestVoteExtensionEnableHeight(t *testing.T) { m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe() m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe() cs1, vss := randStateWithAppWithHeight(numValidators, m, testCase.enableHeight) - cs1.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.enableHeight + cs1.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = testCase.enableHeight height, round := cs1.Height, cs1.Round timeoutCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) @@ -1952,7 +1953,7 @@ func TestStateDoesntCrashOnInvalidVote(t *testing.T) { voteMessage := &VoteMessage{vote} assert.NotPanics(t, func() { - cs.handleMsg(msgInfo{voteMessage, peer.ID()}) + cs.handleMsg(msgInfo{voteMessage, peer.ID(), time.Time{}}) }) added, err := cs.AddVote(vote, peer.ID()) @@ -2465,26 +2466,26 @@ func TestStateOutputsBlockPartsStats(t *testing.T) { } cs.ProposalBlockParts = types.NewPartSetFromHeader(parts.Header()) - cs.handleMsg(msgInfo{msg, peer.ID()}) + cs.handleMsg(msgInfo{msg, peer.ID(), time.Time{}}) statsMessage := <-cs.statsMsgQueue require.Equal(t, msg, statsMessage.Msg, "") require.Equal(t, peer.ID(), statsMessage.PeerID, "") // sending the same part from different peer - cs.handleMsg(msgInfo{msg, "peer2"}) + cs.handleMsg(msgInfo{msg, "peer2", time.Time{}}) // sending the part with the same height, but different round msg.Round = 1 - cs.handleMsg(msgInfo{msg, peer.ID()}) + cs.handleMsg(msgInfo{msg, peer.ID(), time.Time{}}) // sending the part from the smaller height msg.Height = 0 - cs.handleMsg(msgInfo{msg, peer.ID()}) + cs.handleMsg(msgInfo{msg, peer.ID(), time.Time{}}) // sending the part from the bigger height msg.Height = 3 - cs.handleMsg(msgInfo{msg, peer.ID()}) + cs.handleMsg(msgInfo{msg, peer.ID(), time.Time{}}) select { case <-cs.statsMsgQueue: @@ -2537,20 +2538,20 @@ func TestStateOutputVoteStats(t *testing.T) { vote := signVote(vss[1], cmtproto.PrecommitType, randBytes, types.PartSetHeader{}, true) voteMessage := &VoteMessage{vote} - cs.handleMsg(msgInfo{voteMessage, peer.ID()}) + cs.handleMsg(msgInfo{voteMessage, peer.ID(), time.Time{}}) statsMessage := <-cs.statsMsgQueue require.Equal(t, voteMessage, statsMessage.Msg, "") require.Equal(t, peer.ID(), statsMessage.PeerID, "") // sending the same part from different peer - cs.handleMsg(msgInfo{&VoteMessage{vote}, "peer2"}) + cs.handleMsg(msgInfo{&VoteMessage{vote}, "peer2", time.Time{}}) // sending the vote for the bigger height incrementHeight(vss[1]) vote = signVote(vss[1], cmtproto.PrecommitType, randBytes, types.PartSetHeader{}, true) - cs.handleMsg(msgInfo{&VoteMessage{vote}, peer.ID()}) + cs.handleMsg(msgInfo{&VoteMessage{vote}, peer.ID(), time.Time{}}) select { case <-cs.statsMsgQueue: diff --git a/consensus/types/height_vote_set.go b/consensus/types/height_vote_set.go index a4bb05f9906..90f56115776 100644 --- a/consensus/types/height_vote_set.go +++ b/consensus/types/height_vote_set.go @@ -46,6 +46,7 @@ type HeightVoteSet struct { round int32 // max tracked round roundVoteSets map[int32]RoundVoteSet // keys: [0...round] peerCatchupRounds map[p2p.ID][]int32 // keys: peer.ID; values: at most 2 rounds + commit *types.Commit // a whole aggregated commit received via gossip (catch-up) } func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet { @@ -165,6 +166,25 @@ func (hvs *HeightVoteSet) Precommits(round int32) *types.VoteSet { return hvs.getVoteSet(round, cmtproto.PrecommitType) } +// GetCommit returns the whole aggregated commit for the given round, if one +// was received via gossip (catch-up path). +func (hvs *HeightVoteSet) GetCommit(round int32) *types.Commit { + hvs.mtx.Lock() + defer hvs.mtx.Unlock() + if hvs.commit == nil || hvs.commit.Round != round { + return nil + } + + return hvs.commit +} + +// SetCommit stores a whole aggregated commit received via gossip. +func (hvs *HeightVoteSet) SetCommit(commit *types.Commit) { + hvs.mtx.Lock() + defer hvs.mtx.Unlock() + hvs.commit = commit +} + // Last round and blockID that has +2/3 prevotes for a particular block or nil. // Returns -1 if no such round exists. func (hvs *HeightVoteSet) POLInfo() (polRound int32, polBlockID types.BlockID) { diff --git a/consensus/types/peer_round_state.go b/consensus/types/peer_round_state.go index 4e8b440c015..4b1219f3504 100644 --- a/consensus/types/peer_round_state.go +++ b/consensus/types/peer_round_state.go @@ -39,6 +39,8 @@ type PeerRoundState struct { // All commit precommits peer has for this height & CatchupCommitRound CatchupCommit *bits.BitArray `json:"catchup_commit"` + + HasCatchupCommit bool `json:"has_catchup_commit"` // True if peer has a catchup commit. } // String returns a string representation of the PeerRoundState diff --git a/consensus/types/round_state.go b/consensus/types/round_state.go index b9929647160..a413bd564cd 100644 --- a/consensus/types/round_state.go +++ b/consensus/types/round_state.go @@ -71,14 +71,15 @@ type RoundState struct { StartTime time.Time `json:"start_time"` // Subjective time when +2/3 precommits for Block at Round were found - CommitTime time.Time `json:"commit_time"` - Validators *types.ValidatorSet `json:"validators"` - Proposal *types.Proposal `json:"proposal"` - ProposalBlock *types.Block `json:"proposal_block"` - ProposalBlockParts *types.PartSet `json:"proposal_block_parts"` - LockedRound int32 `json:"locked_round"` - LockedBlock *types.Block `json:"locked_block"` - LockedBlockParts *types.PartSet `json:"locked_block_parts"` + CommitTime time.Time `json:"commit_time"` + Validators *types.ValidatorSet `json:"validators"` + Proposal *types.Proposal `json:"proposal"` + ProposalReceiveTime time.Time `json:"proposal_receive_time"` + ProposalBlock *types.Block `json:"proposal_block"` + ProposalBlockParts *types.PartSet `json:"proposal_block_parts"` + LockedRound int32 `json:"locked_round"` + LockedBlock *types.Block `json:"locked_block"` + LockedBlockParts *types.PartSet `json:"locked_block_parts"` // The variables below starting with "Valid..." derive their name from // the algorithm presented in this paper: @@ -97,7 +98,7 @@ type RoundState struct { ValidBlockParts *types.PartSet `json:"valid_block_parts"` Votes *HeightVoteSet `json:"votes"` CommitRound int32 `json:"commit_round"` // - LastCommit *types.VoteSet `json:"last_commit"` // Last precommits at Height-1 + LastCommit types.VoteSetReader `json:"last_commit"` // Last precommits at Height-1 LastValidators *types.ValidatorSet `json:"last_validators"` TriggeredTimeoutPrecommit bool `json:"triggered_timeout_precommit"` } @@ -186,6 +187,16 @@ func (rs *RoundState) String() string { // StringIndented returns a string func (rs *RoundState) StringIndented(indent string) string { + var lcStr string + switch lc := rs.LastCommit.(type) { + case *types.VoteSet: + lcStr = lc.StringShort() + case *types.Commit: + lcStr = lc.StringIndented(" ") + default: + lcStr = fmt.Sprintf("", lc) + } + return fmt.Sprintf(`RoundState{ %s H:%v R:%v S:%v %s StartTime: %v @@ -212,7 +223,7 @@ func (rs *RoundState) StringIndented(indent string) string { indent, rs.ValidRound, indent, rs.ValidBlockParts.StringShort(), rs.ValidBlock.StringShort(), indent, rs.Votes.StringIndented(indent+" "), - indent, rs.LastCommit.StringShort(), + indent, lcStr, indent, rs.LastValidators.StringIndented(indent+" "), indent) } diff --git a/crypto/bls12381/aggregation.go b/crypto/bls12381/aggregation.go new file mode 100644 index 00000000000..1186dbafe6b --- /dev/null +++ b/crypto/bls12381/aggregation.go @@ -0,0 +1,37 @@ +//go:build bls12381 + +package bls12381 + +import ( + "errors" +) + +// ErrAggregation is returned when aggregation fails. +var ErrAggregation = errors.New("bls12381: failed to aggregate signatures") + +// AggregateSignatures aggregates the given compressed signatures. +// +// Does not group-check the signatures. +func AggregateSignatures(sigsToAgg [][]byte) ([]byte, error) { + var agProj blstAggregateSignature + if !agProj.AggregateCompressed(sigsToAgg, false) { + return nil, ErrAggregation + } + agSig := agProj.ToAffine() + return agSig.Compress(), nil +} + +// VerifyAggregateSignature verifies the given compressed aggregate signature. +// +// Group-checks the signature. +func VerifyAggregateSignature(agSigCompressed []byte, pubks []*PubKey, msg []byte) bool { + agSig := new(blstSignature).Uncompress(agSigCompressed) + if agSig == nil { + return false + } + blsPubKeys := make([]*blstPublicKey, len(pubks)) + for i, pubk := range pubks { + blsPubKeys[i] = pubk.pk + } + return agSig.FastAggregateVerify(true, blsPubKeys, msg, dstMinPk) +} diff --git a/crypto/bls12381/aggregation_none.go b/crypto/bls12381/aggregation_none.go new file mode 100644 index 00000000000..a7a88c3d019 --- /dev/null +++ b/crypto/bls12381/aggregation_none.go @@ -0,0 +1,24 @@ +//go:build !bls12381 + +package bls12381 + +import ( + "errors" +) + +// ErrAggregation is returned when aggregation fails. +var ErrAggregation = errors.New("bls12381: failed to aggregate signatures") + +// AggregateSignatures aggregates the given compressed signatures. +// +// It returns ErrDisabled when built without the `bls12381` build tag. +func AggregateSignatures([][]byte) ([]byte, error) { + return nil, ErrDisabled +} + +// VerifyAggregateSignature verifies the given compressed aggregate signature. +// +// It returns false when built without the `bls12381` build tag. +func VerifyAggregateSignature([]byte, []*PubKey, []byte) bool { + return false +} diff --git a/crypto/bls12381/aggregation_test.go b/crypto/bls12381/aggregation_test.go new file mode 100644 index 00000000000..43541695417 --- /dev/null +++ b/crypto/bls12381/aggregation_test.go @@ -0,0 +1,63 @@ +//go:build bls12381 + +package bls12381_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cometbft/cometbft/crypto/bls12381" +) + +func TestAggregateAndVerify(t *testing.T) { + // Generate private keys. + genPrivKeyFn := func() *bls12381.PrivKey { + k, err := bls12381.GenPrivKey() + require.NoError(t, err) + return k + } + + privateKeys := []*bls12381.PrivKey{ + genPrivKeyFn(), + genPrivKeyFn(), + genPrivKeyFn(), + } + + msg := []byte("hello world") + + // Generate signatures. + signatures := make([][]byte, len(privateKeys)) + for i, privKey := range privateKeys { + sig, err := privKey.Sign(msg) + require.NoError(t, err) + signatures[i] = sig + + valid := privKey.PubKey().VerifySignature(msg, sig) + require.True(t, valid) + } + + // Aggregate signatures. + aggregatedSignature, err := bls12381.AggregateSignatures(signatures) + require.NoError(t, err) + require.NotNil(t, aggregatedSignature) + + pubKeys := make([]*bls12381.PubKey, len(privateKeys)) + for i, privKey := range privateKeys { + pubKey := privKey.PubKey().(bls12381.PubKey) + pubKeys[i] = &pubKey + } + + // Verify aggregated signature + valid := bls12381.VerifyAggregateSignature(aggregatedSignature, pubKeys, msg) + require.True(t, valid) + + // Test with invalid aggregated signature + invalidSignature := []byte("Invalid") + valid = bls12381.VerifyAggregateSignature(invalidSignature, pubKeys, msg) + require.False(t, valid) + + // Verify aggregated signature with one missing pubKey + valid = bls12381.VerifyAggregateSignature(aggregatedSignature, pubKeys[1:], msg) + require.False(t, valid) +} diff --git a/crypto/bls12381/const.go b/crypto/bls12381/const.go index 439a3d55b3e..18cf579ba90 100644 --- a/crypto/bls12381/const.go +++ b/crypto/bls12381/const.go @@ -5,6 +5,8 @@ const ( PrivKeySize = 32 // PubKeySize defines the length of the PubKey byte array. PubKeySize = 96 + // PubKeyCompressedSize defines the length of the compressed PubKey byte array. + PubKeyCompressedSize = 48 // SignatureLength defines the byte length of a BLS signature. SignatureLength = 96 // KeyType is the string constant for the BLS12-381 algorithm. diff --git a/crypto/bls12381/key.go b/crypto/bls12381/key.go index 20f081b563f..5882a91361d 100644 --- a/crypto/bls12381/key.go +++ b/crypto/bls12381/key.go @@ -31,17 +31,17 @@ var _ crypto.PrivKey = &PrivKey{} type PrivKey []byte // GenPrivKeyFromSecret returns ErrDisabled. -func GenPrivKeyFromSecret([]byte) (PrivKey, error) { +func GenPrivKeyFromSecret([]byte) (*PrivKey, error) { return nil, ErrDisabled } // NewPrivateKeyFromBytes returns ErrDisabled. -func NewPrivateKeyFromBytes([]byte) (PrivKey, error) { +func NewPrivateKeyFromBytes([]byte) (*PrivKey, error) { return nil, ErrDisabled } // GenPrivKey returns ErrDisabled. -func GenPrivKey() (PrivKey, error) { +func GenPrivKey() (*PrivKey, error) { return nil, ErrDisabled } @@ -94,6 +94,11 @@ func NewPublicKeyFromBytes([]byte) (*PubKey, error) { return nil, ErrDisabled } +// NewPublicKeyFromCompressedBytes returns ErrDisabled. +func NewPublicKeyFromCompressedBytes([]byte) (*PubKey, error) { + return nil, ErrDisabled +} + // Address always panics. func (PubKey) Address() crypto.Address { panic("bls12_381 is disabled") @@ -109,6 +114,11 @@ func (PubKey) Bytes() []byte { panic("bls12_381 is disabled") } +// Compress always panics. +func (PubKey) Compress() []byte { + panic("bls12_381 is disabled") +} + // Type returns the key's type. func (PubKey) Type() string { return KeyType diff --git a/crypto/bls12381/key_bls12381.go b/crypto/bls12381/key_bls12381.go index e7b89a22fec..95f5a3a3e76 100644 --- a/crypto/bls12381/key_bls12381.go +++ b/crypto/bls12381/key_bls12381.go @@ -22,6 +22,10 @@ const ( ) var ( + // ErrPubKeyDecompression is returned when the decompression of a compressed + // 48-byte long BLS12-381 public key fails. + ErrPubKeyDecompression = errors.New("bls12381: public key decompression error") + // ErrDeserialization is returned when deserialization fails. ErrDeserialization = errors.New("bls12381: deserialization error") // ErrInfinitePubKey is returned when the public key is infinite. It is part @@ -36,10 +40,11 @@ var ( // Changing this to 'minimal-signature-size' would render CometBFT not Ethereum // compatible. type ( - blstPublicKey = blst.P1Affine - blstSignature = blst.P2Affine - blstAggregateSignature = blst.P1Aggregate - blstAggregatePublicKey = blst.P2Aggregate + blstPublicKey = blst.P1Affine + blstSignature = blst.P2Affine + // Signatures live in G2 under min-pubkey-size, so an aggregate signature + // is a P2 aggregate. + blstAggregateSignature = blst.P2Aggregate ) // -------------------------------------. @@ -174,6 +179,20 @@ func NewPublicKeyFromBytes(bz []byte) (*PubKey, error) { return &PubKey{pk: pk}, nil } +// NewPublicKeyFromCompressedBytes returns a new BLS12-381 public key from the +// given bytes. bz must be a compressed BLS12-381 public key of length 48 bytes. +func NewPublicKeyFromCompressedBytes(bz []byte) (*PubKey, error) { + pk := new(blstPublicKey).Uncompress(bz) + if pk == nil { + return nil, ErrPubKeyDecompression + } + // Subgroup and infinity check + if !pk.KeyValidate() { + return nil, ErrInfinitePubKey + } + return &PubKey{pk: pk}, nil +} + // Address returns the address of the key. // // The function will panic if the public key is invalid. @@ -181,6 +200,13 @@ func (pubKey PubKey) Address() crypto.Address { return crypto.Address(tmhash.SumTruncated(pubKey.pk.Serialize())) } +// Compress returns a compressed 48-byte long BLS12-381 public key. +// It does not modify the original public key. Rather, it returns a new slice +// storing the compressed key. +func (pubKey PubKey) Compress() []byte { + return pubKey.pk.Compress() +} + // VerifySignature verifies the given signature. func (pubKey PubKey) VerifySignature(msg, sig []byte) bool { signature := new(blstSignature).Uncompress(sig) diff --git a/crypto/encoding/codec.go b/crypto/encoding/codec.go index 37f90dfaa0b..403b19a1108 100644 --- a/crypto/encoding/codec.go +++ b/crypto/encoding/codec.go @@ -41,23 +41,32 @@ func init() { } } -// PubKeyToProto takes crypto.PubKey and transforms it to a protobuf Pubkey +// PubKeyToProto takes crypto.PubKey and transforms it to a protobuf Pubkey. +// +// NOTE: it switches on the key type, not the concrete Go type, because BLS +// public keys appear both as values and as pointers (PubKeyFromProto returns +// a pointer), and both must serialize identically. func PubKeyToProto(k crypto.PubKey) (pc.PublicKey, error) { var kp pc.PublicKey - switch k := k.(type) { - case ed25519.PubKey: + + if k == nil { + return kp, fmt.Errorf("toproto: key type %v is not supported", k) + } + + switch k.Type() { + case ed25519.KeyType: kp = pc.PublicKey{ Sum: &pc.PublicKey_Ed25519{ - Ed25519: k, + Ed25519: k.Bytes(), }, } - case secp256k1.PubKey: + case secp256k1.KeyType: kp = pc.PublicKey{ Sum: &pc.PublicKey_Secp256K1{ - Secp256K1: k, + Secp256K1: k.Bytes(), }, } - case bls12381.PubKey: + case bls12381.KeyType: if !bls12381.Enabled { return kp, ErrUnsupportedKey{Key: k} } @@ -97,14 +106,18 @@ func PubKeyFromProto(k pc.PublicKey) (crypto.PubKey, error) { return nil, ErrUnsupportedKey{Key: k} } - if len(k.Bls12381) != bls12381.PubKeySize { + switch len(k.Bls12381) { + case bls12381.PubKeySize: + return bls12381.NewPublicKeyFromBytes(k.Bls12381) + case bls12381.PubKeyCompressedSize: + return bls12381.NewPublicKeyFromCompressedBytes(k.Bls12381) + default: return nil, ErrInvalidKeyLen{ Key: k, Got: len(k.Bls12381), Want: bls12381.PubKeySize, } } - return bls12381.NewPublicKeyFromBytes(k.Bls12381) default: return nil, fmt.Errorf("fromproto: key type %v is not supported", k) } @@ -145,15 +158,18 @@ func PubKeyFromTypeAndBytes(pkType string, bytes []byte) (crypto.PubKey, error) return nil, ErrUnsupportedKey{Key: pkType} } - if len(bytes) != bls12381.PubKeySize { + switch len(bytes) { + case bls12381.PubKeySize: + return bls12381.NewPublicKeyFromBytes(bytes) + case bls12381.PubKeyCompressedSize: + return bls12381.NewPublicKeyFromCompressedBytes(bytes) + default: return nil, ErrInvalidKeyLen{ Key: pkType, Got: len(bytes), Want: bls12381.PubKeySize, } } - - return bls12381.NewPublicKeyFromBytes(bytes) default: return nil, ErrUnsupportedKey{Key: pkType} } diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 6f2deec31fb..29632bbf696 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -82,7 +82,7 @@ func TestEvidencePoolBasic(t *testing.T) { next := pool.EvidenceFront() assert.Equal(t, ev, next.Value.(types.Evidence)) - const evidenceBytes int64 = 372 + const evidenceBytes int64 = 382 evs, size = pool.PendingEvidence(evidenceBytes) assert.Equal(t, 1, len(evs)) assert.Equal(t, evidenceBytes, size) // check that the size of the single evidence in bytes is correct diff --git a/internal/test/commit.go b/internal/test/commit.go index 3decd96b65e..b9a28716602 100644 --- a/internal/test/commit.go +++ b/internal/test/commit.go @@ -36,7 +36,7 @@ func MakeCommitFromVoteSet(blockID types.BlockID, voteSet *types.VoteSet, valida } } - return voteSet.MakeExtendedCommit(types.ABCIParams{VoteExtensionsEnableHeight: 0}).ToCommit(), nil + return voteSet.MakeExtendedCommit(types.FeatureParams{VoteExtensionsEnableHeight: 0}).ToCommit(), nil } func MakeCommit(blockID types.BlockID, height int64, round int32, valSet *types.ValidatorSet, privVals []types.PrivValidator, chainID string, now time.Time) (*types.Commit, error) { diff --git a/internal/test/config.go b/internal/test/config.go index 0a7da5f5c43..3b0f8efaca3 100644 --- a/internal/test/config.go +++ b/internal/test/config.go @@ -62,10 +62,15 @@ var testGenesisFmt = `{ "ed25519" ] }, - "abci": { - "vote_extensions_enable_height": "0" + "synchrony": { + "message_delay": "500000000", + "precision": "10000000" }, - "version": {} + "version": {}, + "feature": { + "vote_extensions_enable_height": "0", + "pbts_enable_height": "1" + } }, "validators": [ { diff --git a/internal/test/params.go b/internal/test/params.go index c4421d53cc4..eabc337798a 100644 --- a/internal/test/params.go +++ b/internal/test/params.go @@ -9,6 +9,6 @@ import ( func ConsensusParams() *types.ConsensusParams { c := types.DefaultConsensusParams() // enable vote extensions - c.ABCI.VoteExtensionsEnableHeight = 1 + c.Feature.VoteExtensionsEnableHeight = 1 return c } diff --git a/node/setup.go b/node/setup.go index ba4f82d6980..e0128c2f806 100644 --- a/node/setup.go +++ b/node/setup.go @@ -539,11 +539,11 @@ func createPEXReactorAndAddToSwitch( Seeds: splitAndTrimEmpty(config.P2P.Seeds, ",", " "), SeedMode: config.P2P.SeedMode, // See consensus/reactor.go: blocksToContributeToBecomeGoodPeer 10000 - // blocks assuming 10s blocks ~ 28 hours. + // blocks assuming 2s blocks ~ 6 hours. // TODO (melekes): make it dynamic based on the actual block latencies // from the live network. // https://github.com/tendermint/tendermint/issues/3523 - SeedDisconnectWaitPeriod: 28 * time.Hour, + SeedDisconnectWaitPeriod: 6 * time.Hour, PersistentPeersMaxDialPeriod: config.P2P.PersistentPeersMaxDialPeriod, } diff --git a/privval/file.go b/privval/file.go index a4d024e057d..9040f7d6365 100644 --- a/privval/file.go +++ b/privval/file.go @@ -274,6 +274,11 @@ func (pv *FilePV) SignProposal(chainID string, proposal *cmtproto.Proposal) erro return nil } +// SignBytes signs the given bytes. Implements PrivValidator. +func (pv *FilePV) SignBytes(bytes []byte) ([]byte, error) { + return pv.Key.PrivKey.Sign(bytes) +} + // Save persists the FilePV to disk. func (pv *FilePV) Save() { pv.Key.Save() @@ -335,16 +340,13 @@ func (pv *FilePV) signVote(chainID string, vote *cmtproto.Vote) error { // We might crash before writing to the wal, // causing us to try to re-sign for the same HRS. // If signbytes are the same, use the last signature. - // If they only differ by timestamp, use last timestamp and signature - // Otherwise, return error + // Otherwise, return error. + // NOTE: vote sign bytes do not include a timestamp (it was removed so + // that BLS signatures can be aggregated), so there is no + // differ-only-by-timestamp case for votes. if sameHRS { if bytes.Equal(signBytes, lss.SignBytes) { vote.Signature = lss.Signature - } else if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { - // Compares the canonicalized votes (i.e. without vote extensions - // or vote extension signatures). - vote.Timestamp = timestamp - vote.Signature = lss.Signature } else { err = fmt.Errorf("conflicting data") } @@ -422,28 +424,6 @@ func (pv *FilePV) saveSigned(height int64, round int32, step int8, //----------------------------------------------------------------------------------------- -// Returns the timestamp from the lastSignBytes. -// Returns true if the only difference in the votes is their timestamp. -// Performs these checks on the canonical votes (excluding the vote extension -// and vote extension signatures). -func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) { - var lastVote, newVote cmtproto.CanonicalVote - if err := protoio.UnmarshalDelimited(lastSignBytes, &lastVote); err != nil { - panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into vote: %v", err)) - } - if err := protoio.UnmarshalDelimited(newSignBytes, &newVote); err != nil { - panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err)) - } - - lastTime := lastVote.Timestamp - // set the times to the same value and check equality - now := cmttime.Now() - lastVote.Timestamp = now - newVote.Timestamp = now - - return lastTime, proto.Equal(&newVote, &lastVote) -} - // returns the timestamp from the lastSignBytes. // returns true if the only difference in the proposals is their timestamp func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) { diff --git a/privval/file_test.go b/privval/file_test.go index cba7cc3165b..12904171318 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -291,9 +291,10 @@ func TestDifferByTimestamp(t *testing.T) { signBytes := types.VoteSignBytes(chainID, v) sig := v.Signature extSig := v.ExtensionSignature - timeStamp := vote.Timestamp - // manipulate the timestamp. should get changed back + // The vote timestamp is not part of the signed bytes (it was removed + // to enable BLS signature aggregation), so re-signing the same vote + // reuses the previous signature regardless of the timestamp value. v.Timestamp = v.Timestamp.Add(time.Millisecond) var emptySig []byte v.Signature = emptySig @@ -301,7 +302,6 @@ func TestDifferByTimestamp(t *testing.T) { err = privVal.SignVote("mychainid", v) assert.NoError(t, err, "expected no error on signing same vote") - assert.Equal(t, timeStamp, v.Timestamp) assert.Equal(t, signBytes, types.VoteSignBytes(chainID, v)) assert.Equal(t, sig, v.Signature) assert.Equal(t, extSig, v.ExtensionSignature) @@ -349,17 +349,15 @@ func TestVoteExtensionsAreAlwaysSigned(t *testing.T) { assert.True(t, pubKey.VerifySignature(vesb2, vpb2.ExtensionSignature)) assert.False(t, pubKey.VerifySignature(vesb1, vpb2.ExtensionSignature)) - // We now manipulate the timestamp of the vote with the extension, as per - // TestDifferByTimestamp - expectedTimestamp := vpb2.Timestamp - + // We now manipulate the timestamp of the vote with the extension. The + // timestamp is not part of the signed bytes, so signing succeeds and the + // extension is re-signed. vpb2.Timestamp = vpb2.Timestamp.Add(time.Millisecond) vpb2.Signature = nil vpb2.ExtensionSignature = nil err = privVal.SignVote("mychainid", vpb2) assert.NoError(t, err, "expected no error signing same vote with manipulated timestamp and vote extension") - assert.Equal(t, expectedTimestamp, vpb2.Timestamp) vesb3 := types.VoteExtensionSignBytes("mychainid", vpb2) assert.True(t, pubKey.VerifySignature(vesb3, vpb2.ExtensionSignature)) diff --git a/privval/msgs.go b/privval/msgs.go index 4b440e612df..2bcc33b0bf9 100644 --- a/privval/msgs.go +++ b/privval/msgs.go @@ -28,6 +28,10 @@ func mustWrapMsg(pb proto.Message) privvalproto.Message { msg.Sum = &privvalproto.Message_SignedProposalResponse{SignedProposalResponse: pb} case *privvalproto.SignProposalRequest: msg.Sum = &privvalproto.Message_SignProposalRequest{SignProposalRequest: pb} + case *privvalproto.SignBytesRequest: + msg.Sum = &privvalproto.Message_SignBytesRequest{SignBytesRequest: pb} + case *privvalproto.SignBytesResponse: + msg.Sum = &privvalproto.Message_SignBytesResponse{SignBytesResponse: pb} case *privvalproto.PingRequest: msg.Sum = &privvalproto.Message_PingRequest{PingRequest: pb} case *privvalproto.PingResponse: diff --git a/privval/retry_signer_client.go b/privval/retry_signer_client.go index 271e146474c..83edcf89cf4 100644 --- a/privval/retry_signer_client.go +++ b/privval/retry_signer_client.go @@ -94,3 +94,22 @@ func (sc *RetrySignerClient) SignProposal(chainID string, proposal *cmtproto.Pro } return fmt.Errorf("exhausted all attempts to sign proposal: %w", err) } + +func (sc *RetrySignerClient) SignBytes(bytes []byte) ([]byte, error) { + var ( + sig []byte + err error + ) + for i := 0; i < sc.retries || sc.retries == 0; i++ { + sig, err = sc.next.SignBytes(bytes) + if err == nil { + return sig, nil + } + // If remote signer errors, we don't retry. + if _, ok := err.(*RemoteSignerError); ok { + return nil, err + } + time.Sleep(sc.timeout) + } + return nil, fmt.Errorf("exhausted all attempts to sign bytes: %w", err) +} diff --git a/privval/signer_client.go b/privval/signer_client.go index 062e6cdf4d7..3e967ad9474 100644 --- a/privval/signer_client.go +++ b/privval/signer_client.go @@ -133,3 +133,21 @@ func (sc *SignerClient) SignProposal(chainID string, proposal *cmtproto.Proposal return nil } + +// SignBytes requests a remote signer to sign bytes. +func (sc *SignerClient) SignBytes(bytes []byte) ([]byte, error) { + response, err := sc.endpoint.SendRequest(mustWrapMsg(&privvalproto.SignBytesRequest{Value: bytes})) + if err != nil { + return nil, err + } + + resp := response.GetSignBytesResponse() + if resp == nil { + return nil, cmterrors.ErrRequiredField{Field: "response"} + } + if resp.Error != nil { + return nil, &RemoteSignerError{Code: int(resp.Error.Code), Description: resp.Error.Description} + } + + return resp.Signature, nil +} diff --git a/privval/signer_client_test.go b/privval/signer_client_test.go index 91fe69c3094..19d24dbae62 100644 --- a/privval/signer_client_test.go +++ b/privval/signer_client_test.go @@ -302,6 +302,29 @@ func TestSignerVoteKeepAlive(t *testing.T) { } } +func TestSignerSignBytes(t *testing.T) { + for _, tc := range getSignerTestCases(t) { + t.Cleanup(func() { + if err := tc.signerServer.Stop(); err != nil { + t.Error(err) + } + }) + t.Cleanup(func() { + if err := tc.signerClient.Close(); err != nil { + t.Error(err) + } + }) + + bytes := cmtrand.Bytes(32) + signature, err := tc.signerClient.SignBytes(bytes) + require.NoError(t, err) + + pubKey, err := tc.mockPV.GetPubKey() + require.NoError(t, err) + require.True(t, pubKey.VerifySignature(bytes, signature)) + } +} + func TestSignerSignProposalErrors(t *testing.T) { for _, tc := range getSignerTestCases(t) { // Replace service with a mock that always fails diff --git a/privval/signer_requestHandler.go b/privval/signer_requestHandler.go index 23a9ffd190c..cbfb091fe2a 100644 --- a/privval/signer_requestHandler.go +++ b/privval/signer_requestHandler.go @@ -88,6 +88,16 @@ func DefaultValidationRequestHandler( } else { res = mustWrapMsg(&privvalproto.SignedProposalResponse{Proposal: *proposal, Error: nil}) } + case *privvalproto.Message_SignBytesRequest: + var signature []byte + signature, err = privVal.SignBytes(r.SignBytesRequest.Value) + if err != nil { + res = mustWrapMsg(&privvalproto.SignBytesResponse{ + Signature: nil, Error: &privvalproto.RemoteSignerError{Code: 0, Description: err.Error()}, + }) + } else { + res = mustWrapMsg(&privvalproto.SignBytesResponse{Signature: signature, Error: nil}) + } case *privvalproto.Message_PingRequest: err, res = nil, mustWrapMsg(&privvalproto.PingResponse{}) diff --git a/proto/buf.yaml b/proto/buf.yaml index a646c2030a7..06acd6a646a 100644 --- a/proto/buf.yaml +++ b/proto/buf.yaml @@ -5,6 +5,12 @@ deps: breaking: use: - FILE + ignore: + # Intentional wire-format changes from the bera re-fork (PR #51): + # CanonicalVote drops the timestamp field and ConsensusParams replaces + # authority with synchrony to align with bera-v1.x. + - tendermint/types/canonical.proto + - tendermint/types/params.proto lint: use: - BASIC diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index e9194cab6eb..f818c3e6334 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package tendermint.abci; import "gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "tendermint/crypto/keys.proto"; // For more information on gogo.proto, see: @@ -171,6 +172,10 @@ message RequestProcessProposal { bytes next_validators_hash = 7; // address of the public key of the original proposer of the block. bytes proposer_address = 8; + // address of the public key of the validator that will propose the block at the + // next height (also the proposer at round 1 of the current height if consensus + // goes into multiple rounds). + bytes next_proposer_address = 9; } // Extends a vote with application-injected data @@ -402,6 +407,12 @@ message ResponseFinalizeBlock { tendermint.types.ConsensusParams consensus_param_updates = 4; // app_hash is the hash of the applications' state which is used to confirm that execution of the transactions was deterministic. It is up to the application to decide which algorithm to use. bytes app_hash = 5; + // delay between the time when this block is committed and the next height is started. + // previously `timeout_commit` in config.toml + google.protobuf.Duration next_block_delay = 6 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; } //---------------------------------------- diff --git a/proto/tendermint/consensus/message.go b/proto/tendermint/consensus/message.go index 8acfc137ffd..4df89cc1b84 100644 --- a/proto/tendermint/consensus/message.go +++ b/proto/tendermint/consensus/message.go @@ -17,9 +17,17 @@ var ( _ p2p.Wrapper = &NewValidBlock{} _ p2p.Wrapper = &NewRoundStep{} _ p2p.Wrapper = &HasVote{} + _ p2p.Wrapper = &HasProposalBlockPart{} _ p2p.Wrapper = &BlockPart{} + _ p2p.Wrapper = &Commit{} ) +func (m *Commit) Wrap() proto.Message { + cm := &Message{} + cm.Sum = &Message_Commit{Commit: m} + return cm +} + func (m *VoteSetBits) Wrap() proto.Message { cm := &Message{} cm.Sum = &Message_VoteSetBits{VoteSetBits: m} @@ -38,6 +46,12 @@ func (m *HasVote) Wrap() proto.Message { return cm } +func (m *HasProposalBlockPart) Wrap() proto.Message { + cm := &Message{} + cm.Sum = &Message_HasProposalBlockPart{HasProposalBlockPart: m} + return cm +} + func (m *Vote) Wrap() proto.Message { cm := &Message{} cm.Sum = &Message_Vote{Vote: m} @@ -99,12 +113,18 @@ func (m *Message) Unwrap() (proto.Message, error) { case *Message_HasVote: return m.GetHasVote(), nil + case *Message_HasProposalBlockPart: + return m.GetHasProposalBlockPart(), nil + case *Message_VoteSetMaj23: return m.GetVoteSetMaj23(), nil case *Message_VoteSetBits: return m.GetVoteSetBits(), nil + case *Message_Commit: + return m.GetCommit(), nil + default: return nil, fmt.Errorf("unknown message: %T", msg) } diff --git a/proto/tendermint/consensus/types.pb.go b/proto/tendermint/consensus/types.pb.go index 7fda3f33bd4..b665a9ec373 100644 --- a/proto/tendermint/consensus/types.pb.go +++ b/proto/tendermint/consensus/types.pb.go @@ -394,6 +394,52 @@ func (m *Vote) GetVote() *types.Vote { return nil } +// An entire Commit is sent when signatures are aggregated and we cannot +// extract a single signature. +type Commit struct { + Commit *types.Commit `protobuf:"bytes,1,opt,name=commit,proto3" json:"commit,omitempty"` +} + +func (m *Commit) Reset() { *m = Commit{} } +func (m *Commit) String() string { return proto.CompactTextString(m) } +func (*Commit) ProtoMessage() {} +func (*Commit) Descriptor() ([]byte, []int) { + return fileDescriptor_81a22d2efc008981, []int{6} +} +func (m *Commit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Commit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Commit.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Commit) XXX_Merge(src proto.Message) { + xxx_messageInfo_Commit.Merge(m, src) +} +func (m *Commit) XXX_Size() int { + return m.Size() +} +func (m *Commit) XXX_DiscardUnknown() { + xxx_messageInfo_Commit.DiscardUnknown(m) +} + +var xxx_messageInfo_Commit proto.InternalMessageInfo + +func (m *Commit) GetCommit() *types.Commit { + if m != nil { + return m.Commit + } + return nil +} + // HasVote is sent to indicate that a particular vote has been received. type HasVote struct { Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` @@ -406,7 +452,7 @@ func (m *HasVote) Reset() { *m = HasVote{} } func (m *HasVote) String() string { return proto.CompactTextString(m) } func (*HasVote) ProtoMessage() {} func (*HasVote) Descriptor() ([]byte, []int) { - return fileDescriptor_81a22d2efc008981, []int{6} + return fileDescriptor_81a22d2efc008981, []int{7} } func (m *HasVote) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -463,6 +509,67 @@ func (m *HasVote) GetIndex() int32 { return 0 } +// HasProposalBlockPart is sent to indicate that a particular block part has been received. +type HasProposalBlockPart struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Round int32 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` + Index int32 `protobuf:"varint,3,opt,name=index,proto3" json:"index,omitempty"` +} + +func (m *HasProposalBlockPart) Reset() { *m = HasProposalBlockPart{} } +func (m *HasProposalBlockPart) String() string { return proto.CompactTextString(m) } +func (*HasProposalBlockPart) ProtoMessage() {} +func (*HasProposalBlockPart) Descriptor() ([]byte, []int) { + return fileDescriptor_81a22d2efc008981, []int{8} +} +func (m *HasProposalBlockPart) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HasProposalBlockPart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HasProposalBlockPart.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *HasProposalBlockPart) XXX_Merge(src proto.Message) { + xxx_messageInfo_HasProposalBlockPart.Merge(m, src) +} +func (m *HasProposalBlockPart) XXX_Size() int { + return m.Size() +} +func (m *HasProposalBlockPart) XXX_DiscardUnknown() { + xxx_messageInfo_HasProposalBlockPart.DiscardUnknown(m) +} + +var xxx_messageInfo_HasProposalBlockPart proto.InternalMessageInfo + +func (m *HasProposalBlockPart) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *HasProposalBlockPart) GetRound() int32 { + if m != nil { + return m.Round + } + return 0 +} + +func (m *HasProposalBlockPart) GetIndex() int32 { + if m != nil { + return m.Index + } + return 0 +} + // VoteSetMaj23 is sent to indicate that a given BlockID has seen +2/3 votes. type VoteSetMaj23 struct { Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` @@ -475,7 +582,7 @@ func (m *VoteSetMaj23) Reset() { *m = VoteSetMaj23{} } func (m *VoteSetMaj23) String() string { return proto.CompactTextString(m) } func (*VoteSetMaj23) ProtoMessage() {} func (*VoteSetMaj23) Descriptor() ([]byte, []int) { - return fileDescriptor_81a22d2efc008981, []int{7} + return fileDescriptor_81a22d2efc008981, []int{9} } func (m *VoteSetMaj23) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -545,7 +652,7 @@ func (m *VoteSetBits) Reset() { *m = VoteSetBits{} } func (m *VoteSetBits) String() string { return proto.CompactTextString(m) } func (*VoteSetBits) ProtoMessage() {} func (*VoteSetBits) Descriptor() ([]byte, []int) { - return fileDescriptor_81a22d2efc008981, []int{8} + return fileDescriptor_81a22d2efc008981, []int{10} } func (m *VoteSetBits) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -620,6 +727,8 @@ type Message struct { // *Message_HasVote // *Message_VoteSetMaj23 // *Message_VoteSetBits + // *Message_HasProposalBlockPart + // *Message_Commit Sum isMessage_Sum `protobuf_oneof:"sum"` } @@ -627,7 +736,7 @@ func (m *Message) Reset() { *m = Message{} } func (m *Message) String() string { return proto.CompactTextString(m) } func (*Message) ProtoMessage() {} func (*Message) Descriptor() ([]byte, []int) { - return fileDescriptor_81a22d2efc008981, []int{9} + return fileDescriptor_81a22d2efc008981, []int{11} } func (m *Message) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -689,16 +798,24 @@ type Message_VoteSetMaj23 struct { type Message_VoteSetBits struct { VoteSetBits *VoteSetBits `protobuf:"bytes,9,opt,name=vote_set_bits,json=voteSetBits,proto3,oneof" json:"vote_set_bits,omitempty"` } +type Message_HasProposalBlockPart struct { + HasProposalBlockPart *HasProposalBlockPart `protobuf:"bytes,10,opt,name=has_proposal_block_part,json=hasProposalBlockPart,proto3,oneof" json:"has_proposal_block_part,omitempty"` +} +type Message_Commit struct { + Commit *Commit `protobuf:"bytes,11,opt,name=commit,proto3,oneof" json:"commit,omitempty"` +} -func (*Message_NewRoundStep) isMessage_Sum() {} -func (*Message_NewValidBlock) isMessage_Sum() {} -func (*Message_Proposal) isMessage_Sum() {} -func (*Message_ProposalPol) isMessage_Sum() {} -func (*Message_BlockPart) isMessage_Sum() {} -func (*Message_Vote) isMessage_Sum() {} -func (*Message_HasVote) isMessage_Sum() {} -func (*Message_VoteSetMaj23) isMessage_Sum() {} -func (*Message_VoteSetBits) isMessage_Sum() {} +func (*Message_NewRoundStep) isMessage_Sum() {} +func (*Message_NewValidBlock) isMessage_Sum() {} +func (*Message_Proposal) isMessage_Sum() {} +func (*Message_ProposalPol) isMessage_Sum() {} +func (*Message_BlockPart) isMessage_Sum() {} +func (*Message_Vote) isMessage_Sum() {} +func (*Message_HasVote) isMessage_Sum() {} +func (*Message_VoteSetMaj23) isMessage_Sum() {} +func (*Message_VoteSetBits) isMessage_Sum() {} +func (*Message_HasProposalBlockPart) isMessage_Sum() {} +func (*Message_Commit) isMessage_Sum() {} func (m *Message) GetSum() isMessage_Sum { if m != nil { @@ -770,6 +887,20 @@ func (m *Message) GetVoteSetBits() *VoteSetBits { return nil } +func (m *Message) GetHasProposalBlockPart() *HasProposalBlockPart { + if x, ok := m.GetSum().(*Message_HasProposalBlockPart); ok { + return x.HasProposalBlockPart + } + return nil +} + +func (m *Message) GetCommit() *Commit { + if x, ok := m.GetSum().(*Message_Commit); ok { + return x.Commit + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*Message) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -782,6 +913,8 @@ func (*Message) XXX_OneofWrappers() []interface{} { (*Message_HasVote)(nil), (*Message_VoteSetMaj23)(nil), (*Message_VoteSetBits)(nil), + (*Message_HasProposalBlockPart)(nil), + (*Message_Commit)(nil), } } @@ -792,7 +925,9 @@ func init() { proto.RegisterType((*ProposalPOL)(nil), "tendermint.consensus.ProposalPOL") proto.RegisterType((*BlockPart)(nil), "tendermint.consensus.BlockPart") proto.RegisterType((*Vote)(nil), "tendermint.consensus.Vote") + proto.RegisterType((*Commit)(nil), "tendermint.consensus.Commit") proto.RegisterType((*HasVote)(nil), "tendermint.consensus.HasVote") + proto.RegisterType((*HasProposalBlockPart)(nil), "tendermint.consensus.HasProposalBlockPart") proto.RegisterType((*VoteSetMaj23)(nil), "tendermint.consensus.VoteSetMaj23") proto.RegisterType((*VoteSetBits)(nil), "tendermint.consensus.VoteSetBits") proto.RegisterType((*Message)(nil), "tendermint.consensus.Message") @@ -801,61 +936,66 @@ func init() { func init() { proto.RegisterFile("tendermint/consensus/types.proto", fileDescriptor_81a22d2efc008981) } var fileDescriptor_81a22d2efc008981 = []byte{ - // 855 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4f, 0x6f, 0xe3, 0x44, - 0x14, 0xb7, 0xa9, 0xd3, 0x24, 0xcf, 0x49, 0x0b, 0xa3, 0xee, 0x2a, 0x04, 0x48, 0x82, 0xb9, 0x54, - 0x08, 0x39, 0x28, 0x3d, 0xac, 0xb4, 0x42, 0x02, 0xcc, 0x9f, 0xf5, 0xae, 0x36, 0xbb, 0xc1, 0x59, - 0xad, 0x10, 0x17, 0xcb, 0x89, 0x87, 0x64, 0xd8, 0xd8, 0x63, 0x79, 0x26, 0x29, 0xbd, 0xf2, 0x09, - 0xf8, 0x00, 0x7c, 0x0d, 0x24, 0x3e, 0x42, 0x8f, 0x3d, 0x72, 0xaa, 0x50, 0xfa, 0x11, 0x10, 0x9c, - 0xd1, 0x8c, 0x27, 0xb1, 0x43, 0xdd, 0x42, 0x2f, 0x48, 0xdc, 0x66, 0xe6, 0xbd, 0xf7, 0x9b, 0x37, - 0xbf, 0xf7, 0xde, 0xcf, 0x86, 0x1e, 0xc7, 0x71, 0x88, 0xd3, 0x88, 0xc4, 0xbc, 0x3f, 0xa5, 0x31, - 0xc3, 0x31, 0x5b, 0xb2, 0x3e, 0x3f, 0x4b, 0x30, 0xb3, 0x93, 0x94, 0x72, 0x8a, 0x8e, 0x72, 0x0f, - 0x7b, 0xeb, 0xd1, 0x3e, 0x9a, 0xd1, 0x19, 0x95, 0x0e, 0x7d, 0xb1, 0xca, 0x7c, 0xdb, 0x45, 0xb4, - 0x05, 0x99, 0xb0, 0xfe, 0x84, 0xf0, 0x1d, 0xb4, 0xf6, 0xdb, 0x05, 0x0f, 0x79, 0x5e, 0xb4, 0x5a, - 0x3f, 0xeb, 0xd0, 0x78, 0x86, 0x4f, 0x3d, 0xba, 0x8c, 0xc3, 0x31, 0xc7, 0x09, 0xba, 0x0f, 0xfb, - 0x73, 0x4c, 0x66, 0x73, 0xde, 0xd2, 0x7b, 0xfa, 0xf1, 0x9e, 0xa7, 0x76, 0xe8, 0x08, 0x2a, 0xa9, - 0x70, 0x6a, 0xbd, 0xd6, 0xd3, 0x8f, 0x2b, 0x5e, 0xb6, 0x41, 0x08, 0x0c, 0xc6, 0x71, 0xd2, 0xda, - 0xeb, 0xe9, 0xc7, 0x4d, 0x4f, 0xae, 0xd1, 0x03, 0x68, 0x31, 0x3c, 0xa5, 0x71, 0xc8, 0x7c, 0x46, - 0xe2, 0x29, 0xf6, 0x19, 0x0f, 0x52, 0xee, 0x73, 0x12, 0xe1, 0x96, 0x21, 0x31, 0xef, 0x29, 0xfb, - 0x58, 0x98, 0xc7, 0xc2, 0xfa, 0x82, 0x44, 0x18, 0xbd, 0x0f, 0x6f, 0x2c, 0x02, 0xc6, 0xfd, 0x29, - 0x8d, 0x22, 0xc2, 0xfd, 0xec, 0xba, 0x8a, 0xbc, 0xee, 0x50, 0x18, 0x3e, 0x93, 0xe7, 0x32, 0x55, - 0xeb, 0x0f, 0x1d, 0x9a, 0xcf, 0xf0, 0xe9, 0xcb, 0x60, 0x41, 0x42, 0x67, 0x41, 0xa7, 0xaf, 0xee, - 0x98, 0xf8, 0xd7, 0x70, 0x6f, 0x22, 0xc2, 0xfc, 0x44, 0xe4, 0xc6, 0x30, 0xf7, 0xe7, 0x38, 0x08, - 0x71, 0x2a, 0x5f, 0x62, 0x0e, 0xba, 0x76, 0xa1, 0x06, 0x19, 0x5f, 0xa3, 0x20, 0xe5, 0x63, 0xcc, - 0x5d, 0xe9, 0xe6, 0x18, 0xe7, 0x97, 0x5d, 0xcd, 0x43, 0x12, 0x63, 0xc7, 0x82, 0x3e, 0x06, 0x33, - 0x47, 0x66, 0xf2, 0xc5, 0xe6, 0xa0, 0x53, 0xc4, 0x13, 0x75, 0xb2, 0x45, 0x9d, 0x6c, 0x87, 0xf0, - 0x4f, 0xd3, 0x34, 0x38, 0xf3, 0x60, 0x0b, 0xc4, 0xd0, 0x5b, 0x50, 0x27, 0x4c, 0x91, 0x20, 0x9f, - 0x5f, 0xf3, 0x6a, 0x84, 0x65, 0x8f, 0xb7, 0x5c, 0xa8, 0x8d, 0x52, 0x9a, 0x50, 0x16, 0x2c, 0xd0, - 0x47, 0x50, 0x4b, 0xd4, 0x5a, 0xbe, 0xd9, 0x1c, 0xb4, 0x4b, 0xd2, 0x56, 0x1e, 0x2a, 0xe3, 0x6d, - 0x84, 0xf5, 0x93, 0x0e, 0xe6, 0xc6, 0x38, 0x7a, 0xfe, 0xf4, 0x46, 0xfe, 0x3e, 0x00, 0xb4, 0x89, - 0xf1, 0x13, 0xba, 0xf0, 0x8b, 0x64, 0xbe, 0xbe, 0xb1, 0x8c, 0xe8, 0x42, 0xd6, 0x05, 0x3d, 0x82, - 0x46, 0xd1, 0x5b, 0xd1, 0xf9, 0x0f, 0xcf, 0x57, 0xb9, 0x99, 0x05, 0x34, 0xeb, 0x15, 0xd4, 0x9d, - 0x0d, 0x27, 0x77, 0xac, 0xed, 0x87, 0x60, 0x08, 0xee, 0xd5, 0xdd, 0xf7, 0xcb, 0x4b, 0xa9, 0xee, - 0x94, 0x9e, 0xd6, 0x00, 0x8c, 0x97, 0x94, 0x8b, 0x0e, 0x34, 0x56, 0x94, 0x63, 0xc5, 0x66, 0x49, - 0xa4, 0xf0, 0xf2, 0xa4, 0x8f, 0xf5, 0x83, 0x0e, 0x55, 0x37, 0x60, 0x32, 0xee, 0x6e, 0xf9, 0x9d, - 0x80, 0x21, 0xd0, 0x64, 0x7e, 0x07, 0x65, 0xad, 0x36, 0x26, 0xb3, 0x18, 0x87, 0x43, 0x36, 0x7b, - 0x71, 0x96, 0x60, 0x4f, 0x3a, 0x0b, 0x28, 0x12, 0x87, 0xf8, 0x7b, 0xd9, 0x50, 0x15, 0x2f, 0xdb, - 0x58, 0xbf, 0xe8, 0xd0, 0x10, 0x19, 0x8c, 0x31, 0x1f, 0x06, 0xdf, 0x0d, 0x4e, 0xfe, 0x8b, 0x4c, - 0xbe, 0x80, 0x5a, 0xd6, 0xe0, 0x24, 0x54, 0xdd, 0xfd, 0xe6, 0xf5, 0x40, 0x59, 0xbb, 0xc7, 0x9f, - 0x3b, 0x87, 0x82, 0xe5, 0xf5, 0x65, 0xb7, 0xaa, 0x0e, 0xbc, 0xaa, 0x8c, 0x7d, 0x1c, 0x5a, 0xbf, - 0xeb, 0x60, 0xaa, 0xd4, 0x1d, 0xc2, 0xd9, 0xff, 0x27, 0x73, 0xf4, 0x10, 0x2a, 0xa2, 0x03, 0x98, - 0x1c, 0xce, 0x7f, 0xdb, 0xdc, 0x59, 0x88, 0xf5, 0xa7, 0x01, 0xd5, 0x21, 0x66, 0x2c, 0x98, 0x61, - 0xf4, 0x04, 0x0e, 0x62, 0x7c, 0x9a, 0x0d, 0x94, 0x2f, 0x65, 0x34, 0xeb, 0x3b, 0xcb, 0x2e, 0xfb, - 0x00, 0xd8, 0x45, 0x99, 0x76, 0x35, 0xaf, 0x11, 0x17, 0x65, 0x7b, 0x08, 0x87, 0x02, 0x6b, 0x25, - 0xf4, 0xd0, 0x97, 0x89, 0x4a, 0xbe, 0xcc, 0xc1, 0x7b, 0x37, 0x82, 0xe5, 0xda, 0xe9, 0x6a, 0x5e, - 0x33, 0xde, 0x11, 0xd3, 0xa2, 0xb4, 0x94, 0x8c, 0x70, 0x8e, 0xb3, 0x51, 0x10, 0xb7, 0x20, 0x2d, - 0xe8, 0xcb, 0xbf, 0x89, 0x40, 0xc6, 0xf5, 0xbb, 0xb7, 0x23, 0x8c, 0x9e, 0x3f, 0x75, 0x77, 0x35, - 0x00, 0x7d, 0x02, 0x90, 0x4b, 0xa9, 0x62, 0xbb, 0x5b, 0x8e, 0xb2, 0xd5, 0x0a, 0x57, 0xf3, 0xea, - 0x5b, 0x31, 0x15, 0x52, 0x20, 0x07, 0x7a, 0xff, 0xba, 0x3c, 0xe6, 0xb1, 0xa2, 0x0b, 0x5d, 0x2d, - 0x1b, 0x6b, 0xf4, 0x10, 0x6a, 0xf3, 0x80, 0xf9, 0x32, 0xaa, 0x2a, 0xa3, 0xde, 0x29, 0x8f, 0x52, - 0xb3, 0xef, 0x6a, 0x5e, 0x75, 0xae, 0x64, 0xe0, 0x09, 0x1c, 0x88, 0x38, 0xf9, 0x39, 0x89, 0xc4, - 0x38, 0xb6, 0x6a, 0xb7, 0x15, 0xb4, 0x38, 0xb8, 0xa2, 0xa0, 0xab, 0xe2, 0x20, 0x3f, 0x82, 0xe6, - 0x16, 0x4b, 0xf4, 0x53, 0xab, 0x7e, 0x1b, 0x89, 0x85, 0x41, 0x12, 0x24, 0xae, 0xf2, 0xad, 0x53, - 0x81, 0x3d, 0xb6, 0x8c, 0x9c, 0xaf, 0xce, 0xd7, 0x1d, 0xfd, 0x62, 0xdd, 0xd1, 0x7f, 0x5b, 0x77, - 0xf4, 0x1f, 0xaf, 0x3a, 0xda, 0xc5, 0x55, 0x47, 0xfb, 0xf5, 0xaa, 0xa3, 0x7d, 0xf3, 0x60, 0x46, - 0xf8, 0x7c, 0x39, 0xb1, 0xa7, 0x34, 0xea, 0x4f, 0x69, 0x84, 0xf9, 0xe4, 0x5b, 0x9e, 0x2f, 0xb2, - 0x3f, 0x8e, 0xb2, 0x7f, 0x96, 0xc9, 0xbe, 0xb4, 0x9d, 0xfc, 0x15, 0x00, 0x00, 0xff, 0xff, 0x94, - 0xdf, 0xca, 0xd0, 0xd2, 0x08, 0x00, 0x00, + // 930 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4f, 0x6f, 0xdc, 0x44, + 0x14, 0xb7, 0xc9, 0xfe, 0xcb, 0x73, 0xfe, 0xc0, 0x68, 0x5b, 0x96, 0x50, 0x36, 0xc1, 0x5c, 0xa2, + 0x0a, 0xed, 0x56, 0x1b, 0x89, 0x4a, 0x11, 0x12, 0xb0, 0xfc, 0xa9, 0x5b, 0x35, 0xed, 0xe2, 0xad, + 0x2a, 0xd4, 0x8b, 0xe5, 0xb5, 0x87, 0xdd, 0xa1, 0x6b, 0x8f, 0xe5, 0x99, 0x24, 0xe4, 0xca, 0x27, + 0x40, 0xe2, 0xca, 0xd7, 0x40, 0xe2, 0x23, 0xf4, 0xd8, 0x23, 0xa7, 0x0a, 0x25, 0x1f, 0x01, 0x71, + 0x47, 0xf3, 0x3c, 0xeb, 0x9d, 0x10, 0x27, 0x10, 0x0e, 0x48, 0xbd, 0xd9, 0x7e, 0xef, 0xfd, 0xe6, + 0xbd, 0xdf, 0x7b, 0xef, 0x37, 0x86, 0x1d, 0x49, 0xd3, 0x98, 0xe6, 0x09, 0x4b, 0x65, 0x3f, 0xe2, + 0xa9, 0xa0, 0xa9, 0x38, 0x14, 0x7d, 0x79, 0x92, 0x51, 0xd1, 0xcb, 0x72, 0x2e, 0x39, 0x69, 0x2f, + 0x3d, 0x7a, 0xa5, 0xc7, 0x56, 0x7b, 0xca, 0xa7, 0x1c, 0x1d, 0xfa, 0xea, 0xa9, 0xf0, 0xdd, 0x32, + 0xd1, 0xe6, 0x6c, 0x22, 0xfa, 0x13, 0x26, 0xcf, 0xa1, 0x6d, 0xdd, 0x32, 0x3c, 0xf0, 0xbb, 0x69, + 0x75, 0x7f, 0xb1, 0x61, 0xed, 0x11, 0x3d, 0xf6, 0xf9, 0x61, 0x1a, 0x8f, 0x25, 0xcd, 0xc8, 0x4d, + 0x68, 0xcc, 0x28, 0x9b, 0xce, 0x64, 0xc7, 0xde, 0xb1, 0x77, 0x57, 0x7c, 0xfd, 0x46, 0xda, 0x50, + 0xcf, 0x95, 0x53, 0xe7, 0x8d, 0x1d, 0x7b, 0xb7, 0xee, 0x17, 0x2f, 0x84, 0x40, 0x4d, 0x48, 0x9a, + 0x75, 0x56, 0x76, 0xec, 0xdd, 0x75, 0x1f, 0x9f, 0xc9, 0x5d, 0xe8, 0x08, 0x1a, 0xf1, 0x34, 0x16, + 0x81, 0x60, 0x69, 0x44, 0x03, 0x21, 0xc3, 0x5c, 0x06, 0x92, 0x25, 0xb4, 0x53, 0x43, 0xcc, 0x1b, + 0xda, 0x3e, 0x56, 0xe6, 0xb1, 0xb2, 0x3e, 0x61, 0x09, 0x25, 0xb7, 0xe1, 0xad, 0x79, 0x28, 0x64, + 0x10, 0xf1, 0x24, 0x61, 0x32, 0x28, 0x8e, 0xab, 0xe3, 0x71, 0x9b, 0xca, 0xf0, 0x39, 0x7e, 0xc7, + 0x54, 0xdd, 0x3f, 0x6d, 0x58, 0x7f, 0x44, 0x8f, 0x9f, 0x86, 0x73, 0x16, 0x0f, 0xe7, 0x3c, 0x7a, + 0x7e, 0xcd, 0xc4, 0xbf, 0x81, 0x1b, 0x13, 0x15, 0x16, 0x64, 0x2a, 0x37, 0x41, 0x65, 0x30, 0xa3, + 0x61, 0x4c, 0x73, 0xac, 0xc4, 0x19, 0x6c, 0xf7, 0x8c, 0x1e, 0x14, 0x7c, 0x8d, 0xc2, 0x5c, 0x8e, + 0xa9, 0xf4, 0xd0, 0x6d, 0x58, 0x7b, 0xf1, 0x6a, 0xdb, 0xf2, 0x09, 0x62, 0x9c, 0xb3, 0x90, 0x4f, + 0xc0, 0x59, 0x22, 0x0b, 0xac, 0xd8, 0x19, 0x74, 0x4d, 0x3c, 0xd5, 0xa7, 0x9e, 0xea, 0x53, 0x6f, + 0xc8, 0xe4, 0x67, 0x79, 0x1e, 0x9e, 0xf8, 0x50, 0x02, 0x09, 0xf2, 0x2e, 0xac, 0x32, 0xa1, 0x49, + 0xc0, 0xf2, 0x5b, 0x7e, 0x8b, 0x89, 0xa2, 0x78, 0xd7, 0x83, 0xd6, 0x28, 0xe7, 0x19, 0x17, 0xe1, + 0x9c, 0x7c, 0x0c, 0xad, 0x4c, 0x3f, 0x63, 0xcd, 0xce, 0x60, 0xab, 0x22, 0x6d, 0xed, 0xa1, 0x33, + 0x2e, 0x23, 0xdc, 0x9f, 0x6d, 0x70, 0x16, 0xc6, 0xd1, 0xe3, 0x87, 0x97, 0xf2, 0xf7, 0x21, 0x90, + 0x45, 0x4c, 0x90, 0xf1, 0x79, 0x60, 0x92, 0xf9, 0xe6, 0xc2, 0x32, 0xe2, 0x73, 0xec, 0x0b, 0xb9, + 0x07, 0x6b, 0xa6, 0xb7, 0xa6, 0xf3, 0x1f, 0xca, 0xd7, 0xb9, 0x39, 0x06, 0x9a, 0xfb, 0x1c, 0x56, + 0x87, 0x0b, 0x4e, 0xae, 0xd9, 0xdb, 0x3b, 0x50, 0x53, 0xdc, 0xeb, 0xb3, 0x6f, 0x56, 0xb7, 0x52, + 0x9f, 0x89, 0x9e, 0xee, 0x00, 0x6a, 0x4f, 0xb9, 0x54, 0x13, 0x58, 0x3b, 0xe2, 0x92, 0x6a, 0x36, + 0x2b, 0x22, 0x95, 0x97, 0x8f, 0x3e, 0xee, 0x3e, 0x34, 0x8a, 0x9e, 0x90, 0x3b, 0xd0, 0xd0, 0xdd, + 0x2a, 0xe2, 0x3a, 0x17, 0xe3, 0xf4, 0xe8, 0x6a, 0x3f, 0xf7, 0x07, 0x1b, 0x9a, 0x5e, 0x28, 0xf0, + 0xcc, 0xeb, 0xd5, 0xb6, 0x07, 0x35, 0x85, 0x88, 0xb5, 0x6d, 0x54, 0x8d, 0xe9, 0x98, 0x4d, 0x53, + 0x1a, 0x1f, 0x88, 0xe9, 0x93, 0x93, 0x8c, 0xfa, 0xe8, 0xac, 0xa0, 0x58, 0x1a, 0xd3, 0xef, 0x71, + 0x18, 0xeb, 0x7e, 0xf1, 0xe2, 0x3e, 0x83, 0xb6, 0x17, 0x8a, 0x72, 0x3e, 0xfe, 0x23, 0xd9, 0x25, + 0xf6, 0x8a, 0x89, 0xfd, 0xab, 0x0d, 0x6b, 0xaa, 0xba, 0x31, 0x95, 0x07, 0xe1, 0x77, 0x83, 0xbd, + 0xff, 0xa3, 0xca, 0x2f, 0xa1, 0x55, 0x2c, 0x1e, 0x8b, 0xf5, 0xd6, 0xbd, 0x73, 0x31, 0x10, 0xcb, + 0xbc, 0xff, 0xc5, 0x70, 0x53, 0x75, 0xff, 0xf4, 0xd5, 0x76, 0x53, 0x7f, 0xf0, 0x9b, 0x18, 0x7b, + 0x3f, 0x76, 0xff, 0xb0, 0xc1, 0xd1, 0xa9, 0x0f, 0x99, 0x14, 0xaf, 0x4f, 0xe6, 0x64, 0x1f, 0xea, + 0x6a, 0x32, 0x05, 0x8a, 0xc6, 0xbf, 0x5d, 0xba, 0x22, 0xc4, 0xfd, 0xa9, 0x01, 0xcd, 0x03, 0x2a, + 0x44, 0x38, 0xa5, 0xe4, 0x01, 0x6c, 0xa4, 0xf4, 0xb8, 0x58, 0xf4, 0x00, 0xe5, 0xbd, 0x98, 0x6b, + 0xb7, 0x57, 0x75, 0x31, 0xf5, 0xcc, 0xeb, 0xc3, 0xb3, 0xfc, 0xb5, 0xd4, 0xbc, 0x4e, 0x0e, 0x60, + 0x53, 0x61, 0x1d, 0x29, 0x9d, 0x0e, 0x30, 0x51, 0xe4, 0xcb, 0x19, 0x7c, 0x70, 0x29, 0xd8, 0x52, + 0xd3, 0x3d, 0xcb, 0x5f, 0x4f, 0xcf, 0x89, 0xbc, 0x29, 0x79, 0x15, 0xd2, 0xb2, 0xc4, 0x59, 0x8c, + 0xb5, 0x67, 0x48, 0x1e, 0xf9, 0xea, 0x6f, 0xe2, 0x54, 0x70, 0xfd, 0xfe, 0xd5, 0x08, 0xa3, 0xc7, + 0x0f, 0xbd, 0xf3, 0xda, 0x44, 0x3e, 0x05, 0x58, 0x4a, 0xbc, 0x66, 0x7b, 0xbb, 0x1a, 0xa5, 0x5c, + 0x2b, 0xcf, 0xf2, 0x57, 0x4b, 0x91, 0x57, 0x12, 0x85, 0x42, 0xd3, 0xb8, 0x28, 0xdb, 0xcb, 0x58, + 0x35, 0x85, 0x9e, 0x55, 0xc8, 0x0d, 0xd9, 0x87, 0xd6, 0x2c, 0x14, 0x01, 0x46, 0x35, 0x31, 0xea, + 0xbd, 0xea, 0x28, 0xad, 0x2b, 0x9e, 0xe5, 0x37, 0x67, 0x5a, 0x62, 0x1e, 0xc0, 0x86, 0x8a, 0xc3, + 0x6b, 0x2e, 0x51, 0xeb, 0xd8, 0x69, 0x5d, 0xd5, 0x50, 0x73, 0x71, 0x55, 0x43, 0x8f, 0xcc, 0x45, + 0xbe, 0x07, 0xeb, 0x25, 0x96, 0x9a, 0xa7, 0xce, 0xea, 0x55, 0x24, 0x1a, 0x8b, 0xa4, 0x48, 0x3c, + 0x32, 0xf6, 0x2a, 0x82, 0xb7, 0x55, 0x41, 0x65, 0x43, 0x0c, 0x46, 0x01, 0x21, 0x6f, 0x5f, 0x5a, + 0xdf, 0x05, 0xcd, 0xf2, 0x2c, 0xbf, 0x3d, 0xab, 0xd2, 0xb2, 0x8f, 0x4a, 0x69, 0x76, 0x10, 0xf3, + 0x56, 0x35, 0x66, 0x21, 0xcf, 0x9e, 0xb5, 0x10, 0xe8, 0x61, 0x1d, 0x56, 0xc4, 0x61, 0x32, 0xfc, + 0xfa, 0xc5, 0x69, 0xd7, 0x7e, 0x79, 0xda, 0xb5, 0x7f, 0x3f, 0xed, 0xda, 0x3f, 0x9e, 0x75, 0xad, + 0x97, 0x67, 0x5d, 0xeb, 0xb7, 0xb3, 0xae, 0xf5, 0xec, 0xee, 0x94, 0xc9, 0xd9, 0xe1, 0xa4, 0x17, + 0xf1, 0xa4, 0x1f, 0xf1, 0x84, 0xca, 0xc9, 0xb7, 0x72, 0xf9, 0x50, 0xfc, 0xa6, 0x55, 0xfd, 0xe8, + 0x4d, 0x1a, 0x68, 0xdb, 0xfb, 0x2b, 0x00, 0x00, 0xff, 0xff, 0xda, 0xbc, 0x35, 0x01, 0x07, 0x0a, + 0x00, 0x00, } func (m *NewRoundStep) Marshal() (dAtA []byte, err error) { @@ -1125,6 +1265,41 @@ func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *Commit) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Commit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Commit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Commit != nil { + { + size, err := m.Commit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *HasVote) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1168,6 +1343,44 @@ func (m *HasVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *HasProposalBlockPart) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *HasProposalBlockPart) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HasProposalBlockPart) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Index != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x18 + } + if m.Round != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Round)) + i-- + dAtA[i] = 0x10 + } + if m.Height != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *VoteSetMaj23) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1495,6 +1708,48 @@ func (m *Message_VoteSetBits) MarshalToSizedBuffer(dAtA []byte) (int, error) { } return len(dAtA) - i, nil } +func (m *Message_HasProposalBlockPart) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Message_HasProposalBlockPart) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.HasProposalBlockPart != nil { + { + size, err := m.HasProposalBlockPart.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + return len(dAtA) - i, nil +} +func (m *Message_Commit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Message_Commit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Commit != nil { + { + size, err := m.Commit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + return len(dAtA) - i, nil +} func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -1612,6 +1867,19 @@ func (m *Vote) Size() (n int) { return n } +func (m *Commit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Commit != nil { + l = m.Commit.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func (m *HasVote) Size() (n int) { if m == nil { return 0 @@ -1633,6 +1901,24 @@ func (m *HasVote) Size() (n int) { return n } +func (m *HasProposalBlockPart) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovTypes(uint64(m.Height)) + } + if m.Round != 0 { + n += 1 + sovTypes(uint64(m.Round)) + } + if m.Index != 0 { + n += 1 + sovTypes(uint64(m.Index)) + } + return n +} + func (m *VoteSetMaj23) Size() (n int) { if m == nil { return 0 @@ -1795,6 +2081,30 @@ func (m *Message_VoteSetBits) Size() (n int) { } return n } +func (m *Message_HasProposalBlockPart) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.HasProposalBlockPart != nil { + l = m.HasProposalBlockPart.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} +func (m *Message_Commit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Commit != nil { + l = m.Commit.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 @@ -2535,6 +2845,92 @@ func (m *Vote) Unmarshal(dAtA []byte) error { } return nil } +func (m *Commit) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Commit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Commit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Commit == nil { + m.Commit = &types.Commit{} + } + if err := m.Commit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *HasVote) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2661,6 +3057,113 @@ func (m *HasVote) Unmarshal(dAtA []byte) error { } return nil } +func (m *HasProposalBlockPart) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HasProposalBlockPart: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HasProposalBlockPart: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Round", wireType) + } + m.Round = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Round |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Index |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *VoteSetMaj23) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -3318,6 +3821,76 @@ func (m *Message) Unmarshal(dAtA []byte) error { } m.Sum = &Message_VoteSetBits{v} iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasProposalBlockPart", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &HasProposalBlockPart{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &Message_HasProposalBlockPart{v} + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Commit{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &Message_Commit{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/proto/tendermint/consensus/types.proto b/proto/tendermint/consensus/types.proto index ad0830eac83..18f4dd0d074 100644 --- a/proto/tendermint/consensus/types.proto +++ b/proto/tendermint/consensus/types.proto @@ -52,6 +52,12 @@ message Vote { tendermint.types.Vote vote = 1; } +// An entire Commit is sent when signatures are aggregated and we cannot +// extract a single signature. +message Commit { + tendermint.types.Commit commit = 1; +} + // HasVote is sent to indicate that a particular vote has been received. message HasVote { int64 height = 1; @@ -60,6 +66,13 @@ message HasVote { int32 index = 4; } +// HasProposalBlockPart is sent to indicate that a particular block part has been received. +message HasProposalBlockPart { + int64 height = 1; + int32 round = 2; + int32 index = 3; +} + // VoteSetMaj23 is sent to indicate that a given BlockID has seen +2/3 votes. message VoteSetMaj23 { int64 height = 1; @@ -94,5 +107,9 @@ message Message { HasVote has_vote = 7; VoteSetMaj23 vote_set_maj23 = 8; VoteSetBits vote_set_bits = 9; + // Fields 10 and 11 are fork-pinned wire numbers (bera-v1.x compatibility, relied on by + // rolling upgrades). If an upstream rebase adds new entries here, renumber upstream's to 12+. + HasProposalBlockPart has_proposal_block_part = 10; + Commit commit = 11; } } diff --git a/proto/tendermint/privval/types.pb.go b/proto/tendermint/privval/types.pb.go index 7c57002811a..28cbf9f8d7b 100644 --- a/proto/tendermint/privval/types.pb.go +++ b/proto/tendermint/privval/types.pb.go @@ -424,6 +424,104 @@ func (m *SignedProposalResponse) GetError() *RemoteSignerError { return nil } +// SignBytesRequest is a request to sign arbitrary bytes +type SignBytesRequest struct { + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *SignBytesRequest) Reset() { *m = SignBytesRequest{} } +func (m *SignBytesRequest) String() string { return proto.CompactTextString(m) } +func (*SignBytesRequest) ProtoMessage() {} +func (*SignBytesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cb4e437a5328cf9c, []int{7} +} +func (m *SignBytesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignBytesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignBytesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignBytesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignBytesRequest.Merge(m, src) +} +func (m *SignBytesRequest) XXX_Size() int { + return m.Size() +} +func (m *SignBytesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SignBytesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SignBytesRequest proto.InternalMessageInfo + +func (m *SignBytesRequest) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +// SignBytesResponse is a response containing a signature or an error +type SignBytesResponse struct { + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + Error *RemoteSignerError `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *SignBytesResponse) Reset() { *m = SignBytesResponse{} } +func (m *SignBytesResponse) String() string { return proto.CompactTextString(m) } +func (*SignBytesResponse) ProtoMessage() {} +func (*SignBytesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cb4e437a5328cf9c, []int{8} +} +func (m *SignBytesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignBytesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignBytesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignBytesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignBytesResponse.Merge(m, src) +} +func (m *SignBytesResponse) XXX_Size() int { + return m.Size() +} +func (m *SignBytesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SignBytesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SignBytesResponse proto.InternalMessageInfo + +func (m *SignBytesResponse) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *SignBytesResponse) GetError() *RemoteSignerError { + if m != nil { + return m.Error + } + return nil +} + // PingRequest is a request to confirm that the connection is alive. type PingRequest struct { } @@ -432,7 +530,7 @@ func (m *PingRequest) Reset() { *m = PingRequest{} } func (m *PingRequest) String() string { return proto.CompactTextString(m) } func (*PingRequest) ProtoMessage() {} func (*PingRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cb4e437a5328cf9c, []int{7} + return fileDescriptor_cb4e437a5328cf9c, []int{9} } func (m *PingRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -469,7 +567,7 @@ func (m *PingResponse) Reset() { *m = PingResponse{} } func (m *PingResponse) String() string { return proto.CompactTextString(m) } func (*PingResponse) ProtoMessage() {} func (*PingResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cb4e437a5328cf9c, []int{8} + return fileDescriptor_cb4e437a5328cf9c, []int{10} } func (m *PingResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -508,6 +606,8 @@ type Message struct { // *Message_SignedProposalResponse // *Message_PingRequest // *Message_PingResponse + // *Message_SignBytesRequest + // *Message_SignBytesResponse Sum isMessage_Sum `protobuf_oneof:"sum"` } @@ -515,7 +615,7 @@ func (m *Message) Reset() { *m = Message{} } func (m *Message) String() string { return proto.CompactTextString(m) } func (*Message) ProtoMessage() {} func (*Message) Descriptor() ([]byte, []int) { - return fileDescriptor_cb4e437a5328cf9c, []int{9} + return fileDescriptor_cb4e437a5328cf9c, []int{11} } func (m *Message) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -574,6 +674,12 @@ type Message_PingRequest struct { type Message_PingResponse struct { PingResponse *PingResponse `protobuf:"bytes,8,opt,name=ping_response,json=pingResponse,proto3,oneof" json:"ping_response,omitempty"` } +type Message_SignBytesRequest struct { + SignBytesRequest *SignBytesRequest `protobuf:"bytes,9,opt,name=sign_bytes_request,json=signBytesRequest,proto3,oneof" json:"sign_bytes_request,omitempty"` +} +type Message_SignBytesResponse struct { + SignBytesResponse *SignBytesResponse `protobuf:"bytes,10,opt,name=sign_bytes_response,json=signBytesResponse,proto3,oneof" json:"sign_bytes_response,omitempty"` +} func (*Message_PubKeyRequest) isMessage_Sum() {} func (*Message_PubKeyResponse) isMessage_Sum() {} @@ -583,6 +689,8 @@ func (*Message_SignProposalRequest) isMessage_Sum() {} func (*Message_SignedProposalResponse) isMessage_Sum() {} func (*Message_PingRequest) isMessage_Sum() {} func (*Message_PingResponse) isMessage_Sum() {} +func (*Message_SignBytesRequest) isMessage_Sum() {} +func (*Message_SignBytesResponse) isMessage_Sum() {} func (m *Message) GetSum() isMessage_Sum { if m != nil { @@ -647,6 +755,20 @@ func (m *Message) GetPingResponse() *PingResponse { return nil } +func (m *Message) GetSignBytesRequest() *SignBytesRequest { + if x, ok := m.GetSum().(*Message_SignBytesRequest); ok { + return x.SignBytesRequest + } + return nil +} + +func (m *Message) GetSignBytesResponse() *SignBytesResponse { + if x, ok := m.GetSum().(*Message_SignBytesResponse); ok { + return x.SignBytesResponse + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*Message) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -658,6 +780,8 @@ func (*Message) XXX_OneofWrappers() []interface{} { (*Message_SignedProposalResponse)(nil), (*Message_PingRequest)(nil), (*Message_PingResponse)(nil), + (*Message_SignBytesRequest)(nil), + (*Message_SignBytesResponse)(nil), } } @@ -670,6 +794,8 @@ func init() { proto.RegisterType((*SignedVoteResponse)(nil), "tendermint.privval.SignedVoteResponse") proto.RegisterType((*SignProposalRequest)(nil), "tendermint.privval.SignProposalRequest") proto.RegisterType((*SignedProposalResponse)(nil), "tendermint.privval.SignedProposalResponse") + proto.RegisterType((*SignBytesRequest)(nil), "tendermint.privval.SignBytesRequest") + proto.RegisterType((*SignBytesResponse)(nil), "tendermint.privval.SignBytesResponse") proto.RegisterType((*PingRequest)(nil), "tendermint.privval.PingRequest") proto.RegisterType((*PingResponse)(nil), "tendermint.privval.PingResponse") proto.RegisterType((*Message)(nil), "tendermint.privval.Message") @@ -678,55 +804,60 @@ func init() { func init() { proto.RegisterFile("tendermint/privval/types.proto", fileDescriptor_cb4e437a5328cf9c) } var fileDescriptor_cb4e437a5328cf9c = []byte{ - // 757 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x95, 0x4d, 0x4f, 0xe3, 0x46, - 0x18, 0xc7, 0x6d, 0xc8, 0x0b, 0x3c, 0x21, 0x21, 0x0c, 0x94, 0x86, 0x88, 0x9a, 0x34, 0x55, 0x5b, - 0x94, 0x43, 0x52, 0x51, 0xb5, 0x17, 0x7a, 0x29, 0x60, 0x35, 0x51, 0x84, 0x9d, 0x4e, 0x42, 0x41, - 0x48, 0x95, 0x95, 0x97, 0xc1, 0x58, 0x10, 0x8f, 0xd7, 0xe3, 0x20, 0xe5, 0xbc, 0xb7, 0x3d, 0xad, - 0xb4, 0x5f, 0x62, 0xcf, 0xfb, 0x29, 0x38, 0x72, 0xdc, 0xd3, 0x6a, 0x05, 0x5f, 0x64, 0x95, 0xf1, - 0xc4, 0x76, 0xde, 0xd0, 0xae, 0xb8, 0x79, 0x9e, 0xe7, 0xc9, 0xef, 0xf9, 0xff, 0x3d, 0xff, 0xc8, - 0xa0, 0x78, 0xc4, 0xee, 0x11, 0xb7, 0x6f, 0xd9, 0x5e, 0xc5, 0x71, 0xad, 0xbb, 0xbb, 0xf6, 0x6d, - 0xc5, 0x1b, 0x3a, 0x84, 0x95, 0x1d, 0x97, 0x7a, 0x14, 0xa1, 0xb0, 0x5f, 0x16, 0xfd, 0xfc, 0x96, - 0x49, 0x4d, 0xca, 0xdb, 0x95, 0xd1, 0x93, 0x3f, 0x99, 0xdf, 0x8d, 0x90, 0xba, 0xee, 0xd0, 0xf1, - 0x68, 0xe5, 0x86, 0x0c, 0xd9, 0x9c, 0x2e, 0xe7, 0x47, 0xb7, 0x14, 0x6b, 0xb0, 0x81, 0x49, 0x9f, - 0x7a, 0xa4, 0x69, 0x99, 0x36, 0x71, 0x55, 0xd7, 0xa5, 0x2e, 0x42, 0x10, 0xeb, 0xd2, 0x1e, 0xc9, - 0xc9, 0x05, 0x79, 0x3f, 0x8e, 0xf9, 0x33, 0x2a, 0x40, 0xaa, 0x47, 0x58, 0xd7, 0xb5, 0x1c, 0xcf, - 0xa2, 0x76, 0x6e, 0xa9, 0x20, 0xef, 0xaf, 0xe2, 0x68, 0xa9, 0x58, 0x82, 0x74, 0x63, 0xd0, 0xa9, - 0x93, 0x21, 0x26, 0xaf, 0x06, 0x84, 0x79, 0x68, 0x07, 0x56, 0xba, 0xd7, 0x6d, 0xcb, 0x36, 0xac, - 0x1e, 0x47, 0xad, 0xe2, 0x24, 0x3f, 0xd7, 0x7a, 0xc5, 0x37, 0x32, 0x64, 0xc6, 0xc3, 0xcc, 0xa1, - 0x36, 0x23, 0xe8, 0x10, 0x92, 0xce, 0xa0, 0x63, 0xdc, 0x90, 0x21, 0x1f, 0x4e, 0x1d, 0xec, 0x96, - 0x23, 0x6f, 0xc0, 0xf7, 0x55, 0x6e, 0x0c, 0x3a, 0xb7, 0x56, 0xb7, 0x4e, 0x86, 0x47, 0xb1, 0xfb, - 0x4f, 0x7b, 0x12, 0x4e, 0x38, 0x1c, 0x82, 0x0e, 0x21, 0x4e, 0x46, 0xd2, 0xb9, 0xae, 0xd4, 0xc1, - 0xcf, 0xe5, 0xd9, 0x97, 0x57, 0x9e, 0xf1, 0x89, 0xfd, 0xdf, 0x14, 0x2f, 0x60, 0x7d, 0x54, 0xfd, - 0x8f, 0x7a, 0x64, 0x2c, 0xbd, 0x04, 0xb1, 0x3b, 0xea, 0x11, 0xa1, 0x64, 0x3b, 0x8a, 0xf3, 0xdf, - 0x1e, 0x1f, 0xe6, 0x33, 0x13, 0x36, 0x97, 0x26, 0x6d, 0xbe, 0x96, 0x01, 0xf1, 0x85, 0x3d, 0x1f, - 0x2e, 0xac, 0xfe, 0xf6, 0x35, 0x74, 0xe1, 0xd0, 0xdf, 0xf1, 0x22, 0x7f, 0xd7, 0xb0, 0x39, 0xaa, - 0x36, 0x5c, 0xea, 0x50, 0xd6, 0xbe, 0x1d, 0x7b, 0xfc, 0x13, 0x56, 0x1c, 0x51, 0x12, 0x4a, 0xf2, - 0xb3, 0x4a, 0x82, 0x1f, 0x05, 0xb3, 0xcf, 0xf9, 0x7d, 0x27, 0xc3, 0xb6, 0xef, 0x37, 0x5c, 0x26, - 0x3c, 0xff, 0xf5, 0x2d, 0xdb, 0x84, 0xf7, 0x70, 0xe7, 0x8b, 0xfc, 0xa7, 0x21, 0xd5, 0xb0, 0x6c, - 0x53, 0xf8, 0x2e, 0x66, 0x60, 0xcd, 0x3f, 0xfa, 0xca, 0x8a, 0x1f, 0xe2, 0x90, 0x3c, 0x25, 0x8c, - 0xb5, 0x4d, 0x82, 0xea, 0xb0, 0x2e, 0x42, 0x68, 0xb8, 0xfe, 0xb8, 0x10, 0xfb, 0xe3, 0xbc, 0x8d, - 0x13, 0x71, 0xaf, 0x4a, 0x38, 0xed, 0x4c, 0xe4, 0x5f, 0x83, 0x6c, 0x08, 0xf3, 0x97, 0x09, 0xfd, - 0xc5, 0xe7, 0x68, 0xfe, 0x64, 0x55, 0xc2, 0x19, 0x67, 0xf2, 0x1f, 0xf2, 0x2f, 0x6c, 0x30, 0xcb, - 0xb4, 0x8d, 0x51, 0x22, 0x02, 0x79, 0xcb, 0x1c, 0xf8, 0xd3, 0x3c, 0xe0, 0x54, 0xa8, 0xab, 0x12, - 0x5e, 0x67, 0x53, 0x39, 0xbf, 0x84, 0x2d, 0xc6, 0xef, 0x6b, 0x0c, 0x15, 0x32, 0x63, 0x9c, 0xfa, - 0xcb, 0x22, 0xea, 0x64, 0x9e, 0xab, 0x12, 0x46, 0x6c, 0x36, 0xe5, 0xff, 0xc3, 0x77, 0x5c, 0xee, - 0xf8, 0x12, 0x03, 0xc9, 0x71, 0x0e, 0xff, 0x75, 0x11, 0x7c, 0x2a, 0xa7, 0x55, 0x09, 0x6f, 0xb2, - 0x39, 0xf1, 0xbd, 0x82, 0x9c, 0x90, 0x1e, 0x59, 0x20, 0xe4, 0x27, 0xf8, 0x86, 0xd2, 0x62, 0xf9, - 0xd3, 0xf1, 0xac, 0x4a, 0x78, 0x9b, 0xcd, 0x0f, 0xee, 0x09, 0xac, 0x39, 0x96, 0x6d, 0x06, 0xea, - 0x93, 0x9c, 0xbd, 0x37, 0xf7, 0x06, 0xc3, 0x94, 0x55, 0x25, 0x9c, 0x72, 0xc2, 0x23, 0xfa, 0x07, - 0xd2, 0x82, 0x22, 0x24, 0xae, 0x70, 0x4c, 0x61, 0x31, 0x26, 0x10, 0xb6, 0xe6, 0x44, 0xce, 0x47, - 0x71, 0x58, 0x66, 0x83, 0x7e, 0xe9, 0xbd, 0x0c, 0x09, 0x1e, 0x72, 0x86, 0x10, 0x64, 0x54, 0x8c, - 0x75, 0xdc, 0x34, 0xce, 0xb4, 0xba, 0xa6, 0x9f, 0x6b, 0x59, 0x09, 0x29, 0x90, 0x0f, 0x6a, 0xea, - 0x45, 0x43, 0x3d, 0x6e, 0xa9, 0x27, 0x06, 0x56, 0x9b, 0x0d, 0x5d, 0x6b, 0xaa, 0x59, 0x19, 0xe5, - 0x60, 0x4b, 0xf4, 0x35, 0xdd, 0x38, 0xd6, 0x35, 0x4d, 0x3d, 0x6e, 0xd5, 0x74, 0x2d, 0xbb, 0x84, - 0x7e, 0x80, 0x1d, 0xd1, 0x09, 0xcb, 0x46, 0xab, 0x76, 0xaa, 0xea, 0x67, 0xad, 0xec, 0x32, 0xfa, - 0x1e, 0x36, 0x45, 0x1b, 0xab, 0x7f, 0x9f, 0x04, 0x8d, 0x58, 0x84, 0x78, 0x8e, 0x6b, 0x2d, 0x35, - 0xe8, 0xc4, 0x8f, 0xf4, 0xfb, 0x47, 0x45, 0x7e, 0x78, 0x54, 0xe4, 0xcf, 0x8f, 0x8a, 0xfc, 0xf6, - 0x49, 0x91, 0x1e, 0x9e, 0x14, 0xe9, 0xe3, 0x93, 0x22, 0x5d, 0xfe, 0x61, 0x5a, 0xde, 0xf5, 0xa0, - 0x53, 0xee, 0xd2, 0x7e, 0xa5, 0x4b, 0xfb, 0xc4, 0xeb, 0x5c, 0x79, 0xe1, 0x83, 0xff, 0x9d, 0x9b, - 0xfd, 0x4a, 0x76, 0x12, 0xbc, 0xf3, 0xfb, 0x97, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa9, 0x65, 0xab, - 0xc1, 0x42, 0x07, 0x00, 0x00, + // 839 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4b, 0x6f, 0xe3, 0x54, + 0x14, 0xb6, 0xdb, 0x24, 0x6d, 0x4f, 0xfa, 0x48, 0x6f, 0x43, 0xc9, 0x44, 0xc5, 0x13, 0xcc, 0xab, + 0xca, 0x22, 0x41, 0x83, 0x60, 0x33, 0x6c, 0x48, 0x6b, 0xe1, 0x28, 0x1a, 0x3b, 0xdc, 0x64, 0xe8, + 0x68, 0x24, 0x64, 0xe5, 0x71, 0xc7, 0xb5, 0x26, 0xf1, 0x35, 0xbe, 0x76, 0xa4, 0xac, 0xd9, 0xb1, + 0x42, 0xe2, 0x4f, 0xf0, 0x53, 0x66, 0x39, 0x4b, 0x56, 0x08, 0xda, 0x3f, 0x82, 0x7c, 0x7d, 0x63, + 0x3b, 0xaf, 0x0a, 0xd4, 0x9d, 0xef, 0x39, 0xc7, 0xdf, 0xe3, 0xf8, 0x7e, 0x92, 0x41, 0x09, 0x88, + 0x3b, 0x26, 0xfe, 0xd4, 0x71, 0x83, 0xa6, 0xe7, 0x3b, 0xb3, 0xd9, 0x60, 0xd2, 0x0c, 0xe6, 0x1e, + 0x61, 0x0d, 0xcf, 0xa7, 0x01, 0x45, 0x28, 0xed, 0x37, 0x44, 0xbf, 0x5a, 0xb6, 0xa9, 0x4d, 0x79, + 0xbb, 0x19, 0x3d, 0xc5, 0x93, 0xd5, 0x8b, 0x0c, 0xd2, 0xc8, 0x9f, 0x7b, 0x01, 0x6d, 0xbe, 0x25, + 0x73, 0xb6, 0xa1, 0xcb, 0xf1, 0xb3, 0x2c, 0x6a, 0x1b, 0x4e, 0x31, 0x99, 0xd2, 0x80, 0xf4, 0x1c, + 0xdb, 0x25, 0xbe, 0xe6, 0xfb, 0xd4, 0x47, 0x08, 0x72, 0x23, 0x3a, 0x26, 0x15, 0xb9, 0x26, 0x5f, + 0xe6, 0x31, 0x7f, 0x46, 0x35, 0x28, 0x8e, 0x09, 0x1b, 0xf9, 0x8e, 0x17, 0x38, 0xd4, 0xad, 0xec, + 0xd4, 0xe4, 0xcb, 0x03, 0x9c, 0x2d, 0xa9, 0x75, 0x38, 0xea, 0x86, 0xc3, 0x0e, 0x99, 0x63, 0xf2, + 0x73, 0x48, 0x58, 0x80, 0x9e, 0xc0, 0xfe, 0xe8, 0x76, 0xe0, 0xb8, 0x96, 0x33, 0xe6, 0x50, 0x07, + 0x78, 0x8f, 0x9f, 0xdb, 0x63, 0xf5, 0x57, 0x19, 0x8e, 0x17, 0xc3, 0xcc, 0xa3, 0x2e, 0x23, 0xe8, + 0x39, 0xec, 0x79, 0xe1, 0xd0, 0x7a, 0x4b, 0xe6, 0x7c, 0xb8, 0xf8, 0xec, 0xa2, 0x91, 0xd9, 0x40, + 0xec, 0xab, 0xd1, 0x0d, 0x87, 0x13, 0x67, 0xd4, 0x21, 0xf3, 0x56, 0xee, 0xdd, 0x5f, 0x4f, 0x25, + 0x5c, 0xf0, 0x38, 0x08, 0x7a, 0x0e, 0x79, 0x12, 0x49, 0xe7, 0xba, 0x8a, 0xcf, 0x3e, 0x6b, 0xac, + 0x2f, 0xaf, 0xb1, 0xe6, 0x13, 0xc7, 0xef, 0xa8, 0xaf, 0xe0, 0x24, 0xaa, 0xfe, 0x48, 0x03, 0xb2, + 0x90, 0x5e, 0x87, 0xdc, 0x8c, 0x06, 0x44, 0x28, 0x39, 0xcf, 0xc2, 0xc5, 0xdb, 0xe3, 0xc3, 0x7c, + 0x66, 0xc9, 0xe6, 0xce, 0xb2, 0xcd, 0x5f, 0x64, 0x40, 0x9c, 0x70, 0x1c, 0x83, 0x0b, 0xab, 0x5f, + 0xfe, 0x17, 0x74, 0xe1, 0x30, 0xe6, 0x78, 0x94, 0xbf, 0x5b, 0x38, 0x8b, 0xaa, 0x5d, 0x9f, 0x7a, + 0x94, 0x0d, 0x26, 0x0b, 0x8f, 0xdf, 0xc0, 0xbe, 0x27, 0x4a, 0x42, 0x49, 0x75, 0x5d, 0x49, 0xf2, + 0x52, 0x32, 0xfb, 0x90, 0xdf, 0xdf, 0x65, 0x38, 0x8f, 0xfd, 0xa6, 0x64, 0xc2, 0xf3, 0xb7, 0xff, + 0x87, 0x4d, 0x78, 0x4f, 0x39, 0x1f, 0xe5, 0xff, 0x12, 0x4a, 0x51, 0xb5, 0x35, 0x0f, 0x08, 0x5b, + 0x98, 0x2f, 0x43, 0x7e, 0x36, 0x98, 0x84, 0xf1, 0x37, 0x38, 0xc4, 0xf1, 0x41, 0x75, 0xe1, 0x34, + 0x33, 0x29, 0x94, 0x5f, 0xc0, 0x01, 0x73, 0x6c, 0x77, 0x10, 0x84, 0xfe, 0x62, 0x3c, 0x2d, 0x3c, + 0x4e, 0xd9, 0x11, 0x14, 0xbb, 0x8e, 0x6b, 0x0b, 0x51, 0xea, 0x31, 0x1c, 0xc6, 0xc7, 0x98, 0x59, + 0xfd, 0xa7, 0x00, 0x7b, 0x2f, 0x08, 0x63, 0x03, 0x9b, 0xa0, 0x0e, 0x9c, 0x88, 0x78, 0x58, 0x7e, + 0x3c, 0x2e, 0xd6, 0xf8, 0xf1, 0x26, 0xc6, 0xa5, 0x20, 0xea, 0x12, 0x3e, 0xf2, 0x96, 0x92, 0x69, + 0x40, 0x29, 0x05, 0x8b, 0xc9, 0x84, 0x7e, 0xf5, 0x21, 0xb4, 0x78, 0x52, 0x97, 0xf0, 0xb1, 0xb7, + 0x9c, 0xdd, 0x1f, 0xe0, 0x34, 0xda, 0x88, 0x15, 0xdd, 0xd5, 0x44, 0xde, 0x2e, 0x07, 0xfc, 0x64, + 0x13, 0xe0, 0x4a, 0xdc, 0x74, 0x09, 0x9f, 0xb0, 0x95, 0x04, 0xbe, 0x86, 0x32, 0xe3, 0x37, 0x69, + 0x01, 0x2a, 0x64, 0xe6, 0x38, 0xea, 0xe7, 0xdb, 0x50, 0x97, 0x93, 0xa6, 0x4b, 0x18, 0xb1, 0xf5, + 0xfc, 0xfd, 0x04, 0x1f, 0x70, 0xb9, 0x8b, 0xeb, 0x95, 0x48, 0xce, 0x73, 0xf0, 0x2f, 0xb6, 0x81, + 0xaf, 0x24, 0x48, 0x97, 0xf0, 0x19, 0xdb, 0x10, 0xac, 0x37, 0x50, 0x11, 0xd2, 0x33, 0x04, 0x42, + 0x7e, 0x81, 0x33, 0xd4, 0xb7, 0xcb, 0x5f, 0x0d, 0x8e, 0x2e, 0xe1, 0x73, 0xb6, 0x39, 0x52, 0xd7, + 0x70, 0xe8, 0x39, 0xae, 0x9d, 0xa8, 0xdf, 0xe3, 0xd8, 0x4f, 0x37, 0x7e, 0xc1, 0xf4, 0x96, 0xe9, + 0x12, 0x2e, 0x7a, 0xe9, 0x11, 0x7d, 0x0f, 0x47, 0x02, 0x45, 0x48, 0xdc, 0xe7, 0x30, 0xb5, 0xed, + 0x30, 0x89, 0xb0, 0x43, 0x2f, 0x73, 0x46, 0x7d, 0xe0, 0xbb, 0xb6, 0x86, 0x51, 0x7a, 0x12, 0x51, + 0x07, 0x1c, 0xed, 0xd3, 0x6d, 0x86, 0xb3, 0xa1, 0xd4, 0x25, 0x5c, 0x62, 0xab, 0x41, 0xbd, 0x81, + 0xb3, 0x25, 0x54, 0x21, 0x12, 0xb6, 0xa7, 0x6d, 0x2d, 0xc1, 0xba, 0x84, 0x4f, 0xd9, 0x6a, 0xb1, + 0x95, 0x87, 0x5d, 0x16, 0x4e, 0xeb, 0x7f, 0xc8, 0x50, 0xe0, 0x99, 0x64, 0x08, 0xc1, 0xb1, 0x86, + 0xb1, 0x89, 0x7b, 0xd6, 0x4b, 0xa3, 0x63, 0x98, 0x37, 0x46, 0x49, 0x42, 0x0a, 0x54, 0x93, 0x9a, + 0xf6, 0xaa, 0xab, 0x5d, 0xf5, 0xb5, 0x6b, 0x0b, 0x6b, 0xbd, 0xae, 0x69, 0xf4, 0xb4, 0x92, 0x8c, + 0x2a, 0x50, 0x16, 0x7d, 0xc3, 0xb4, 0xae, 0x4c, 0xc3, 0xd0, 0xae, 0xfa, 0x6d, 0xd3, 0x28, 0xed, + 0xa0, 0x8f, 0xe0, 0x89, 0xe8, 0xa4, 0x65, 0xab, 0xdf, 0x7e, 0xa1, 0x99, 0x2f, 0xfb, 0xa5, 0x5d, + 0xf4, 0x21, 0x9c, 0x89, 0x36, 0xd6, 0xbe, 0xbb, 0x4e, 0x1a, 0xb9, 0x0c, 0xe2, 0x0d, 0x6e, 0xf7, + 0xb5, 0xa4, 0x93, 0x6f, 0x99, 0xef, 0xee, 0x14, 0xf9, 0xfd, 0x9d, 0x22, 0xff, 0x7d, 0xa7, 0xc8, + 0xbf, 0xdd, 0x2b, 0xd2, 0xfb, 0x7b, 0x45, 0xfa, 0xf3, 0x5e, 0x91, 0x5e, 0x7f, 0x6d, 0x3b, 0xc1, + 0x6d, 0x38, 0x6c, 0x8c, 0xe8, 0xb4, 0x39, 0xa2, 0x53, 0x12, 0x0c, 0xdf, 0x04, 0xe9, 0x43, 0xfc, + 0xc3, 0xb0, 0xfe, 0xbb, 0x31, 0x2c, 0xf0, 0xce, 0x57, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xf7, + 0xec, 0xc6, 0xc0, 0x8b, 0x08, 0x00, 0x00, } func (m *RemoteSignerError) Marshal() (dAtA []byte, err error) { @@ -1013,6 +1144,78 @@ func (m *SignedProposalResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *SignBytesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignBytesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignBytesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignBytesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignBytesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignBytesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Error != nil { + { + size, err := m.Error.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *PingRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1259,6 +1462,48 @@ func (m *Message_PingResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { } return len(dAtA) - i, nil } +func (m *Message_SignBytesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Message_SignBytesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.SignBytesRequest != nil { + { + size, err := m.SignBytesRequest.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + return len(dAtA) - i, nil +} +func (m *Message_SignBytesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Message_SignBytesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.SignBytesResponse != nil { + { + size, err := m.SignBytesResponse.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + return len(dAtA) - i, nil +} func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -1378,6 +1623,36 @@ func (m *SignedProposalResponse) Size() (n int) { return n } +func (m *SignBytesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Value) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + +func (m *SignBytesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.Error != nil { + l = m.Error.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func (m *PingRequest) Size() (n int) { if m == nil { return 0 @@ -1504,6 +1779,30 @@ func (m *Message_PingResponse) Size() (n int) { } return n } +func (m *Message_SignBytesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SignBytesRequest != nil { + l = m.SignBytesRequest.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} +func (m *Message_SignBytesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SignBytesResponse != nil { + l = m.SignBytesResponse.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 @@ -2287,6 +2586,210 @@ func (m *SignedProposalResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *SignBytesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignBytesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignBytesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignBytesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignBytesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignBytesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + 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 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Error == nil { + m.Error = &RemoteSignerError{} + } + if err := m.Error.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *PingRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2696,6 +3199,76 @@ func (m *Message) Unmarshal(dAtA []byte) error { } m.Sum = &Message_PingResponse{v} iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignBytesRequest", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SignBytesRequest{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &Message_SignBytesRequest{v} + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignBytesResponse", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SignBytesResponse{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &Message_SignBytesResponse{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/proto/tendermint/privval/types.proto b/proto/tendermint/privval/types.proto index 4d9a25238fe..7411ec323b0 100644 --- a/proto/tendermint/privval/types.proto +++ b/proto/tendermint/privval/types.proto @@ -56,6 +56,17 @@ message SignedProposalResponse { RemoteSignerError error = 2; } +// SignBytesRequest is a request to sign arbitrary bytes +message SignBytesRequest { + bytes value = 1; +} + +// SignBytesResponse is a response containing a signature or an error +message SignBytesResponse { + bytes signature = 1; + RemoteSignerError error = 2; +} + // PingRequest is a request to confirm that the connection is alive. message PingRequest {} @@ -72,5 +83,7 @@ message Message { SignedProposalResponse signed_proposal_response = 6; PingRequest ping_request = 7; PingResponse ping_response = 8; + SignBytesRequest sign_bytes_request = 9; + SignBytesResponse sign_bytes_response = 10; } } diff --git a/proto/tendermint/state/types.pb.go b/proto/tendermint/state/types.pb.go index 56273c8af0b..a2e0bbdfb2a 100644 --- a/proto/tendermint/state/types.pb.go +++ b/proto/tendermint/state/types.pb.go @@ -12,6 +12,7 @@ import ( proto "github.com/cosmos/gogoproto/proto" _ "github.com/cosmos/gogoproto/types" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + _ "github.com/golang/protobuf/ptypes/duration" io "io" math "math" math_bits "math/bits" @@ -445,6 +446,9 @@ type State struct { LastResultsHash []byte `protobuf:"bytes,12,opt,name=last_results_hash,json=lastResultsHash,proto3" json:"last_results_hash,omitempty"` // the latest AppHash we've received from calling abci.Commit() AppHash []byte `protobuf:"bytes,13,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` + // delay between the time when this block is committed and the next height is started. + // previously `timeout_commit` in config.toml + NextBlockDelay time.Duration `protobuf:"bytes,15,opt,name=next_block_delay,json=nextBlockDelay,proto3,stdduration" json:"next_block_delay"` } func (m *State) Reset() { *m = State{} } @@ -578,6 +582,13 @@ func (m *State) GetAppHash() []byte { return nil } +func (m *State) GetNextBlockDelay() time.Duration { + if m != nil { + return m.NextBlockDelay + } + return 0 +} + func init() { proto.RegisterType((*LegacyABCIResponses)(nil), "tendermint.state.LegacyABCIResponses") proto.RegisterType((*ResponseBeginBlock)(nil), "tendermint.state.ResponseBeginBlock") @@ -592,68 +603,71 @@ func init() { func init() { proto.RegisterFile("tendermint/state/types.proto", fileDescriptor_ccfacf933f22bf93) } var fileDescriptor_ccfacf933f22bf93 = []byte{ - // 964 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xcf, 0x6f, 0x1b, 0x45, - 0x14, 0xce, 0xd6, 0xad, 0x7f, 0x3c, 0xc7, 0x89, 0x33, 0x26, 0xad, 0xeb, 0xb6, 0xb6, 0xb1, 0xda, - 0x2a, 0x42, 0x68, 0x2d, 0xb5, 0x27, 0x2e, 0x45, 0xb1, 0x13, 0x88, 0xa5, 0x80, 0xd0, 0x26, 0x54, - 0x2a, 0x87, 0xae, 0xc6, 0xbb, 0x63, 0x7b, 0x84, 0xbd, 0xbb, 0xda, 0x19, 0x1b, 0x87, 0x3b, 0x37, - 0x0e, 0xbd, 0xf2, 0x1f, 0xf5, 0xd8, 0x23, 0x17, 0x02, 0x38, 0x12, 0x07, 0xfe, 0x0a, 0x34, 0x3f, - 0xf6, 0x87, 0xbd, 0x41, 0x04, 0xe5, 0xe6, 0x9d, 0xf7, 0xbd, 0xef, 0x7d, 0xef, 0x9b, 0x79, 0x33, - 0x86, 0xc7, 0x9c, 0x78, 0x2e, 0x09, 0x67, 0xd4, 0xe3, 0x5d, 0xc6, 0x31, 0x27, 0x5d, 0x7e, 0x11, - 0x10, 0x66, 0x06, 0xa1, 0xcf, 0x7d, 0x54, 0x4d, 0xa2, 0xa6, 0x8c, 0x36, 0x3e, 0x1a, 0xfb, 0x63, - 0x5f, 0x06, 0xbb, 0xe2, 0x97, 0xc2, 0x35, 0x5a, 0x63, 0xdf, 0x1f, 0x4f, 0x49, 0x57, 0x7e, 0x0d, - 0xe7, 0xa3, 0x2e, 0xa7, 0x33, 0xc2, 0x38, 0x9e, 0x05, 0x1a, 0xf0, 0x28, 0x55, 0x06, 0x0f, 0x1d, - 0x9a, 0xae, 0xd2, 0x78, 0x92, 0x0a, 0xca, 0xf5, 0x6e, 0x80, 0x43, 0x3c, 0x8b, 0xc2, 0x8f, 0x33, - 0xe1, 0x74, 0x72, 0x3b, 0x13, 0x5d, 0xe0, 0x29, 0x75, 0x31, 0xf7, 0x43, 0x8d, 0x68, 0xa6, 0x10, - 0x0b, 0x12, 0x32, 0xea, 0x7b, 0x69, 0x86, 0xce, 0x6f, 0x06, 0xd4, 0x4e, 0xc9, 0x18, 0x3b, 0x17, - 0x87, 0xbd, 0xfe, 0xc0, 0x22, 0x2c, 0xf0, 0x3d, 0x46, 0x18, 0x7a, 0x05, 0x65, 0x97, 0x4c, 0xe9, - 0x82, 0x84, 0x36, 0x5f, 0xb2, 0xba, 0xd1, 0xce, 0x1d, 0x94, 0x5f, 0x3c, 0x31, 0x53, 0x96, 0x88, - 0x4e, 0xcc, 0xe3, 0x25, 0x71, 0xce, 0x97, 0x16, 0x61, 0xf3, 0x29, 0xb7, 0x40, 0x67, 0x9c, 0x2f, - 0x19, 0xfa, 0x1c, 0x4a, 0xc4, 0x73, 0xed, 0xe1, 0xd4, 0x77, 0xbe, 0xaf, 0xdf, 0x69, 0x1b, 0x07, - 0xe5, 0x17, 0x1d, 0x73, 0xd3, 0x50, 0x33, 0xaa, 0x77, 0xec, 0xb9, 0x3d, 0x81, 0xb4, 0x8a, 0x44, - 0xff, 0x42, 0xc7, 0x50, 0x1e, 0x92, 0x31, 0xf5, 0x34, 0x45, 0x4e, 0x52, 0x3c, 0xfd, 0x77, 0x8a, - 0x9e, 0x00, 0x2b, 0x12, 0x18, 0xc6, 0xbf, 0x3b, 0x6f, 0x01, 0x65, 0x11, 0xe8, 0x04, 0xf2, 0x64, - 0x41, 0x3c, 0x1e, 0x35, 0x76, 0x3f, 0xdb, 0x98, 0x08, 0xf7, 0xea, 0xef, 0x2f, 0x5b, 0x5b, 0x7f, - 0x5f, 0xb6, 0xaa, 0x0a, 0xfd, 0xa9, 0x3f, 0xa3, 0x9c, 0xcc, 0x02, 0x7e, 0x61, 0xe9, 0xfc, 0xce, - 0xcf, 0x77, 0xa0, 0xba, 0xd9, 0x05, 0x3a, 0x83, 0xbd, 0x78, 0x1f, 0xec, 0x79, 0xe0, 0x62, 0x4e, - 0xa2, 0x4a, 0xed, 0x4c, 0xa5, 0xd7, 0x11, 0xf2, 0x5b, 0x09, 0xec, 0xdd, 0x15, 0x35, 0xad, 0xea, - 0x62, 0x7d, 0x99, 0xa1, 0x37, 0xf0, 0xc0, 0x11, 0x55, 0x3c, 0x36, 0x67, 0xb6, 0x3c, 0x23, 0x31, - 0xb5, 0xf2, 0xf7, 0xe3, 0x34, 0xb5, 0xda, 0xe3, 0x7e, 0x94, 0xf0, 0x8d, 0x3c, 0x53, 0xd6, 0xbe, - 0xb3, 0xb6, 0x10, 0x51, 0x27, 0x76, 0xe4, 0x6e, 0x69, 0xc7, 0x4f, 0x06, 0xec, 0xc4, 0x0d, 0xb1, - 0x81, 0x37, 0xf2, 0x51, 0x1f, 0x2a, 0x89, 0x19, 0x8c, 0xf0, 0xba, 0x21, 0xd5, 0x36, 0xb3, 0x6a, - 0xe3, 0xc4, 0x33, 0xc2, 0xad, 0xed, 0x45, 0xea, 0x0b, 0x99, 0x50, 0x9b, 0x62, 0xc6, 0xed, 0x09, - 0xa1, 0xe3, 0x09, 0xb7, 0x9d, 0x09, 0xf6, 0xc6, 0xc4, 0x95, 0x8d, 0xe7, 0xac, 0x3d, 0x11, 0x3a, - 0x91, 0x91, 0xbe, 0x0a, 0x74, 0x7e, 0x31, 0xa0, 0xb6, 0xd1, 0xbc, 0x14, 0x63, 0x41, 0x75, 0xc3, - 0x44, 0xa6, 0xf5, 0xfc, 0xb7, 0x7b, 0x7a, 0x67, 0x76, 0xd7, 0x3d, 0x64, 0xff, 0x5b, 0xdb, 0x5f, - 0x06, 0xec, 0xad, 0x0d, 0x9b, 0x54, 0xf6, 0x06, 0xf6, 0xa7, 0x72, 0x0e, 0x6d, 0x61, 0xb8, 0x1d, - 0x46, 0x41, 0x2d, 0xef, 0x59, 0xf6, 0xe4, 0x5f, 0x33, 0xb6, 0x56, 0x4d, 0x71, 0x1c, 0x0e, 0x1d, - 0x9a, 0xcc, 0xf2, 0x7d, 0xc8, 0x2b, 0x6d, 0x5a, 0x93, 0xfe, 0x42, 0x6f, 0xe1, 0x41, 0x54, 0xc6, - 0x1e, 0x51, 0x0f, 0x4f, 0xe9, 0x8f, 0x64, 0x6d, 0xdc, 0x9e, 0x67, 0xce, 0x41, 0x44, 0xfa, 0x85, - 0x86, 0xab, 0x81, 0xdb, 0x0f, 0xaf, 0x5b, 0xee, 0x4c, 0xa0, 0xf0, 0x5a, 0x5d, 0x39, 0xe8, 0x10, - 0x4a, 0xb1, 0x6d, 0xba, 0xa3, 0xb5, 0xcb, 0x44, 0x5f, 0x4d, 0x89, 0xe5, 0xda, 0xec, 0x24, 0x0b, - 0x35, 0xa0, 0xc8, 0xfc, 0x11, 0xff, 0x01, 0x87, 0x44, 0xf6, 0x51, 0xb2, 0xe2, 0xef, 0xce, 0x9f, - 0x79, 0xb8, 0x77, 0x26, 0x4c, 0x41, 0x9f, 0x41, 0x41, 0x73, 0xe9, 0x32, 0x0f, 0xb3, 0xc6, 0x69, - 0x51, 0xba, 0x44, 0x84, 0x47, 0xcf, 0xa1, 0xe8, 0x4c, 0x30, 0xf5, 0x6c, 0xaa, 0x36, 0xaf, 0xd4, - 0x2b, 0xaf, 0x2e, 0x5b, 0x85, 0xbe, 0x58, 0x1b, 0x1c, 0x59, 0x05, 0x19, 0x1c, 0xb8, 0xe8, 0x19, - 0xec, 0x50, 0x8f, 0x72, 0x8a, 0xa7, 0x7a, 0xcb, 0xeb, 0x3b, 0xd2, 0xd6, 0x8a, 0x5e, 0x55, 0xbb, - 0x8d, 0x3e, 0x01, 0xb9, 0xf7, 0xca, 0xd0, 0x08, 0x99, 0x93, 0xc8, 0x5d, 0x11, 0x90, 0x1e, 0x69, - 0xac, 0x05, 0x95, 0x14, 0x96, 0xba, 0xf5, 0xbb, 0x59, 0xed, 0xea, 0x4c, 0xca, 0xac, 0xc1, 0x51, - 0xaf, 0x26, 0xb4, 0xaf, 0x2e, 0x5b, 0xe5, 0xd3, 0x88, 0x6a, 0x70, 0x64, 0x95, 0x63, 0xde, 0x81, - 0x8b, 0x4e, 0x61, 0x37, 0xc5, 0x29, 0xde, 0xa4, 0xfa, 0x3d, 0xc9, 0xda, 0x30, 0xd5, 0x83, 0x65, - 0x46, 0x0f, 0x96, 0x79, 0x1e, 0x3d, 0x58, 0xbd, 0xa2, 0xa0, 0x7d, 0xf7, 0x7b, 0xcb, 0xb0, 0x2a, - 0x31, 0x97, 0x88, 0xa2, 0x2f, 0x61, 0xd7, 0x23, 0x4b, 0x6e, 0xc7, 0x53, 0xc9, 0xea, 0xf9, 0x1b, - 0xcd, 0xf1, 0x8e, 0x48, 0x4b, 0xae, 0x04, 0xf4, 0x0a, 0x20, 0xc5, 0x51, 0xb8, 0x11, 0x47, 0x2a, - 0x43, 0x08, 0x91, 0x6d, 0xa5, 0x48, 0x8a, 0x37, 0x13, 0x22, 0xd2, 0x52, 0x42, 0xfa, 0xd0, 0x4c, - 0x8f, 0x6d, 0xc2, 0x17, 0x4f, 0x70, 0x49, 0x6e, 0xd6, 0xa3, 0x64, 0x82, 0x93, 0x6c, 0x3d, 0xcb, - 0xd7, 0xde, 0x27, 0x70, 0xcb, 0xfb, 0xe4, 0x6b, 0x78, 0xba, 0x76, 0x9f, 0x6c, 0xf0, 0xc7, 0xf2, - 0xca, 0x52, 0x5e, 0x3b, 0x75, 0xc1, 0xac, 0x13, 0x45, 0x1a, 0xa3, 0x83, 0x18, 0xca, 0x57, 0x9a, - 0xd9, 0x13, 0xcc, 0x26, 0xf5, 0xed, 0xb6, 0x71, 0xb0, 0xad, 0x0e, 0xa2, 0x7a, 0xbd, 0xd9, 0x09, - 0x66, 0x13, 0xf4, 0x10, 0x8a, 0x38, 0x08, 0x14, 0xa4, 0x22, 0x21, 0x05, 0x1c, 0x04, 0x22, 0xd4, - 0xfb, 0xea, 0xfd, 0xaa, 0x69, 0x7c, 0x58, 0x35, 0x8d, 0x3f, 0x56, 0x4d, 0xe3, 0xdd, 0x55, 0x73, - 0xeb, 0xc3, 0x55, 0x73, 0xeb, 0xd7, 0xab, 0xe6, 0xd6, 0x77, 0x2f, 0xc7, 0x94, 0x4f, 0xe6, 0x43, - 0xd3, 0xf1, 0x67, 0x5d, 0xc7, 0x9f, 0x11, 0x3e, 0x1c, 0xf1, 0xe4, 0x87, 0xfa, 0xbf, 0xb4, 0xf9, - 0x4f, 0x6b, 0x98, 0x97, 0xeb, 0x2f, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x11, 0x85, 0xd5, 0x91, - 0x84, 0x09, 0x00, 0x00, + // 1009 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4d, 0x6f, 0xdb, 0x46, + 0x10, 0x35, 0xe3, 0xc4, 0x92, 0x47, 0xfe, 0x90, 0xd7, 0x75, 0xc2, 0x28, 0x89, 0xa4, 0x0a, 0x49, + 0x60, 0x14, 0x05, 0x05, 0x24, 0xa7, 0x5e, 0x52, 0x58, 0x92, 0x5b, 0x0b, 0x70, 0x8a, 0x82, 0x76, + 0x03, 0xa4, 0x87, 0x10, 0x2b, 0x72, 0x25, 0x2d, 0x4a, 0x91, 0x04, 0x77, 0xa5, 0xca, 0xbd, 0xf7, + 0xd6, 0x43, 0x8e, 0xed, 0xbf, 0xe8, 0xcf, 0xc8, 0x31, 0xc7, 0x5e, 0xea, 0x16, 0x32, 0xd0, 0x43, + 0x7f, 0x45, 0xb1, 0x1f, 0xfc, 0x90, 0xe8, 0xa2, 0x2e, 0x72, 0x23, 0xf7, 0xcd, 0xbc, 0x7d, 0xf3, + 0x76, 0x66, 0x49, 0x78, 0xc8, 0x49, 0xe0, 0x91, 0x78, 0x42, 0x03, 0xde, 0x66, 0x1c, 0x73, 0xd2, + 0xe6, 0x17, 0x11, 0x61, 0x56, 0x14, 0x87, 0x3c, 0x44, 0xd5, 0x0c, 0xb5, 0x24, 0x5a, 0xfb, 0x68, + 0x14, 0x8e, 0x42, 0x09, 0xb6, 0xc5, 0x93, 0x8a, 0xab, 0xd5, 0x47, 0x61, 0x38, 0xf2, 0x49, 0x5b, + 0xbe, 0x0d, 0xa6, 0xc3, 0xb6, 0x37, 0x8d, 0x31, 0xa7, 0x61, 0xa0, 0xf1, 0xc6, 0x2a, 0xce, 0xe9, + 0x84, 0x30, 0x8e, 0x27, 0x91, 0x0e, 0x78, 0x90, 0x93, 0x81, 0x07, 0x2e, 0xcd, 0xab, 0xa8, 0x3d, + 0xca, 0x81, 0x72, 0xbd, 0x1d, 0xe1, 0x18, 0x4f, 0x12, 0xf8, 0x61, 0x01, 0xce, 0x27, 0x37, 0x0b, + 0xe8, 0x0c, 0xfb, 0xd4, 0xc3, 0x3c, 0x8c, 0x13, 0xf1, 0xb9, 0x88, 0x19, 0x89, 0x19, 0x0d, 0x83, + 0x3c, 0x43, 0xeb, 0x77, 0x03, 0xf6, 0x4f, 0xc9, 0x08, 0xbb, 0x17, 0x47, 0x9d, 0x6e, 0xdf, 0x26, + 0x2c, 0x0a, 0x03, 0x46, 0x18, 0x7a, 0x01, 0x15, 0x8f, 0xf8, 0x74, 0x46, 0x62, 0x87, 0xcf, 0x99, + 0x69, 0x34, 0xd7, 0x0f, 0x2b, 0xcf, 0x1e, 0x59, 0x39, 0xcb, 0x44, 0x25, 0xd6, 0xf1, 0x9c, 0xb8, + 0xe7, 0x73, 0x9b, 0xb0, 0xa9, 0xcf, 0x6d, 0xd0, 0x19, 0xe7, 0x73, 0x86, 0x3e, 0x87, 0x4d, 0x12, + 0x78, 0xce, 0xc0, 0x0f, 0xdd, 0xef, 0xcc, 0x5b, 0x4d, 0xe3, 0xb0, 0xf2, 0xac, 0x65, 0xad, 0x1a, + 0x6e, 0x25, 0xfb, 0x1d, 0x07, 0x5e, 0x47, 0x44, 0xda, 0x65, 0xa2, 0x9f, 0xd0, 0x31, 0x54, 0x06, + 0x64, 0x44, 0x03, 0x4d, 0xb1, 0x2e, 0x29, 0x1e, 0xff, 0x3b, 0x45, 0x47, 0x04, 0x2b, 0x12, 0x18, + 0xa4, 0xcf, 0xad, 0x37, 0x80, 0x8a, 0x11, 0xe8, 0x04, 0x36, 0xc8, 0x8c, 0x04, 0x3c, 0x29, 0xec, + 0x6e, 0xb1, 0x30, 0x01, 0x77, 0xcc, 0x77, 0x97, 0x8d, 0xb5, 0xbf, 0x2f, 0x1b, 0x55, 0x15, 0xfd, + 0x69, 0x38, 0xa1, 0x9c, 0x4c, 0x22, 0x7e, 0x61, 0xeb, 0xfc, 0xd6, 0x4f, 0xb7, 0xa0, 0xba, 0x5a, + 0x05, 0x3a, 0x83, 0xbd, 0xf4, 0x1c, 0x9c, 0x69, 0xe4, 0x61, 0x4e, 0x92, 0x9d, 0x9a, 0x85, 0x9d, + 0x5e, 0x25, 0x91, 0xdf, 0xc8, 0xc0, 0xce, 0x6d, 0xb1, 0xa7, 0x5d, 0x9d, 0x2d, 0x2f, 0x33, 0xf4, + 0x1a, 0xee, 0xb9, 0x62, 0x97, 0x80, 0x4d, 0x99, 0x23, 0x7b, 0x24, 0xa5, 0x56, 0xfe, 0x7e, 0x9c, + 0xa7, 0x56, 0x67, 0xdc, 0x4d, 0x12, 0xbe, 0x96, 0x3d, 0x65, 0x1f, 0xb8, 0x4b, 0x0b, 0x09, 0x75, + 0x66, 0xc7, 0xfa, 0x07, 0xda, 0xf1, 0xa3, 0x01, 0x3b, 0x69, 0x41, 0xac, 0x1f, 0x0c, 0x43, 0xd4, + 0x85, 0xed, 0xcc, 0x0c, 0x46, 0xb8, 0x69, 0x48, 0xb5, 0xf5, 0xa2, 0xda, 0x34, 0xf1, 0x8c, 0x70, + 0x7b, 0x6b, 0x96, 0x7b, 0x43, 0x16, 0xec, 0xfb, 0x98, 0x71, 0x67, 0x4c, 0xe8, 0x68, 0xcc, 0x1d, + 0x77, 0x8c, 0x83, 0x11, 0xf1, 0x64, 0xe1, 0xeb, 0xf6, 0x9e, 0x80, 0x4e, 0x24, 0xd2, 0x55, 0x40, + 0xeb, 0x17, 0x03, 0xf6, 0x57, 0x8a, 0x97, 0x62, 0x6c, 0xa8, 0xae, 0x98, 0xc8, 0xb4, 0x9e, 0xff, + 0x76, 0x4f, 0x9f, 0xcc, 0xee, 0xb2, 0x87, 0xec, 0x7f, 0x6b, 0xfb, 0xcb, 0x80, 0xbd, 0xa5, 0x61, + 0x93, 0xca, 0x5e, 0xc3, 0x81, 0x2f, 0xe7, 0xd0, 0x11, 0x86, 0x3b, 0x71, 0x02, 0x6a, 0x79, 0x4f, + 0x8a, 0x9d, 0x7f, 0xcd, 0xd8, 0xda, 0xfb, 0x8a, 0xe3, 0x68, 0xe0, 0xd2, 0x6c, 0x96, 0xef, 0xc2, + 0x86, 0xd2, 0xa6, 0x35, 0xe9, 0x37, 0xf4, 0x06, 0xee, 0x25, 0xdb, 0x38, 0x43, 0x1a, 0x60, 0x9f, + 0xfe, 0x40, 0x96, 0xc6, 0xed, 0x69, 0xa1, 0x0f, 0x12, 0xd2, 0x2f, 0x74, 0xb8, 0x1a, 0xb8, 0x83, + 0xf8, 0xba, 0xe5, 0xd6, 0x18, 0x4a, 0xaf, 0xd4, 0x95, 0x83, 0x8e, 0x60, 0x33, 0xb5, 0x4d, 0x57, + 0xb4, 0x74, 0x99, 0xe8, 0xab, 0x29, 0xb3, 0x5c, 0x9b, 0x9d, 0x65, 0xa1, 0x1a, 0x94, 0x59, 0x38, + 0xe4, 0xdf, 0xe3, 0x98, 0xc8, 0x3a, 0x36, 0xed, 0xf4, 0xbd, 0xf5, 0x6b, 0x09, 0xee, 0x9c, 0x09, + 0x53, 0xd0, 0x67, 0x50, 0xd2, 0x5c, 0x7a, 0x9b, 0xfb, 0x45, 0xe3, 0xb4, 0x28, 0xbd, 0x45, 0x12, + 0x8f, 0x9e, 0x42, 0xd9, 0x1d, 0x63, 0x1a, 0x38, 0x54, 0x1d, 0xde, 0x66, 0xa7, 0xb2, 0xb8, 0x6c, + 0x94, 0xba, 0x62, 0xad, 0xdf, 0xb3, 0x4b, 0x12, 0xec, 0x7b, 0xe8, 0x09, 0xec, 0xd0, 0x80, 0x72, + 0x8a, 0x7d, 0x7d, 0xe4, 0xe6, 0x8e, 0xb4, 0x75, 0x5b, 0xaf, 0xaa, 0xd3, 0x46, 0x9f, 0x80, 0x3c, + 0x7b, 0x65, 0x68, 0x12, 0xb9, 0x2e, 0x23, 0x77, 0x05, 0x20, 0x3d, 0xd2, 0xb1, 0x36, 0x6c, 0xe7, + 0x62, 0xa9, 0x67, 0xde, 0x2e, 0x6a, 0x57, 0x3d, 0x29, 0xb3, 0xfa, 0xbd, 0xce, 0xbe, 0xd0, 0xbe, + 0xb8, 0x6c, 0x54, 0x4e, 0x13, 0xaa, 0x7e, 0xcf, 0xae, 0xa4, 0xbc, 0x7d, 0x0f, 0x9d, 0xc2, 0x6e, + 0x8e, 0x53, 0x7c, 0x93, 0xcc, 0x3b, 0x92, 0xb5, 0x66, 0xa9, 0x0f, 0x96, 0x95, 0x7c, 0xb0, 0xac, + 0xf3, 0xe4, 0x83, 0xd5, 0x29, 0x0b, 0xda, 0xb7, 0x7f, 0x34, 0x0c, 0x7b, 0x3b, 0xe5, 0x12, 0x28, + 0xfa, 0x12, 0x76, 0x03, 0x32, 0xe7, 0x4e, 0x3a, 0x95, 0xcc, 0xdc, 0xb8, 0xd1, 0x1c, 0xef, 0x88, + 0xb4, 0xec, 0x4a, 0x40, 0x2f, 0x00, 0x72, 0x1c, 0xa5, 0x1b, 0x71, 0xe4, 0x32, 0x84, 0x10, 0x59, + 0x56, 0x8e, 0xa4, 0x7c, 0x33, 0x21, 0x22, 0x2d, 0x27, 0xa4, 0x0b, 0xf5, 0xfc, 0xd8, 0x66, 0x7c, + 0xe9, 0x04, 0x6f, 0xca, 0xc3, 0x7a, 0x90, 0x4d, 0x70, 0x96, 0xad, 0x67, 0xf9, 0xda, 0xfb, 0x04, + 0x3e, 0xf0, 0x3e, 0xf9, 0x0a, 0x1e, 0x2f, 0xdd, 0x27, 0x2b, 0xfc, 0xa9, 0xbc, 0x8a, 0x94, 0xd7, + 0xcc, 0x5d, 0x30, 0xcb, 0x44, 0x89, 0xc6, 0xa4, 0x11, 0x63, 0xf9, 0x95, 0x66, 0xce, 0x18, 0xb3, + 0xb1, 0xb9, 0xd5, 0x34, 0x0e, 0xb7, 0x54, 0x23, 0xaa, 0xaf, 0x37, 0x3b, 0xc1, 0x6c, 0x8c, 0xee, + 0x43, 0x19, 0x47, 0x91, 0x0a, 0xd9, 0x96, 0x21, 0x25, 0x1c, 0x45, 0x12, 0x7a, 0x09, 0x55, 0xd9, + 0x01, 0xaa, 0x9f, 0x3c, 0xe2, 0xe3, 0x0b, 0x73, 0x57, 0xb7, 0xe9, 0x6a, 0x43, 0xf5, 0xf4, 0x1f, + 0x92, 0xea, 0xa7, 0x9f, 0x45, 0x3f, 0xc9, 0x3e, 0x90, 0xfd, 0xd4, 0x13, 0xa9, 0x9d, 0x97, 0xef, + 0x16, 0x75, 0xe3, 0xfd, 0xa2, 0x6e, 0xfc, 0xb9, 0xa8, 0x1b, 0x6f, 0xaf, 0xea, 0x6b, 0xef, 0xaf, + 0xea, 0x6b, 0xbf, 0x5d, 0xd5, 0xd7, 0xbe, 0x7d, 0x3e, 0xa2, 0x7c, 0x3c, 0x1d, 0x58, 0x6e, 0x38, + 0x69, 0xbb, 0xe1, 0x84, 0xf0, 0xc1, 0x90, 0x67, 0x0f, 0xea, 0xf7, 0x6c, 0xf5, 0xc7, 0x6e, 0xb0, + 0x21, 0xd7, 0x9f, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0x6b, 0x28, 0xc8, 0x81, 0xf3, 0x09, 0x00, + 0x00, } func (m *LegacyABCIResponses) Marshal() (dAtA []byte, err error) { @@ -1007,6 +1021,14 @@ func (m *State) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + n9, err9 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.NextBlockDelay, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.NextBlockDelay):]) + if err9 != nil { + return 0, err9 + } + i -= n9 + i = encodeVarintTypes(dAtA, i, uint64(n9)) + i-- + dAtA[i] = 0x7a if m.InitialHeight != 0 { i = encodeVarintTypes(dAtA, i, uint64(m.InitialHeight)) i-- @@ -1082,12 +1104,12 @@ func (m *State) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n13, err13 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.LastBlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.LastBlockTime):]) - if err13 != nil { - return 0, err13 + n14, err14 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.LastBlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.LastBlockTime):]) + if err14 != nil { + return 0, err14 } - i -= n13 - i = encodeVarintTypes(dAtA, i, uint64(n13)) + i -= n14 + i = encodeVarintTypes(dAtA, i, uint64(n14)) i-- dAtA[i] = 0x2a { @@ -1314,6 +1336,8 @@ func (m *State) Size() (n int) { if m.InitialHeight != 0 { n += 1 + sovTypes(uint64(m.InitialHeight)) } + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.NextBlockDelay) + n += 1 + l + sovTypes(uint64(l)) return n } @@ -2625,6 +2649,39 @@ func (m *State) Unmarshal(dAtA []byte) error { break } } + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextBlockDelay", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.NextBlockDelay, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/proto/tendermint/state/types.proto b/proto/tendermint/state/types.proto index f9ed2753218..c2711e32b0c 100644 --- a/proto/tendermint/state/types.proto +++ b/proto/tendermint/state/types.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package tendermint.state; import "gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "tendermint/abci/types.proto"; import "tendermint/types/params.proto"; @@ -101,4 +102,11 @@ message State { // the latest AppHash we've received from calling abci.Commit() bytes app_hash = 13; + + // delay between the time when this block is committed and the next height is started. + // previously `timeout_commit` in config.toml + google.protobuf.Duration next_block_delay = 15 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; } diff --git a/proto/tendermint/types/canonical.pb.go b/proto/tendermint/types/canonical.pb.go index 9df89c73a2b..b26d13bcb14 100644 --- a/proto/tendermint/types/canonical.pb.go +++ b/proto/tendermint/types/canonical.pb.go @@ -225,12 +225,11 @@ func (m *CanonicalProposal) GetChainID() string { } type CanonicalVote struct { - Type SignedMsgType `protobuf:"varint,1,opt,name=type,proto3,enum=tendermint.types.SignedMsgType" json:"type,omitempty"` - Height int64 `protobuf:"fixed64,2,opt,name=height,proto3" json:"height,omitempty"` - Round int64 `protobuf:"fixed64,3,opt,name=round,proto3" json:"round,omitempty"` - BlockID *CanonicalBlockID `protobuf:"bytes,4,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` - Timestamp time.Time `protobuf:"bytes,5,opt,name=timestamp,proto3,stdtime" json:"timestamp"` - ChainID string `protobuf:"bytes,6,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + Type SignedMsgType `protobuf:"varint,1,opt,name=type,proto3,enum=tendermint.types.SignedMsgType" json:"type,omitempty"` + Height int64 `protobuf:"fixed64,2,opt,name=height,proto3" json:"height,omitempty"` + Round int64 `protobuf:"fixed64,3,opt,name=round,proto3" json:"round,omitempty"` + BlockID *CanonicalBlockID `protobuf:"bytes,4,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + ChainID string `protobuf:"bytes,6,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` } func (m *CanonicalVote) Reset() { *m = CanonicalVote{} } @@ -294,13 +293,6 @@ func (m *CanonicalVote) GetBlockID() *CanonicalBlockID { return nil } -func (m *CanonicalVote) GetTimestamp() time.Time { - if m != nil { - return m.Timestamp - } - return time.Time{} -} - func (m *CanonicalVote) GetChainID() string { if m != nil { return m.ChainID @@ -389,40 +381,41 @@ func init() { func init() { proto.RegisterFile("tendermint/types/canonical.proto", fileDescriptor_8d1a1a84ff7267ed) } var fileDescriptor_8d1a1a84ff7267ed = []byte{ - // 525 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0xc1, 0x6e, 0x9b, 0x40, - 0x10, 0x35, 0x0e, 0xb6, 0x61, 0x13, 0xb7, 0xee, 0x2a, 0x8a, 0xa8, 0x15, 0x01, 0xe2, 0x50, 0xd1, - 0x0b, 0x48, 0xf1, 0x1f, 0x90, 0x56, 0xaa, 0xab, 0x46, 0x8d, 0x48, 0x94, 0x43, 0x2f, 0xd6, 0x02, - 0x1b, 0x40, 0x05, 0x16, 0xc1, 0x5a, 0x6a, 0x2e, 0xed, 0x2f, 0xe4, 0x3b, 0xfa, 0x25, 0x39, 0xe6, - 0xd8, 0x5e, 0xdc, 0x0a, 0xff, 0x48, 0xb5, 0x0b, 0x06, 0x2b, 0xa9, 0x2c, 0x55, 0xad, 0x7a, 0x41, - 0x33, 0x6f, 0xde, 0xce, 0x3c, 0xbd, 0x1d, 0x16, 0xe8, 0x14, 0x67, 0x01, 0x2e, 0xd2, 0x38, 0xa3, - 0x36, 0xbd, 0xc9, 0x71, 0x69, 0xfb, 0x28, 0x23, 0x59, 0xec, 0xa3, 0xc4, 0xca, 0x0b, 0x42, 0x09, - 0x9c, 0x74, 0x0c, 0x8b, 0x33, 0xa6, 0x87, 0x21, 0x09, 0x09, 0x2f, 0xda, 0x2c, 0xaa, 0x79, 0x53, - 0x2d, 0x24, 0x24, 0x4c, 0xb0, 0xcd, 0x33, 0x6f, 0x79, 0x6d, 0xd3, 0x38, 0xc5, 0x25, 0x45, 0x69, - 0xde, 0x10, 0x8e, 0x1f, 0x8d, 0xe2, 0xdf, 0xba, 0x6a, 0x7c, 0x06, 0x93, 0xd3, 0xcd, 0x64, 0x27, - 0x21, 0xfe, 0xc7, 0xf9, 0x2b, 0x08, 0x81, 0x18, 0xa1, 0x32, 0x52, 0x04, 0x5d, 0x30, 0x0f, 0x5c, - 0x1e, 0xc3, 0x2b, 0xf0, 0x34, 0x47, 0x05, 0x5d, 0x94, 0x98, 0x2e, 0x22, 0x8c, 0x02, 0x5c, 0x28, - 0x7d, 0x5d, 0x30, 0xf7, 0x4f, 0x4c, 0xeb, 0xa1, 0x50, 0xab, 0x6d, 0x78, 0x8e, 0x0a, 0x7a, 0x81, - 0xe9, 0x1b, 0xce, 0x77, 0xc4, 0xbb, 0x95, 0xd6, 0x73, 0xc7, 0xf9, 0x36, 0x68, 0x38, 0xe0, 0xe8, - 0xf7, 0x74, 0x78, 0x08, 0x06, 0x94, 0x50, 0x94, 0x70, 0x19, 0x63, 0xb7, 0x4e, 0x5a, 0x6d, 0xfd, - 0x4e, 0x9b, 0xf1, 0xbd, 0x0f, 0x9e, 0x75, 0x4d, 0x0a, 0x92, 0x93, 0x12, 0x25, 0x70, 0x06, 0x44, - 0x26, 0x87, 0x1f, 0x7f, 0x72, 0xa2, 0x3d, 0x96, 0x79, 0x11, 0x87, 0x19, 0x0e, 0xce, 0xca, 0xf0, - 0xf2, 0x26, 0xc7, 0x2e, 0x27, 0xc3, 0x23, 0x30, 0x8c, 0x70, 0x1c, 0x46, 0x94, 0x0f, 0x98, 0xb8, - 0x4d, 0xc6, 0xc4, 0x14, 0x64, 0x99, 0x05, 0xca, 0x1e, 0x87, 0xeb, 0x04, 0xbe, 0x04, 0x72, 0x4e, - 0x92, 0x45, 0x5d, 0x11, 0x75, 0xc1, 0xdc, 0x73, 0x0e, 0xaa, 0x95, 0x26, 0x9d, 0xbf, 0x7f, 0xe7, - 0x32, 0xcc, 0x95, 0x72, 0x92, 0xf0, 0x08, 0xbe, 0x05, 0x92, 0xc7, 0xec, 0x5d, 0xc4, 0x81, 0x32, - 0xe0, 0xc6, 0x19, 0x3b, 0x8c, 0x6b, 0x6e, 0xc2, 0xd9, 0xaf, 0x56, 0xda, 0xa8, 0x49, 0xdc, 0x11, - 0x6f, 0x30, 0x0f, 0xa0, 0x03, 0xe4, 0xf6, 0x92, 0x95, 0x21, 0x6f, 0x36, 0xb5, 0xea, 0x35, 0xb0, - 0x36, 0x6b, 0x60, 0x5d, 0x6e, 0x18, 0x8e, 0xc4, 0x7c, 0xbf, 0xfd, 0xa1, 0x09, 0x6e, 0x77, 0x0c, - 0xbe, 0x00, 0x92, 0x1f, 0xa1, 0x38, 0x63, 0x7a, 0x46, 0xba, 0x60, 0xca, 0xf5, 0xac, 0x53, 0x86, - 0xb1, 0x59, 0xbc, 0x38, 0x0f, 0x8c, 0xaf, 0x7d, 0x30, 0x6e, 0x65, 0x5d, 0x11, 0x8a, 0xff, 0x87, - 0xaf, 0xdb, 0x66, 0x89, 0xff, 0xd2, 0xac, 0xc1, 0xdf, 0x9b, 0x35, 0xdc, 0x61, 0xd6, 0x97, 0xad, - 0x65, 0x66, 0x5e, 0xbd, 0xfe, 0x44, 0x71, 0x56, 0xc6, 0x24, 0x83, 0xc7, 0x40, 0xc6, 0x9b, 0xa4, - 0xf9, 0xaf, 0x3a, 0xe0, 0x0f, 0xdd, 0x79, 0xbe, 0xa5, 0x86, 0xb9, 0x23, 0xb7, 0x02, 0x9c, 0xb3, - 0xbb, 0x4a, 0x15, 0xee, 0x2b, 0x55, 0xf8, 0x59, 0xa9, 0xc2, 0xed, 0x5a, 0xed, 0xdd, 0xaf, 0xd5, - 0xde, 0xb7, 0xb5, 0xda, 0xfb, 0x30, 0x0b, 0x63, 0x1a, 0x2d, 0x3d, 0xcb, 0x27, 0xa9, 0xed, 0x93, - 0x14, 0x53, 0xef, 0x9a, 0x76, 0x41, 0xfd, 0xaa, 0x3c, 0x7c, 0x28, 0xbc, 0x21, 0xc7, 0x67, 0xbf, - 0x02, 0x00, 0x00, 0xff, 0xff, 0x35, 0x24, 0xc5, 0xf2, 0xae, 0x04, 0x00, 0x00, + // 530 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0x4f, 0x6b, 0x9c, 0x40, + 0x14, 0xdf, 0x49, 0xdc, 0x5d, 0x77, 0x92, 0x6d, 0xb7, 0x43, 0x08, 0xdb, 0x25, 0xa8, 0x78, 0x28, + 0xf6, 0xa2, 0x90, 0xfd, 0x06, 0xa6, 0x85, 0x6e, 0x68, 0x68, 0x30, 0x21, 0x87, 0x5e, 0x96, 0x51, + 0x27, 0x2a, 0x55, 0x47, 0x74, 0x16, 0x9a, 0x4b, 0xfb, 0x15, 0xf2, 0xb1, 0x72, 0xcc, 0xb1, 0xbd, + 0x6c, 0x8b, 0x7b, 0xed, 0x87, 0x28, 0x33, 0xba, 0x2a, 0x49, 0x5b, 0x28, 0x85, 0x5e, 0xe4, 0xfd, + 0xf9, 0xcd, 0x7b, 0xbf, 0xf7, 0x9b, 0x37, 0x42, 0x8d, 0x91, 0xd4, 0x27, 0x79, 0x12, 0xa5, 0xcc, + 0x62, 0x37, 0x19, 0x29, 0x2c, 0x0f, 0xa7, 0x34, 0x8d, 0x3c, 0x1c, 0x9b, 0x59, 0x4e, 0x19, 0x45, + 0x93, 0x16, 0x61, 0x0a, 0xc4, 0xec, 0x20, 0xa0, 0x01, 0x15, 0x49, 0x8b, 0x5b, 0x15, 0x6e, 0xa6, + 0x06, 0x94, 0x06, 0x31, 0xb1, 0x84, 0xe7, 0xae, 0xae, 0x2d, 0x16, 0x25, 0xa4, 0x60, 0x38, 0xc9, + 0x6a, 0xc0, 0xd1, 0xa3, 0x56, 0xe2, 0x5b, 0x65, 0xf5, 0x4f, 0x70, 0x72, 0xb2, 0xed, 0x6c, 0xc7, + 0xd4, 0xfb, 0xb0, 0x78, 0x85, 0x10, 0x94, 0x42, 0x5c, 0x84, 0x53, 0xa0, 0x01, 0x63, 0xdf, 0x11, + 0x36, 0xba, 0x82, 0x4f, 0x33, 0x9c, 0xb3, 0x65, 0x41, 0xd8, 0x32, 0x24, 0xd8, 0x27, 0xf9, 0x74, + 0x47, 0x03, 0xc6, 0xde, 0xb1, 0x61, 0x3e, 0x24, 0x6a, 0x36, 0x05, 0xcf, 0x71, 0xce, 0x2e, 0x08, + 0x7b, 0x23, 0xf0, 0xb6, 0x74, 0xb7, 0x56, 0x7b, 0xce, 0x38, 0xeb, 0x06, 0x75, 0x1b, 0x1e, 0xfe, + 0x1a, 0x8e, 0x0e, 0x60, 0x9f, 0x51, 0x86, 0x63, 0x41, 0x63, 0xec, 0x54, 0x4e, 0xc3, 0x6d, 0xa7, + 0xe5, 0xa6, 0x7f, 0xdd, 0x81, 0xcf, 0xda, 0x22, 0x39, 0xcd, 0x68, 0x81, 0x63, 0x34, 0x87, 0x12, + 0xa7, 0x23, 0x8e, 0x3f, 0x39, 0x56, 0x1f, 0xd3, 0xbc, 0x88, 0x82, 0x94, 0xf8, 0x67, 0x45, 0x70, + 0x79, 0x93, 0x11, 0x47, 0x80, 0xd1, 0x21, 0x1c, 0x84, 0x24, 0x0a, 0x42, 0x26, 0x1a, 0x4c, 0x9c, + 0xda, 0xe3, 0x64, 0x72, 0xba, 0x4a, 0xfd, 0xe9, 0xae, 0x08, 0x57, 0x0e, 0x7a, 0x09, 0x47, 0x19, + 0x8d, 0x97, 0x55, 0x46, 0xd2, 0x80, 0xb1, 0x6b, 0xef, 0x97, 0x6b, 0x55, 0x3e, 0x7f, 0xf7, 0xd6, + 0xe1, 0x31, 0x47, 0xce, 0x68, 0x2c, 0x2c, 0x74, 0x0a, 0x65, 0x97, 0xcb, 0xbb, 0x8c, 0xfc, 0x69, + 0x5f, 0x08, 0xa7, 0xff, 0x41, 0xb8, 0xfa, 0x26, 0xec, 0xbd, 0x72, 0xad, 0x0e, 0x6b, 0xc7, 0x19, + 0x8a, 0x02, 0x0b, 0x1f, 0xd9, 0x70, 0xd4, 0x5c, 0xf2, 0x74, 0x20, 0x8a, 0xcd, 0xcc, 0x6a, 0x0d, + 0xcc, 0xed, 0x1a, 0x98, 0x97, 0x5b, 0x84, 0x2d, 0x73, 0xdd, 0x6f, 0xbf, 0xa9, 0xc0, 0x69, 0x8f, + 0xa1, 0x17, 0x50, 0xf6, 0x42, 0x1c, 0xa5, 0x9c, 0xcf, 0x50, 0x03, 0xc6, 0xa8, 0xea, 0x75, 0xc2, + 0x63, 0xbc, 0x97, 0x48, 0x2e, 0x7c, 0xfd, 0x07, 0x80, 0xe3, 0x86, 0xd6, 0x15, 0x65, 0xe4, 0x7f, + 0xe8, 0xda, 0x15, 0x4b, 0xfa, 0x47, 0xb1, 0xba, 0x83, 0x0e, 0x7e, 0x3f, 0xe8, 0xa9, 0x24, 0xf7, + 0x27, 0x03, 0xfd, 0x73, 0x67, 0x1d, 0xf9, 0xb4, 0xaf, 0x3f, 0x32, 0x92, 0x16, 0x11, 0x4d, 0xd1, + 0x11, 0x1c, 0x91, 0xad, 0x53, 0xbf, 0x8c, 0x36, 0xf0, 0x97, 0xf3, 0x3d, 0xef, 0x70, 0xe2, 0xf3, + 0x8d, 0x1a, 0x1a, 0xf6, 0xd9, 0x5d, 0xa9, 0x80, 0xfb, 0x52, 0x01, 0xdf, 0x4b, 0x05, 0xdc, 0x6e, + 0x94, 0xde, 0xfd, 0x46, 0xe9, 0x7d, 0xd9, 0x28, 0xbd, 0xf7, 0xf3, 0x20, 0x62, 0xe1, 0xca, 0x35, + 0x3d, 0x9a, 0x58, 0x1e, 0x4d, 0x08, 0x73, 0xaf, 0x59, 0x6b, 0x54, 0xff, 0x85, 0x87, 0x4f, 0xdd, + 0x1d, 0x88, 0xf8, 0xfc, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf3, 0x3c, 0x27, 0x43, 0x70, 0x04, + 0x00, 0x00, } func (m *CanonicalBlockID) Marshal() (dAtA []byte, err error) { @@ -599,14 +592,6 @@ func (m *CanonicalVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n4, err4 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):]) - if err4 != nil { - return 0, err4 - } - i -= n4 - i = encodeVarintCanonical(dAtA, i, uint64(n4)) - i-- - dAtA[i] = 0x2a if m.BlockID != nil { { size, err := m.BlockID.MarshalToSizedBuffer(dAtA[:i]) @@ -780,8 +765,6 @@ func (m *CanonicalVote) Size() (n int) { l = m.BlockID.Size() n += 1 + l + sovCanonical(uint64(l)) } - l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp) - n += 1 + l + sovCanonical(uint64(l)) l = len(m.ChainID) if l > 0 { n += 1 + l + sovCanonical(uint64(l)) @@ -1351,39 +1334,6 @@ func (m *CanonicalVote) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowCanonical - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthCanonical - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthCanonical - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) diff --git a/proto/tendermint/types/canonical.proto b/proto/tendermint/types/canonical.proto index adcad2d8846..2bb700b6657 100644 --- a/proto/tendermint/types/canonical.proto +++ b/proto/tendermint/types/canonical.proto @@ -35,11 +35,11 @@ message CanonicalVote { sfixed64 height = 2; // canonicalization requires fixed size encoding here sfixed64 round = 3; // canonicalization requires fixed size encoding here CanonicalBlockID block_id = 4 [(gogoproto.customname) = "BlockID"]; - google.protobuf.Timestamp timestamp = 5 [ - (gogoproto.nullable) = false, - (gogoproto.stdtime) = true - ]; string chain_id = 6 [(gogoproto.customname) = "ChainID"]; + + // The per-vote timestamp was removed so that all validators sign identical + // bytes for a given block, enabling BLS signature aggregation. + reserved 5; } // CanonicalVoteExtension provides us a way to serialize a vote extension from diff --git a/proto/tendermint/types/params.pb.go b/proto/tendermint/types/params.pb.go index 156b73ad7f1..66b916134cd 100644 --- a/proto/tendermint/types/params.pb.go +++ b/proto/tendermint/types/params.pb.go @@ -8,6 +8,7 @@ import ( _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + types "github.com/cosmos/gogoproto/types" _ "github.com/golang/protobuf/ptypes/duration" io "io" math "math" @@ -35,7 +36,12 @@ type ConsensusParams struct { Validator *ValidatorParams `protobuf:"bytes,3,opt,name=validator,proto3" json:"validator,omitempty"` Version *VersionParams `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` Abci *ABCIParams `protobuf:"bytes,5,opt,name=abci,proto3" json:"abci,omitempty"` - Authority *AuthorityParams `protobuf:"bytes,6,opt,name=authority,proto3" json:"authority,omitempty"` + // NOTE(berachain): synchrony=6 and feature=7 carry the field numbers used + // by bera-v1.x so that state persisted by existing networks decodes + // unchanged. Upstream v0.39 assigns authority=6; it is moved to 8 here. + Synchrony *SynchronyParams `protobuf:"bytes,6,opt,name=synchrony,proto3" json:"synchrony,omitempty"` + Feature *FeatureParams `protobuf:"bytes,7,opt,name=feature,proto3" json:"feature,omitempty"` + Authority *AuthorityParams `protobuf:"bytes,8,opt,name=authority,proto3" json:"authority,omitempty"` } func (m *ConsensusParams) Reset() { *m = ConsensusParams{} } @@ -106,6 +112,20 @@ func (m *ConsensusParams) GetAbci() *ABCIParams { return nil } +func (m *ConsensusParams) GetSynchrony() *SynchronyParams { + if m != nil { + return m.Synchrony + } + return nil +} + +func (m *ConsensusParams) GetFeature() *FeatureParams { + if m != nil { + return m.Feature + } + return nil +} + func (m *ConsensusParams) GetAuthority() *AuthorityParams { if m != nil { return m.Authority @@ -389,6 +409,142 @@ func (m *HashedParams) GetBlockMaxGas() int64 { return 0 } +// SynchronyParams determine the validity of block timestamps. +// +// These parameters are part of the Proposer-Based Timestamps (PBTS) algorithm. +// For more information on the relationship of the synchrony parameters to +// block timestamps validity, refer to the PBTS specification: +// https://github.com/tendermint/spec/blob/master/spec/consensus/proposer-based-timestamp/README.md +type SynchronyParams struct { + // Bound for how skewed a proposer's clock may be from any validator on the + // network while still producing valid proposals. + Precision *time.Duration `protobuf:"bytes,1,opt,name=precision,proto3,stdduration" json:"precision,omitempty"` + // Bound for how long a proposal message may take to reach all validators on + // a network and still be considered valid. + MessageDelay *time.Duration `protobuf:"bytes,2,opt,name=message_delay,json=messageDelay,proto3,stdduration" json:"message_delay,omitempty"` +} + +func (m *SynchronyParams) Reset() { *m = SynchronyParams{} } +func (m *SynchronyParams) String() string { return proto.CompactTextString(m) } +func (*SynchronyParams) ProtoMessage() {} +func (*SynchronyParams) Descriptor() ([]byte, []int) { + return fileDescriptor_e12598271a686f57, []int{6} +} +func (m *SynchronyParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SynchronyParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SynchronyParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SynchronyParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_SynchronyParams.Merge(m, src) +} +func (m *SynchronyParams) XXX_Size() int { + return m.Size() +} +func (m *SynchronyParams) XXX_DiscardUnknown() { + xxx_messageInfo_SynchronyParams.DiscardUnknown(m) +} + +var xxx_messageInfo_SynchronyParams proto.InternalMessageInfo + +func (m *SynchronyParams) GetPrecision() *time.Duration { + if m != nil { + return m.Precision + } + return nil +} + +func (m *SynchronyParams) GetMessageDelay() *time.Duration { + if m != nil { + return m.MessageDelay + } + return nil +} + +// FeatureParams configure the height from which features of CometBFT are enabled. +type FeatureParams struct { + // Height during which vote extensions will be enabled. + // + // A value of 0 means vote extensions are disabled. A value > 0 denotes + // the height at which vote extensions will be (or have been) enabled. + // + // Cannot be set to heights lower or equal to the current blockchain height. + VoteExtensionsEnableHeight *types.Int64Value `protobuf:"bytes,1,opt,name=vote_extensions_enable_height,json=voteExtensionsEnableHeight,proto3" json:"vote_extensions_enable_height,omitempty"` + // Height at which Proposer-Based Timestamps (PBTS) will be enabled. + // + // A value of 0 means PBTS is disabled. A value > 0 denotes the height at + // which PBTS will be (or has been) enabled. + // + // Cannot be set to heights lower or equal to the current blockchain height. + PbtsEnableHeight *types.Int64Value `protobuf:"bytes,2,opt,name=pbts_enable_height,json=pbtsEnableHeight,proto3" json:"pbts_enable_height,omitempty"` + // Height at which Stable Block Time (SBT) will be enabled. + SbtEnableHeight *types.Int64Value `protobuf:"bytes,3,opt,name=sbt_enable_height,json=sbtEnableHeight,proto3" json:"sbt_enable_height,omitempty"` +} + +func (m *FeatureParams) Reset() { *m = FeatureParams{} } +func (m *FeatureParams) String() string { return proto.CompactTextString(m) } +func (*FeatureParams) ProtoMessage() {} +func (*FeatureParams) Descriptor() ([]byte, []int) { + return fileDescriptor_e12598271a686f57, []int{7} +} +func (m *FeatureParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FeatureParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FeatureParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *FeatureParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_FeatureParams.Merge(m, src) +} +func (m *FeatureParams) XXX_Size() int { + return m.Size() +} +func (m *FeatureParams) XXX_DiscardUnknown() { + xxx_messageInfo_FeatureParams.DiscardUnknown(m) +} + +var xxx_messageInfo_FeatureParams proto.InternalMessageInfo + +func (m *FeatureParams) GetVoteExtensionsEnableHeight() *types.Int64Value { + if m != nil { + return m.VoteExtensionsEnableHeight + } + return nil +} + +func (m *FeatureParams) GetPbtsEnableHeight() *types.Int64Value { + if m != nil { + return m.PbtsEnableHeight + } + return nil +} + +func (m *FeatureParams) GetSbtEnableHeight() *types.Int64Value { + if m != nil { + return m.SbtEnableHeight + } + return nil +} + // ABCIParams configure functionality specific to the Application Blockchain Interface. type ABCIParams struct { // vote_extensions_enable_height configures the first height during which @@ -407,7 +563,7 @@ func (m *ABCIParams) Reset() { *m = ABCIParams{} } func (m *ABCIParams) String() string { return proto.CompactTextString(m) } func (*ABCIParams) ProtoMessage() {} func (*ABCIParams) Descriptor() ([]byte, []int) { - return fileDescriptor_e12598271a686f57, []int{6} + return fileDescriptor_e12598271a686f57, []int{8} } func (m *ABCIParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -454,7 +610,7 @@ func (m *AuthorityParams) Reset() { *m = AuthorityParams{} } func (m *AuthorityParams) String() string { return proto.CompactTextString(m) } func (*AuthorityParams) ProtoMessage() {} func (*AuthorityParams) Descriptor() ([]byte, []int) { - return fileDescriptor_e12598271a686f57, []int{7} + return fileDescriptor_e12598271a686f57, []int{9} } func (m *AuthorityParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -497,6 +653,8 @@ func init() { proto.RegisterType((*ValidatorParams)(nil), "tendermint.types.ValidatorParams") proto.RegisterType((*VersionParams)(nil), "tendermint.types.VersionParams") proto.RegisterType((*HashedParams)(nil), "tendermint.types.HashedParams") + proto.RegisterType((*SynchronyParams)(nil), "tendermint.types.SynchronyParams") + proto.RegisterType((*FeatureParams)(nil), "tendermint.types.FeatureParams") proto.RegisterType((*ABCIParams)(nil), "tendermint.types.ABCIParams") proto.RegisterType((*AuthorityParams)(nil), "tendermint.types.AuthorityParams") } @@ -504,46 +662,56 @@ func init() { func init() { proto.RegisterFile("tendermint/types/params.proto", fileDescriptor_e12598271a686f57) } var fileDescriptor_e12598271a686f57 = []byte{ - // 614 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x94, 0xcb, 0x6e, 0xd3, 0x4c, - 0x14, 0xc7, 0xe3, 0x3a, 0x6d, 0x93, 0x93, 0x2f, 0x4d, 0x34, 0xfa, 0x24, 0x4c, 0x69, 0x9c, 0xe0, - 0x05, 0xaa, 0x54, 0xc9, 0x46, 0x64, 0x05, 0x42, 0xaa, 0x92, 0x12, 0xb5, 0x05, 0x95, 0x8b, 0x85, - 0x58, 0x74, 0x63, 0x8d, 0x93, 0xa9, 0x63, 0x35, 0xf6, 0x58, 0x9e, 0x71, 0x94, 0xbc, 0x05, 0x4b, - 0x96, 0x5d, 0xc2, 0x1b, 0xf0, 0x08, 0x5d, 0x76, 0xc9, 0x0a, 0x50, 0xb2, 0x61, 0xcf, 0x0b, 0x20, - 0x8f, 0xed, 0x38, 0x17, 0xd8, 0x8d, 0xe7, 0xfc, 0x7e, 0x73, 0xf9, 0xcf, 0x91, 0xa1, 0xc1, 0x89, - 0x3f, 0x20, 0xa1, 0xe7, 0xfa, 0xdc, 0xe0, 0xd3, 0x80, 0x30, 0x23, 0xc0, 0x21, 0xf6, 0x98, 0x1e, - 0x84, 0x94, 0x53, 0x54, 0xcf, 0xcb, 0xba, 0x28, 0xef, 0xff, 0xef, 0x50, 0x87, 0x8a, 0xa2, 0x11, - 0x8f, 0x12, 0x6e, 0x5f, 0x75, 0x28, 0x75, 0x46, 0xc4, 0x10, 0x5f, 0x76, 0x74, 0x65, 0x0c, 0xa2, - 0x10, 0x73, 0x97, 0xfa, 0x49, 0x5d, 0xfb, 0xbd, 0x05, 0xb5, 0x13, 0xea, 0x33, 0xe2, 0xb3, 0x88, - 0xbd, 0x15, 0x3b, 0xa0, 0x36, 0x6c, 0xdb, 0x23, 0xda, 0xbf, 0x56, 0xa4, 0x96, 0x74, 0x58, 0x79, - 0xd2, 0xd0, 0xd7, 0xf7, 0xd2, 0xbb, 0x71, 0x39, 0xa1, 0xcd, 0x84, 0x45, 0xcf, 0xa1, 0x44, 0xc6, - 0xee, 0x80, 0xf8, 0x7d, 0xa2, 0x6c, 0x09, 0xaf, 0xb5, 0xe9, 0xf5, 0x52, 0x22, 0x55, 0x17, 0x06, - 0x3a, 0x86, 0xf2, 0x18, 0x8f, 0xdc, 0x01, 0xe6, 0x34, 0x54, 0x64, 0xa1, 0x3f, 0xdc, 0xd4, 0x3f, - 0x64, 0x48, 0xea, 0xe7, 0x0e, 0x7a, 0x0a, 0xbb, 0x63, 0x12, 0x32, 0x97, 0xfa, 0x4a, 0x51, 0xe8, - 0xcd, 0xbf, 0xe8, 0x09, 0x90, 0xca, 0x19, 0x8f, 0x1e, 0x43, 0x11, 0xdb, 0x7d, 0x57, 0xd9, 0x16, - 0xde, 0xc1, 0xa6, 0xd7, 0xe9, 0x9e, 0x9c, 0xa7, 0x92, 0x20, 0xe3, 0xd3, 0xe2, 0x88, 0x0f, 0x69, - 0xe8, 0xf2, 0xa9, 0xb2, 0xf3, 0xaf, 0xd3, 0x76, 0x32, 0x24, 0x3b, 0xed, 0xc2, 0xd1, 0xce, 0xa1, - 0xb2, 0x14, 0x21, 0x7a, 0x00, 0x65, 0x0f, 0x4f, 0x2c, 0x7b, 0xca, 0x09, 0x13, 0xa1, 0xcb, 0x66, - 0xc9, 0xc3, 0x93, 0x6e, 0xfc, 0x8d, 0xee, 0xc1, 0x6e, 0x5c, 0x74, 0x30, 0x13, 0xb9, 0xca, 0xe6, - 0x8e, 0x87, 0x27, 0xa7, 0x98, 0xbd, 0x2c, 0x96, 0xe4, 0x7a, 0x51, 0xfb, 0x22, 0xc1, 0xde, 0x6a, - 0xac, 0xe8, 0x08, 0x50, 0x6c, 0x60, 0x87, 0x58, 0x7e, 0xe4, 0x59, 0xe2, 0x7d, 0xb2, 0x75, 0x6b, - 0x1e, 0x9e, 0x74, 0x1c, 0xf2, 0x3a, 0xf2, 0xc4, 0x01, 0x18, 0xba, 0x80, 0x7a, 0x06, 0x67, 0xad, - 0x91, 0xbe, 0xdf, 0x7d, 0x3d, 0xe9, 0x1d, 0x3d, 0xeb, 0x1d, 0xfd, 0x45, 0x0a, 0x74, 0x4b, 0xb7, - 0xdf, 0x9b, 0x85, 0x4f, 0x3f, 0x9a, 0x92, 0xb9, 0x97, 0xac, 0x97, 0x55, 0x56, 0xaf, 0x22, 0xaf, - 0x5e, 0x45, 0x3b, 0x86, 0xda, 0xda, 0x13, 0x22, 0x0d, 0xaa, 0x41, 0x64, 0x5b, 0xd7, 0x64, 0x6a, - 0x89, 0xd4, 0x14, 0xa9, 0x25, 0x1f, 0x96, 0xcd, 0x4a, 0x10, 0xd9, 0xaf, 0xc8, 0xf4, 0x7d, 0x3c, - 0xf5, 0xac, 0xf4, 0xf5, 0xa6, 0x29, 0xfd, 0xba, 0x69, 0x4a, 0xda, 0x11, 0x54, 0x57, 0x1e, 0x11, - 0xd5, 0x41, 0xc6, 0x41, 0x20, 0xee, 0x56, 0x34, 0xe3, 0xe1, 0x12, 0x7c, 0x09, 0xff, 0x9d, 0x61, - 0x36, 0x24, 0x83, 0x94, 0x7d, 0x04, 0x35, 0x11, 0x85, 0xb5, 0x9e, 0x75, 0x55, 0x4c, 0x5f, 0x64, - 0x81, 0x6b, 0x50, 0xcd, 0xb9, 0x3c, 0xf6, 0x4a, 0x46, 0x9d, 0x62, 0xa6, 0xbd, 0x01, 0xc8, 0xbb, - 0x02, 0x75, 0xa0, 0x31, 0xa6, 0x9c, 0x58, 0x64, 0xc2, 0x89, 0x1f, 0x9f, 0x8e, 0x59, 0xc4, 0xc7, - 0xf6, 0x88, 0x58, 0x43, 0xe2, 0x3a, 0x43, 0x9e, 0xee, 0xb3, 0x1f, 0x43, 0xbd, 0x05, 0xd3, 0x13, - 0xc8, 0x99, 0x20, 0x34, 0x03, 0x6a, 0x6b, 0xfd, 0x82, 0x0e, 0x96, 0xbb, 0x2c, 0x5e, 0xa1, 0xbc, - 0xd4, 0x42, 0xdd, 0x77, 0x9f, 0x67, 0xaa, 0x74, 0x3b, 0x53, 0xa5, 0xbb, 0x99, 0x2a, 0xfd, 0x9c, - 0xa9, 0xd2, 0xc7, 0xb9, 0x5a, 0xb8, 0x9b, 0xab, 0x85, 0x6f, 0x73, 0xb5, 0x70, 0xd9, 0x76, 0x5c, - 0x3e, 0x8c, 0x6c, 0xbd, 0x4f, 0x3d, 0xa3, 0x4f, 0x3d, 0xc2, 0xed, 0x2b, 0x9e, 0x0f, 0x92, 0xbf, - 0xc4, 0xfa, 0x0f, 0xc6, 0xde, 0x11, 0xf3, 0xed, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xd7, 0xf7, - 0xad, 0x2a, 0x7b, 0x04, 0x00, 0x00, + // 775 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x95, 0xcd, 0x6e, 0xd3, 0x4a, + 0x14, 0xc7, 0xe3, 0x3a, 0x6d, 0x93, 0x49, 0xd3, 0xe4, 0x8e, 0xae, 0x74, 0x7d, 0xfb, 0xe1, 0x14, + 0x2f, 0x50, 0xa5, 0x4a, 0x0e, 0xa2, 0x08, 0x09, 0x04, 0xaa, 0x92, 0xb6, 0xb4, 0x05, 0x95, 0x42, + 0x40, 0x5d, 0x74, 0x63, 0x8d, 0x93, 0xa9, 0x63, 0x35, 0xf6, 0x58, 0x9e, 0x71, 0x88, 0xdf, 0x82, + 0x0d, 0x88, 0x65, 0x97, 0xf0, 0x06, 0x3c, 0x42, 0x97, 0x5d, 0xb2, 0x2a, 0x28, 0xdd, 0xb0, 0xe3, + 0x15, 0xd0, 0x8c, 0xed, 0x7c, 0x38, 0x84, 0x76, 0x67, 0xfb, 0xfc, 0x7f, 0xff, 0x73, 0xe6, 0x9c, + 0x63, 0x1b, 0xac, 0x32, 0xec, 0xb6, 0xb0, 0xef, 0xd8, 0x2e, 0xab, 0xb2, 0xd0, 0xc3, 0xb4, 0xea, + 0x21, 0x1f, 0x39, 0x54, 0xf7, 0x7c, 0xc2, 0x08, 0x2c, 0x0f, 0xc3, 0xba, 0x08, 0x2f, 0xfd, 0x6b, + 0x11, 0x8b, 0x88, 0x60, 0x95, 0x5f, 0x45, 0xba, 0x25, 0xd5, 0x22, 0xc4, 0xea, 0xe0, 0xaa, 0xb8, + 0x33, 0x83, 0xd3, 0x6a, 0x2b, 0xf0, 0x11, 0xb3, 0x89, 0x3b, 0x2d, 0xfe, 0xce, 0x47, 0x9e, 0x87, + 0xfd, 0x38, 0x8f, 0xf6, 0x4b, 0x06, 0xa5, 0x6d, 0xe2, 0x52, 0xec, 0xd2, 0x80, 0xbe, 0x12, 0x15, + 0xc0, 0x4d, 0x30, 0x6b, 0x76, 0x48, 0xf3, 0x4c, 0x91, 0xd6, 0xa4, 0xf5, 0xc2, 0xfd, 0x55, 0x3d, + 0x5d, 0x8b, 0x5e, 0xe7, 0xe1, 0x48, 0xdd, 0x88, 0xb4, 0xf0, 0x09, 0xc8, 0xe1, 0xae, 0xdd, 0xc2, + 0x6e, 0x13, 0x2b, 0x33, 0x82, 0x5b, 0x9b, 0xe4, 0x76, 0x63, 0x45, 0x8c, 0x0e, 0x08, 0xb8, 0x05, + 0xf2, 0x5d, 0xd4, 0xb1, 0x5b, 0x88, 0x11, 0x5f, 0x91, 0x05, 0x7e, 0x67, 0x12, 0x3f, 0x4e, 0x24, + 0x31, 0x3f, 0x64, 0xe0, 0x23, 0x30, 0xdf, 0xc5, 0x3e, 0xb5, 0x89, 0xab, 0x64, 0x05, 0x5e, 0xf9, + 0x03, 0x1e, 0x09, 0x62, 0x38, 0xd1, 0xc3, 0x7b, 0x20, 0x8b, 0xcc, 0xa6, 0xad, 0xcc, 0x0a, 0x6e, + 0x65, 0x92, 0xab, 0xd5, 0xb7, 0x0f, 0x62, 0x48, 0x28, 0x79, 0xb5, 0x34, 0x74, 0x9b, 0x6d, 0x9f, + 0xb8, 0xa1, 0x32, 0x37, 0xad, 0xda, 0x37, 0x89, 0x24, 0xa9, 0x76, 0xc0, 0xf0, 0x6a, 0x4f, 0x31, + 0x62, 0x81, 0x8f, 0x95, 0xf9, 0x69, 0xd5, 0x3e, 0x8b, 0x04, 0x49, 0xb5, 0xb1, 0x9e, 0xe7, 0x46, + 0x01, 0x6b, 0x13, 0xdf, 0x66, 0xa1, 0x92, 0x9b, 0x96, 0xbb, 0x96, 0x48, 0x92, 0xdc, 0x03, 0x46, + 0x3b, 0x00, 0x85, 0x91, 0xf1, 0xc1, 0x65, 0x90, 0x77, 0x50, 0xcf, 0x30, 0x43, 0x86, 0xa9, 0x18, + 0xb8, 0xdc, 0xc8, 0x39, 0xa8, 0x57, 0xe7, 0xf7, 0xf0, 0x3f, 0x30, 0xcf, 0x83, 0x16, 0xa2, 0x62, + 0xa6, 0x72, 0x63, 0xce, 0x41, 0xbd, 0x3d, 0x44, 0x9f, 0x67, 0x73, 0x72, 0x39, 0xab, 0x7d, 0x91, + 0xc0, 0xe2, 0xf8, 0x48, 0xe1, 0x06, 0x80, 0x9c, 0x40, 0x16, 0x36, 0xdc, 0xc0, 0x31, 0xc4, 0x6e, + 0x24, 0xbe, 0x25, 0x07, 0xf5, 0x6a, 0x16, 0x7e, 0x19, 0x38, 0xa2, 0x00, 0x0a, 0x0f, 0x41, 0x39, + 0x11, 0x27, 0x6b, 0x1b, 0xef, 0xce, 0xff, 0x7a, 0xb4, 0xb7, 0x7a, 0xb2, 0xb7, 0xfa, 0x4e, 0x2c, + 0xa8, 0xe7, 0x2e, 0xae, 0x2a, 0x99, 0x4f, 0xdf, 0x2b, 0x52, 0x63, 0x31, 0xf2, 0x4b, 0x22, 0xe3, + 0x47, 0x91, 0xc7, 0x8f, 0xa2, 0x6d, 0x81, 0x52, 0x6a, 0x7d, 0xa0, 0x06, 0x8a, 0x5e, 0x60, 0x1a, + 0x67, 0x38, 0x34, 0x44, 0xd7, 0x14, 0x69, 0x4d, 0x5e, 0xcf, 0x37, 0x0a, 0x5e, 0x60, 0xbe, 0xc0, + 0xe1, 0x5b, 0xfe, 0xe8, 0x71, 0xee, 0xeb, 0x79, 0x45, 0xfa, 0x79, 0x5e, 0x91, 0xb4, 0x0d, 0x50, + 0x1c, 0x5b, 0x20, 0x58, 0x06, 0x32, 0xf2, 0x3c, 0x71, 0xb6, 0x6c, 0x83, 0x5f, 0x8e, 0x88, 0x4f, + 0xc0, 0xc2, 0x3e, 0xa2, 0x6d, 0xdc, 0x8a, 0xb5, 0x77, 0x41, 0x49, 0xb4, 0xc2, 0x48, 0xf7, 0xba, + 0x28, 0x1e, 0x1f, 0x26, 0x0d, 0xd7, 0x40, 0x71, 0xa8, 0x1b, 0xb6, 0xbd, 0x90, 0xa8, 0xf6, 0x10, + 0xd5, 0x3e, 0x4a, 0xa0, 0x94, 0xda, 0x2d, 0xf8, 0x14, 0xe4, 0x3d, 0x1f, 0x37, 0x6d, 0xf1, 0x02, + 0x48, 0x37, 0xb5, 0x30, 0x2b, 0xda, 0x37, 0x24, 0xe0, 0x0e, 0x28, 0x3a, 0x98, 0x52, 0x31, 0x08, + 0xdc, 0x41, 0xe1, 0xcd, 0x53, 0x88, 0x2c, 0x16, 0x62, 0x6a, 0x87, 0x43, 0xda, 0x87, 0x19, 0x50, + 0x1c, 0xdb, 0x5a, 0xd8, 0x02, 0xab, 0x5d, 0xc2, 0xb0, 0x81, 0x7b, 0x0c, 0xbb, 0x3c, 0x13, 0x35, + 0xb0, 0x8b, 0xcc, 0x0e, 0x36, 0xda, 0xd8, 0xb6, 0xda, 0x2c, 0x2e, 0x75, 0x79, 0x22, 0xcf, 0x81, + 0xcb, 0x1e, 0x3e, 0x38, 0x46, 0x9d, 0x00, 0xd7, 0xb3, 0x17, 0x57, 0x15, 0xa9, 0xb1, 0xc4, 0x7d, + 0x76, 0x07, 0x36, 0xbb, 0xc2, 0x65, 0x5f, 0x98, 0xc0, 0x23, 0x00, 0x3d, 0x93, 0xa5, 0xad, 0x67, + 0x6e, 0x6b, 0x5d, 0xe6, 0xf0, 0x98, 0xe1, 0x21, 0xf8, 0x87, 0x9a, 0x2c, 0xe5, 0x27, 0xdf, 0xd6, + 0xaf, 0x44, 0x4d, 0x36, 0x6a, 0xa7, 0x1d, 0x01, 0x30, 0xfc, 0x84, 0xc0, 0xda, 0x6d, 0x7a, 0x22, + 0xff, 0xed, 0xc0, 0x5a, 0x15, 0x94, 0x52, 0x2f, 0x38, 0x5c, 0x19, 0xfd, 0x2c, 0x70, 0x87, 0xfc, + 0xc8, 0x3b, 0x5f, 0x7f, 0xfd, 0xb9, 0xaf, 0x4a, 0x17, 0x7d, 0x55, 0xba, 0xec, 0xab, 0xd2, 0x8f, + 0xbe, 0x2a, 0xbd, 0xbf, 0x56, 0x33, 0x97, 0xd7, 0x6a, 0xe6, 0xdb, 0xb5, 0x9a, 0x39, 0xd9, 0xb4, + 0x6c, 0xd6, 0x0e, 0x4c, 0xbd, 0x49, 0x9c, 0x6a, 0x93, 0x38, 0x98, 0x99, 0xa7, 0x6c, 0x78, 0x11, + 0xfd, 0x72, 0xd2, 0x7f, 0x2b, 0x73, 0x4e, 0x3c, 0xdf, 0xfc, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xaa, + 0x98, 0xd6, 0xfd, 0xc8, 0x06, 0x00, 0x00, } func (this *ConsensusParams) Equal(that interface{}) bool { @@ -580,6 +748,12 @@ func (this *ConsensusParams) Equal(that interface{}) bool { if !this.Abci.Equal(that1.Abci) { return false } + if !this.Synchrony.Equal(that1.Synchrony) { + return false + } + if !this.Feature.Equal(that1.Feature) { + return false + } if !this.Authority.Equal(that1.Authority) { return false } @@ -722,6 +896,75 @@ func (this *HashedParams) Equal(that interface{}) bool { } return true } +func (this *SynchronyParams) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*SynchronyParams) + if !ok { + that2, ok := that.(SynchronyParams) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Precision != nil && that1.Precision != nil { + if *this.Precision != *that1.Precision { + return false + } + } else if this.Precision != nil { + return false + } else if that1.Precision != nil { + return false + } + if this.MessageDelay != nil && that1.MessageDelay != nil { + if *this.MessageDelay != *that1.MessageDelay { + return false + } + } else if this.MessageDelay != nil { + return false + } else if that1.MessageDelay != nil { + return false + } + return true +} +func (this *FeatureParams) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*FeatureParams) + if !ok { + that2, ok := that.(FeatureParams) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.VoteExtensionsEnableHeight.Equal(that1.VoteExtensionsEnableHeight) { + return false + } + if !this.PbtsEnableHeight.Equal(that1.PbtsEnableHeight) { + return false + } + if !this.SbtEnableHeight.Equal(that1.SbtEnableHeight) { + return false + } + return true +} func (this *ABCIParams) Equal(that interface{}) bool { if that == nil { return this == nil @@ -800,6 +1043,30 @@ func (m *ConsensusParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintParams(dAtA, i, uint64(size)) } i-- + dAtA[i] = 0x42 + } + if m.Feature != nil { + { + size, err := m.Feature.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + if m.Synchrony != nil { + { + size, err := m.Synchrony.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x32 } if m.Abci != nil { @@ -923,12 +1190,12 @@ func (m *EvidenceParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x18 } - n7, err7 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.MaxAgeDuration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.MaxAgeDuration):]) - if err7 != nil { - return 0, err7 + n9, err9 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.MaxAgeDuration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.MaxAgeDuration):]) + if err9 != nil { + return 0, err9 } - i -= n7 - i = encodeVarintParams(dAtA, i, uint64(n7)) + i -= n9 + i = encodeVarintParams(dAtA, i, uint64(n9)) i-- dAtA[i] = 0x12 if m.MaxAgeNumBlocks != 0 { @@ -1032,6 +1299,108 @@ func (m *HashedParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *SynchronyParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SynchronyParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SynchronyParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MessageDelay != nil { + n10, err10 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(*m.MessageDelay, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(*m.MessageDelay):]) + if err10 != nil { + return 0, err10 + } + i -= n10 + i = encodeVarintParams(dAtA, i, uint64(n10)) + i-- + dAtA[i] = 0x12 + } + if m.Precision != nil { + n11, err11 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(*m.Precision, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(*m.Precision):]) + if err11 != nil { + return 0, err11 + } + i -= n11 + i = encodeVarintParams(dAtA, i, uint64(n11)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FeatureParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *FeatureParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FeatureParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SbtEnableHeight != nil { + { + size, err := m.SbtEnableHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.PbtsEnableHeight != nil { + { + size, err := m.PbtsEnableHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.VoteExtensionsEnableHeight != nil { + { + size, err := m.VoteExtensionsEnableHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *ABCIParams) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1219,6 +1588,14 @@ func (m *ConsensusParams) Size() (n int) { l = m.Abci.Size() n += 1 + l + sovParams(uint64(l)) } + if m.Synchrony != nil { + l = m.Synchrony.Size() + n += 1 + l + sovParams(uint64(l)) + } + if m.Feature != nil { + l = m.Feature.Size() + n += 1 + l + sovParams(uint64(l)) + } if m.Authority != nil { l = m.Authority.Size() n += 1 + l + sovParams(uint64(l)) @@ -1300,33 +1677,71 @@ func (m *HashedParams) Size() (n int) { return n } -func (m *ABCIParams) Size() (n int) { +func (m *SynchronyParams) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.VoteExtensionsEnableHeight != 0 { - n += 1 + sovParams(uint64(m.VoteExtensionsEnableHeight)) + if m.Precision != nil { + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(*m.Precision) + n += 1 + l + sovParams(uint64(l)) + } + if m.MessageDelay != nil { + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(*m.MessageDelay) + n += 1 + l + sovParams(uint64(l)) } return n } -func (m *AuthorityParams) Size() (n int) { +func (m *FeatureParams) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = len(m.Authority) - if l > 0 { + if m.VoteExtensionsEnableHeight != nil { + l = m.VoteExtensionsEnableHeight.Size() + n += 1 + l + sovParams(uint64(l)) + } + if m.PbtsEnableHeight != nil { + l = m.PbtsEnableHeight.Size() + n += 1 + l + sovParams(uint64(l)) + } + if m.SbtEnableHeight != nil { + l = m.SbtEnableHeight.Size() n += 1 + l + sovParams(uint64(l)) } return n } -func sovParams(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 +func (m *ABCIParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.VoteExtensionsEnableHeight != 0 { + n += 1 + sovParams(uint64(m.VoteExtensionsEnableHeight)) + } + return n +} + +func (m *AuthorityParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + return n +} + +func sovParams(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 } func sozParams(x uint64) (n int) { return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -1541,6 +1956,78 @@ func (m *ConsensusParams) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Synchrony", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Synchrony == nil { + m.Synchrony = &SynchronyParams{} + } + if err := m.Synchrony.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Feature", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Feature == nil { + m.Feature = &FeatureParams{} + } + if err := m.Feature.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) } @@ -2045,6 +2532,286 @@ func (m *HashedParams) Unmarshal(dAtA []byte) error { } return nil } +func (m *SynchronyParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SynchronyParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SynchronyParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Precision", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Precision == nil { + m.Precision = new(time.Duration) + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(m.Precision, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MessageDelay", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.MessageDelay == nil { + m.MessageDelay = new(time.Duration) + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(m.MessageDelay, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FeatureParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: FeatureParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FeatureParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtensionsEnableHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.VoteExtensionsEnableHeight == nil { + m.VoteExtensionsEnableHeight = &types.Int64Value{} + } + if err := m.VoteExtensionsEnableHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PbtsEnableHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PbtsEnableHeight == nil { + m.PbtsEnableHeight = &types.Int64Value{} + } + if err := m.PbtsEnableHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SbtEnableHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SbtEnableHeight == nil { + m.SbtEnableHeight = &types.Int64Value{} + } + if err := m.SbtEnableHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ABCIParams) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/proto/tendermint/types/params.proto b/proto/tendermint/types/params.proto index 10e9c71122c..2e8fa97c8f4 100644 --- a/proto/tendermint/types/params.proto +++ b/proto/tendermint/types/params.proto @@ -3,6 +3,7 @@ package tendermint.types; import "gogoproto/gogo.proto"; import "google/protobuf/duration.proto"; +import "google/protobuf/wrappers.proto"; option go_package = "github.com/cometbft/cometbft/proto/tendermint/types"; option (gogoproto.equal_all) = true; @@ -15,7 +16,12 @@ message ConsensusParams { ValidatorParams validator = 3; VersionParams version = 4; ABCIParams abci = 5; - AuthorityParams authority = 6; + // NOTE(berachain): synchrony=6 and feature=7 carry the field numbers used + // by bera-v1.x so that state persisted by existing networks decodes + // unchanged. Upstream v0.39 assigns authority=6; it is moved to 8 here. + SynchronyParams synchrony = 6; + FeatureParams feature = 7; + AuthorityParams authority = 8; } // BlockParams contains limits on the block size. @@ -79,6 +85,43 @@ message HashedParams { int64 block_max_gas = 2; } +// SynchronyParams determine the validity of block timestamps. +// +// These parameters are part of the Proposer-Based Timestamps (PBTS) algorithm. +// For more information on the relationship of the synchrony parameters to +// block timestamps validity, refer to the PBTS specification: +// https://github.com/tendermint/spec/blob/master/spec/consensus/proposer-based-timestamp/README.md +message SynchronyParams { + // Bound for how skewed a proposer's clock may be from any validator on the + // network while still producing valid proposals. + google.protobuf.Duration precision = 1 [(gogoproto.stdduration) = true]; + // Bound for how long a proposal message may take to reach all validators on + // a network and still be considered valid. + google.protobuf.Duration message_delay = 2 [(gogoproto.stdduration) = true]; +} + +// FeatureParams configure the height from which features of CometBFT are enabled. +message FeatureParams { + // Height during which vote extensions will be enabled. + // + // A value of 0 means vote extensions are disabled. A value > 0 denotes + // the height at which vote extensions will be (or have been) enabled. + // + // Cannot be set to heights lower or equal to the current blockchain height. + google.protobuf.Int64Value vote_extensions_enable_height = 1 [(gogoproto.nullable) = true]; + + // Height at which Proposer-Based Timestamps (PBTS) will be enabled. + // + // A value of 0 means PBTS is disabled. A value > 0 denotes the height at + // which PBTS will be (or has been) enabled. + // + // Cannot be set to heights lower or equal to the current blockchain height. + google.protobuf.Int64Value pbts_enable_height = 2 [(gogoproto.nullable) = true]; + + // Height at which Stable Block Time (SBT) will be enabled. + google.protobuf.Int64Value sbt_enable_height = 3 [(gogoproto.nullable) = true]; +} + // ABCIParams configure functionality specific to the Application Blockchain Interface. message ABCIParams { // vote_extensions_enable_height configures the first height during which diff --git a/proto/tendermint/types/validator.pb.go b/proto/tendermint/types/validator.pb.go index 45faec7e6ea..4c79d5b58da 100644 --- a/proto/tendermint/types/validator.pb.go +++ b/proto/tendermint/types/validator.pb.go @@ -32,6 +32,15 @@ const ( BlockIDFlagAbsent BlockIDFlag = 1 BlockIDFlagCommit BlockIDFlag = 2 BlockIDFlagNil BlockIDFlag = 3 + // Aggregated commit + // Voted for the block and contains aggregated signature. + BlockIDFlagAggCommit BlockIDFlag = 4 + // Voted for the block. + BlockIDFlagAggCommitAbsent BlockIDFlag = 5 + // Voted for nil and contains aggregated signature. + BlockIDFlagAggNil BlockIDFlag = 6 + // Voted for nil. + BlockIDFlagAggNilAbsent BlockIDFlag = 7 ) var BlockIDFlag_name = map[int32]string{ @@ -39,13 +48,21 @@ var BlockIDFlag_name = map[int32]string{ 1: "BLOCK_ID_FLAG_ABSENT", 2: "BLOCK_ID_FLAG_COMMIT", 3: "BLOCK_ID_FLAG_NIL", + 4: "BLOCK_ID_FLAG_AGG_COMMIT", + 5: "BLOCK_ID_FLAG_AGG_COMMIT_ABSENT", + 6: "BLOCK_ID_FLAG_AGG_NIL", + 7: "BLOCK_ID_FLAG_AGG_NIL_ABSENT", } var BlockIDFlag_value = map[string]int32{ - "BLOCK_ID_FLAG_UNKNOWN": 0, - "BLOCK_ID_FLAG_ABSENT": 1, - "BLOCK_ID_FLAG_COMMIT": 2, - "BLOCK_ID_FLAG_NIL": 3, + "BLOCK_ID_FLAG_UNKNOWN": 0, + "BLOCK_ID_FLAG_ABSENT": 1, + "BLOCK_ID_FLAG_COMMIT": 2, + "BLOCK_ID_FLAG_NIL": 3, + "BLOCK_ID_FLAG_AGG_COMMIT": 4, + "BLOCK_ID_FLAG_AGG_COMMIT_ABSENT": 5, + "BLOCK_ID_FLAG_AGG_NIL": 6, + "BLOCK_ID_FLAG_AGG_NIL_ABSENT": 7, } func (x BlockIDFlag) String() string { @@ -117,10 +134,14 @@ func (m *ValidatorSet) GetTotalVotingPower() int64 { } type Validator struct { - Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - PubKey crypto.PublicKey `protobuf:"bytes,2,opt,name=pub_key,json=pubKey,proto3" json:"pub_key"` - VotingPower int64 `protobuf:"varint,3,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` - ProposerPriority int64 `protobuf:"varint,4,opt,name=proposer_priority,json=proposerPriority,proto3" json:"proposer_priority,omitempty"` + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // NOTE(berachain): bera-v1.x serializes keys via pub_key_bytes/pub_key_type + // (the v1.x layout); pub_key is kept as a decode fallback only. + PubKey *crypto.PublicKey `protobuf:"bytes,2,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` // Deprecated: Do not use. + VotingPower int64 `protobuf:"varint,3,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` + ProposerPriority int64 `protobuf:"varint,4,opt,name=proposer_priority,json=proposerPriority,proto3" json:"proposer_priority,omitempty"` + PubKeyBytes []byte `protobuf:"bytes,5,opt,name=pub_key_bytes,json=pubKeyBytes,proto3" json:"pub_key_bytes,omitempty"` + PubKeyType string `protobuf:"bytes,6,opt,name=pub_key_type,json=pubKeyType,proto3" json:"pub_key_type,omitempty"` } func (m *Validator) Reset() { *m = Validator{} } @@ -163,11 +184,12 @@ func (m *Validator) GetAddress() []byte { return nil } -func (m *Validator) GetPubKey() crypto.PublicKey { +// Deprecated: Do not use. +func (m *Validator) GetPubKey() *crypto.PublicKey { if m != nil { return m.PubKey } - return crypto.PublicKey{} + return nil } func (m *Validator) GetVotingPower() int64 { @@ -184,6 +206,20 @@ func (m *Validator) GetProposerPriority() int64 { return 0 } +func (m *Validator) GetPubKeyBytes() []byte { + if m != nil { + return m.PubKeyBytes + } + return nil +} + +func (m *Validator) GetPubKeyType() string { + if m != nil { + return m.PubKeyType + } + return "" +} + type SimpleValidator struct { PubKey *crypto.PublicKey `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` VotingPower int64 `protobuf:"varint,2,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` @@ -246,39 +282,45 @@ func init() { func init() { proto.RegisterFile("tendermint/types/validator.proto", fileDescriptor_4e92274df03d3088) } var fileDescriptor_4e92274df03d3088 = []byte{ - // 502 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x93, 0xcd, 0x6e, 0xd3, 0x40, - 0x14, 0x85, 0x3d, 0x4d, 0xd5, 0x96, 0x49, 0x04, 0xce, 0xa8, 0x45, 0x91, 0xa9, 0x8c, 0xe9, 0x2a, - 0xfc, 0xc8, 0x16, 0x54, 0x88, 0x45, 0x57, 0x49, 0x4a, 0x51, 0x94, 0xc4, 0x89, 0x92, 0xb6, 0x48, - 0x6c, 0xac, 0x38, 0x19, 0xcc, 0xc8, 0x3f, 0x33, 0x1a, 0x4f, 0x52, 0xf9, 0x0d, 0x50, 0x56, 0xbc, - 0x40, 0x56, 0xb0, 0x60, 0xcd, 0x53, 0x74, 0xd9, 0x1d, 0xac, 0x10, 0x4a, 0x5e, 0x04, 0xc5, 0x6e, - 0x62, 0xb7, 0x01, 0x75, 0x77, 0x7d, 0xcf, 0x39, 0xf7, 0x7e, 0x1e, 0xe9, 0x42, 0x4d, 0xe0, 0x60, - 0x88, 0xb9, 0x4f, 0x02, 0x61, 0x88, 0x88, 0xe1, 0xd0, 0x18, 0xf7, 0x3d, 0x32, 0xec, 0x0b, 0xca, - 0x75, 0xc6, 0xa9, 0xa0, 0x48, 0x4e, 0x1d, 0x7a, 0xec, 0x50, 0x76, 0x1d, 0xea, 0xd0, 0x58, 0x34, - 0x16, 0x55, 0xe2, 0x53, 0xf6, 0x33, 0x93, 0x06, 0x3c, 0x62, 0x82, 0x1a, 0x2e, 0x8e, 0xc2, 0x44, - 0x3d, 0xf8, 0x01, 0x60, 0xe1, 0x7c, 0x39, 0xb9, 0x87, 0x05, 0x3a, 0x82, 0x70, 0xb5, 0x29, 0x2c, - 0x01, 0x2d, 0x57, 0xce, 0xbf, 0x7a, 0xa4, 0xdf, 0xde, 0xa5, 0xaf, 0x32, 0xdd, 0x8c, 0x1d, 0xbd, - 0x81, 0x3b, 0x8c, 0x53, 0x46, 0x43, 0xcc, 0x4b, 0x1b, 0x1a, 0xb8, 0x2b, 0xba, 0x32, 0xa3, 0x17, - 0x10, 0x09, 0x2a, 0xfa, 0x9e, 0x35, 0xa6, 0x82, 0x04, 0x8e, 0xc5, 0xe8, 0x05, 0xe6, 0xa5, 0x9c, - 0x06, 0xca, 0xb9, 0xae, 0x1c, 0x2b, 0xe7, 0xb1, 0xd0, 0x59, 0xf4, 0x17, 0xd0, 0xf7, 0x56, 0x53, - 0x50, 0x09, 0x6e, 0xf7, 0x87, 0x43, 0x8e, 0xc3, 0x05, 0x2e, 0x28, 0x17, 0xba, 0xcb, 0x4f, 0x74, - 0x04, 0xb7, 0xd9, 0xc8, 0xb6, 0x5c, 0x1c, 0x5d, 0xd3, 0xec, 0x67, 0x69, 0x92, 0xc7, 0xd0, 0x3b, - 0x23, 0xdb, 0x23, 0x83, 0x06, 0x8e, 0xaa, 0x9b, 0x97, 0xbf, 0x1f, 0x4b, 0xdd, 0x2d, 0x36, 0xb2, - 0x1b, 0x38, 0x42, 0x4f, 0x60, 0xe1, 0x1f, 0x30, 0xf9, 0x71, 0xca, 0x81, 0x9e, 0xc3, 0xe2, 0xf2, - 0x0f, 0x2c, 0xc6, 0x09, 0xe5, 0x44, 0x44, 0xa5, 0xcd, 0x04, 0x7a, 0x29, 0x74, 0xae, 0xfb, 0x07, - 0x2e, 0x7c, 0xd0, 0x23, 0x3e, 0xf3, 0x70, 0x4a, 0xfe, 0x3a, 0xe5, 0x03, 0x77, 0xf3, 0xfd, 0x97, - 0x6c, 0x63, 0x8d, 0xec, 0xd9, 0x4f, 0x00, 0xf3, 0x55, 0x8f, 0x0e, 0xdc, 0xfa, 0xf1, 0x89, 0xd7, - 0x77, 0xd0, 0x4b, 0xb8, 0x57, 0x6d, 0xb6, 0x6b, 0x0d, 0xab, 0x7e, 0x6c, 0x9d, 0x34, 0x2b, 0xef, - 0xac, 0x33, 0xb3, 0x61, 0xb6, 0xdf, 0x9b, 0xb2, 0xa4, 0x3c, 0x9c, 0x4c, 0x35, 0x94, 0xf1, 0x9e, - 0x05, 0x6e, 0x40, 0x2f, 0x02, 0x64, 0xc0, 0xdd, 0x9b, 0x91, 0x4a, 0xb5, 0xf7, 0xd6, 0x3c, 0x95, - 0x81, 0xb2, 0x37, 0x99, 0x6a, 0xc5, 0x4c, 0xa2, 0x62, 0x87, 0x38, 0x10, 0xeb, 0x81, 0x5a, 0xbb, - 0xd5, 0xaa, 0x9f, 0xca, 0x1b, 0x6b, 0x81, 0x1a, 0xf5, 0x7d, 0x22, 0xd0, 0x53, 0x58, 0xbc, 0x19, - 0x30, 0xeb, 0x4d, 0x39, 0xa7, 0xa0, 0xc9, 0x54, 0xbb, 0x9f, 0x71, 0x9b, 0xc4, 0x53, 0x76, 0x3e, - 0x7f, 0x55, 0xa5, 0xef, 0xdf, 0x54, 0x50, 0x6d, 0x5d, 0xce, 0x54, 0x70, 0x35, 0x53, 0xc1, 0x9f, - 0x99, 0x0a, 0xbe, 0xcc, 0x55, 0xe9, 0x6a, 0xae, 0x4a, 0xbf, 0xe6, 0xaa, 0xf4, 0xe1, 0xd0, 0x21, - 0xe2, 0xd3, 0xc8, 0xd6, 0x07, 0xd4, 0x37, 0x06, 0xd4, 0xc7, 0xc2, 0xfe, 0x28, 0xd2, 0x22, 0xb9, - 0x8b, 0xdb, 0x57, 0x65, 0x6f, 0xc5, 0xfd, 0xc3, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbb, 0x8c, - 0xe3, 0x20, 0x70, 0x03, 0x00, 0x00, + // 608 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xcd, 0x6e, 0xd3, 0x4e, + 0x14, 0xc5, 0x33, 0x49, 0x9b, 0xb6, 0x93, 0xfc, 0xff, 0xb8, 0xa3, 0x16, 0x2c, 0xb7, 0x32, 0x43, + 0x56, 0xe1, 0x43, 0x36, 0x50, 0x01, 0x42, 0x88, 0x45, 0x9d, 0xd2, 0x2a, 0x4a, 0xeb, 0x56, 0xe9, + 0x07, 0x12, 0x1b, 0x2b, 0x4e, 0x06, 0x63, 0xc5, 0xf6, 0x58, 0xf6, 0xa4, 0x95, 0xdf, 0x00, 0x79, + 0xc5, 0x0b, 0x78, 0x05, 0x0b, 0xd6, 0xbc, 0x03, 0x12, 0xcb, 0x2e, 0x59, 0xa2, 0xe6, 0x1d, 0x58, + 0x23, 0xdb, 0x71, 0xe2, 0x26, 0xa9, 0xba, 0xb3, 0xef, 0x3d, 0xe7, 0xde, 0xdf, 0xb9, 0x8b, 0x81, + 0x98, 0x11, 0xa7, 0x47, 0x3c, 0xdb, 0x74, 0x98, 0xcc, 0x02, 0x97, 0xf8, 0xf2, 0x79, 0xc7, 0x32, + 0x7b, 0x1d, 0x46, 0x3d, 0xc9, 0xf5, 0x28, 0xa3, 0x88, 0x9b, 0x28, 0xa4, 0x44, 0x21, 0xac, 0x19, + 0xd4, 0xa0, 0x49, 0x53, 0x8e, 0xbf, 0x52, 0x9d, 0xb0, 0x99, 0x9b, 0xd4, 0xf5, 0x02, 0x97, 0x51, + 0xb9, 0x4f, 0x02, 0x3f, 0xed, 0xd6, 0x7e, 0x00, 0x58, 0x3d, 0xcb, 0x26, 0x1f, 0x13, 0x86, 0xde, + 0x40, 0x38, 0xde, 0xe4, 0xf3, 0x00, 0x97, 0xea, 0x95, 0xe7, 0x1b, 0xd2, 0xf4, 0x2e, 0x69, 0xec, + 0x69, 0xe7, 0xe4, 0xe8, 0x15, 0x5c, 0x76, 0x3d, 0xea, 0x52, 0x9f, 0x78, 0x7c, 0x11, 0x83, 0xdb, + 0xac, 0x63, 0x31, 0x7a, 0x02, 0x11, 0xa3, 0xac, 0x63, 0x69, 0xe7, 0x94, 0x99, 0x8e, 0xa1, 0xb9, + 0xf4, 0x82, 0x78, 0x7c, 0x09, 0x83, 0x7a, 0xa9, 0xcd, 0x25, 0x9d, 0xb3, 0xa4, 0x71, 0x14, 0xd7, + 0x6b, 0x7f, 0x01, 0x5c, 0x19, 0x4f, 0x41, 0x3c, 0x5c, 0xea, 0xf4, 0x7a, 0x1e, 0xf1, 0x63, 0x5c, + 0x50, 0xaf, 0xb6, 0xb3, 0x5f, 0xf4, 0x1a, 0x2e, 0xb9, 0x03, 0x5d, 0xeb, 0x93, 0x60, 0x44, 0xb3, + 0x99, 0xa7, 0x49, 0x8f, 0x21, 0x1d, 0x0d, 0x74, 0xcb, 0xec, 0xb6, 0x48, 0xa0, 0x14, 0x79, 0xd0, + 0x2e, 0xbb, 0x03, 0xbd, 0x45, 0x02, 0xf4, 0x00, 0x56, 0xe7, 0xa0, 0x54, 0xce, 0x27, 0x14, 0xe8, + 0x31, 0x5c, 0xcd, 0xf8, 0x35, 0xd7, 0x33, 0xa9, 0x67, 0xb2, 0x80, 0x5f, 0x48, 0x91, 0xb3, 0xc6, + 0xd1, 0xa8, 0x8e, 0x6a, 0xf0, 0xbf, 0x11, 0x8a, 0xa6, 0x07, 0x8c, 0xf8, 0xfc, 0x62, 0x82, 0x5a, + 0x49, 0xd7, 0x29, 0x71, 0x09, 0x61, 0x58, 0xcd, 0x34, 0xf1, 0xa5, 0xf8, 0x32, 0x06, 0xf5, 0x95, + 0x36, 0x4c, 0x25, 0x27, 0x81, 0x4b, 0x6a, 0x7d, 0x78, 0xe7, 0xd8, 0xb4, 0x5d, 0x8b, 0x4c, 0xd2, + 0xbf, 0x98, 0x64, 0x04, 0xb7, 0x67, 0xbc, 0x31, 0x5f, 0x71, 0x26, 0xdf, 0xa3, 0x9f, 0x25, 0x58, + 0x51, 0x2c, 0xda, 0xed, 0x37, 0x77, 0x76, 0xad, 0x8e, 0x81, 0x9e, 0xc1, 0x75, 0x65, 0xff, 0xb0, + 0xd1, 0xd2, 0x9a, 0x3b, 0xda, 0xee, 0xfe, 0xf6, 0x9e, 0x76, 0xaa, 0xb6, 0xd4, 0xc3, 0xf7, 0x2a, + 0x57, 0x10, 0xee, 0x86, 0x11, 0x46, 0x39, 0xed, 0xa9, 0xd3, 0x77, 0xe8, 0x85, 0x83, 0x64, 0xb8, + 0x76, 0xdd, 0xb2, 0xad, 0x1c, 0xbf, 0x53, 0x4f, 0x38, 0x20, 0xac, 0x87, 0x11, 0x5e, 0xcd, 0x39, + 0xb6, 0x75, 0x9f, 0x38, 0x6c, 0xd6, 0xd0, 0x38, 0x3c, 0x38, 0x68, 0x9e, 0x70, 0xc5, 0x19, 0x43, + 0x83, 0xda, 0xb6, 0xc9, 0xd0, 0x43, 0xb8, 0x7a, 0xdd, 0xa0, 0x36, 0xf7, 0xb9, 0x92, 0x80, 0xc2, + 0x08, 0xff, 0x9f, 0x53, 0xab, 0xa6, 0x85, 0x5e, 0x42, 0x7e, 0x0a, 0x66, 0x6f, 0x3c, 0x7f, 0x41, + 0xe0, 0xc3, 0x08, 0xaf, 0xe5, 0x81, 0x8c, 0x6c, 0x45, 0x03, 0xde, 0xbf, 0xc9, 0x97, 0xe5, 0x59, + 0x14, 0xc4, 0x30, 0xc2, 0xc2, 0x3c, 0xfb, 0x28, 0xd8, 0xd3, 0xe9, 0xe3, 0xc5, 0x43, 0x62, 0xd6, + 0xf2, 0xec, 0x29, 0x8c, 0x04, 0xf7, 0x2d, 0xdc, 0x9c, 0xeb, 0xc8, 0x76, 0x2e, 0x09, 0x1b, 0x61, + 0x84, 0xef, 0xcd, 0x18, 0xd3, 0x85, 0xc2, 0xf2, 0xe7, 0xaf, 0x62, 0xe1, 0xfb, 0x37, 0x11, 0x28, + 0x07, 0xbf, 0xae, 0x44, 0x70, 0x79, 0x25, 0x82, 0x3f, 0x57, 0x22, 0xf8, 0x32, 0x14, 0x0b, 0x97, + 0x43, 0xb1, 0xf0, 0x7b, 0x28, 0x16, 0x3e, 0x6c, 0x19, 0x26, 0xfb, 0x34, 0xd0, 0xa5, 0x2e, 0xb5, + 0xe5, 0x2e, 0xb5, 0x09, 0xd3, 0x3f, 0xb2, 0xc9, 0x47, 0xfa, 0x92, 0x4c, 0xbf, 0x43, 0x7a, 0x39, + 0xa9, 0x6f, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x84, 0x4d, 0xf8, 0xa2, 0x04, 0x00, 0x00, } func (m *ValidatorSet) Marshal() (dAtA []byte, err error) { @@ -355,6 +397,20 @@ func (m *Validator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.PubKeyType) > 0 { + i -= len(m.PubKeyType) + copy(dAtA[i:], m.PubKeyType) + i = encodeVarintValidator(dAtA, i, uint64(len(m.PubKeyType))) + i-- + dAtA[i] = 0x32 + } + if len(m.PubKeyBytes) > 0 { + i -= len(m.PubKeyBytes) + copy(dAtA[i:], m.PubKeyBytes) + i = encodeVarintValidator(dAtA, i, uint64(len(m.PubKeyBytes))) + i-- + dAtA[i] = 0x2a + } if m.ProposerPriority != 0 { i = encodeVarintValidator(dAtA, i, uint64(m.ProposerPriority)) i-- @@ -365,16 +421,18 @@ func (m *Validator) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x18 } - { - size, err := m.PubKey.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err + if m.PubKey != nil { + { + size, err := m.PubKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintValidator(dAtA, i, uint64(size)) } - i -= size - i = encodeVarintValidator(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 } - i-- - dAtA[i] = 0x12 if len(m.Address) > 0 { i -= len(m.Address) copy(dAtA[i:], m.Address) @@ -468,14 +526,24 @@ func (m *Validator) Size() (n int) { if l > 0 { n += 1 + l + sovValidator(uint64(l)) } - l = m.PubKey.Size() - n += 1 + l + sovValidator(uint64(l)) + if m.PubKey != nil { + l = m.PubKey.Size() + n += 1 + l + sovValidator(uint64(l)) + } if m.VotingPower != 0 { n += 1 + sovValidator(uint64(m.VotingPower)) } if m.ProposerPriority != 0 { n += 1 + sovValidator(uint64(m.ProposerPriority)) } + l = len(m.PubKeyBytes) + if l > 0 { + n += 1 + l + sovValidator(uint64(l)) + } + l = len(m.PubKeyType) + if l > 0 { + n += 1 + l + sovValidator(uint64(l)) + } return n } @@ -732,6 +800,9 @@ func (m *Validator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } + if m.PubKey == nil { + m.PubKey = &crypto.PublicKey{} + } if err := m.PubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -774,6 +845,72 @@ func (m *Validator) Unmarshal(dAtA []byte) error { break } } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKeyBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowValidator + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthValidator + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthValidator + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PubKeyBytes = append(m.PubKeyBytes[:0], dAtA[iNdEx:postIndex]...) + if m.PubKeyBytes == nil { + m.PubKeyBytes = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKeyType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowValidator + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthValidator + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthValidator + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PubKeyType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipValidator(dAtA[iNdEx:]) diff --git a/proto/tendermint/types/validator.proto b/proto/tendermint/types/validator.proto index 18639779eef..4659b68f76d 100644 --- a/proto/tendermint/types/validator.proto +++ b/proto/tendermint/types/validator.proto @@ -15,6 +15,16 @@ enum BlockIDFlag { BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; // the vote was not received BLOCK_ID_FLAG_COMMIT = 2 [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; // voted for the block that received the majority BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; // voted for nil + + // Aggregated commit + // Voted for the block and contains aggregated signature. + BLOCK_ID_FLAG_AGG_COMMIT = 4 [(gogoproto.enumvalue_customname) = "BlockIDFlagAggCommit"]; + // Voted for the block. + BLOCK_ID_FLAG_AGG_COMMIT_ABSENT = 5 [(gogoproto.enumvalue_customname) = "BlockIDFlagAggCommitAbsent"]; + // Voted for nil and contains aggregated signature. + BLOCK_ID_FLAG_AGG_NIL = 6 [(gogoproto.enumvalue_customname) = "BlockIDFlagAggNil"]; + // Voted for nil. + BLOCK_ID_FLAG_AGG_NIL_ABSENT = 7 [(gogoproto.enumvalue_customname) = "BlockIDFlagAggNilAbsent"]; } message ValidatorSet { @@ -25,9 +35,13 @@ message ValidatorSet { message Validator { bytes address = 1; - tendermint.crypto.PublicKey pub_key = 2 [(gogoproto.nullable) = false]; + // NOTE(berachain): bera-v1.x serializes keys via pub_key_bytes/pub_key_type + // (the v1.x layout); pub_key is kept as a decode fallback only. + tendermint.crypto.PublicKey pub_key = 2 [deprecated = true]; int64 voting_power = 3; int64 proposer_priority = 4; + bytes pub_key_bytes = 5; + string pub_key_type = 6; } message SimpleValidator { diff --git a/rpc/client/evidence_test.go b/rpc/client/evidence_test.go index b25f02d5234..23af080179f 100644 --- a/rpc/client/evidence_test.go +++ b/rpc/client/evidence_test.go @@ -22,15 +22,9 @@ import ( "github.com/cometbft/cometbft/types" ) -// For some reason the empty node used in tests has a time of -// 2018-10-10 08:20:13.695936996 +0000 UTC -// this is because the test genesis time is set here -// so in order to validate evidence we need evidence to be the same time -var defaultTestTime = time.Date(2018, 10, 10, 8, 20, 13, 695936996, time.UTC) - func newEvidence(t *testing.T, val *privval.FilePV, vote *types.Vote, vote2 *types.Vote, - chainID string, + chainID string, evTime time.Time, ) *types.DuplicateVoteEvidence { var err error @@ -46,7 +40,7 @@ func newEvidence(t *testing.T, val *privval.FilePV, validator := types.NewValidator(val.Key.PubKey, 10) valSet := types.NewValidatorSet([]*types.Validator{validator}) - ev, err := types.NewDuplicateVoteEvidence(vote, vote2, defaultTestTime, valSet) + ev, err := types.NewDuplicateVoteEvidence(vote, vote2, evTime, valSet) require.NoError(t, err) return ev } @@ -55,6 +49,7 @@ func makeEvidences( t *testing.T, val *privval.FilePV, chainID string, + evTime time.Time, ) (correct *types.DuplicateVoteEvidence, fakes []*types.DuplicateVoteEvidence) { vote := types.Vote{ ValidatorAddress: val.Key.Address, @@ -62,7 +57,6 @@ func makeEvidences( Height: 1, Round: 0, Type: cmtproto.PrevoteType, - Timestamp: defaultTestTime, BlockID: types.BlockID{ Hash: tmhash.Sum(cmtrand.Bytes(tmhash.Size)), PartSetHeader: types.PartSetHeader{ @@ -74,7 +68,7 @@ func makeEvidences( vote2 := vote vote2.BlockID.Hash = tmhash.Sum([]byte("blockhash2")) - correct = newEvidence(t, val, &vote, &vote2, chainID) + correct = newEvidence(t, val, &vote, &vote2, chainID, evTime) fakes = make([]*types.DuplicateVoteEvidence, 0) @@ -82,34 +76,34 @@ func makeEvidences( { v := vote2 v.ValidatorAddress = []byte("some_address") - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, evTime)) } // different height { v := vote2 v.Height = vote.Height + 1 - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, evTime)) } // different round { v := vote2 v.Round = vote.Round + 1 - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, evTime)) } // different type { v := vote2 v.Type = cmtproto.PrecommitType - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, evTime)) } // exactly same vote { v := vote - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, evTime)) } return correct, fakes @@ -123,9 +117,18 @@ func TestBroadcastEvidence_DuplicateVoteEvidence(t *testing.T) { ) for i, c := range GetClients() { - correct, fakes := makeEvidences(t, pv, chainID) t.Logf("client %d", i) + // The evidence is for height 1; make sure the node has committed the + // block at that height before broadcasting. Under PBTS the block time + // is the proposer's wall clock, so the evidence must be stamped with + // the actual block time. + require.NoError(t, client.WaitForHeight(c, 1, nil)) + h := int64(1) + blk, err := c.Block(context.Background(), &h) + require.NoError(t, err) + correct, fakes := makeEvidences(t, pv, chainID, blk.Block.Time) + result, err := c.BroadcastEvidence(context.Background(), correct) require.NoError(t, err, "BroadcastEvidence(%s) failed", correct) assert.Equal(t, correct.Hash(), result.Hash, "expected result hash to match evidence hash") diff --git a/state/execution.go b/state/execution.go index 229c7c2baac..c1ff17a1410 100644 --- a/state/execution.go +++ b/state/execution.go @@ -3,6 +3,7 @@ package state import ( "bytes" "context" + "errors" "fmt" "sync/atomic" "time" @@ -146,7 +147,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock( &abci.RequestPrepareProposal{ MaxTxBytes: maxDataBytes, Txs: block.Txs.ToSliceOfBytes(), - LocalLastCommit: buildExtendedCommitInfoFromStore(lastExtCommit, blockExec.store, state.InitialHeight, state.ConsensusParams.ABCI), + LocalLastCommit: buildExtendedCommitInfoFromStore(lastExtCommit, blockExec.store, state.InitialHeight, state.ConsensusParams.Feature), Misbehavior: block.Evidence.Evidence.ToABCI(), Height: block.Height, Time: block.Time, @@ -179,14 +180,15 @@ func (blockExec *BlockExecutor) ProcessProposal( state State, ) (bool, error) { resp, err := blockExec.proxyApp.ProcessProposal(context.TODO(), &abci.RequestProcessProposal{ - Hash: block.Header.Hash(), - Height: block.Height, - Time: block.Time, - Txs: block.Txs.ToSliceOfBytes(), - ProposedLastCommit: buildLastCommitInfoFromStore(block, blockExec.store, state.InitialHeight), - Misbehavior: block.Evidence.Evidence.ToABCI(), - ProposerAddress: block.ProposerAddress, - NextValidatorsHash: block.NextValidatorsHash, + Hash: block.Header.Hash(), + Height: block.Height, + Time: block.Time, + Txs: block.Txs.ToSliceOfBytes(), + ProposedLastCommit: buildLastCommitInfoFromStore(block, blockExec.store, state.InitialHeight), + Misbehavior: block.Evidence.Evidence.ToABCI(), + ProposerAddress: block.ProposerAddress, + NextValidatorsHash: block.NextValidatorsHash, + NextProposerAddress: state.NextValidators.GetProposer().Address, }) if err != nil { return false, err @@ -337,6 +339,10 @@ func (blockExec *BlockExecutor) applyBlock(state State, blockID types.BlockID, b blockExec.metrics.ConsensusParamUpdates.Add(1) } + if err := validateNextBlockDelay(abciResponse.NextBlockDelay); err != nil { + return state, fmt.Errorf("error in next block delay: %w", err) + } + // Update the state with the block and responses. state, err = updateState(state, blockID, &block.Header, abciResponse, validatorUpdates) if err != nil { @@ -592,7 +598,7 @@ func BuildLastCommitInfo(block *types.Block, lastValSet *types.ValidatorSet, ini // data, it returns an empty record. // // Assumes that the commit signatures are sorted according to validator index. -func buildExtendedCommitInfoFromStore(ec *types.ExtendedCommit, store Store, initialHeight int64, ap types.ABCIParams) abci.ExtendedCommitInfo { +func buildExtendedCommitInfoFromStore(ec *types.ExtendedCommit, store Store, initialHeight int64, fp types.FeatureParams) abci.ExtendedCommitInfo { if ec.Height < initialHeight { // There are no extended commits for heights below the initial height. return abci.ExtendedCommitInfo{} @@ -603,13 +609,13 @@ func buildExtendedCommitInfoFromStore(ec *types.ExtendedCommit, store Store, ini panic(fmt.Errorf("failed to load validator set at height %d, initial height %d: %w", ec.Height, initialHeight, err)) } - return BuildExtendedCommitInfo(ec, valSet, initialHeight, ap) + return BuildExtendedCommitInfo(ec, valSet, initialHeight, fp) } // BuildExtendedCommitInfo builds an ExtendedCommitInfo from the given block and validator set. // If you want to load the validator set from the store instead of providing it, // use buildExtendedCommitInfoFromStore. -func BuildExtendedCommitInfo(ec *types.ExtendedCommit, valSet *types.ValidatorSet, initialHeight int64, ap types.ABCIParams) abci.ExtendedCommitInfo { +func BuildExtendedCommitInfo(ec *types.ExtendedCommit, valSet *types.ValidatorSet, initialHeight int64, fp types.FeatureParams) abci.ExtendedCommitInfo { if ec.Height < initialHeight { // There are no extended commits for heights below the initial height. return abci.ExtendedCommitInfo{} @@ -646,7 +652,7 @@ func BuildExtendedCommitInfo(ec *types.ExtendedCommit, valSet *types.ValidatorSe // during that height, we ensure they are present and deliver the data to // the proposer. If they were not enabled during this previous height, we // will not deliver extension data. - if err := ecs.EnsureExtension(ap.VoteExtensionsEnabled(ec.Height)); err != nil { + if err := ecs.EnsureExtension(fp.VoteExtensionsEnabled(ec.Height)); err != nil { panic(fmt.Errorf("commit at height %d has problems with vote extension data; err %w", ec.Height, err)) } @@ -757,9 +763,17 @@ func updateState( LastHeightConsensusParamsChanged: lastHeightParamsChanged, LastResultsHash: TxResultsHash(abciResponse.TxResults), AppHash: nil, + NextBlockDelay: abciResponse.NextBlockDelay, }, nil } +func validateNextBlockDelay(nextBlockDelay time.Duration) error { + if nextBlockDelay < 0 { + return errors.New("negative duration") + } + return nil +} + // Fire NewBlock, NewBlockHeader. // Fire TxEvent for every tx. // NOTE: if CometBFT crashes before commit, some or all of these events may be published again. diff --git a/state/execution_test.go b/state/execution_test.go index 3249deae503..5ad8d1c097f 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -184,7 +184,6 @@ func TestFinalizeBlockValidators(t *testing.T) { CommitSig: types.CommitSig{ BlockIDFlag: types.BlockIDFlagCommit, ValidatorAddress: state.Validators.Validators[0].Address, - Timestamp: now, Signature: []byte("Signature1"), }, Extension: []byte("extension1"), @@ -195,7 +194,6 @@ func TestFinalizeBlockValidators(t *testing.T) { CommitSig: types.CommitSig{ BlockIDFlag: types.BlockIDFlagCommit, ValidatorAddress: state.Validators.Validators[1].Address, - Timestamp: now, Signature: []byte("Signature2"), }, Extension: []byte("extension2"), @@ -297,7 +295,6 @@ func TestFinalizeBlockMisbehavior(t *testing.T) { Signatures: []types.CommitSig{{ BlockIDFlag: types.BlockIDFlagNil, ValidatorAddress: crypto.AddressHash([]byte("validator_address")), - Timestamp: defaultEvidenceTime, Signature: crypto.CRandBytes(types.MaxSignatureSize), }}, }, @@ -440,8 +437,9 @@ func TestProcessProposal(t *testing.T) { Round: 0, Votes: voteInfos, }, - NextValidatorsHash: block1.NextValidatorsHash, - ProposerAddress: block1.ProposerAddress, + NextValidatorsHash: block1.NextValidatorsHash, + ProposerAddress: block1.ProposerAddress, + NextProposerAddress: state.NextValidators.GetProposer().Address, } acceptBlock, err := blockExec.ProcessProposal(block1, state) @@ -1073,7 +1071,7 @@ func TestCreateProposalAbsentVoteExtensions(t *testing.T) { stateStore := sm.NewStore(stateDB, sm.StoreOptions{ DiscardABCIResponses: false, }) - state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.extensionEnableHeight + state.ConsensusParams.Feature.VoteExtensionsEnableHeight = testCase.extensionEnableHeight mp := &mpmocks.Mempool{} mp.On("Lock").Return() mp.On("Unlock").Return() diff --git a/state/state.go b/state/state.go index acde953c7bb..a8b2d611988 100644 --- a/state/state.go +++ b/state/state.go @@ -77,6 +77,10 @@ type State struct { // the latest AppHash we've received from calling abci.Commit() AppHash []byte + + // delay between the time when this block is committed and the next height is started. + // previously `timeout_commit` in config.toml + NextBlockDelay time.Duration } // Copy makes a copy of the State for mutating. @@ -101,6 +105,8 @@ func (state State) Copy() State { AppHash: state.AppHash, LastResultsHash: state.LastResultsHash, + + NextBlockDelay: state.NextBlockDelay, } } @@ -169,6 +175,7 @@ func (state *State) ToProto() (*cmtstate.State, error) { sm.LastHeightConsensusParamsChanged = state.LastHeightConsensusParamsChanged sm.LastResultsHash = state.LastResultsHash sm.AppHash = state.AppHash + sm.NextBlockDelay = state.NextBlockDelay return sm, nil } @@ -220,6 +227,7 @@ func FromProto(pb *cmtstate.State) (*State, error) { //nolint:golint state.LastHeightConsensusParamsChanged = pb.LastHeightConsensusParamsChanged state.LastResultsHash = pb.LastResultsHash state.AppHash = pb.AppHash + state.NextBlockDelay = pb.NextBlockDelay return state, nil } @@ -243,9 +251,16 @@ func (state State) MakeBlock( // Set time. var timestamp time.Time - if height == state.InitialHeight { + switch { + case state.ConsensusParams.Feature.PbtsEnabled(height): + // Under Proposer-Based Timestamps the proposer stamps the block with + // its local time. With BLS signature aggregation the per-vote + // timestamps that BFT Time medians over are not signed, so PBTS is + // the only viable block-time source for aggregating networks. + timestamp = cmttime.Now() + case height == state.InitialHeight: timestamp = state.LastBlockTime // genesis time - } else { + default: ts, err := MedianTime(lastCommit, state.LastValidators) if err != nil { return nil, fmt.Errorf("error making block while calculating median time: %w", err) diff --git a/state/validation.go b/state/validation.go index 73e8bc1903d..f7678982ce9 100644 --- a/state/validation.go +++ b/state/validation.go @@ -8,6 +8,7 @@ import ( "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" ) //----------------------------------------------------- @@ -127,6 +128,14 @@ func validateBlock(state State, block *types.Block, opts ...func(*blockValidatio block.Time, time.Now(), tol, ) } + pbtsEnabled := state.ConsensusParams.Feature.PbtsEnabled(block.Height) + if pbtsEnabled { + // Times must be canonical + if cantime := cmttime.Canonical(block.Time); block.Time != cantime { + return fmt.Errorf("block time %v is not canonical", block.Time) + } + } + switch { case block.Height > state.InitialHeight: if !block.Time.After(state.LastBlockTime) { @@ -136,20 +145,33 @@ func validateBlock(state State, block *types.Block, opts ...func(*blockValidatio ) } - medianTime, err := MedianTime(block.LastCommit, state.LastValidators) - if err != nil { - return fmt.Errorf("error validating block while calculating median time: %w", err) - } - if !block.Time.Equal(medianTime) { - return fmt.Errorf("invalid block time. Expected %v, got %v", - medianTime, - block.Time, - ) + // Under PBTS the block time is the proposer's local time; its + // timeliness is enforced by the consensus prevote step, so only + // monotonicity is validated here. Without PBTS the block time must + // equal the BFT Time median of the last commit's vote timestamps. + if !pbtsEnabled { + medianTime, err := MedianTime(block.LastCommit, state.LastValidators) + if err != nil { + return fmt.Errorf("error validating block while calculating median time: %w", err) + } + if !block.Time.Equal(medianTime) { + return fmt.Errorf("invalid block time. Expected %v, got %v", + medianTime, + block.Time, + ) + } } case block.Height == state.InitialHeight: genesisTime := state.LastBlockTime - if !block.Time.Equal(genesisTime) { + if pbtsEnabled { + if block.Time.Before(genesisTime) { + return fmt.Errorf("block time %v is before genesis time %v", + block.Time, + genesisTime, + ) + } + } else if !block.Time.Equal(genesisTime) { return fmt.Errorf("block time %v is not equal to genesis time %v", block.Time, genesisTime, diff --git a/state/validation_test.go b/state/validation_test.go index bfa99f93845..61e660a3cd4 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -197,9 +197,15 @@ func TestValidateBlockCommit(t *testing.T) { /* #2589: test len(block.LastCommit.Signatures) == state.LastValidators.Size() */ - _, err = makeBlock(state, height, wrongSigsCommit) - require.Error(t, err) - require.ErrorContains(t, err, "error making block") + block, err = makeBlock(state, height, wrongSigsCommit) + require.NoError(t, err) + err = blockExec.ValidateBlock(state, block) + _, isErrInvalidCommitSignatures := err.(errors.ErrInvalidCommitSignatures) + require.True(t, isErrInvalidCommitSignatures, + "expected ErrInvalidCommitSignatures at height %d, but got: %v", + height, + err, + ) } /* @@ -369,116 +375,6 @@ func TestValidateBlockEvidence(t *testing.T) { } } -func TestValidateBlockTime(t *testing.T) { - proxyApp := newTestApp() - require.NoError(t, proxyApp.Start()) - defer proxyApp.Stop() //nolint:errcheck // ignore for tests - - state, stateDB, privVals := makeState(3, 1) - stateStore := sm.NewStore(stateDB, sm.StoreOptions{ - DiscardABCIResponses: false, - }) - mp := &mpmocks.Mempool{} - mp.On("Lock").Return() - mp.On("Unlock").Return() - mp.On("FlushAppConn", mock.Anything).Return(nil) - mp.On("Update", - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything, - mock.Anything).Return(nil) - - blockStore := store.NewBlockStore(dbm.NewMemDB()) - - blockExec := sm.NewBlockExecutor( - stateStore, - log.TestingLogger(), - proxyApp.Consensus(), - mp, - sm.EmptyEvidencePool{}, - blockStore, - ) - lastCommit := &types.Commit{} - var lastExtCommit *types.ExtendedCommit - - // Build up state for test - for height := int64(1); height < 3; height++ { - var err error - state, _, lastExtCommit, err = makeAndCommitGoodBlock( - state, height, lastCommit, state.Validators.GetProposer().Address, blockExec, privVals, nil) - require.NoError(t, err, "height %d", height) - lastCommit = lastExtCommit.ToCommit() - } - - t.Run("block time before last block time", func(t *testing.T) { - height := int64(3) - block, err := makeBlock(state, height, lastCommit) - require.NoError(t, err) - - // Set time to before last block time - block.Time = block.Time.Add(-time.Millisecond * 10) - err = blockExec.ValidateBlock(state, block) - - require.ErrorContains(t, err, "not greater than last block time") - }) - - t.Run("block time after last block time, different than median time", func(t *testing.T) { - height := int64(3) - block, err := makeBlock(state, height, lastCommit) - require.NoError(t, err) - // Set time to after the median time - block.Time = block.Time.Add(time.Second) - err = blockExec.ValidateBlock(state, block) - require.Error(t, err) - require.Contains(t, err.Error(), "invalid block time") - }) - - t.Run("block time after last block time, same as median time", func(t *testing.T) { - height := int64(3) - block, err := makeBlock(state, height, lastCommit) - require.NoError(t, err) - err = blockExec.ValidateBlock(state, block) - require.NoError(t, err) - }) - - t.Run("block time exceeds wall clock tolerance", func(t *testing.T) { - blockExecWithTol := sm.NewBlockExecutor( - stateStore, - log.TestingLogger(), - proxyApp.Consensus(), - mp, - sm.EmptyEvidencePool{}, - blockStore, - sm.BlockExecutorWithBlockTimeTolerance(30*time.Second), - ) - height := int64(3) - block, err := makeBlock(state, height, lastCommit) - require.NoError(t, err) - block.Time = time.Now().Add(1000 * time.Hour) - err = blockExecWithTol.ValidateBlock(state, block) - require.Error(t, err) - assert.Contains(t, err.Error(), "too far in the future") - }) - - t.Run("tolerance not set still allows valid blocks", func(t *testing.T) { - blockExecNoTol := sm.NewBlockExecutor( - stateStore, - log.TestingLogger(), - proxyApp.Consensus(), - mp, - sm.EmptyEvidencePool{}, - blockStore, - ) - height := int64(3) - block, err := makeBlock(state, height, lastCommit) - require.NoError(t, err) - err = blockExecNoTol.ValidateBlock(state, block) - require.NoError(t, err) - }) -} - func TestValidateBlockInvalidCommit(t *testing.T) { proxyApp := newTestApp() require.NoError(t, proxyApp.Start()) @@ -543,8 +439,9 @@ func TestValidateBlockInvalidCommit(t *testing.T) { }, } - _, err := makeBlock(state, height, invalidCommit) + block, err := makeBlock(state, height, invalidCommit) + require.NoError(t, err) + err = blockExec.ValidateBlock(state, block) require.Error(t, err) - require.Contains(t, err.Error(), "commit validator not found in validator set") }) } diff --git a/store/store_test.go b/store/store_test.go index 155655d36f4..6b7c11ad48d 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -43,7 +43,6 @@ func makeTestExtCommitWithNumSigs(height int64, timestamp time.Time, numSigs int CommitSig: types.CommitSig{ BlockIDFlag: types.BlockIDFlagCommit, ValidatorAddress: cmtrand.Bytes(crypto.AddressSize), - Timestamp: timestamp, Signature: cmtrand.Bytes(64), }, ExtensionSignature: []byte("ExtensionSignature"), @@ -557,6 +556,10 @@ func TestPruneBlocks(t *testing.T) { }) state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile()) require.NoError(t, err) + // Use BFT Time for this test: with PBTS the proposer stamps blocks with + // the current time, which would keep every block inside the evidence + // max-age duration and defeat the height-based retain assertions below. + state.ConsensusParams.Feature.PbtsEnableHeight = 0 db := dbm.NewMemDB() bs := NewBlockStore(db) assert.EqualValues(t, 0, bs.Base()) diff --git a/test/e2e/runner/setup.go b/test/e2e/runner/setup.go index 16a1fef61b2..b2cb78a8195 100644 --- a/test/e2e/runner/setup.go +++ b/test/e2e/runner/setup.go @@ -142,7 +142,7 @@ func MakeGenesis(testnet *e2e.Testnet) (types.GenesisDoc, error) { genesis.ConsensusParams.Block.MaxBytes = testnet.BlockMaxBytes } if testnet.VoteExtensionsUpdateHeight == -1 { - genesis.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testnet.VoteExtensionsEnableHeight + genesis.ConsensusParams.Feature.VoteExtensionsEnableHeight = testnet.VoteExtensionsEnableHeight } for validator, power := range testnet.Validators { genesis.Validators = append(genesis.Validators, types.GenesisValidator{ diff --git a/types/block.go b/types/block.go index 5cf0742e826..3866f78f7ea 100644 --- a/types/block.go +++ b/types/block.go @@ -15,6 +15,7 @@ import ( "github.com/cometbft/cometbft/crypto/tmhash" "github.com/cometbft/cometbft/libs/bits" cmtbytes "github.com/cometbft/cometbft/libs/bytes" + cmtjson "github.com/cometbft/cometbft/libs/json" cmtmath "github.com/cometbft/cometbft/libs/math" cmtsync "github.com/cometbft/cometbft/libs/sync" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" @@ -39,6 +40,13 @@ const ( MaxOverheadForBlock int64 = 11 ) +func init() { + // RoundState.LastCommit is an interface (VoteSetReader), so the concrete + // types it can hold must be registered for JSON encoding (e.g. the + // /dump_consensus_state RPC endpoint). + cmtjson.RegisterType(&Commit{}, "cometbft/Commit") +} + // Block defines the atomic unit of a CometBFT blockchain. type Block struct { mtx cmtsync.Mutex @@ -586,6 +594,14 @@ const ( BlockIDFlagCommit // BlockIDFlagNil - voted for nil. BlockIDFlagNil + // BlockIDFlagAggCommit - voted for the Commit.BlockID and contains aggregated signature.. + BlockIDFlagAggCommit + // BlockIDFlagAggCommitAbsent - voted for the Commit.BlockID; aggregated signature in another entry. + BlockIDFlagAggCommitAbsent + // BlockIDFlagAggNil - voted for nil and contains aggregated signature.. + BlockIDFlagAggNil + // BlockIDFlagAggNilAbsent - voted for nil; aggregated signature in another entry. + BlockIDFlagAggNilAbsent ) const ( @@ -643,12 +659,10 @@ func (cs CommitSig) String() string { func (cs CommitSig) BlockID(commitBlockID BlockID) BlockID { var blockID BlockID switch cs.BlockIDFlag { - case BlockIDFlagAbsent: + case BlockIDFlagAbsent, BlockIDFlagNil, BlockIDFlagAggNil, BlockIDFlagAggNilAbsent: blockID = BlockID{} - case BlockIDFlagCommit: + case BlockIDFlagCommit, BlockIDFlagAggCommit, BlockIDFlagAggCommitAbsent: blockID = commitBlockID - case BlockIDFlagNil: - blockID = BlockID{} default: panic(fmt.Sprintf("Unknown BlockIDFlag: %v", cs.BlockIDFlag)) } @@ -661,18 +675,23 @@ func (cs CommitSig) ValidateBasic() error { case BlockIDFlagAbsent: case BlockIDFlagCommit: case BlockIDFlagNil: + case BlockIDFlagAggCommit: + case BlockIDFlagAggCommitAbsent: + case BlockIDFlagAggNil: + case BlockIDFlagAggNilAbsent: default: return fmt.Errorf("unknown BlockIDFlag: %v", cs.BlockIDFlag) } + if !cs.Timestamp.IsZero() { + return errors.New("time is present") + } + switch cs.BlockIDFlag { case BlockIDFlagAbsent: if len(cs.ValidatorAddress) != 0 { return errors.New("validator address is present") } - if !cs.Timestamp.IsZero() { - return errors.New("time is present") - } if len(cs.Signature) != 0 { return errors.New("signature is present") } @@ -684,9 +703,7 @@ func (cs CommitSig) ValidateBasic() error { ) } // NOTE: Timestamp validation is subtle and handled elsewhere. - if len(cs.Signature) == 0 { - return errors.New("signature is missing") - } + // NOTE: Signature can be empty when using BLS aggregation. if len(cs.Signature) > MaxSignatureSize { return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize) } @@ -775,19 +792,20 @@ func (ecs ExtendedCommitSig) ValidateBasic() error { // this ExtendedCommitSig. func (ecs ExtendedCommitSig) EnsureExtension(extEnabled bool) error { if extEnabled { - if ecs.BlockIDFlag == BlockIDFlagCommit && len(ecs.ExtensionSignature) == 0 { + sigForBlock := ecs.BlockIDFlag == BlockIDFlagCommit || ecs.BlockIDFlag == BlockIDFlagAggCommit || ecs.BlockIDFlag == BlockIDFlagAggCommitAbsent + if sigForBlock && len(ecs.ExtensionSignature) == 0 { return fmt.Errorf("vote extension signature is missing; validator addr %s, timestamp %v", ecs.ValidatorAddress.String(), ecs.Timestamp, ) } - if ecs.BlockIDFlag != BlockIDFlagCommit && len(ecs.Extension) != 0 { + if !sigForBlock && len(ecs.Extension) != 0 { return fmt.Errorf("non-commit vote extension present; validator addr %s, timestamp %v", ecs.ValidatorAddress.String(), ecs.Timestamp, ) } - if ecs.BlockIDFlag != BlockIDFlagCommit && len(ecs.ExtensionSignature) != 0 { + if !sigForBlock && len(ecs.ExtensionSignature) != 0 { return fmt.Errorf("non-commit vote extension signature present; validator addr %s, timestamp %v", ecs.ValidatorAddress.String(), ecs.Timestamp, @@ -857,18 +875,72 @@ type Commit struct { // Memoized in first call to corresponding method. // NOTE: can't memoize in constructor because constructor isn't used for // unmarshaling. - hash cmtbytes.HexBytes + hash cmtbytes.HexBytes + bitArray *bits.BitArray + mtx cmtsync.Mutex // Protects hash and bitArray. } +// A whole aggregated Commit can stand in for a VoteSet as the source of the +// last commit (individual precommit votes cannot be reconstructed from an +// aggregated commit). +var _ VoteSetReader = (*Commit)(nil) + // Clone creates a deep copy of this commit. func (commit *Commit) Clone() *Commit { sigs := make([]CommitSig, len(commit.Signatures)) copy(sigs, commit.Signatures) - commCopy := *commit - commCopy.Signatures = sigs + commCopy := Commit{ + Height: commit.Height, + Round: commit.Round, + BlockID: commit.BlockID, + Signatures: sigs, + } return &commCopy } +// Type returns the vote type of the commit, which is always +// VoteTypePrecommit +// Implements VoteSetReader. +func (*Commit) Type() byte { return byte(cmtproto.PrecommitType) } + +// BitArray returns a BitArray of which validators voted for BlockID or nil in +// this commit. +// Implements VoteSetReader. +func (commit *Commit) BitArray() *bits.BitArray { + commit.mtx.Lock() + defer commit.mtx.Unlock() + if commit.bitArray == nil { + commit.bitArray = bits.NewBitArray(len(commit.Signatures)) + for i, commitSig := range commit.Signatures { + // TODO: need to check the BlockID otherwise we could be counting conflicts, + // not just the one with +2/3 ! + commit.bitArray.SetIndex(i, commitSig.BlockIDFlag != BlockIDFlagAbsent) + } + } + return commit.bitArray +} + +// GetByIndex returns nil: individual votes cannot be reconstructed from an +// aggregated Commit. +// Implements VoteSetReader. +func (*Commit) GetByIndex(int32) *Vote { + return nil +} + +// IsCommit returns true if there is at least one signature. +// Implements VoteSetReader. +func (commit *Commit) IsCommit() bool { + return len(commit.Signatures) != 0 +} + +// GetHeight returns height of the commit. +// Implements VoteSetReader. +func (commit *Commit) GetHeight() int64 { return commit.Height } + +// GetRound returns round of the commit. +// Implements VoteSetReader. +func (commit *Commit) GetRound() int32 { return commit.Round } + // GetVote converts the CommitSig for the given valIdx to a Vote. Commits do // not contain vote extensions, so the vote extension and vote extension // signature will not be present in the returned vote. @@ -881,10 +953,10 @@ func (commit *Commit) GetVote(valIdx int32) *Vote { Height: commit.Height, Round: commit.Round, BlockID: commitSig.BlockID(commit.BlockID), - Timestamp: commitSig.Timestamp, ValidatorAddress: commitSig.ValidatorAddress, ValidatorIndex: valIdx, Signature: commitSig.Signature, + Timestamp: time.Time{}, } } @@ -937,11 +1009,24 @@ func (commit *Commit) ValidateBasic() error { return nil } +// HasAggregatedSignature returns true if the commit contains an aggregated signature. +func (commit *Commit) HasAggregatedSignature() bool { + for _, sig := range commit.Signatures { + if sig.BlockIDFlag == BlockIDFlagAggCommit || sig.BlockIDFlag == BlockIDFlagAggNil || + sig.BlockIDFlag == BlockIDFlagAggCommitAbsent || sig.BlockIDFlag == BlockIDFlagAggNilAbsent { + return true + } + } + return false +} + // Hash returns the hash of the commit. func (commit *Commit) Hash() cmtbytes.HexBytes { if commit == nil { return nil } + commit.mtx.Lock() + defer commit.mtx.Unlock() if commit.hash == nil { bs := make([][]byte, len(commit.Signatures)) for i, commitSig := range commit.Signatures { @@ -987,6 +1072,9 @@ func (commit *Commit) StringIndented(indent string) string { for i, commitSig := range commit.Signatures { commitSigStrings[i] = commitSig.String() } + commit.mtx.Lock() + hash := commit.hash + commit.mtx.Unlock() return fmt.Sprintf(`Commit{ %s Height: %d %s Round: %d @@ -999,7 +1087,7 @@ func (commit *Commit) StringIndented(indent string) string { indent, commit.BlockID, indent, indent, strings.Join(commitSigStrings, "\n"+indent+" "), - indent, commit.hash) + indent, hash) } // ToProto converts Commit to protobuf @@ -1157,12 +1245,12 @@ func (ec *ExtendedCommit) GetExtendedVote(valIndex int32) *Vote { Height: ec.Height, Round: ec.Round, BlockID: ecs.BlockID(ec.BlockID), - Timestamp: ecs.Timestamp, ValidatorAddress: ecs.ValidatorAddress, ValidatorIndex: valIndex, Signature: ecs.Signature, Extension: ecs.Extension, ExtensionSignature: ecs.ExtensionSignature, + Timestamp: time.Time{}, } } @@ -1505,6 +1593,11 @@ func (blockID BlockID) IsZero() bool { blockID.PartSetHeader.IsZero() } +// IsNil returns true if this is the BlockID of a nil block. +func (blockID BlockID) IsNil() bool { + return blockID.IsZero() +} + // IsComplete returns true if this is a valid BlockID of a non-nil block. func (blockID BlockID) IsComplete() bool { return len(blockID.Hash) == tmhash.Size && diff --git a/types/block_test.go b/types/block_test.go index bfc2039a966..92abb326887 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -24,7 +24,6 @@ import ( cmtrand "github.com/cometbft/cometbft/libs/rand" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" cmtversion "github.com/cometbft/cometbft/proto/tendermint/version" - cmttime "github.com/cometbft/cometbft/types/time" "github.com/cometbft/cometbft/version" ) @@ -548,7 +547,6 @@ func TestVoteSetToExtendedCommit(t *testing.T) { Round: 1, Type: cmtproto.PrecommitType, BlockID: blockID, - Timestamp: time.Now(), } v := vote.ToProto() err = vals[i].SignVote(voteSet.ChainID(), v) @@ -565,7 +563,7 @@ func TestVoteSetToExtendedCommit(t *testing.T) { if testCase.includeExtension { veHeight = 1 } - ec := voteSet.MakeExtendedCommit(ABCIParams{VoteExtensionsEnableHeight: veHeight}) + ec := voteSet.MakeExtendedCommit(FeatureParams{VoteExtensionsEnableHeight: veHeight}) for i := int32(0); int(i) < len(vals); i++ { vote1 := voteSet.GetByIndex(i) @@ -686,7 +684,6 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { Round: round, Type: cmtproto.PrecommitType, BlockID: tc.blockIDs[n], - Timestamp: cmttime.Now(), } added, err := signAddVote(vals[vi], vote, voteSet) @@ -697,7 +694,7 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { } } - veHeightParam := ABCIParams{VoteExtensionsEnableHeight: 0} + veHeightParam := FeatureParams{VoteExtensionsEnableHeight: 0} if tc.valid { extCommit := voteSet.MakeExtendedCommit(veHeightParam) // panics without > 2/3 valid votes assert.NotNil(t, extCommit) @@ -789,7 +786,7 @@ func TestBlockProtoBuf(t *testing.T) { require.EqualValues(t, tc.b1.Header, block.Header, tc.msg) require.EqualValues(t, tc.b1.Data, block.Data, tc.msg) require.EqualValues(t, tc.b1.Evidence.Evidence, block.Evidence.Evidence, tc.msg) - require.EqualValues(t, *tc.b1.LastCommit, *block.LastCommit, tc.msg) + require.EqualValues(t, tc.b1.LastCommit, block.LastCommit, tc.msg) } else { require.Error(t, err, tc.msg) } diff --git a/types/canonical.go b/types/canonical.go index ee82f660f58..8ff3dedf896 100644 --- a/types/canonical.go +++ b/types/canonical.go @@ -54,14 +54,17 @@ func CanonicalizeProposal(chainID string, proposal *cmtproto.Proposal) cmtproto. // CanonicalizeVote transforms the given Vote to a CanonicalVote, which does // not contain ValidatorIndex and ValidatorAddress fields, or any fields // relating to vote extensions. +// +// The per-vote Timestamp is deliberately excluded from the canonical +// (signed) representation: all validators must sign identical bytes for a +// given block so that their BLS signatures can be aggregated. func CanonicalizeVote(chainID string, vote *cmtproto.Vote) cmtproto.CanonicalVote { return cmtproto.CanonicalVote{ - Type: vote.Type, - Height: vote.Height, // encoded as sfixed64 - Round: int64(vote.Round), // encoded as sfixed64 - BlockID: CanonicalizeBlockID(vote.BlockID), - Timestamp: vote.Timestamp, - ChainID: chainID, + Type: vote.Type, + Height: vote.Height, // encoded as sfixed64 + Round: int64(vote.Round), // encoded as sfixed64 + BlockID: CanonicalizeBlockID(vote.BlockID), + ChainID: chainID, } } diff --git a/types/events.go b/types/events.go index 7b40178aa53..92092446093 100644 --- a/types/events.go +++ b/types/events.go @@ -31,6 +31,7 @@ const ( EventNewRound = "NewRound" EventNewRoundStep = "NewRoundStep" EventPolka = "Polka" + EventProposalBlockPart = "ProposalBlockPart" EventRelock = "Relock" EventTimeoutPropose = "TimeoutPropose" EventTimeoutWait = "TimeoutWait" diff --git a/types/evidence.go b/types/evidence.go index faa6873c7c3..8fb04216c95 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -603,14 +603,14 @@ func NewMockDuplicateVoteEvidenceWithValidator(height int64, time time.Time, return nil, err } val := NewValidator(pubKey, 10) - voteA := makeMockVote(height, 0, 0, pubKey.Address(), randBlockID(), time) + voteA := makeMockVote(height, 0, 0, pubKey.Address(), randBlockID()) vA := voteA.ToProto() err = pv.SignVote(chainID, vA) if err != nil { return nil, err } voteA.Signature = vA.Signature - voteB := makeMockVote(height, 0, 0, pubKey.Address(), randBlockID(), time) + voteB := makeMockVote(height, 0, 0, pubKey.Address(), randBlockID()) vB := voteB.ToProto() err = pv.SignVote(chainID, vB) if err != nil { @@ -621,14 +621,13 @@ func NewMockDuplicateVoteEvidenceWithValidator(height int64, time time.Time, } func makeMockVote(height int64, round, index int32, addr Address, - blockID BlockID, time time.Time, + blockID BlockID, ) *Vote { return &Vote{ Type: cmtproto.SignedMsgType(2), Height: height, Round: round, BlockID: blockID, - Timestamp: time, ValidatorAddress: addr, ValidatorIndex: index, } diff --git a/types/params.go b/types/params.go index 3a98034fb56..5277070c31c 100644 --- a/types/params.go +++ b/types/params.go @@ -3,8 +3,11 @@ package types import ( "errors" "fmt" + "math" "time" + gogo "github.com/cosmos/gogoproto/types" + "github.com/cometbft/cometbft/crypto/bls12381" "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/crypto/secp256k1" @@ -25,6 +28,17 @@ const ( ABCIPubKeyTypeEd25519 = ed25519.KeyType ABCIPubKeyTypeSecp256k1 = secp256k1.KeyType ABCIPubKeyTypeBls12381 = bls12381.KeyType + + // MaxMessageDelay is the maximum allowed value for SynchronyParams.MessageDelay. + // + // It ensures that the SynchronyParams.MessageDelay does not overflow int64. + // The 24hr value was chosen based on common sense. + MaxMessageDelay = 24 * time.Hour + // MaxPrecision is the maximum allowed value for SynchronyParams.Precision. + // + // It ensures that the SynchronyParams.Precision does not overflow int64. The + // 30s value was chosen based on common sense. + MaxPrecision = 30 * time.Second ) var ABCIPubKeyTypesToNames = map[string]string{ @@ -45,7 +59,8 @@ type ConsensusParams struct { Evidence EvidenceParams `json:"evidence"` Validator ValidatorParams `json:"validator"` Version VersionParams `json:"version"` - ABCI ABCIParams `json:"abci"` + Synchrony SynchronyParams `json:"synchrony"` + Feature FeatureParams `json:"feature"` Authority AuthorityParams `json:"authority"` } @@ -73,22 +88,89 @@ type VersionParams struct { App uint64 `json:"app"` } -// ABCIParams configure ABCI functionality specific to the Application Blockchain -// Interface. -type ABCIParams struct { +// FeatureParams configure the height from which features of CometBFT are enabled. +// A value of 0 means the feature is disabled. A value > 0 denotes +// the height at which the feature will be (or has been) enabled. +type FeatureParams struct { VoteExtensionsEnableHeight int64 `json:"vote_extensions_enable_height"` + PbtsEnableHeight int64 `json:"pbts_enable_height"` + SBTEnableHeight int64 `json:"sbt_enable_height"` } // VoteExtensionsEnabled returns true if vote extensions are enabled at height h // and false otherwise. -func (a ABCIParams) VoteExtensionsEnabled(h int64) bool { - if h < 1 { - panic(fmt.Errorf("cannot check if vote extensions enabled for height %d (< 1)", h)) +func (p FeatureParams) VoteExtensionsEnabled(h int64) bool { + enabledHeight := p.VoteExtensionsEnableHeight + + return featureEnabled(enabledHeight, h, "Vote Extensions") +} + +// PbtsEnabled returns true if PBTS is enabled at height h and false otherwise. +func (p FeatureParams) PbtsEnabled(h int64) bool { + enabledHeight := p.PbtsEnableHeight + + return featureEnabled(enabledHeight, h, "PBTS") +} + +// SBTEnabled returns true if SBT is enabled at height h and false otherwise. +func (p FeatureParams) SBTEnabled(h int64) bool { + enabledHeight := p.SBTEnableHeight + + return featureEnabled(enabledHeight, h, "SBT") +} + +// featureEnabled returns true if `enabledHeight` points to a height that is smaller than `currentHeight`. +func featureEnabled(enableHeight int64, currentHeight int64, f string) bool { + if currentHeight < 1 { + panic(fmt.Errorf("cannot check if %s is enabled for height %d (< 1)", f, currentHeight)) } - if a.VoteExtensionsEnableHeight == 0 { + + if enableHeight <= 0 { return false } - return a.VoteExtensionsEnableHeight <= h + + return enableHeight <= currentHeight +} + +// SynchronyParams determine the validity of block timestamps. +// +// These parameters are part of the Proposer-Based Timestamps (PBTS) algorithm. +// For more information on the relationship of the synchrony parameters to +// block timestamps validity, refer to the PBTS specification: +// https://github.com/cometbft/cometbft/tree/main/spec/consensus/proposer-based-timestamp +type SynchronyParams struct { + // Maximum allowed value: MaxPrecision. + Precision time.Duration `json:"precision,string"` + // Maximum allowed value: MaxMessageDelay. + MessageDelay time.Duration `json:"message_delay,string"` +} + +// InRound ensures an exponential back-off of SynchronyParams.MessageDelay for +// block timestamps validation, as the associated proposal rounds increase. +// +// The adaptation is achieve by increasing MessageDelay by a factor of 10% each +// subsequent round a proposal's timeliness is calculated, namely: +// +// MessageDelay(round) == MessageDelay * (1.1)^round +// +// The goal is facilitate the progression of consensus when improper synchrony +// parameters are set or become insufficient to preserve liveness. Refer to +// https://github.com/cometbft/cometbft/issues/2184 for more details. +// +// There's a cap (MaxMessageDelay) on the MessageDelay to prevent overflow. +func (sp SynchronyParams) InRound(round int32) SynchronyParams { + if round <= 0 { + return sp + } + + d := time.Duration(math.Min( + float64(MaxMessageDelay), + math.Pow(1.1, float64(round))*float64(sp.MessageDelay), + )) + return SynchronyParams{ + Precision: sp.Precision, + MessageDelay: d, + } } // AuthorityParams holds an opaque authority string to be configured and @@ -105,7 +187,8 @@ func DefaultConsensusParams() *ConsensusParams { Evidence: DefaultEvidenceParams(), Validator: DefaultValidatorParams(), Version: DefaultVersionParams(), - ABCI: DefaultABCIParams(), + Feature: DefaultFeatureParams(), + Synchrony: DefaultSynchronyParams(), Authority: DefaultAuthorityParams(), } } @@ -141,10 +224,24 @@ func DefaultVersionParams() VersionParams { } } -func DefaultABCIParams() ABCIParams { - return ABCIParams{ - // When set to 0, vote extensions are not required. +// DefaultFeatureParams returns the default FeatureParams. PBTS is enabled +// from the first height; with BLS signature aggregation the per-vote +// timestamps that BFT Time medians over are not signed, so PBTS is the only +// viable block-time source on this fork. +func DefaultFeatureParams() FeatureParams { + return FeatureParams{ VoteExtensionsEnableHeight: 0, + PbtsEnableHeight: 1, // PBTS from start + SBTEnableHeight: 0, // SBT disabled by default + } +} + +func DefaultSynchronyParams() SynchronyParams { + // Default values determined based on experimental results and on + // https://github.com/cometbft/cometbft/issues/4246 + return SynchronyParams{ + Precision: 505 * time.Millisecond, + MessageDelay: 15 * time.Second, } } @@ -211,8 +308,34 @@ func (params ConsensusParams) ValidateBasic() error { params.Evidence.MaxBytes) } - if params.ABCI.VoteExtensionsEnableHeight < 0 { - return fmt.Errorf("ABCI.VoteExtensionsEnableHeight cannot be negative. Got: %d", params.ABCI.VoteExtensionsEnableHeight) + if params.Feature.VoteExtensionsEnableHeight < 0 { + return fmt.Errorf("Feature.VoteExtensionsEnableHeight cannot be negative. Got: %d", params.Feature.VoteExtensionsEnableHeight) + } + + if params.Feature.SBTEnableHeight < 0 { + return fmt.Errorf("Feature.SBTEnableHeight cannot be negative. Got: %d", params.Feature.SBTEnableHeight) + } + + if params.Feature.PbtsEnableHeight < 0 { + return fmt.Errorf("Feature.PbtsEnableHeight cannot be negative. Got: %d", params.Feature.PbtsEnableHeight) + } + + // Synchrony params are only relevant when PBTS is enabled + if params.Feature.PbtsEnableHeight > 0 { + if params.Synchrony.MessageDelay <= 0 { + return fmt.Errorf("synchrony.MessageDelay must be greater than 0. Got: %d", + params.Synchrony.MessageDelay) + } + if params.Synchrony.Precision <= 0 { + return fmt.Errorf("synchrony.Precision must be greater than 0. Got: %d", + params.Synchrony.Precision) + } + if params.Synchrony.MessageDelay > MaxMessageDelay { + return fmt.Errorf("synchrony.MessageDelay is too big, must be less than or equal to %v", MaxMessageDelay) + } + if params.Synchrony.Precision > MaxPrecision { + return fmt.Errorf("synchrony.Precision is too big, must be less than or equal to %v", MaxPrecision) + } } if len(params.Validator.PubKeyTypes) == 0 { @@ -238,63 +361,98 @@ func (params ConsensusParams) ValidateBasic() error { return nil } -// ValidateUpdate validates the updated VoteExtensionsEnableHeight. +// ValidateUpdate validates the updated Consensus Params +// if updated == nil, then pass. +func (params ConsensusParams) ValidateUpdate(updated *cmtproto.ConsensusParams, h int64) error { + if updated == nil { + return nil + } + + var err error + // Validate feature update parameters. + if updated.Feature != nil { + err = validateUpdateFeatures(params.Feature, *updated.Feature, h) + } + return err +} + +// validateUpdateFeatures validates the updated PBTSEnableHeight. // | r | params...EnableHeight | updated...EnableHeight | result (nil == pass) -// | 1 | * | (nil) | nil -// | 2 | * | < 0 | VoteExtensionsEnableHeight must be positive +// | 2 | * | < 0 | EnableHeight must be positive // | 3 | <=0 | 0 | nil // | 4 | X | X (>=0) | nil -// | 5 | > 0; <=height | 0 | vote extensions cannot be disabled once enabled +// | 5 | > 0; <=height | 0 | Feature cannot be disabled once enabled // | 6 | > 0; > height | 0 | nil (disable a previous proposal) -// | 7 | * | <=height | vote extensions cannot be updated to a past height +// | 7 | * | <=height | Feature cannot be updated to a past height // | 8 | <=0 | > height (*) | nil -// | 9 | (> 0) <=height | > height (*) | vote extensions cannot be modified once enabled +// | 9 | (> 0) <=height | > height (*) | Feature cannot be modified once enabled // | 10 | (> 0) > height | > height (*) | nil -func (params ConsensusParams) ValidateUpdate(updated *cmtproto.ConsensusParams, h int64) error { - // 1 - if updated == nil || updated.Abci == nil { - return nil +// The table above reflects all cases covered. +func validateUpdateFeatures(params FeatureParams, updated cmtproto.FeatureParams, h int64) error { + if updated.VoteExtensionsEnableHeight != nil { + err := validateUpdateFeatureEnableHeight(params.VoteExtensionsEnableHeight, updated.VoteExtensionsEnableHeight.Value, h, "Vote Extensions") + if err != nil { + return err + } } + + if updated.PbtsEnableHeight != nil { + err := validateUpdateFeatureEnableHeight(params.PbtsEnableHeight, updated.PbtsEnableHeight.Value, h, "PBTS") + if err != nil { + return err + } + } + + if updated.SbtEnableHeight != nil { + err := validateUpdateFeatureEnableHeight(params.SBTEnableHeight, updated.SbtEnableHeight.Value, h, "SBT") + if err != nil { + return err + } + } + return nil +} + +func validateUpdateFeatureEnableHeight(param int64, updated int64, h int64, featureName string) error { // 2 - if updated.Abci.VoteExtensionsEnableHeight < 0 { - return errors.New("VoteExtensionsEnableHeight must be positive") + if updated < 0 { + return fmt.Errorf("%s EnableHeight must be positive", featureName) } // 3 - if params.ABCI.VoteExtensionsEnableHeight <= 0 && updated.Abci.VoteExtensionsEnableHeight == 0 { + if param <= 0 && updated == 0 { return nil } - // 4 (implicit: updated.Abci.VoteExtensionsEnableHeight >= 0) - if params.ABCI.VoteExtensionsEnableHeight == updated.Abci.VoteExtensionsEnableHeight { + // 4 (implicit: updated >= 0) + if param == updated { return nil } // 5 & 6 - if params.ABCI.VoteExtensionsEnableHeight > 0 && updated.Abci.VoteExtensionsEnableHeight == 0 { + if param > 0 && updated == 0 { // 5 - if params.ABCI.VoteExtensionsEnableHeight <= h { - return fmt.Errorf("vote extensions cannot be disabled once enabled"+ - "old enable height: %d, current height %d", - params.ABCI.VoteExtensionsEnableHeight, h) + if param <= h { + return fmt.Errorf("%s cannot be disabled once enabled"+ + "enabled height: %d, current height: %d", + featureName, param, h) } // 6 return nil } - // 7 (implicit: updated.Abci.VoteExtensionsEnableHeight > 0) - if updated.Abci.VoteExtensionsEnableHeight <= h { - return fmt.Errorf("vote extensions cannot be updated to a past or current height, "+ - "enable height: %d, current height %d", - updated.Abci.VoteExtensionsEnableHeight, h) + // 7 (implicit: updated > 0) + if updated <= h { + return fmt.Errorf("%s cannot be updated to a past or current height, "+ + "enabled height: %d, enable height: %d, current height %d", + featureName, param, updated, h) } - // 8 (implicit: updated.Abci.VoteExtensionsEnableHeight > h) - if params.ABCI.VoteExtensionsEnableHeight <= 0 { + // 8 (implicit: updated > h) + if param <= 0 { return nil } - // 9 (implicit: params.ABCI.VoteExtensionsEnableHeight > 0 && updated.Abci.VoteExtensionsEnableHeight > h) - if params.ABCI.VoteExtensionsEnableHeight <= h { - return fmt.Errorf("vote extensions cannot be modified once enabled"+ - "enable height: %d, current height %d", - params.ABCI.VoteExtensionsEnableHeight, h) + // 9 (implicit: param > 0 && updated > h) + if param <= h { + return fmt.Errorf("%s cannot be modified once enabled"+ + "enabled height: %d, current height: %d", + featureName, param, h) } - // 10 (implicit: params.ABCI.VoteExtensionsEnableHeight > h && updated.Abci.VoteExtensionsEnableHeight > h) + // 10 (implicit: param > h && updated > h) return nil } @@ -349,8 +507,29 @@ func (params ConsensusParams) Update(params2 *cmtproto.ConsensusParams) Consensu if params2.Version != nil { res.Version.App = params2.Version.App } - if params2.Abci != nil { - res.ABCI.VoteExtensionsEnableHeight = params2.Abci.GetVoteExtensionsEnableHeight() + if params2.Feature != nil { + if params2.Feature.VoteExtensionsEnableHeight != nil { + res.Feature.VoteExtensionsEnableHeight = 0 // disable vote extensions in Berachain + } + + if params2.Feature.PbtsEnableHeight != nil { + // Disabled because it should be enabled from the beginning + if res.Feature.PbtsEnableHeight != 1 { + panic("PBTS has to be enabled") + } + } + + if params2.Feature.SbtEnableHeight != nil { + res.Feature.SBTEnableHeight = params2.Feature.GetSbtEnableHeight().GetValue() + } + } + if params2.Synchrony != nil { + if params2.Synchrony.MessageDelay != nil { + res.Synchrony.MessageDelay = *params2.Synchrony.GetMessageDelay() + } + if params2.Synchrony.Precision != nil { + res.Synchrony.Precision = *params2.Synchrony.GetPrecision() + } } if params2.Authority != nil { res.Authority.Authority = params2.Authority.Authority @@ -359,7 +538,7 @@ func (params ConsensusParams) Update(params2 *cmtproto.ConsensusParams) Consensu } func (params *ConsensusParams) ToProto() cmtproto.ConsensusParams { - return cmtproto.ConsensusParams{ + pb := cmtproto.ConsensusParams{ Block: &cmtproto.BlockParams{ MaxBytes: params.Block.MaxBytes, MaxGas: params.Block.MaxGas, @@ -375,13 +554,24 @@ func (params *ConsensusParams) ToProto() cmtproto.ConsensusParams { Version: &cmtproto.VersionParams{ App: params.Version.App, }, - Abci: &cmtproto.ABCIParams{ - VoteExtensionsEnableHeight: params.ABCI.VoteExtensionsEnableHeight, + Feature: &cmtproto.FeatureParams{ + PbtsEnableHeight: &gogo.Int64Value{Value: params.Feature.PbtsEnableHeight}, + VoteExtensionsEnableHeight: &gogo.Int64Value{Value: params.Feature.VoteExtensionsEnableHeight}, + SbtEnableHeight: &gogo.Int64Value{Value: params.Feature.SBTEnableHeight}, }, - Authority: &cmtproto.AuthorityParams{ - Authority: params.Authority.Authority, + Synchrony: &cmtproto.SynchronyParams{ + MessageDelay: ¶ms.Synchrony.MessageDelay, + Precision: ¶ms.Synchrony.Precision, }, } + // Emitted only when set, so that the serialized form of params written by + // this fork matches what bera-v1.x wrote (it has no authority field). + if params.Authority.Authority != "" { + pb.Authority = &cmtproto.AuthorityParams{ + Authority: params.Authority.Authority, + } + } + return pb } func ConsensusParamsFromProto(pbParams cmtproto.ConsensusParams) ConsensusParams { @@ -401,9 +591,25 @@ func ConsensusParamsFromProto(pbParams cmtproto.ConsensusParams) ConsensusParams Version: VersionParams{ App: pbParams.Version.App, }, + Feature: FeatureParams{ + VoteExtensionsEnableHeight: pbParams.GetFeature().GetVoteExtensionsEnableHeight().GetValue(), + PbtsEnableHeight: pbParams.GetFeature().GetPbtsEnableHeight().GetValue(), + SBTEnableHeight: pbParams.GetFeature().GetSbtEnableHeight().GetValue(), + }, + } + if pbParams.GetSynchrony().GetMessageDelay() != nil { + c.Synchrony.MessageDelay = *pbParams.GetSynchrony().GetMessageDelay() } - if pbParams.Abci != nil { - c.ABCI.VoteExtensionsEnableHeight = pbParams.Abci.GetVoteExtensionsEnableHeight() + if pbParams.GetSynchrony().GetPrecision() != nil { + c.Synchrony.Precision = *pbParams.GetSynchrony().GetPrecision() + } + if pbParams.GetAbci().GetVoteExtensionsEnableHeight() > 0 { + // Value set before the upgrade to V1. We can safely overwrite here because + // ABCIParams and FeatureParams being set is mutually exclusive (=V1). + if pbParams.GetFeature().GetVoteExtensionsEnableHeight().GetValue() > 0 { + panic("vote_extension_enable_height is set in two different places") + } + c.Feature.VoteExtensionsEnableHeight = pbParams.Abci.VoteExtensionsEnableHeight } if pbParams.Authority != nil { c.Authority.Authority = pbParams.Authority.Authority diff --git a/types/params_test.go b/types/params_test.go index 4c0fbe5672f..18cfeacfc36 100644 --- a/types/params_test.go +++ b/types/params_test.go @@ -6,6 +6,8 @@ import ( "testing" "time" + gogo "github.com/cosmos/gogoproto/types" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -78,9 +80,11 @@ func makeParams( Validator: ValidatorParams{ PubKeyTypes: pubkeyTypes, }, - ABCI: ABCIParams{ + Feature: FeatureParams{ VoteExtensionsEnableHeight: abciExtensionHeight, + PbtsEnableHeight: 1, }, + Synchrony: DefaultSynchronyParams(), Authority: auth, } } @@ -230,12 +234,12 @@ func TestConsensusParamsUpdate_VoteExtensionsEnableHeight(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(*testing.T) { initialParams := makeParams(1, 0, 2, 0, valEd25519, tc.from, "") - update := &cmtproto.ConsensusParams{} + update := &cmtproto.ConsensusParams{Feature: &cmtproto.FeatureParams{}} if tc.to == nilTest { - update.Abci = nil + update.Feature.VoteExtensionsEnableHeight = nil } else { - update.Abci = &cmtproto.ABCIParams{ - VoteExtensionsEnableHeight: tc.to, + update.Feature = &cmtproto.FeatureParams{ + VoteExtensionsEnableHeight: &gogo.Int64Value{Value: tc.to}, } } if tc.expectedErr { diff --git a/types/priv_validator.go b/types/priv_validator.go index 1b33f46f00a..00cfa66f8a4 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -6,7 +6,9 @@ import ( "fmt" "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/bls12381" "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/secp256k1" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" ) @@ -17,6 +19,9 @@ type PrivValidator interface { SignVote(chainID string, vote *cmtproto.Vote) error SignProposal(chainID string, proposal *cmtproto.Proposal) error + + // SignBytes signs an arbitrary array of bytes. + SignBytes(bytes []byte) ([]byte, error) } type PrivValidatorsByAddress []PrivValidator @@ -57,6 +62,25 @@ func NewMockPV() MockPV { return MockPV{ed25519.GenPrivKey(), false, false} } +func NewMockPVWithKeyType(keyType string) MockPV { + var pk crypto.PrivKey + switch keyType { + case "", ed25519.KeyType: + pk = ed25519.GenPrivKey() + case secp256k1.KeyType: + pk = secp256k1.GenPrivKey() + case bls12381.KeyType: + blsPk, err := bls12381.GenPrivKey() + if err != nil { + panic(err) + } + pk = blsPk + default: + panic("unexpected key type: " + keyType) + } + return MockPV{pk, false, false} +} + // NewMockPVWithParams allows one to create a MockPV instance, but with finer // grained control over the operation of the mock validator. This is useful for // mocking test failures. @@ -114,6 +138,11 @@ func (pv MockPV) SignProposal(chainID string, proposal *cmtproto.Proposal) error return nil } +// SignBytes implements PrivValidator. +func (pv MockPV) SignBytes(bytes []byte) ([]byte, error) { + return pv.PrivKey.Sign(bytes) +} + func (pv MockPV) ExtractIntoValidator(votingPower int64) *Validator { pubKey, _ := pv.GetPubKey() return &Validator{ diff --git a/types/proposal.go b/types/proposal.go index b66a0d6dfa9..5ead8e9114f 100644 --- a/types/proposal.go +++ b/types/proposal.go @@ -34,14 +34,16 @@ type Proposal struct { // NewProposal returns a new Proposal. // If there is no POLRound, polRound should be -1. -func NewProposal(height int64, round int32, polRound int32, blockID BlockID) *Proposal { +// Under PBTS the proposal timestamp must equal the proposed block's time, so +// the caller provides it (it is canonicalized here). +func NewProposal(height int64, round int32, polRound int32, blockID BlockID, ts time.Time) *Proposal { return &Proposal{ Type: cmtproto.ProposalType, Height: height, Round: round, BlockID: blockID, POLRound: polRound, - Timestamp: cmttime.Now(), + Timestamp: cmttime.Canonical(ts), } } @@ -50,8 +52,8 @@ func (p *Proposal) ValidateBasic() error { if p.Type != cmtproto.ProposalType { return errors.New("invalid Type") } - if p.Height < 0 { - return errors.New("negative Height") + if p.Height <= 0 { + return errors.New("non positive Height") } if p.Round < 0 { return errors.New("negative Round") @@ -59,6 +61,9 @@ func (p *Proposal) ValidateBasic() error { if p.POLRound < -1 { return errors.New("negative POLRound (exception: -1)") } + if p.POLRound >= p.Round { + return errors.New("POLRound >= Round") + } if err := p.BlockID.ValidateBasic(); err != nil { return fmt.Errorf("wrong BlockID: %v", err) } @@ -67,7 +72,10 @@ func (p *Proposal) ValidateBasic() error { return fmt.Errorf("expected a complete, non-empty BlockID, got: %v", p.BlockID) } - // NOTE: Timestamp validation is subtle and handled elsewhere. + // Times must be canonical + if cmttime.Canonical(p.Timestamp) != p.Timestamp { + return fmt.Errorf("expected a canonical timestamp, got: %v", p.Timestamp) + } if len(p.Signature) == 0 { return errors.New("signature is missing") @@ -79,6 +87,30 @@ func (p *Proposal) ValidateBasic() error { return nil } +// IsTimely validates that the proposal timestamp is 'timely' according to the +// proposer-based timestamp algorithm. To evaluate if a proposal is timely, its +// timestamp is compared to the local time of the validator when it receives +// the proposal along with the configured Precision and MessageDelay +// parameters. Specifically, a proposed proposal timestamp is considered timely +// if it is satisfies the following inequalities: +// +// proposalReceiveTime >= proposalTimestamp - Precision +// proposalReceiveTime <= proposalTimestamp + MessageDelay + Precision +// +// For more information on the meaning of 'timely', refer to the specification: +// https://github.com/cometbft/cometbft/tree/main/spec/consensus/proposer-based-timestamp +func (p *Proposal) IsTimely(recvTime time.Time, sp SynchronyParams) bool { + // lhs is `proposalTimestamp - Precision` in the first inequality + lhs := p.Timestamp.Add(-sp.Precision) + // rhs is `proposalTimestamp + MessageDelay + Precision` in the second inequality + rhs := p.Timestamp.Add(sp.MessageDelay).Add(sp.Precision) + + if recvTime.Before(lhs) || recvTime.After(rhs) { + return false + } + return true +} + // ValidateBlockSize block size ensures that a proposal block is not larger // than a maximum number of bytes, based on the total amount of parts reported // in the PartSetHeader. If -1 is passed as the maxBlockSizeBytes, diff --git a/types/proposal_test.go b/types/proposal_test.go index c2c50d82881..8782e744c01 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -13,6 +13,7 @@ import ( "github.com/cometbft/cometbft/libs/protoio" cmtrand "github.com/cometbft/cometbft/libs/rand" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttime "github.com/cometbft/cometbft/types/time" ) var ( @@ -62,8 +63,8 @@ func TestProposalVerifySignature(t *testing.T) { require.NoError(t, err) prop := NewProposal( - 4, 2, 2, - BlockID{cmtrand.Bytes(tmhash.Size), PartSetHeader{777, cmtrand.Bytes(tmhash.Size)}}) + 4, 2, 1, + BlockID{cmtrand.Bytes(tmhash.Size), PartSetHeader{777, cmtrand.Bytes(tmhash.Size)}}, cmttime.Now()) p := prop.ToProto() signBytes := ProposalSignBytes("test_chain_id", p) @@ -151,8 +152,8 @@ func TestProposalValidateBasic(t *testing.T) { for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { prop := NewProposal( - 4, 2, 2, - blockID) + 4, 2, 1, + blockID, cmttime.Now()) p := prop.ToProto() err := privVal.SignProposal("test_chain_id", p) prop.Signature = p.Signature @@ -164,9 +165,9 @@ func TestProposalValidateBasic(t *testing.T) { } func TestProposalProtoBuf(t *testing.T) { - proposal := NewProposal(1, 2, 3, makeBlockID([]byte("hash"), 2, []byte("part_set_hash"))) + proposal := NewProposal(1, 2, 1, makeBlockID([]byte("hash"), 2, []byte("part_set_hash")), cmttime.Now()) proposal.Signature = []byte("sig") - proposal2 := NewProposal(1, 2, 3, BlockID{}) + proposal2 := NewProposal(1, 2, 3, BlockID{}, cmttime.Now()) testCases := []struct { msg string @@ -198,12 +199,12 @@ func TestProposalValidateBlockSize(t *testing.T) { proposal *Proposal expectPass bool }{ - {"10 chunk max, 5 chunk proposal, success", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 5}}), true}, - {"10 chunk max, 20 chunk proposal, fail", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 20}}), false}, - {"10 chunk max, max uint32 chunk proposal, fail", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}), false}, - {"-1 chunk max, max uint32 chunk proposal, fail", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}), false}, - {"0 chunk max, max uint32 chunk proposal, fail", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}), false}, - {"total parts equals chunk max, success", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 1600}}), true}, + {"10 chunk max, 5 chunk proposal, success", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 5}}, cmttime.Now()), true}, + {"10 chunk max, 20 chunk proposal, fail", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 20}}, cmttime.Now()), false}, + {"10 chunk max, max uint32 chunk proposal, fail", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}, cmttime.Now()), false}, + {"-1 chunk max, max uint32 chunk proposal, fail", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}, cmttime.Now()), false}, + {"0 chunk max, max uint32 chunk proposal, fail", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}, cmttime.Now()), false}, + {"total parts equals chunk max, success", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 1600}}, cmttime.Now()), true}, } for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { diff --git a/types/test_util.go b/types/test_util.go index 2e5ed414a82..ef073f833ca 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -12,7 +12,7 @@ import ( ) func MakeExtCommit(blockID BlockID, height int64, round int32, - voteSet *VoteSet, validators []PrivValidator, now time.Time, extEnabled bool, + voteSet *VoteSet, validators []PrivValidator, _ time.Time, extEnabled bool, ) (*ExtendedCommit, error) { // all sign for i := 0; i < len(validators); i++ { @@ -27,7 +27,6 @@ func MakeExtCommit(blockID BlockID, height int64, round int32, Round: round, Type: cmtproto.PrecommitType, BlockID: blockID, - Timestamp: now, } _, err = signAddVote(validators[i], vote, voteSet) @@ -41,7 +40,7 @@ func MakeExtCommit(blockID BlockID, height int64, round int32, enableHeight = height } - return voteSet.MakeExtendedCommit(ABCIParams{VoteExtensionsEnableHeight: enableHeight}), nil + return voteSet.MakeExtendedCommit(FeatureParams{VoteExtensionsEnableHeight: enableHeight}), nil } func signAddVote(privVal PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) { @@ -62,7 +61,7 @@ func MakeVote( round int32, step cmtproto.SignedMsgType, blockID BlockID, - time time.Time, + _ time.Time, ) (*Vote, error) { pubKey, err := val.GetPubKey() if err != nil { @@ -76,7 +75,6 @@ func MakeVote( Round: round, Type: step, BlockID: blockID, - Timestamp: time, } extensionsEnabled := step == cmtproto.PrecommitType @@ -96,9 +94,9 @@ func MakeVoteNoError( round int32, step cmtproto.SignedMsgType, blockID BlockID, - time time.Time, + ts time.Time, ) *Vote { - vote, err := MakeVote(val, chainID, valIndex, height, round, step, blockID, time) + vote, err := MakeVote(val, chainID, valIndex, height, round, step, blockID, ts) require.NoError(t, err) return vote } diff --git a/types/time/mocks/source.go b/types/time/mocks/source.go new file mode 100644 index 00000000000..3f5d153ba75 --- /dev/null +++ b/types/time/mocks/source.go @@ -0,0 +1,46 @@ +// Code generated by mockery v2.53.6. DO NOT EDIT. + +package mocks + +import ( + time "time" + + mock "github.com/stretchr/testify/mock" +) + +// Source is an autogenerated mock type for the Source type +type Source struct { + mock.Mock +} + +// Now provides a mock function with no fields +func (_m *Source) Now() time.Time { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Now") + } + + var r0 time.Time + if rf, ok := ret.Get(0).(func() time.Time); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Time) + } + + return r0 +} + +// NewSource creates a new instance of Source. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSource(t interface { + mock.TestingT + Cleanup(func()) +}) *Source { + mock := &Source{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/types/time/time.go b/types/time/time.go index 6f7d3dfa634..9d9c296e972 100644 --- a/types/time/time.go +++ b/types/time/time.go @@ -17,6 +17,20 @@ func Canonical(t time.Time) time.Time { return t.Round(0).UTC() } +//go:generate ../../scripts/mockery_generate.sh Source + +// Source is an interface that defines a way to fetch the current time. +type Source interface { + Now() time.Time +} + +// DefaultSource implements the Source interface using the system clock provided by the standard library. +type DefaultSource struct{} + +func (DefaultSource) Now() time.Time { + return Now() +} + // WeightedTime for computing a median. type WeightedTime struct { Time time.Time diff --git a/types/validation.go b/types/validation.go index 4211903cf2a..9686c74b40b 100644 --- a/types/validation.go +++ b/types/validation.go @@ -7,11 +7,16 @@ import ( "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/batch" + "github.com/cometbft/cometbft/crypto/bls12381" "github.com/cometbft/cometbft/crypto/tmhash" cmtmath "github.com/cometbft/cometbft/libs/math" cmterrors "github.com/cometbft/cometbft/types/errors" ) +// ErrInvalidAggCommit is returned when a commit carries aggregated signature +// flags but the validator set is not made of BLS12-381 keys. +var ErrInvalidAggCommit = errors.New("commit has aggregated signature flags, but was not created by vals with BLS12-381 keys") + const batchVerifyThreshold = 2 func shouldBatchVerify(vals *ValidatorSet, commit *Commit) bool { @@ -20,6 +25,13 @@ func shouldBatchVerify(vals *ValidatorSet, commit *Commit) bool { vals.AllKeysHaveSameType() } +// isAggregatedCommit returns true if the commit is an aggregated one. +func isAggregatedCommit(vals *ValidatorSet, commit *Commit) bool { + _, ok := vals.GetProposer().PubKey.(*bls12381.PubKey) + _, ok2 := vals.GetProposer().PubKey.(bls12381.PubKey) + return (ok || ok2) && vals.AllKeysHaveSameType() && commit.HasAggregatedSignature() +} + // VerifyCommit verifies +2/3 of the set had signed the given commit. // // It checks all the signatures! While it's safe to exit as soon as we have @@ -42,6 +54,22 @@ func VerifyCommit(chainID string, vals *ValidatorSet, blockID BlockID, // ignore all absent signatures ignore := func(c CommitSig) bool { return c.BlockIDFlag == BlockIDFlagAbsent } + // attempt to verify aggregated commit + if isAggregatedCommit(vals, commit) { + // only count the signatures that are for the block + count := func(c CommitSig) bool { + return c.BlockIDFlag == BlockIDFlagAggCommit || c.BlockIDFlag == BlockIDFlagAggCommitAbsent + } + + return verifyAggregatedCommit(chainID, vals, commit, + votingPowerNeeded, ignore, count, true) + } + + // Guard against invalid aggregated commits. + if commit.HasAggregatedSignature() { + return ErrInvalidAggCommit + } + // only count the signatures that are for the block count := func(c CommitSig) bool { return c.BlockIDFlag == BlockIDFlagCommit } @@ -120,12 +148,28 @@ func verifyCommitLightInternal( // calculate voting power needed votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 + // count all the remaining signatures + count := func(_ CommitSig) bool { return true } + + // attempt to verify aggregated commit + if isAggregatedCommit(vals, commit) { + // ignore all commit signatures that are not for the block + ignore := func(c CommitSig) bool { + return c.BlockIDFlag != BlockIDFlagAggCommit && c.BlockIDFlag != BlockIDFlagAggCommitAbsent + } + + return verifyAggregatedCommit(chainID, vals, commit, + votingPowerNeeded, ignore, count, true) + } + + // Guard against invalid aggregated commits. + if commit.HasAggregatedSignature() { + return ErrInvalidAggCommit + } + // ignore all commit signatures that are not for the block ignore := func(c CommitSig) bool { return c.BlockIDFlag != BlockIDFlagCommit } - // count all the remaining signatures - count := func(c CommitSig) bool { return true } - // attempt to batch verify if shouldBatchVerify(vals, commit) { return verifyCommitBatch(chainID, vals, commit, @@ -219,12 +263,28 @@ func verifyCommitLightTrustingInternal( } votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator) + // count all the remaining signatures + count := func(_ CommitSig) bool { return true } + + // attempt to verify aggregated commit + if isAggregatedCommit(vals, commit) { + // ignore all commit signatures that are not for the block + ignore := func(c CommitSig) bool { + return c.BlockIDFlag != BlockIDFlagAggCommit && c.BlockIDFlag != BlockIDFlagAggCommitAbsent + } + + return verifyAggregatedCommit(chainID, vals, commit, + votingPowerNeeded, ignore, count, false) + } + + // Guard against invalid aggregated commits. + if commit.HasAggregatedSignature() { + return ErrInvalidAggCommit + } + // ignore all commit signatures that are not for the block ignore := func(c CommitSig) bool { return c.BlockIDFlag != BlockIDFlagCommit } - // count all the remaining signatures - count := func(c CommitSig) bool { return true } - // attempt to batch verify commit. As the validator set doesn't necessarily // correspond with the validator set that signed the block we need to look // up by address rather than index. @@ -531,3 +591,168 @@ func verifyBasicValsAndCommit(vals *ValidatorSet, commit *Commit, height int64, return nil } + +// verifyAggregatedCommit verifies an aggregated commit, in which the +// individual BLS signatures have been collapsed into (at most) one aggregated +// signature for the block and one for nil. +// +// The aggregated signature is carried by the first CommitSig with +// BlockIDFlagAggCommit (respectively BlockIDFlagAggNil); the remaining +// signers for the same message carry BlockIDFlagAggCommitAbsent +// (BlockIDFlagAggNilAbsent) with an empty signature. +// +// If lookUpByIndex is true the commit signatures must have a 1-to-1 +// correspondence with the validator set (and the validator address is checked +// against the commit entry); otherwise validators are looked up by address. +func verifyAggregatedCommit( + chainID string, + vals *ValidatorSet, + commit *Commit, + votingPowerNeeded int64, + ignoreSig func(CommitSig) bool, + countSig func(CommitSig) bool, + lookUpByIndex bool, +) error { + var ( + val *Validator + valIdx int32 + seenVals = make(map[int32]int, len(commit.Signatures)) + talliedVotingPower int64 + aggSig1, aggSig2 []byte + msg1, msg2 []byte + + aggSig1Found, aggSig2Found bool + ) + + pubkeys1 := make([]*bls12381.PubKey, 0, len(commit.Signatures)) + pubkeys2 := make([]*bls12381.PubKey, 0, len(commit.Signatures)) + + for idx, commitSig := range commit.Signatures { + // skip over signatures that should be ignored + if ignoreSig(commitSig) { + continue + } + + // If the vals and commit have a 1-to-1 correspondence we can retrieve + // them by index else we need to retrieve them by address + if lookUpByIndex { + val = vals.Validators[idx] + if !bytes.Equal(val.Address, commitSig.ValidatorAddress) { + return fmt.Errorf("validator address mismatch at index %d: expected %X, got %X", + idx, val.Address, commitSig.ValidatorAddress) + } + } else { + valIdx, val = vals.GetByAddressMut(commitSig.ValidatorAddress) + + // if the signature doesn't belong to anyone in the validator set + // then we just skip over it + if val == nil { + continue + } + + // because we are getting validators by address we need to make sure + // that the same validator doesn't commit twice + if firstIndex, ok := seenVals[valIdx]; ok { + secondIndex := idx + return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex) + } + seenVals[valIdx] = idx + } + + if val.PubKey == nil { + return fmt.Errorf("validator %v has a nil PubKey at index %d", val, idx) + } + + switch commitSig.BlockIDFlag { + case BlockIDFlagAggCommit: + if aggSig1Found { + return errors.New("multiple aggregated signatures for block") + } + + aggSig1 = commitSig.Signature + msg1 = commit.VoteSignBytes(chainID, int32(idx)) + aggSig1Found = true + + pk, ok := val.PubKey.(*bls12381.PubKey) + if !ok { + pk2 := val.PubKey.(bls12381.PubKey) + pk = &pk2 + } + pubkeys1 = append(pubkeys1, pk) + case BlockIDFlagAggCommitAbsent: + if !aggSig1Found { + return fmt.Errorf("aggregated signature for block absent %d but no previously defined aggregated signature", idx) + } + + pk, ok := val.PubKey.(*bls12381.PubKey) + if !ok { + pk2 := val.PubKey.(bls12381.PubKey) + pk = &pk2 + } + pubkeys1 = append(pubkeys1, pk) + case BlockIDFlagAggNil: + if aggSig2Found { + return errors.New("multiple aggregated signatures for nil") + } + + aggSig2 = commitSig.Signature + msg2 = commit.VoteSignBytes(chainID, int32(idx)) + aggSig2Found = true + + pk, ok := val.PubKey.(*bls12381.PubKey) + if !ok { + pk2 := val.PubKey.(bls12381.PubKey) + pk = &pk2 + } + pubkeys2 = append(pubkeys2, pk) + case BlockIDFlagAggNilAbsent: + if !aggSig2Found { + return fmt.Errorf("aggregated signature for nil absent %d but no previously defined aggregated signature", idx) + } + pk, ok := val.PubKey.(*bls12381.PubKey) + if !ok { + pk2 := val.PubKey.(bls12381.PubKey) + pk = &pk2 + } + pubkeys2 = append(pubkeys2, pk) + default: + return fmt.Errorf("wrong block ID flag %d for sig %d (expected aggregated signature flags)", commitSig.BlockIDFlag, idx) + } + + if countSig(commitSig) { + talliedVotingPower += val.VotingPower + } + } + + // ensure that we have batched together enough signatures to exceed the + // voting power needed else there is no need to even verify + if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { + return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} + } + + // Additional guard. + if !aggSig1Found { + return errors.New("missing aggregated signature for block") + } + + // Since we are above the voting power threshold needed, we know `aggSig1`, + // `pubkeys1`, and `msg1` are not `nil`. + ok := bls12381.VerifyAggregateSignature(aggSig1, pubkeys1, msg1) + if !ok { + return fmt.Errorf("wrong aggregated signature for block: %X (pubkeys: %v)", aggSig1, pubkeys1) + } + + // If we have to verify the aggregated signature for nil and there are + // signatures for nil, then verify the aggregated signature for nil. + if len(pubkeys2) > 0 { + if aggSig2 == nil { + return errors.New("missing aggregated signature for nil") + } + ok = bls12381.VerifyAggregateSignature(aggSig2, pubkeys2, msg2) + if !ok { + return fmt.Errorf("wrong aggregated signature for nil: %X (pubkeys: %v)", aggSig2, pubkeys2) + } + } + + return nil +} diff --git a/types/validator.go b/types/validator.go index 77d62724788..4e5673742a4 100644 --- a/types/validator.go +++ b/types/validator.go @@ -138,20 +138,22 @@ func (v *Validator) Bytes() []byte { return bz } -// ToProto converts Validator to protobuf +// ToProto converts Validator to protobuf. +// The key is serialized via pub_key_bytes/pub_key_type (the bera-v1.x +// layout); the deprecated pub_key field is left unset. func (v *Validator) ToProto() (*cmtproto.Validator, error) { if v == nil { return nil, errors.New("nil validator") } - pk, err := ce.PubKeyToProto(v.PubKey) - if err != nil { - return nil, err + if v.PubKey == nil { + return nil, errors.New("nil pubkey") } vp := cmtproto.Validator{ Address: v.Address, - PubKey: pk, + PubKeyType: v.PubKey.Type(), + PubKeyBytes: v.PubKey.Bytes(), VotingPower: v.VotingPower, ProposerPriority: v.ProposerPriority, } @@ -166,9 +168,15 @@ func ValidatorFromProto(vp *cmtproto.Validator) (*Validator, error) { return nil, errors.New("nil validator") } - pk, err := ce.PubKeyFromProto(vp.PubKey) + pk, err := ce.PubKeyFromTypeAndBytes(vp.PubKeyType, vp.PubKeyBytes) if err != nil { - return nil, err + if vp.PubKey == nil { + return nil, fmt.Errorf("validator pubkey decode failed and legacy pub_key is nil: %w", err) + } + pk, err = ce.PubKeyFromProto(*vp.PubKey) + if err != nil { + return nil, err + } } v := new(Validator) v.Address = vp.GetAddress() diff --git a/types/validator_set_test.go b/types/validator_set_test.go index 2d54b2a97bc..fc14acc6d43 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -14,7 +14,6 @@ import ( "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/ed25519" - cryptoenc "github.com/cometbft/cometbft/crypto/encoding" "github.com/cometbft/cometbft/crypto/secp256k1" cmtmath "github.com/cometbft/cometbft/libs/math" cmtrand "github.com/cometbft/cometbft/libs/rand" @@ -480,18 +479,16 @@ func TestValidatorSetTotalVotingPowerPanicsOnOverflow(t *testing.T) { func TestValidatorSetFromProtoReturnsErrorOnOverflow(t *testing.T) { // ValidatorSetFromProto should return an error instead of panicking when total voting power exceeds MaxTotalVotingPower. pubKey := ed25519.GenPrivKey().PubKey() - pkProto, err := cryptoenc.PubKeyToProto(pubKey) - require.NoError(t, err) protoVals := &cmtproto.ValidatorSet{ Validators: []*cmtproto.Validator{ - {Address: pubKey.Address(), PubKey: pkProto, VotingPower: math.MaxInt64, ProposerPriority: 0}, - {Address: pubKey.Address(), PubKey: pkProto, VotingPower: math.MaxInt64, ProposerPriority: 0}, + {Address: pubKey.Address(), PubKeyBytes: pubKey.Bytes(), PubKeyType: pubKey.Type(), VotingPower: math.MaxInt64, ProposerPriority: 0}, + {Address: pubKey.Address(), PubKeyBytes: pubKey.Bytes(), PubKeyType: pubKey.Type(), VotingPower: math.MaxInt64, ProposerPriority: 0}, }, - Proposer: &cmtproto.Validator{Address: pubKey.Address(), PubKey: pkProto, VotingPower: math.MaxInt64, ProposerPriority: 0}, + Proposer: &cmtproto.Validator{Address: pubKey.Address(), PubKeyBytes: pubKey.Bytes(), PubKeyType: pubKey.Type(), VotingPower: math.MaxInt64, ProposerPriority: 0}, } - _, err = ValidatorSetFromProto(protoVals) + _, err := ValidatorSetFromProto(protoVals) require.Error(t, err) assert.Contains(t, err.Error(), "exceeds maximum") } diff --git a/types/vote.go b/types/vote.go index 7ea9930a777..af11365d43d 100644 --- a/types/vote.go +++ b/types/vote.go @@ -27,10 +27,23 @@ var ( ErrVoteInvalidBlockHash = errors.New("invalid block hash") ErrVoteNonDeterministicSignature = errors.New("non-deterministic signature") ErrVoteNil = errors.New("nil vote") + ErrVoteNoSignature = errors.New("no signature") ErrVoteExtensionAbsent = errors.New("vote extension absent") ErrInvalidVoteExtension = errors.New("invalid vote extension") ) +// ErrVoteTimestampNotZero is returned when a vote carries a non-zero +// timestamp. Vote timestamps are pinned to zero on this fork because they are +// excluded from the signed bytes (BLS aggregation requires all validators to +// sign identical bytes). +type ErrVoteTimestampNotZero struct { + Timestamp time.Time +} + +func (err *ErrVoteTimestampNotZero) Error() string { + return fmt.Sprintf("Vote time stamp should be 0 and not set: %s", err.Timestamp) +} + type ErrVoteConflictingVotes struct { VoteA *Vote VoteB *Vote @@ -50,10 +63,11 @@ func NewConflictingVoteError(vote1, vote2 *Vote) *ErrVoteConflictingVotes { // The vote extension is only valid for non-nil precommits. type ErrVoteExtensionInvalid struct { ExtSignature []byte + Reason string } func (err *ErrVoteExtensionInvalid) Error() string { - return fmt.Sprintf("extensions must be present IFF vote is a non-nil Precommit; extension signature: %X", err.ExtSignature) + return fmt.Sprintf("invalid vote extension: %s; extension signature: %X", err.Reason, err.ExtSignature) } // Address is hex bytes. @@ -93,12 +107,15 @@ func VoteFromProto(pv *cmtproto.Vote) (*Vote, error) { Height: pv.Height, Round: pv.Round, BlockID: *blockID, - Timestamp: pv.Timestamp, ValidatorAddress: pv.ValidatorAddress, ValidatorIndex: pv.ValidatorIndex, Signature: pv.Signature, Extension: pv.Extension, ExtensionSignature: pv.ExtensionSignature, + // The vote timestamp is not part of the signed (canonical) bytes and + // is pinned to zero on this fork (BLS aggregation requires identical + // sign bytes across validators). + Timestamp: time.Time{}, }, nil } @@ -121,7 +138,6 @@ func (vote *Vote) CommitSig() CommitSig { return CommitSig{ BlockIDFlag: blockIDFlag, ValidatorAddress: vote.ValidatorAddress, - Timestamp: vote.Timestamp, Signature: vote.Signature, } } @@ -206,7 +222,7 @@ func (vote *Vote) String() string { panic("Unknown vote type") } - return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X %X @ %s}", + return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X %X @}", vote.ValidatorIndex, cmtbytes.Fingerprint(vote.ValidatorAddress), vote.Height, @@ -216,7 +232,6 @@ func (vote *Vote) String() string { cmtbytes.Fingerprint(vote.BlockID.Hash), cmtbytes.Fingerprint(vote.Signature), cmtbytes.Fingerprint(vote.Extension), - CanonicalTime(vote.Timestamp), ) } @@ -429,7 +444,10 @@ func SignAndCheckVote( isPrecommit := vote.Type == cmtproto.PrecommitType if !isPrecommit && extensionsEnabled { // Non-recoverable because the caller passed parameters that don't make sense - return false, &ErrVoteExtensionInvalid{ExtSignature: v.ExtensionSignature} + return false, &ErrVoteExtensionInvalid{ + Reason: "inconsistent values of `isPrecommit` and `extensionsEnabled`", + ExtSignature: v.ExtensionSignature, + } } isNil := vote.BlockID.IsZero() @@ -438,7 +456,10 @@ func SignAndCheckVote( // Error if prevote contains an extension signature if extSignature && (!isPrecommit || isNil) { // Non-recoverable because the vote is malformed - return false, &ErrVoteExtensionInvalid{ExtSignature: v.ExtensionSignature} + return false, &ErrVoteExtensionInvalid{ + Reason: "vote extension signature must not be present in prevotes or nil-precommits", + ExtSignature: v.ExtensionSignature, + } } vote.ExtensionSignature = nil @@ -446,13 +467,18 @@ func SignAndCheckVote( // Error if missing extension signature for non-nil Precommit if !extSignature && isPrecommit && !isNil { // Non-recoverable because the vote is malformed - return false, &ErrVoteExtensionInvalid{ExtSignature: v.ExtensionSignature} + return false, &ErrVoteExtensionInvalid{ + Reason: "vote extension signature must be present if extensions are enabled", + ExtSignature: v.ExtensionSignature, + } } vote.ExtensionSignature = v.ExtensionSignature } - vote.Timestamp = v.Timestamp + if !v.Timestamp.Equal(time.Time{}) { + return false, &ErrVoteTimestampNotZero{Timestamp: v.Timestamp} + } return true, nil } diff --git a/types/vote_set.go b/types/vote_set.go index ac70c128a1b..3e9d99deb31 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/cometbft/cometbft/crypto/bls12381" "github.com/cometbft/cometbft/libs/bits" cmtjson "github.com/cometbft/cometbft/libs/json" cmtsync "github.com/cometbft/cometbft/libs/sync" @@ -18,6 +19,13 @@ const ( MaxVotesCount = 10000 ) +func init() { + // RoundState.LastCommit is an interface (VoteSetReader), so the concrete + // types it can hold must be registered for JSON encoding (e.g. the + // /dump_consensus_state RPC endpoint). + cmtjson.RegisterType(&VoteSet{}, "cometbft/VoteSet") +} + // UNSTABLE // XXX: duplicate of p2p.ID to avoid dependence between packages. // Perhaps we can have a minimal types package containing this (and other things?) @@ -633,7 +641,7 @@ func (voteSet *VoteSet) sumTotalFrac() (int64, int64, float64) { // // Panics if the vote type is not PrecommitType or if there's no +2/3 votes for // a single block. -func (voteSet *VoteSet) MakeExtendedCommit(ap ABCIParams) *ExtendedCommit { +func (voteSet *VoteSet) MakeExtendedCommit(fp FeatureParams) *ExtendedCommit { voteSet.mtx.Lock() defer voteSet.mtx.Unlock() @@ -664,13 +672,124 @@ func (voteSet *VoteSet) MakeExtendedCommit(ap ABCIParams) *ExtendedCommit { BlockID: *voteSet.maj23, ExtendedSignatures: sigs, } - if err := ec.EnsureExtensions(ap.VoteExtensionsEnabled(ec.Height)); err != nil { + if err := ec.EnsureExtensions(fp.VoteExtensionsEnabled(ec.Height)); err != nil { panic(fmt.Errorf("problem with vote extension data when making extended commit of height %d; %w", ec.Height, err)) } return ec } +// MakeBLSCommit is a variant of MakeExtendedCommit that aggregates BLS signatures. +// +// It additionally aggregates the BLS signatures for the block and nil. The +// resulting commit contains only two aggregated signatures: +// +// 1 - aggregated signature for the block (compressed) +// 2 - aggregated signature for nil (compressed). +// +// Note the signatures count is preserved, but only the first signature in the +// each category (block, nil) is non-empty. +// +// NOTE: it doesn't aggregate vote extension signatures since the extensions +// are all different. +func (voteSet *VoteSet) MakeBLSCommit() *ExtendedCommit { + voteSet.mtx.Lock() + defer voteSet.mtx.Unlock() + + if voteSet.signedMsgType != cmtproto.PrecommitType { + panic("Cannot MakeBLSCommit() unless VoteSet.Type is PrecommitType") + } + + // Make sure we have a 2/3 majority + if voteSet.maj23 == nil { + panic("Cannot MakeBLSCommit() unless a blockhash has +2/3") + } + + // 1. Aggregate the signatures for the block. + sigsToAgg := make([][]byte, 0, len(voteSet.votes)) + for _, v := range voteSet.votes { + if v != nil && v.BlockID.IsComplete() { + // if block ID exists but doesn't match, exclude sig + if !v.BlockID.Equals(*voteSet.maj23) { + continue + } + sigsToAgg = append(sigsToAgg, v.Signature) + } + } + agSig1, err := bls12381.AggregateSignatures(sigsToAgg) + if err != nil { + panic(fmt.Errorf("BLS aggregation error: %w", err)) + } + + // 2. Aggregate the signatures for nil, if any. + sigsToAgg = make([][]byte, 0, len(voteSet.votes)) + for _, v := range voteSet.votes { + if v != nil && v.BlockID.IsNil() { + sigsToAgg = append(sigsToAgg, v.Signature) + } + } + var agSig2 []byte + if len(sigsToAgg) > 0 { + agSig2, err = bls12381.AggregateSignatures(sigsToAgg) + if err != nil { + panic(fmt.Errorf("BLS aggregation error: %w", err)) + } + } + + // For every validator, get the precommit without extensions + sigs := make([]ExtendedCommitSig, len(voteSet.votes)) + for i, v := range voteSet.votes { + sig := v.ExtendedCommitSig() + sig.Signature = []byte{} // clear the signature + + // if block ID exists but doesn't match, exclude sig + if sig.BlockIDFlag == BlockIDFlagCommit && !v.BlockID.Equals(*voteSet.maj23) { + sig = NewExtendedCommitSigAbsent() + } else { + switch sig.BlockIDFlag { + case BlockIDFlagCommit: + sig.BlockIDFlag = BlockIDFlagAggCommitAbsent + case BlockIDFlagNil: + sig.BlockIDFlag = BlockIDFlagAggNilAbsent + default: + } + } + + sigs[i] = sig + } + + // Add agSig1 to the first validator who voted for block. + for i, v := range voteSet.votes { + if v != nil && v.BlockID.IsComplete() { + // if block ID exists but doesn't match, exclude sig + if !v.BlockID.Equals(*voteSet.maj23) { + continue + } + sigs[i].Signature = agSig1 + sigs[i].BlockIDFlag = BlockIDFlagAggCommit + break + } + } + + // Add agSig2 to the first validator who voted for nil. + if agSig2 != nil { + for i, v := range voteSet.votes { + if v != nil && v.BlockID.IsNil() { + sigs[i].Signature = agSig2 + sigs[i].BlockIDFlag = BlockIDFlagAggNil + break + } + } + } + + return &ExtendedCommit{ + Height: voteSet.GetHeight(), + Round: voteSet.GetRound(), + BlockID: *voteSet.maj23, + ExtendedSignatures: sigs, + } +} + //-------------------------------------------------------------------------------- /* diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 01ca884b8d6..6f0663a5283 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -10,7 +10,6 @@ import ( "github.com/cometbft/cometbft/crypto" cmtrand "github.com/cometbft/cometbft/libs/rand" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - cmttime "github.com/cometbft/cometbft/types/time" ) func TestVoteSet_AddVote_Good(t *testing.T) { @@ -33,7 +32,6 @@ func TestVoteSet_AddVote_Good(t *testing.T) { Height: height, Round: round, Type: cmtproto.PrevoteType, - Timestamp: cmttime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, } _, err = signAddVote(val0, vote, voteSet) @@ -54,7 +52,6 @@ func TestVoteSet_AddVote_Bad(t *testing.T) { ValidatorIndex: -1, Height: height, Round: round, - Timestamp: cmttime.Now(), Type: cmtproto.PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, } @@ -129,7 +126,6 @@ func Benchmark_2_3_Maj(b *testing.B) { Height: height, Round: round, Type: cmtproto.PrevoteType, - Timestamp: cmttime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, } blockPartsTotal := uint32(123) @@ -180,7 +176,6 @@ func TestVoteSet_2_3Majority(t *testing.T) { Height: height, Round: round, Type: cmtproto.PrevoteType, - Timestamp: cmttime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, } // 6 out of 10 voted for nil. @@ -233,7 +228,6 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) { ValidatorIndex: -1, // NOTE: must fill in Height: height, Round: round, - Timestamp: cmttime.Now(), Type: cmtproto.PrevoteType, BlockID: BlockID{blockHash, blockPartSetHeader}, } @@ -330,7 +324,6 @@ func TestVoteSet_Conflicts(t *testing.T) { ValidatorIndex: -1, Height: height, Round: round, - Timestamp: cmttime.Now(), Type: cmtproto.PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, } @@ -458,7 +451,6 @@ func TestVoteSet_MakeCommit(t *testing.T) { ValidatorIndex: -1, Height: height, Round: round, - Timestamp: cmttime.Now(), Type: cmtproto.PrecommitType, BlockID: BlockID{blockHash, blockPartSetHeader}, } @@ -476,7 +468,7 @@ func TestVoteSet_MakeCommit(t *testing.T) { } // MakeCommit should fail. - veHeightParam := ABCIParams{VoteExtensionsEnableHeight: height} + veHeightParam := FeatureParams{VoteExtensionsEnableHeight: height} assert.Panics(t, func() { voteSet.MakeExtendedCommit(veHeightParam) }, "Doesn't have +2/3 majority") // 7th voted for some other block. @@ -584,7 +576,6 @@ func TestVoteSet_VoteExtensionsEnabled(t *testing.T) { Height: height, Round: round, Type: cmtproto.PrecommitType, - Timestamp: cmttime.Now(), BlockID: BlockID{blockHash, blockPartSetHeader}, } v := vote.ToProto() diff --git a/types/vote_test.go b/types/vote_test.go index 4848947b519..6a44ef78e5f 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -3,7 +3,6 @@ package types import ( "fmt" "testing" - "time" "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/assert" @@ -14,7 +13,6 @@ import ( "github.com/cometbft/cometbft/crypto/tmhash" "github.com/cometbft/cometbft/libs/protoio" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - cmttime "github.com/cometbft/cometbft/types/time" ) func examplePrevote() *Vote { @@ -29,16 +27,10 @@ func examplePrecommit() *Vote { } func exampleVote(t byte) *Vote { - stamp, err := time.Parse(TimeFormat, "2017-12-25T03:00:01.234Z") - if err != nil { - panic(err) - } - return &Vote{ - Type: cmtproto.SignedMsgType(t), - Height: 12345, - Round: 2, - Timestamp: stamp, + Type: cmtproto.SignedMsgType(t), + Height: 12345, + Round: 2, BlockID: BlockID{ Hash: tmhash.Sum([]byte("blockID_hash")), PartSetHeader: PartSetHeader{ @@ -71,65 +63,53 @@ func TestVoteSignBytesTestVectors(t *testing.T) { 0: { "", &Vote{}, // NOTE: Height and Round are skipped here. This case needs to be considered while parsing. - []byte{0xd, 0x2a, 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, + []byte{0x0}, }, // with proper (fixed size) height and round (PreCommit): 1: { "", &Vote{Height: 1, Round: 1, Type: cmtproto.PrecommitType}, []byte{ - 0x21, // length + 0x14, // length 0x8, // (field_number << 3) | wire_type - 0x2, // PrecommitType + 0x2, // cmtproto.PrecommitType 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round - 0x2a, // (field_number << 3) | wire_type - // remaining fields (timestamp): - 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, }, }, // with proper (fixed size) height and round (PreVote): 2: { "", &Vote{Height: 1, Round: 1, Type: cmtproto.PrevoteType}, []byte{ - 0x21, // length + 0x14, // length 0x8, // (field_number << 3) | wire_type - 0x1, // PrevoteType + 0x1, // cmtproto.PrevoteType 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round - 0x2a, // (field_number << 3) | wire_type - // remaining fields (timestamp): - 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, }, }, 3: { "", &Vote{Height: 1, Round: 1}, []byte{ - 0x1f, // length + 0x12, // length 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round - // remaining fields (timestamp): - 0x2a, - 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, }, }, // containing non-empty chain_id: 4: { "test_chain_id", &Vote{Height: 1, Round: 1}, []byte{ - 0x2e, // length + 0x21, // length 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round - // remaining fields: - 0x2a, // (field_number << 3) | wire_type - 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, // timestamp // (field_number << 3) | wire_type 0x32, 0xd, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, @@ -143,15 +123,12 @@ func TestVoteSignBytesTestVectors(t *testing.T) { Extension: []byte("extension"), }, []byte{ - 0x2e, // length + 0x21, // length 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round // remaining fields: - 0x2a, // (field_number << 3) | wire_type - 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, // timestamp - // (field_number << 3) | wire_type 0x32, 0xd, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, // chainID }, // chainID @@ -164,7 +141,6 @@ func TestVoteSignBytesTestVectors(t *testing.T) { assert.Equal(t, tc.want, got, "test case #%v: got unexpected sign bytes for Vote.", i) } } - func TestVoteProposalNotEq(t *testing.T) { cv := CanonicalizeVote("", &cmtproto.Vote{Height: 1, Round: 1}) p := CanonicalizeProposal("", &cmtproto.Proposal{Height: 1, Round: 1}) @@ -250,7 +226,6 @@ func TestVoteExtension(t *testing.T) { ValidatorIndex: 0, Height: height, Round: round, - Timestamp: cmttime.Now(), Type: cmtproto.PrecommitType, BlockID: makeBlockIDRandom(), } @@ -313,13 +288,13 @@ func TestVoteVerify(t *testing.T) { func TestVoteString(t *testing.T) { str := examplePrecommit().String() - expected := `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PRECOMMIT(Precommit) 8B01023386C3 000000000000 657874656E73 @ 2017-12-25T03:00:01.234Z}` //nolint:lll //ignore line length for tests + expected := `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PRECOMMIT(Precommit) 8B01023386C3 000000000000 657874656E73 @}` //nolint:lll //ignore line length for tests if str != expected { t.Errorf("got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str) } str2 := examplePrevote().String() - expected = `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PREVOTE(Prevote) 8B01023386C3 000000000000 000000000000 @ 2017-12-25T03:00:01.234Z}` //nolint:lll //ignore line length for tests + expected = `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PREVOTE(Prevote) 8B01023386C3 000000000000 000000000000 @}` //nolint:lll //ignore line length for tests if str2 != expected { t.Errorf("got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str2) } diff --git a/version/version.go b/version/version.go index d7b8fc0ef4d..aacff88af33 100644 --- a/version/version.go +++ b/version/version.go @@ -1,9 +1,14 @@ package version const ( - // TMCoreSemVer is the used as the fallback version of CometBFT - // when not using git describe. It is formatted with semantic versioning. - TMCoreSemVer = "0.39.0" + // TMCoreSemVer is used as the fallback version of CometBFT when not using + // git describe. It is formatted with semantic versioning. + // + // Berachain fork scheme: -bera. (e.g. 0.39.3-bera.1). + // The base tracks the upstream CometBFT release this fork is built on; the + // -bera.N counter is bumped per fork release and reset to 1 when the base + // moves to a newer upstream release. + TMCoreSemVer = "0.39.3-bera.1" // ABCISemVer is the semantic version of the ABCI protocol ABCISemVer = "2.0.0" ABCIVersion = ABCISemVer