Compare commits

...

23 Commits

Author SHA1 Message Date
William Banfield
c627a1d762 Revert "rename loadblockextendedcommit -> loadextendedcommit"
This reverts commit 8935da8872.
2022-05-13 15:31:23 -04:00
William Banfield
5dbb706e2c add boolean parameter to vote set 2022-05-13 15:12:06 -04:00
William Banfield
56b5a234bf fix state tests to not check for own extension 2022-05-13 15:11:54 -04:00
William Banfield
c3f64df354 reconstruct last commit works with legacy chains 2022-05-13 14:05:01 -04:00
William Banfield
ec46cc4006 fix buildExtendedCommitInfo logic to allow default 2022-05-13 13:49:00 -04:00
William Banfield
8935da8872 rename loadblockextendedcommit -> loadextendedcommit 2022-05-13 12:53:12 -04:00
William Banfield
ab83d3307d validation shimmed into place for switch to consensus 2022-05-12 18:00:53 -04:00
William Banfield
636cd97712 consolidate and comment vote set tests 2022-05-12 15:52:41 -04:00
William Banfield
a8b85c1999 add internal bool param to internal vote set 2022-05-12 15:02:36 -04:00
William Banfield
eec438ac97 fix require condition to be correct 2022-05-12 14:48:23 -04:00
William Banfield
5e4575695d test comple: still needs comment 2022-05-11 23:04:59 -04:00
William Banfield
b4da26555b remove duplicate logging 2022-05-11 23:00:26 -04:00
William Banfield
d9820182e6 update test to ensure consensus proceeds as expected 2022-05-11 22:52:31 -04:00
William Banfield
667c53dcbc use 3 validators for verify test 2022-05-11 22:22:32 -04:00
William Banfield
c35bcbe320 use 4 validators and only expect 3 verifiy calls 2022-05-11 22:19:46 -04:00
William Banfield
b075117d83 don't validate when constructing extendedcommitsig 2022-05-11 22:17:52 -04:00
William Banfield
3e71e81938 fix test to bypass own verify vote extension 2022-05-11 22:15:57 -04:00
William Banfield
5caea6e01e add logging for verification failed 2022-05-11 22:15:30 -04:00
William Banfield
146c996ec7 fix typo 2022-05-11 22:01:27 -04:00
William Banfield
101d357224 wip: compiles but test fails 2022-05-11 19:27:13 -04:00
William Banfield
5aeee88443 add param to params.go 2022-05-11 16:38:46 -04:00
dependabot[bot]
b0fe38c245 build(deps): Bump github.com/creachadair/tomledit from 0.0.19 to 0.0.22 (#8504)
Bumps [github.com/creachadair/tomledit](https://github.com/creachadair/tomledit) from 0.0.19 to 0.0.22.
<details>
<summary>Commits</summary>
<ul>
<li><a href="f7ad71d86c"><code>f7ad71d</code></a> cli: accept @ prefixed value arguments for strings</li>
<li><a href="2cb36fdb81"><code>2cb36fd</code></a> Release v0.0.21</li>
<li><a href="f56c9925a3"><code>f56c992</code></a> cli: move subcommands to a separate file</li>
<li><a href="0271385b7a"><code>0271385</code></a> cli: allow list arguments as prefix filters</li>
<li><a href="6e4454ec9d"><code>6e4454e</code></a> cli: add subcommand &quot;add&quot;</li>
<li><a href="d59f49c18b"><code>d59f49c</code></a> Add a basic command-line tool to read and set keys.</li>
<li><a href="9f9039fa9b"><code>9f9039f</code></a> Add a test for top-level comment blocking.</li>
<li>See full diff in <a href="https://github.com/creachadair/tomledit/compare/v0.0.19...v0.0.22">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/creachadair/tomledit&package-manager=go_modules&previous-version=0.0.19&new-version=0.0.22)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>
2022-05-11 13:23:47 +00:00
Thane Thomson
06b1812094 abci++: Propagate vote extensions (RFC 017) (#8433)
* Add protos for ExtendedCommit

Cherry-pick from e73f0178b72a16ee81f8e856aadf651f2c62ec6e just the
changes to the .proto files, since we have deleted the .intermediate
files.

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* make proto-gen

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* BlockStore holds extended commit

Cherry-pick 8d504d4b50ec6afbdffe2df7ababbef30e15053d and fix conflicts.

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Reshuffle ExtendedCommit and ExtendedCommitSig

Separate the data structures and functions from their Commit-oriented
counterparts to adhere to the current coding style.

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Fix exit condition in blocksync

* Add note to remove TxResult proto

As Sergio pointed out in 3e31aa6f583cdc71e208ed03a82f1d804ec0de49, this
proto message can probably be removed. We should do this in a separate
PR.

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Lift termination condition into for loop

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Enforce vote extension signature requirement

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Expand on comment for PeekTwoBlocks for posterity

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Isolate TODO more clearly

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* make mockery

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Fix comment

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Make panic output from BlockStore.SaveBlock more readable

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add helper methods to ExtendedCommitSig and ExtendedCommit

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Fix most tests except TestHandshake*

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Fix store prefix collision

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Fix TestBlockFetchAtHeight

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Remove global state from store tests

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Apply suggestions from code review

Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
Co-authored-by: Sergio Mena <sergio@informal.systems>

* blocksync: Just return error

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* make format

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Remove unused/commented-out code

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* blocksync: Change pool AddBlock function signature to return errors

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Improve legibility of switch statements

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* blocksync: Expand on extended commit requirement in AddBlock description

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* blocksync: Return error without also logging it

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* consensus: Rename short-lived local variable

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* consensus: Allocate TODO to Sergio

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* evidence/pool_test: Inline slice construction

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* state: Rename LoadBlockExtCommit to LoadBlockExtendedCommit

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* proto: Remove TODO on TxResult

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Minor format

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Reformat ExtendedCommitSig.BlockID

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Remove NewExtendedCommit constructor

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Remove NewCommit constructor

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Shorten receiver names for ExtendedCommit

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Convert ExtendedCommit.Copy to a deep clone

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Assign TODO to Sergio

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Fix legibility nits

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Improve legibility

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* store/state: Add TODO to move prefixes to common package

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Propagate validator info to PrepareProposal

In order to propagate validator voting power through to PrepareProposal,
we need to load the validator set info from the height corresponding to
the extended commit that we're passing through to PrepareProposal as the
"LocalLastCommit".

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Rename local var for clarity

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Fix TestMaxProposalBlockSize

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Rename local var for clarity

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Remove debug log

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Remove CommigSig.ForBlock helper

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Remove CommigSig.Absent helper

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Remove ExtendedCommitSig.ForBlock helper

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Remove ExtendedCommitSig.Absent helper

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* There are no extended commits below the initial height

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Fix comment grammar

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Remove JSON encoding from ExtendedCommit

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Embed CommitSig into ExtendedCommitSig instead of duplicating fields

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Rename ExtendedCommit vote_extension field to extension for consistency with domain types

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* blocksync: Panic if we peek a block without an extended commit

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Apply suggestions from code review

Co-authored-by: M. J. Fromberger <fromberger@interchain.io>

* Remove Sergio from TODO

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Increase hard-coded vote extension max size to 1MB

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* state: Remove unnecessary comment

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* state: Ensure no of commit sigs equals validator set length

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* make format

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Minor legibility improvements

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Improve legibility

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Remove unused GetVotes function on VoteSet

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Refactor TestMaxProposalBlockSize to construct more realistic extended commit

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Refactor buildExtendedCommitInfo to resemble buildLastCommitInfo

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Apply suggestions from code review

Co-authored-by: M. J. Fromberger <fromberger@interchain.io>

* abci++: Disable VerifyVoteExtension call on nil precommits (#8491)

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* types: Require vote extensions on non-nil precommits and not otherwise

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Disable lint

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Increase timeout for TestReactorVotingPowerChange to counter flakiness

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Only sign and verify vote extensions in non-nil precommits

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Revert "Disable lint"

This reverts commit 6fffbf9402.

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add missing non-nil check uncovered non-deterministically in TestHandshakeReplayAll

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Expand error message for accuracy

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Only call ExtendVote when we make non-nil precommits

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Revert "Increase timeout for TestReactorVotingPowerChange to counter flakiness"

This reverts commit af514939db.

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Refactor ValidateBasic for ExtendedCommitSig for legibility

Signed-off-by: Thane Thomson <connect@thanethomson.com>

Co-authored-by: Sergio Mena <sergio@informal.systems>
Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
2022-05-11 07:10:32 -04:00
59 changed files with 2441 additions and 777 deletions

2
go.mod
View File

@@ -83,7 +83,7 @@ require (
github.com/charithe/durationcheck v0.0.9 // indirect
github.com/chavacava/garif v0.0.0-20220316182200-5cad0b5181d4 // indirect
github.com/containerd/continuity v0.2.1 // indirect
github.com/creachadair/tomledit v0.0.19
github.com/creachadair/tomledit v0.0.22
github.com/daixiang0/gci v0.3.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denis-tingaikin/go-header v0.4.3 // indirect

5
go.sum
View File

@@ -230,10 +230,11 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creachadair/atomicfile v0.2.6 h1:FgYxYvGcqREApTY8Nxg8msM6P/KVKK3ob5h9FaRUTNg=
github.com/creachadair/atomicfile v0.2.6/go.mod h1:BRq8Une6ckFneYXZQ+kO7p1ZZP3I2fzVzf28JxrIkBc=
github.com/creachadair/command v0.0.0-20220426235536-a748effdf6a1/go.mod h1:bAM+qFQb/KwWyCc9MLC4U1jvn3XyakqP5QRkds5T6cY=
github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM=
github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk=
github.com/creachadair/tomledit v0.0.19 h1:zbpfUtYFYFdpRjwJY9HJlto1iZ4M5YwYB6qqc37F6UM=
github.com/creachadair/tomledit v0.0.19/go.mod h1:gvtfnSZLa+YNQD28vaPq0Nk12bRxEhmUdBzAWn+EGF4=
github.com/creachadair/tomledit v0.0.22 h1:lRtepmrwhzDq+g1gv5ftVn5itgo7CjYbm6abKTToqJ4=
github.com/creachadair/tomledit v0.0.22/go.mod h1:cIu/4x5L855oSRejIqr+WRFh+mv9g4fWLiUFaApYn/Y=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=

View File

@@ -200,16 +200,20 @@ func (pool *BlockPool) IsCaughtUp() bool {
return pool.height >= (pool.maxPeerHeight - 1)
}
// PeekTwoBlocks returns blocks at pool.height and pool.height+1.
// We need to see the second block's Commit to validate the first block.
// So we peek two blocks at a time.
// PeekTwoBlocks returns blocks at pool.height and pool.height+1. We need to
// see the second block's Commit to validate the first block. So we peek two
// blocks at a time. We return an extended commit, containing vote extensions
// and their associated signatures, as this is critical to consensus in ABCI++
// as we switch from block sync to consensus mode.
//
// The caller will verify the commit.
func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block) {
func (pool *BlockPool) PeekTwoBlocks() (first, second *types.Block, firstExtCommit *types.ExtendedCommit) {
pool.mtx.RLock()
defer pool.mtx.RUnlock()
if r := pool.requesters[pool.height]; r != nil {
first = r.getBlock()
firstExtCommit = r.getExtendedCommit()
}
if r := pool.requesters[pool.height+1]; r != nil {
second = r.getBlock()
@@ -218,7 +222,8 @@ func (pool *BlockPool) PeekTwoBlocks() (first *types.Block, second *types.Block)
}
// PopRequest pops the first block at pool.height.
// It must have been validated by 'second'.Commit from PeekTwoBlocks().
// It must have been validated by the second Commit from PeekTwoBlocks.
// TODO(thane): (?) and its corresponding ExtendedCommit.
func (pool *BlockPool) PopRequest() {
pool.mtx.Lock()
defer pool.mtx.Unlock()
@@ -262,16 +267,25 @@ func (pool *BlockPool) RedoRequest(height int64) types.NodeID {
return peerID
}
// AddBlock validates that the block comes from the peer it was expected from and calls the requester to store it.
// AddBlock validates that the block comes from the peer it was expected from
// and calls the requester to store it.
//
// This requires an extended commit at the same height as the supplied block -
// the block contains the last commit, but we need the latest commit in case we
// need to switch over from block sync to consensus at this height. If the
// height of the extended commit and the height of the block do not match, we
// do not add the block and return an error.
// TODO: ensure that blocks come in order for each peer.
func (pool *BlockPool) AddBlock(peerID types.NodeID, block *types.Block, blockSize int) {
func (pool *BlockPool) AddBlock(peerID types.NodeID, block *types.Block, extCommit *types.ExtendedCommit, blockSize int) error {
pool.mtx.Lock()
defer pool.mtx.Unlock()
if block.Height != extCommit.Height {
return fmt.Errorf("heights don't match, not adding block (block height: %d, commit height: %d)", block.Height, extCommit.Height)
}
requester := pool.requesters[block.Height]
if requester == nil {
pool.logger.Error("peer sent us a block we didn't expect",
"peer", peerID, "curHeight", pool.height, "blockHeight", block.Height)
diff := pool.height - block.Height
if diff < 0 {
diff *= -1
@@ -279,10 +293,10 @@ func (pool *BlockPool) AddBlock(peerID types.NodeID, block *types.Block, blockSi
if diff > maxDiffBetweenCurrentAndReceivedBlockHeight {
pool.sendError(errors.New("peer sent us a block we didn't expect with a height too far ahead/behind"), peerID)
}
return
return fmt.Errorf("peer sent us a block we didn't expect (peer: %s, current height: %d, block height: %d)", peerID, pool.height, block.Height)
}
if requester.setBlock(block, peerID) {
if requester.setBlock(block, extCommit, peerID) {
atomic.AddInt32(&pool.numPending, -1)
peer := pool.peers[peerID]
if peer != nil {
@@ -290,9 +304,11 @@ func (pool *BlockPool) AddBlock(peerID types.NodeID, block *types.Block, blockSi
}
} else {
err := errors.New("requester is different or block already exists")
pool.logger.Error(err.Error(), "peer", peerID, "requester", requester.getPeerID(), "blockHeight", block.Height)
pool.sendError(err, peerID)
return fmt.Errorf("%w (peer: %s, requester: %s, block height: %d)", err, peerID, requester.getPeerID(), block.Height)
}
return nil
}
// MaxPeerHeight returns the highest reported height.
@@ -456,6 +472,7 @@ func (pool *BlockPool) debug() string {
} else {
str += fmt.Sprintf("H(%v):", h)
str += fmt.Sprintf("B?(%v) ", pool.requesters[h].block != nil)
str += fmt.Sprintf("C?(%v) ", pool.requesters[h].extCommit != nil)
}
}
return str
@@ -544,9 +561,10 @@ type bpRequester struct {
gotBlockCh chan struct{}
redoCh chan types.NodeID // redo may send multitime, add peerId to identify repeat
mtx sync.Mutex
peerID types.NodeID
block *types.Block
mtx sync.Mutex
peerID types.NodeID
block *types.Block
extCommit *types.ExtendedCommit
}
func newBPRequester(logger log.Logger, pool *BlockPool, height int64) *bpRequester {
@@ -572,13 +590,14 @@ func (bpr *bpRequester) OnStart(ctx context.Context) error {
func (*bpRequester) OnStop() {}
// Returns true if the peer matches and block doesn't already exist.
func (bpr *bpRequester) setBlock(block *types.Block, peerID types.NodeID) bool {
func (bpr *bpRequester) setBlock(block *types.Block, extCommit *types.ExtendedCommit, peerID types.NodeID) bool {
bpr.mtx.Lock()
if bpr.block != nil || bpr.peerID != peerID {
bpr.mtx.Unlock()
return false
}
bpr.block = block
bpr.extCommit = extCommit
bpr.mtx.Unlock()
select {
@@ -594,6 +613,12 @@ func (bpr *bpRequester) getBlock() *types.Block {
return bpr.block
}
func (bpr *bpRequester) getExtendedCommit() *types.ExtendedCommit {
bpr.mtx.Lock()
defer bpr.mtx.Unlock()
return bpr.extCommit
}
func (bpr *bpRequester) getPeerID() types.NodeID {
bpr.mtx.Lock()
defer bpr.mtx.Unlock()
@@ -611,6 +636,7 @@ func (bpr *bpRequester) reset() {
bpr.peerID = ""
bpr.block = nil
bpr.extCommit = nil
}
// Tells bpRequester to pick another peer and try again.

View File

@@ -43,7 +43,10 @@ func (p testPeer) runInputRoutine() {
// Request desired, pretend like we got the block immediately.
func (p testPeer) simulateInput(input inputData) {
block := &types.Block{Header: types.Header{Height: input.request.Height}}
input.pool.AddBlock(input.request.PeerID, block, 123)
extCommit := &types.ExtendedCommit{
Height: input.request.Height,
}
_ = input.pool.AddBlock(input.request.PeerID, block, extCommit, 123)
// TODO: uncommenting this creates a race which is detected by:
// https://github.com/golang/go/blob/2bd767b1022dd3254bcec469f0ee164024726486/src/testing/testing.go#L854-L856
// see: https://github.com/tendermint/tendermint/issues/3390#issue-418379890
@@ -110,7 +113,7 @@ func TestBlockPoolBasic(t *testing.T) {
if !pool.IsRunning() {
return
}
first, second := pool.PeekTwoBlocks()
first, second, _ := pool.PeekTwoBlocks()
if first != nil && second != nil {
pool.PopRequest()
} else {
@@ -164,7 +167,7 @@ func TestBlockPoolTimeout(t *testing.T) {
if !pool.IsRunning() {
return
}
first, second := pool.PeekTwoBlocks()
first, second, _ := pool.PeekTwoBlocks()
if first != nil && second != nil {
pool.PopRequest()
} else {

View File

@@ -76,7 +76,7 @@ type Reactor struct {
stateStore sm.Store
blockExec *sm.BlockExecutor
store *store.BlockStore
store sm.BlockStore
pool *BlockPool
consReactor consensusReactor
blockSync *atomicBool
@@ -186,15 +186,21 @@ func (r *Reactor) OnStop() {
func (r *Reactor) respondToPeer(ctx context.Context, msg *bcproto.BlockRequest, peerID types.NodeID, blockSyncCh *p2p.Channel) error {
block := r.store.LoadBlock(msg.Height)
if block != nil {
extCommit := r.store.LoadBlockExtendedCommit(msg.Height)
if extCommit == nil {
return fmt.Errorf("found block in store without extended commit: %v", block)
}
blockProto, err := block.ToProto()
if err != nil {
r.logger.Error("failed to convert msg to protobuf", "err", err)
return err
return fmt.Errorf("failed to convert block to protobuf: %w", err)
}
return blockSyncCh.Send(ctx, p2p.Envelope{
To: peerID,
Message: &bcproto.BlockResponse{Block: blockProto},
To: peerID,
Message: &bcproto.BlockResponse{
Block: blockProto,
ExtCommit: extCommit.ToProto(),
},
})
}
@@ -236,8 +242,17 @@ func (r *Reactor) handleMessage(ctx context.Context, envelope *p2p.Envelope, blo
"err", err)
return err
}
extCommit, err := types.ExtendedCommitFromProto(msg.ExtCommit)
if err != nil {
r.logger.Error("failed to convert extended commit from proto",
"peer", envelope.From,
"err", err)
return err
}
r.pool.AddBlock(envelope.From, block, block.Size())
if err := r.pool.AddBlock(envelope.From, block, extCommit, block.Size()); err != nil {
r.logger.Error("failed to add block", "err", err)
}
case *bcproto.StatusRequest:
return blockSyncCh.Send(ctx, p2p.Envelope{
@@ -448,6 +463,20 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
)
switch {
// TODO(sergio) Might be needed for implementing the upgrading solution. Remove after that
//case state.LastBlockHeight > 0 && r.store.LoadBlockExtCommit(state.LastBlockHeight) == nil:
case state.LastBlockHeight > 0 && blocksSynced == 0:
// Having state-synced, we need to blocksync at least one block
r.logger.Info(
"no seen commit yet",
"height", height,
"last_block_height", state.LastBlockHeight,
"initial_height", state.InitialHeight,
"max_peer_height", r.pool.MaxPeerHeight(),
"timeout_in", syncTimeout-time.Since(lastAdvance),
)
continue
case r.pool.IsCaughtUp():
r.logger.Info("switching to consensus reactor", "height", height)
@@ -490,9 +519,13 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
// TODO: Uncouple from request routine.
// see if there are any blocks to sync
first, second := r.pool.PeekTwoBlocks()
if first == nil || second == nil {
// we need both to sync the first block
first, second, extCommit := r.pool.PeekTwoBlocks()
if first == nil || second == nil || extCommit == nil {
if first != nil && extCommit == nil {
// See https://github.com/tendermint/tendermint/pull/8433#discussion_r866790631
panic(fmt.Errorf("peeked first block without extended commit at height %d - possible node store corruption", first.Height))
}
// we need all to sync the first block
continue
} else {
// try again quickly next loop
@@ -517,6 +550,7 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
// NOTE: We can probably make this more efficient, but note that calling
// first.Hash() doesn't verify the tx contents, so MakePartSet() is
// currently necessary.
// TODO(sergio): Should we also validate against the extended commit?
err = state.Validators.VerifyCommitLight(chainID, firstID, first.Height, second.LastCommit)
if err == nil {
@@ -559,7 +593,7 @@ func (r *Reactor) poolRoutine(ctx context.Context, stateSynced bool, blockSyncCh
r.pool.PopRequest()
// TODO: batch saves so we do not persist to disk every block
r.store.SaveBlock(first, firstParts, second.LastCommit)
r.store.SaveBlock(first, firstParts, extCommit)
// TODO: Same thing for app - but we would need a way to get the hash
// without persisting the state.

View File

@@ -147,39 +147,43 @@ func (rts *reactorTestSuite) addNode(
sm.NopMetrics(),
)
var lastExtCommit *types.ExtendedCommit
// The commit we are building for the current height.
seenExtCommit := &types.ExtendedCommit{}
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil)
lastExtCommit = seenExtCommit.Clone()
if blockHeight > 1 {
lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1)
lastBlock := blockStore.LoadBlock(blockHeight - 1)
vote, err := factory.MakeVote(
ctx,
privVal,
lastBlock.Header.ChainID, 0,
lastBlock.Header.Height, 0, 2,
lastBlockMeta.BlockID,
time.Now(),
)
require.NoError(t, err)
lastCommit = types.NewCommit(
vote.Height,
vote.Round,
lastBlockMeta.BlockID,
[]types.CommitSig{vote.CommitSig()},
)
}
thisBlock := sf.MakeBlock(state, blockHeight, lastCommit)
thisBlock := sf.MakeBlock(state, blockHeight, lastExtCommit.StripExtensions())
thisParts, err := thisBlock.MakePartSet(types.BlockPartSizeBytes)
require.NoError(t, err)
blockID := types.BlockID{Hash: thisBlock.Hash(), PartSetHeader: thisParts.Header()}
// Simulate a commit for the current height
vote, err := factory.MakeVote(
ctx,
privVal,
thisBlock.Header.ChainID,
0,
thisBlock.Header.Height,
0,
2,
blockID,
time.Now(),
)
require.NoError(t, err)
seenExtCommit = &types.ExtendedCommit{
Height: vote.Height,
Round: vote.Round,
BlockID: blockID,
ExtendedSignatures: []types.ExtendedCommitSig{vote.ExtendedCommitSig()},
}
state, err = blockExec.ApplyBlock(ctx, state, blockID, thisBlock)
require.NoError(t, err)
blockStore.SaveBlock(thisBlock, thisParts, lastCommit)
blockStore.SaveBlock(thisBlock, thisParts, seenExtCommit)
}
rts.peerChans[nodeID] = make(chan p2p.PeerUpdate)

View File

@@ -178,22 +178,22 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
lazyNodeState.decideProposal = func(ctx context.Context, height int64, round int32) {
require.NotNil(t, lazyNodeState.privValidator)
var commit *types.Commit
var extCommit *types.ExtendedCommit
switch {
case lazyNodeState.Height == lazyNodeState.state.InitialHeight:
// We're creating a proposal for the first block.
// The commit is empty, but not nil.
commit = types.NewCommit(0, 0, types.BlockID{}, nil)
extCommit = &types.ExtendedCommit{}
case lazyNodeState.LastCommit.HasTwoThirdsMajority():
// Make the commit from LastCommit
commit = lazyNodeState.LastCommit.MakeCommit()
extCommit = lazyNodeState.LastCommit.MakeExtendedCommit()
default: // This shouldn't happen.
lazyNodeState.logger.Error("enterPropose: Cannot propose anything: No commit for the previous block")
return
}
// omit the last signature in the commit
commit.Signatures[len(commit.Signatures)-1] = types.NewCommitSigAbsent()
extCommit.ExtendedSignatures[len(extCommit.ExtendedSignatures)-1] = types.NewExtendedCommitSigAbsent()
if lazyNodeState.privValidatorPubKey == nil {
// If this node is a validator & proposer in the current round, it will
@@ -204,7 +204,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
proposerAddr := lazyNodeState.privValidatorPubKey.Address()
block, err := lazyNodeState.blockExec.CreateProposalBlock(
ctx, lazyNodeState.Height, lazyNodeState.state, commit, proposerAddr, lazyNodeState.LastCommit.GetVotes())
ctx, lazyNodeState.Height, lazyNodeState.state, extCommit, proposerAddr)
require.NoError(t, err)
blockParts, err := block.MakePartSet(types.BlockPartSizeBytes)
require.NoError(t, err)

View File

@@ -160,7 +160,8 @@ func signVote(
blockID types.BlockID) *types.Vote {
var ext []byte
if voteType == tmproto.PrecommitType {
// Only non-nil precommits are allowed to carry vote extensions.
if voteType == tmproto.PrecommitType && !blockID.IsNil() {
ext = []byte("extension")
}
v, err := vs.signVote(ctx, voteType, chainID, blockID, ext)

View File

@@ -6,6 +6,7 @@ import (
testing "testing"
mock "github.com/stretchr/testify/mock"
state "github.com/tendermint/tendermint/internal/state"
)

View File

@@ -67,7 +67,7 @@ func TestMsgToProto(t *testing.T) {
pv := types.NewMockPV()
vote, err := factory.MakeVote(ctx, pv, factory.DefaultTestChainID,
0, 1, 0, 2, types.BlockID{}, time.Now())
0, 1, 0, 2, bi, time.Now())
require.NoError(t, err)
pbVote := vote.ToProto()

View File

@@ -794,10 +794,10 @@ func (r *Reactor) gossipVotesRoutine(ctx context.Context, ps *PeerState, voteCh
// catchup logic -- if peer is lagging by more than 1, send Commit
blockStoreBase := r.state.blockStore.Base()
if blockStoreBase > 0 && prs.Height != 0 && rs.Height >= prs.Height+2 && prs.Height >= blockStoreBase {
// Load the block commit for prs.Height, which contains precommit
// Load the block's extended commit for prs.Height, which contains precommit
// signatures for prs.Height.
if commit := r.state.blockStore.LoadBlockCommit(prs.Height); commit != nil {
if ok, err := r.pickSendVote(ctx, ps, commit, voteCh); err != nil {
if ec := r.state.blockStore.LoadBlockExtendedCommit(prs.Height); ec != nil {
if ok, err := r.pickSendVote(ctx, ps, ec, voteCh); err != nil {
return
} else if ok {
logger.Debug("picked Catchup commit to send", "height", prs.Height)

View File

@@ -32,6 +32,7 @@ import (
"github.com/tendermint/tendermint/internal/test/factory"
"github.com/tendermint/tendermint/libs/log"
tmcons "github.com/tendermint/tendermint/proto/tendermint/consensus"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/types"
)
@@ -600,6 +601,106 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) {
wg.Wait()
}
func TestSwitchToConsensusVoteExtensions(t *testing.T) {
for _, testCase := range []struct {
name string
storedHeight int64
initialRequiredHeight int64
includeExtensions bool
shouldPanic bool
}{
{
name: "no vote extensions but not required",
initialRequiredHeight: 0,
storedHeight: 2,
includeExtensions: false,
shouldPanic: false,
},
{
name: "no vote extensions but required this height",
initialRequiredHeight: 2,
storedHeight: 2,
includeExtensions: false,
shouldPanic: true,
},
{
name: "no vote extensions and required in future",
initialRequiredHeight: 3,
storedHeight: 2,
includeExtensions: false,
shouldPanic: false,
},
{
name: "no vote extensions and required previous height",
initialRequiredHeight: 1,
storedHeight: 2,
includeExtensions: false,
shouldPanic: true,
},
{
name: "vote extensions and required previous height",
initialRequiredHeight: 1,
storedHeight: 2,
includeExtensions: true,
shouldPanic: false,
},
} {
t.Run(testCase.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
cs, vs := makeState(ctx, t, makeStateArgs{validators: 1})
validator := vs[0]
validator.Height = testCase.storedHeight
cs.state.LastBlockHeight = testCase.storedHeight
cs.state.LastValidators = cs.state.Validators.Copy()
cs.state.ConsensusParams.Vote.ExtensionRequireHeight = testCase.initialRequiredHeight
propBlock, err := cs.createProposalBlock(ctx)
require.NoError(t, err)
// Consensus is preparing to do the next height after the stored height.
cs.Height = testCase.storedHeight + 1
propBlock.Height = testCase.storedHeight
blockParts, err := propBlock.MakePartSet(types.BlockPartSizeBytes)
require.NoError(t, err)
voteSet := types.NewVoteSet(cs.state.ChainID, testCase.storedHeight, 0, tmproto.PrecommitType, cs.state.Validators, false)
signedVote := signVote(ctx, t, validator, tmproto.PrecommitType, cs.state.ChainID, types.BlockID{
Hash: propBlock.Hash(),
PartSetHeader: blockParts.Header(),
})
if !testCase.includeExtensions {
signedVote.Extension = nil
signedVote.ExtensionSignature = nil
}
added, err := voteSet.AddVote(signedVote)
require.NoError(t, err)
require.True(t, added)
cs.blockStore.SaveBlock(propBlock, blockParts, voteSet.MakeExtendedCommit())
reactor := NewReactor(
log.NewNopLogger(),
cs,
nil,
nil,
cs.eventBus,
true,
NopMetrics(),
)
if testCase.shouldPanic {
assert.Panics(t, func() {
reactor.SwitchToConsensus(ctx, cs.state, false)
})
} else {
reactor.SwitchToConsensus(ctx, cs.state, false)
}
})
}
}
func TestReactorRecordsVotesAndBlockParts(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

View File

@@ -297,7 +297,7 @@ type simulatorTestSuite struct {
GenesisState sm.State
Config *config.Config
Chain []*types.Block
Commits []*types.Commit
ExtCommits []*types.ExtendedCommit
CleanupFunc cleanupFunc
Mempool mempool.Mempool
@@ -578,11 +578,11 @@ func setupSimulator(ctx context.Context, t *testing.T) *simulatorTestSuite {
}
ensureNewRound(t, newRoundCh, height+1, 0)
sim.Chain = make([]*types.Block, 0)
sim.Commits = make([]*types.Commit, 0)
sim.Chain = []*types.Block{}
sim.ExtCommits = []*types.ExtendedCommit{}
for i := 1; i <= numBlocks; i++ {
sim.Chain = append(sim.Chain, css[0].blockStore.LoadBlock(int64(i)))
sim.Commits = append(sim.Commits, css[0].blockStore.LoadBlockCommit(int64(i)))
sim.ExtCommits = append(sim.ExtCommits, css[0].blockStore.LoadBlockExtendedCommit(int64(i)))
}
return sim
@@ -679,7 +679,7 @@ func testHandshakeReplay(
testValidatorsChange bool,
) {
var chain []*types.Block
var commits []*types.Commit
var extCommits []*types.ExtendedCommit
var store *mockBlockStore
var stateDB dbm.DB
var genesisState sm.State
@@ -699,7 +699,7 @@ func testHandshakeReplay(
genesisState = sim.GenesisState
cfg = sim.Config
chain = append([]*types.Block{}, sim.Chain...) // copy chain
commits = sim.Commits
extCommits = sim.ExtCommits
store = newMockBlockStore(t, cfg, genesisState.ConsensusParams)
} else { // test single node
testConfig, err := ResetConfig(t.TempDir(), fmt.Sprintf("%s_%v_s", t.Name(), mode))
@@ -718,7 +718,7 @@ func testHandshakeReplay(
err = wal.Start(ctx)
require.NoError(t, err)
t.Cleanup(func() { cancel(); wal.Wait() })
chain, commits = makeBlockchainFromWAL(t, wal)
chain, extCommits = makeBlockchainFromWAL(t, wal)
pubKey, err := privVal.GetPubKey(ctx)
require.NoError(t, err)
stateDB, genesisState, store = stateAndStore(t, cfg, pubKey, kvstore.ProtocolVersion)
@@ -726,7 +726,7 @@ func testHandshakeReplay(
}
stateStore := sm.NewStore(stateDB)
store.chain = chain
store.commits = commits
store.extCommits = extCommits
state := genesisState.Copy()
// run the chain through state.ApplyBlock to build up the tendermint state
@@ -1034,7 +1034,7 @@ func (app *badApp) Commit(context.Context) (*abci.ResponseCommit, error) {
//--------------------------
// utils for making blocks
func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.Commit) {
func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.ExtendedCommit) {
t.Helper()
var height int64
@@ -1047,10 +1047,10 @@ func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.Comm
// log.Notice("Build a blockchain by reading from the WAL")
var (
blocks []*types.Block
commits []*types.Commit
thisBlockParts *types.PartSet
thisBlockCommit *types.Commit
blocks []*types.Block
extCommits []*types.ExtendedCommit
thisBlockParts *types.PartSet
thisBlockExtCommit *types.ExtendedCommit
)
dec := NewWALDecoder(gr)
@@ -1082,12 +1082,12 @@ func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.Comm
require.Equal(t, block.Height, height+1,
"read bad block from wal. got height %d, expected %d", block.Height, height+1)
commitHeight := thisBlockCommit.Height
commitHeight := thisBlockExtCommit.Height
require.Equal(t, commitHeight, height+1,
"commit doesnt match. got height %d, expected %d", commitHeight, height+1)
blocks = append(blocks, block)
commits = append(commits, thisBlockCommit)
extCommits = append(extCommits, thisBlockExtCommit)
height++
}
case *types.PartSetHeader:
@@ -1097,8 +1097,12 @@ func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.Comm
require.NoError(t, err)
case *types.Vote:
if p.Type == tmproto.PrecommitType {
thisBlockCommit = types.NewCommit(p.Height, p.Round,
p.BlockID, []types.CommitSig{p.CommitSig()})
thisBlockExtCommit = &types.ExtendedCommit{
Height: p.Height,
Round: p.Round,
BlockID: p.BlockID,
ExtendedSignatures: []types.ExtendedCommitSig{p.ExtendedCommitSig()},
}
}
}
}
@@ -1113,12 +1117,12 @@ func makeBlockchainFromWAL(t *testing.T, wal WAL) ([]*types.Block, []*types.Comm
require.NoError(t, err)
require.Equal(t, block.Height, height+1, "read bad block from wal. got height %d, expected %d", block.Height, height+1)
commitHeight := thisBlockCommit.Height
commitHeight := thisBlockExtCommit.Height
require.Equal(t, commitHeight, height+1, "commit does not match. got height %d, expected %d", commitHeight, height+1)
blocks = append(blocks, block)
commits = append(commits, thisBlockCommit)
return blocks, commits
extCommits = append(extCommits, thisBlockExtCommit)
return blocks, extCommits
}
func readPieceFromWAL(msg *TimedWALMessage) interface{} {
@@ -1162,14 +1166,16 @@ func stateAndStore(
// mock block store
type mockBlockStore struct {
cfg *config.Config
params types.ConsensusParams
chain []*types.Block
commits []*types.Commit
base int64
t *testing.T
cfg *config.Config
params types.ConsensusParams
chain []*types.Block
extCommits []*types.ExtendedCommit
base int64
t *testing.T
}
var _ sm.BlockStore = &mockBlockStore{}
// TODO: NewBlockStore(db.NewMemDB) ...
func newMockBlockStore(t *testing.T, cfg *config.Config, params types.ConsensusParams) *mockBlockStore {
return &mockBlockStore{
@@ -1198,20 +1204,24 @@ func (bs *mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
}
}
func (bs *mockBlockStore) LoadBlockPart(height int64, index int) *types.Part { return nil }
func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
func (bs *mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit) {
}
func (bs *mockBlockStore) LoadBlockCommit(height int64) *types.Commit {
return bs.commits[height-1]
return bs.extCommits[height-1].StripExtensions()
}
func (bs *mockBlockStore) LoadSeenCommit() *types.Commit {
return bs.commits[len(bs.commits)-1]
return bs.extCommits[len(bs.extCommits)-1].StripExtensions()
}
func (bs *mockBlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit {
return bs.extCommits[height-1]
}
func (bs *mockBlockStore) PruneBlocks(height int64) (uint64, error) {
pruned := uint64(0)
for i := int64(0); i < height-1; i++ {
bs.chain[i] = nil
bs.commits[i] = nil
bs.extCommits[i] = nil
pruned++
}
bs.base = height

View File

@@ -695,24 +695,48 @@ func (cs *State) sendInternalMessage(ctx context.Context, mi msgInfo) {
// Reconstruct LastCommit from SeenCommit, which we saved along with the block,
// (which happens even before saving the state)
func (cs *State) reconstructLastCommit(state sm.State) {
requireExtensions := requireVoteExtensions(cs.state.ConsensusParams.Vote.ExtensionRequireHeight, state.LastBlockHeight)
votes, err := cs.votesFromExtendedCommit(state, requireExtensions)
if err == nil {
cs.LastCommit = votes
return
}
if requireExtensions {
panic(fmt.Sprintf("failed to reconstruct last commit; %s", err))
}
votes, err = cs.votesFromSeenCommit(state)
if err != nil {
panic(fmt.Sprintf("failed to reconstruct last commit; %s", err))
}
cs.LastCommit = votes
}
func (cs *State) votesFromExtendedCommit(state sm.State, requireExtensions bool) (*types.VoteSet, error) {
ec := cs.blockStore.LoadBlockExtendedCommit(state.LastBlockHeight)
if ec == nil {
return nil, fmt.Errorf("commit for height %v not found", state.LastBlockHeight)
}
vs := ec.ToVoteSet(state.ChainID, state.LastValidators, requireExtensions)
if !vs.HasTwoThirdsMajority() {
return nil, errors.New("seen commit does not have +2/3 majority")
}
return vs, nil
}
func (cs *State) votesFromSeenCommit(state sm.State) (*types.VoteSet, error) {
commit := cs.blockStore.LoadSeenCommit()
if commit == nil || commit.Height != state.LastBlockHeight {
commit = cs.blockStore.LoadBlockCommit(state.LastBlockHeight)
}
if commit == nil {
panic(fmt.Sprintf(
"failed to reconstruct last commit; commit for height %v not found",
state.LastBlockHeight,
))
return nil, fmt.Errorf("commit for height %v not found", state.LastBlockHeight)
}
lastPrecommits := types.CommitToVoteSet(state.ChainID, commit, state.LastValidators)
if !lastPrecommits.HasTwoThirdsMajority() {
panic("failed to reconstruct last commit; does not have +2/3 maj")
vs := commit.ToVoteSet(state.ChainID, state.LastValidators)
if !vs.HasTwoThirdsMajority() {
return nil, errors.New("commit does not have +2/3 majority")
}
cs.LastCommit = lastPrecommits
return vs, nil
}
// Updates State and increments height to match that of state.
@@ -814,7 +838,7 @@ func (cs *State) updateToState(state sm.State) {
cs.ValidRound = -1
cs.ValidBlock = nil
cs.ValidBlockParts = nil
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators, requireVoteExtensions(state.ConsensusParams.Vote.ExtensionRequireHeight, height))
cs.CommitRound = -1
cs.LastValidators = state.LastValidators
cs.TriggeredTimeoutPrecommit = false
@@ -1401,16 +1425,17 @@ func (cs *State) createProposalBlock(ctx context.Context) (*types.Block, error)
return nil, errors.New("entered createProposalBlock with privValidator being nil")
}
var commit *types.Commit
// TODO(sergio): wouldn't it be easier if CreateProposalBlock accepted cs.LastCommit directly?
var lastExtCommit *types.ExtendedCommit
switch {
case cs.Height == cs.state.InitialHeight:
// We're creating a proposal for the first block.
// The commit is empty, but not nil.
commit = types.NewCommit(0, 0, types.BlockID{}, nil)
lastExtCommit = &types.ExtendedCommit{}
case cs.LastCommit.HasTwoThirdsMajority():
// Make the commit from LastCommit
commit = cs.LastCommit.MakeCommit()
lastExtCommit = cs.LastCommit.MakeExtendedCommit()
default: // This shouldn't happen.
cs.logger.Error("propose step; cannot propose anything without commit for the previous block")
@@ -1426,7 +1451,7 @@ func (cs *State) createProposalBlock(ctx context.Context) (*types.Block, error)
proposerAddr := cs.privValidatorPubKey.Address()
ret, err := cs.blockExec.CreateProposalBlock(ctx, cs.Height, cs.state, commit, proposerAddr, cs.LastCommit.GetVotes())
ret, err := cs.blockExec.CreateProposalBlock(ctx, cs.Height, cs.state, lastExtCommit, proposerAddr)
if err != nil {
panic(err)
}
@@ -1925,8 +1950,7 @@ func (cs *State) finalizeCommit(ctx context.Context, height int64) {
// NOTE: the seenCommit is local justification to commit this block,
// but may differ from the LastCommit included in the next block
precommits := cs.Votes.Precommits(cs.CommitRound)
seenCommit := precommits.MakeCommit()
cs.blockStore.SaveBlock(block, blockParts, seenCommit)
cs.blockStore.SaveBlock(block, blockParts, precommits.MakeExtendedCommit())
} else {
// Happens during replay if we already saved the block but didn't commit
logger.Debug("calling finalizeCommit on already stored block", "height", block.Height)
@@ -2028,7 +2052,7 @@ func (cs *State) RecordMetrics(height int64, block *types.Block) {
for i, val := range cs.LastValidators.Validators {
commitSig := block.LastCommit.Signatures[i]
if commitSig.Absent() {
if commitSig.BlockIDFlag == types.BlockIDFlagAbsent {
missingValidators++
missingValidatorsPower += val.VotingPower
}
@@ -2038,7 +2062,7 @@ func (cs *State) RecordMetrics(height int64, block *types.Block) {
"validator_address", val.Address.String(),
}
cs.metrics.ValidatorPower.With(label...).Set(float64(val.VotingPower))
if commitSig.ForBlock() {
if commitSig.BlockIDFlag == types.BlockIDFlagCommit {
cs.metrics.ValidatorLastSignedHeight.With(label...).Set(float64(height))
} else {
cs.metrics.ValidatorMissedBlocks.With(label...).Add(float64(1))
@@ -2312,6 +2336,7 @@ func (cs *State) addVote(
return
}
// late votes still _must_ have extensions.
added, err = cs.LastCommit.AddVote(vote)
if !added {
return
@@ -2341,12 +2366,33 @@ func (cs *State) addVote(
return
}
// Verify VoteExtension if precommit
if vote.Type == tmproto.PrecommitType {
err := cs.blockExec.VerifyVoteExtension(ctx, vote)
cs.metrics.MarkVoteExtensionReceived(err == nil)
if err != nil {
return false, err
// Verify VoteExtension if precommit and not nil
// https://github.com/tendermint/tendermint/issues/8487
if vote.Type == tmproto.PrecommitType && !vote.BlockID.IsNil() &&
!bytes.Equal(vote.ValidatorAddress, cs.privValidatorPubKey.Address()) {
// The core fields of the vote message were already validated in the
// consensus reactor when the vote was received.
// Here, we valdiate that the vote extension was included in the vote
// message.
// Chains that are not configured to require vote extensions
// will consider the vote valid even if the extension is absent.
// VerifyVoteExtension will not be called in this case if the extension
// is absent.
err := vote.EnsureExtension()
if err == nil {
_, val := cs.state.Validators.GetByIndex(vote.ValidatorIndex)
err = vote.VerifyWithExtension(cs.state.ChainID, val.PubKey)
}
if err == nil {
err := cs.blockExec.VerifyVoteExtension(ctx, vote)
cs.metrics.MarkVoteExtensionReceived(err == nil)
} else {
if !errors.Is(err, types.ErrVoteExtensionAbsent) {
return false, err
}
if requireVoteExtensions(cs.state.ConsensusParams.Vote.ExtensionRequireHeight, cs.Height) {
return false, err
}
}
}
@@ -2497,15 +2543,15 @@ func (cs *State) signVote(
// use our local precommit Timeout as the max wait time for getting a singed commit. The same goes for prevote.
timeout := cs.voteTimeout(cs.Round)
switch msgType {
case tmproto.PrecommitType:
// if the signedMessage type is for a precommit, add VoteExtension
if msgType == tmproto.PrecommitType && !vote.BlockID.IsNil() {
// if the signedMessage type is for a non-nil precommit, add
// VoteExtension
ext, err := cs.blockExec.ExtendVote(ctx, vote)
if err != nil {
return nil, err
}
vote.Extension = ext
default:
} else {
timeout = time.Second
}
@@ -2744,6 +2790,13 @@ func (cs *State) calculateProposalTimestampDifferenceMetric() {
}
}
func requireVoteExtensions(requireHeight, currentHeight int64) bool {
if requireHeight == 0 || currentHeight < requireHeight {
return false
}
return true
}
// proposerWaitTime determines how long the proposer should wait to propose its next block.
// If the result is zero, a block can be proposed immediately.
//

View File

@@ -1950,7 +1950,7 @@ func TestFinalizeBlockCalled(t *testing.T) {
expectCalled bool
}{
{
name: "finalze block called when block committed",
name: "finalize block called when block committed",
voteNil: false,
expectCalled: true,
},
@@ -1970,11 +1970,15 @@ func TestFinalizeBlockCalled(t *testing.T) {
Status: abci.ResponseProcessProposal_ACCEPT,
}, nil)
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
}, nil)
// We only expect VerifyVoteExtension to be called on non-nil precommits.
// https://github.com/tendermint/tendermint/issues/8487
if !testCase.voteNil {
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{}, nil)
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
}, nil)
}
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{}, nil)
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m})
@@ -2072,19 +2076,13 @@ func TestExtendVoteCalled(t *testing.T) {
Hash: blockID.Hash,
})
m.AssertCalled(t, "VerifyVoteExtension", ctx, &abci.RequestVerifyVoteExtension{
Hash: blockID.Hash,
ValidatorAddress: addr,
Height: height,
VoteExtension: []byte("extension"),
})
signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), blockID, vss[1:]...)
ensureNewRound(t, newRoundCh, height+1, 0)
m.AssertExpectations(t)
// Only 3 of the vote extensions are seen, as consensus proceeds as soon as the +2/3 threshold
// is observed by the consensus engine.
for _, pv := range vss[:3] {
for _, pv := range vss[1:3] {
pv, err := pv.GetPubKey(ctx)
require.NoError(t, err)
addr := pv.Address()
@@ -2144,13 +2142,6 @@ func TestVerifyVoteExtensionNotCalledOnAbsentPrecommit(t *testing.T) {
Hash: blockID.Hash,
})
m.AssertCalled(t, "VerifyVoteExtension", mock.Anything, &abci.RequestVerifyVoteExtension{
Hash: blockID.Hash,
ValidatorAddress: addr,
Height: height,
VoteExtension: []byte("extension"),
})
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), blockID, vss[2:]...)
ensureNewRound(t, newRoundCh, height+1, 0)
@@ -2262,6 +2253,118 @@ func TestPrepareProposalReceivesVoteExtensions(t *testing.T) {
}
}
// TestVoteExtensionRequiredHeight tests that 'ExtensionRequireHeight' correctly
// enforces that vote extensions be present in consensus for heights greater than
// or equal to the configured value.
func TestVoteExtensionRequiredHeight(t *testing.T) {
for _, testCase := range []struct {
name string
initialRequiredHeight int64
hasExtension bool
expectSuccessfulRound bool
}{
{
name: "extension present but not required",
hasExtension: true,
initialRequiredHeight: 0,
expectSuccessfulRound: true,
},
{
name: "extension absent but not required",
hasExtension: false,
initialRequiredHeight: 0,
expectSuccessfulRound: true,
},
{
name: "extension present and required",
hasExtension: true,
initialRequiredHeight: 1,
expectSuccessfulRound: true,
},
{
name: "extension absent but required",
hasExtension: false,
initialRequiredHeight: 1,
expectSuccessfulRound: false,
},
{
name: "extension absent but required in future height",
hasExtension: false,
initialRequiredHeight: 2,
expectSuccessfulRound: true,
},
} {
t.Run(testCase.name, func(t *testing.T) {
config := configSetup(t)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
numValidators := 3
m := abcimocks.NewApplication(t)
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{
Status: abci.ResponseProcessProposal_ACCEPT,
}, nil)
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{}, nil)
if testCase.hasExtension {
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
}, nil).Times(numValidators - 1)
}
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m, validators: numValidators})
cs1.state.ConsensusParams.Vote.ExtensionRequireHeight = testCase.initialRequiredHeight
height, round := cs1.Height, cs1.Round
timeoutCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryTimeoutPropose)
proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal)
newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound)
pv1, err := cs1.privValidator.GetPubKey(ctx)
require.NoError(t, err)
addr := pv1.Address()
voteCh := subscribeToVoter(ctx, t, cs1, addr)
startTestRound(ctx, cs1, cs1.Height, round)
ensureNewRound(t, newRoundCh, height, round)
ensureNewProposal(t, proposalCh, height, round)
rs := cs1.GetRoundState()
blockID := types.BlockID{
Hash: rs.ProposalBlock.Hash(),
PartSetHeader: rs.ProposalBlockParts.Header(),
}
// sign all of the votes
signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[1:]...)
ensurePrevoteMatch(t, voteCh, height, round, rs.ProposalBlock.Hash())
var ext []byte
if testCase.hasExtension {
ext = []byte("extension")
}
for _, vs := range vss[1:] {
vote, err := vs.signVote(ctx, tmproto.PrecommitType, config.ChainID(), blockID, ext)
if !testCase.hasExtension {
vote.ExtensionSignature = nil
}
require.NoError(t, err)
addVotes(cs1, vote)
}
if testCase.expectSuccessfulRound {
ensurePrecommit(t, voteCh, height, round)
height++
ensureNewRound(t, newRoundCh, height, round)
} else {
ensureNoNewTimeout(t, timeoutCh, cs1.state.ConsensusParams.Timeout.VoteTimeout(round).Nanoseconds())
}
m.AssertExpectations(t)
})
}
}
// 4 vals, 3 Nil Precommits at P0
// What we want:
// P0 waits for timeoutPrecommit before starting next round

View File

@@ -38,9 +38,10 @@ We let each peer provide us with up to 2 unexpected "catchup" rounds.
One for their LastCommit round, and another for the official commit round.
*/
type HeightVoteSet struct {
chainID string
height int64
valSet *types.ValidatorSet
chainID string
height int64
valSet *types.ValidatorSet
requireExtensions bool
mtx sync.Mutex
round int32 // max tracked round
@@ -48,9 +49,10 @@ type HeightVoteSet struct {
peerCatchupRounds map[types.NodeID][]int32 // keys: peer.ID; values: at most 2 rounds
}
func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet {
func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet, requireExtensions bool) *HeightVoteSet {
hvs := &HeightVoteSet{
chainID: chainID,
chainID: chainID,
requireExtensions: requireExtensions,
}
hvs.Reset(height, valSet)
return hvs
@@ -107,8 +109,8 @@ func (hvs *HeightVoteSet) addRound(round int32) {
panic("addRound() for an existing round")
}
// log.Debug("addRound(round)", "round", round)
prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrevoteType, hvs.valSet)
precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet)
prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrevoteType, hvs.valSet, hvs.requireExtensions)
precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet, hvs.requireExtensions)
hvs.roundVoteSets[round] = RoundVoteSet{
Prevotes: prevotes,
Precommits: precommits,

View File

@@ -27,7 +27,7 @@ func TestPeerCatchupRounds(t *testing.T) {
valSet, privVals := factory.ValidatorSet(ctx, t, 10, 1)
chainID := cfg.ChainID()
hvs := NewHeightVoteSet(chainID, 1, valSet)
hvs := NewHeightVoteSet(chainID, 1, valSet, false)
vote999_0 := makeVoteHR(ctx, t, 1, 0, 999, privVals, chainID)
added, err := hvs.AddVote(vote999_0, "peer1")

View File

@@ -249,8 +249,8 @@ func TestEvidencePoolUpdate(t *testing.T) {
evidenceChainID,
)
require.NoError(t, err)
lastCommit := makeCommit(height, val.PrivKey.PubKey().Address())
block := types.MakeBlock(height+1, []types.Tx{}, lastCommit, []types.Evidence{ev})
lastExtCommit := makeExtCommit(height, val.PrivKey.PubKey().Address())
block := types.MakeBlock(height+1, []types.Tx{}, lastExtCommit.StripExtensions(), []types.Evidence{ev})
// update state (partially)
state.LastBlockHeight = height + 1
@@ -568,8 +568,8 @@ func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) (*store.Blo
blockStore := store.NewBlockStore(db)
for i := int64(1); i <= state.LastBlockHeight; i++ {
lastCommit := makeCommit(i-1, valAddr)
block := sf.MakeBlock(state, i, lastCommit)
lastCommit := makeExtCommit(i-1, valAddr)
block := sf.MakeBlock(state, i, lastCommit.StripExtensions())
block.Header.Time = defaultEvidenceTime.Add(time.Duration(i) * time.Minute)
block.Header.Version = version.Consensus{Block: version.BlockProtocol, App: 1}
@@ -579,22 +579,25 @@ func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) (*store.Blo
return nil, err
}
seenCommit := makeCommit(i, valAddr)
seenCommit := makeExtCommit(i, valAddr)
blockStore.SaveBlock(block, partSet, seenCommit)
}
return blockStore, nil
}
func makeCommit(height int64, valAddr []byte) *types.Commit {
commitSigs := []types.CommitSig{{
BlockIDFlag: types.BlockIDFlagCommit,
ValidatorAddress: valAddr,
Timestamp: defaultEvidenceTime,
Signature: []byte("Signature"),
}}
return types.NewCommit(height, 0, types.BlockID{}, commitSigs)
func makeExtCommit(height int64, valAddr []byte) *types.ExtendedCommit {
return &types.ExtendedCommit{
Height: height,
ExtendedSignatures: []types.ExtendedCommitSig{{
CommitSig: types.CommitSig{
BlockIDFlag: types.BlockIDFlagCommit,
ValidatorAddress: valAddr,
Timestamp: defaultEvidenceTime,
Signature: []byte("Signature"),
},
}},
}
}
func defaultTestPool(ctx context.Context, t *testing.T, height int64) (*evidence.Pool, types.MockPV, *eventbus.EventBus) {

View File

@@ -233,9 +233,10 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) {
// we are simulating a duplicate vote attack where all the validators in the conflictingVals set
// except the last validator vote twice
blockID := factory.MakeBlockIDWithHash(conflictingHeader.Hash())
voteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals)
commit, err := factory.MakeCommit(ctx, blockID, 10, 1, voteSet, conflictingPrivVals[:4], defaultEvidenceTime)
voteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals, false)
extCommit, err := factory.MakeExtendedCommit(ctx, blockID, 10, 1, voteSet, conflictingPrivVals[:4], defaultEvidenceTime)
require.NoError(t, err)
commit := extCommit.StripExtensions()
ev := &types.LightClientAttackEvidence{
ConflictingBlock: &types.LightBlock{
@@ -252,10 +253,11 @@ func TestVerifyLightClientAttack_Equivocation(t *testing.T) {
}
trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash"))
trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals)
trustedCommit, err := factory.MakeCommit(ctx, trustedBlockID, 10, 1,
trustedVoteSet := types.NewVoteSet(evidenceChainID, 10, 1, tmproto.SignedMsgType(2), conflictingVals, false)
trustedExtCommit, err := factory.MakeExtendedCommit(ctx, trustedBlockID, 10, 1,
trustedVoteSet, conflictingPrivVals, defaultEvidenceTime)
require.NoError(t, err)
trustedCommit := trustedExtCommit.StripExtensions()
trustedSignedHeader := &types.SignedHeader{
Header: trustedHeader,
@@ -334,9 +336,10 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) {
// we are simulating an amnesia attack where all the validators in the conflictingVals set
// except the last validator vote twice. However this time the commits are of different rounds.
blockID := makeBlockID(conflictingHeader.Hash(), 1000, []byte("partshash"))
voteSet := types.NewVoteSet(evidenceChainID, height, 0, tmproto.SignedMsgType(2), conflictingVals)
commit, err := factory.MakeCommit(ctx, blockID, height, 0, voteSet, conflictingPrivVals, defaultEvidenceTime)
voteSet := types.NewVoteSet(evidenceChainID, height, 0, tmproto.SignedMsgType(2), conflictingVals, false)
extCommit, err := factory.MakeExtendedCommit(ctx, blockID, height, 0, voteSet, conflictingPrivVals, defaultEvidenceTime)
require.NoError(t, err)
commit := extCommit.StripExtensions()
ev := &types.LightClientAttackEvidence{
ConflictingBlock: &types.LightBlock{
@@ -353,10 +356,11 @@ func TestVerifyLightClientAttack_Amnesia(t *testing.T) {
}
trustedBlockID := makeBlockID(trustedHeader.Hash(), 1000, []byte("partshash"))
trustedVoteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals)
trustedCommit, err := factory.MakeCommit(ctx, trustedBlockID, height, 1,
trustedVoteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals, false)
trustedExtCommit, err := factory.MakeExtendedCommit(ctx, trustedBlockID, height, 1,
trustedVoteSet, conflictingPrivVals, defaultEvidenceTime)
require.NoError(t, err)
trustedCommit := trustedExtCommit.StripExtensions()
trustedSignedHeader := &types.SignedHeader{
Header: trustedHeader,
@@ -549,9 +553,10 @@ func makeLunaticEvidence(
})
blockID := factory.MakeBlockIDWithHash(conflictingHeader.Hash())
voteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals)
commit, err := factory.MakeCommit(ctx, blockID, height, 1, voteSet, conflictingPrivVals, defaultEvidenceTime)
voteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), conflictingVals, false)
extCommit, err := factory.MakeExtendedCommit(ctx, blockID, height, 1, voteSet, conflictingPrivVals, defaultEvidenceTime)
require.NoError(t, err)
commit := extCommit.StripExtensions()
ev = &types.LightClientAttackEvidence{
ConflictingBlock: &types.LightBlock{
@@ -577,9 +582,10 @@ func makeLunaticEvidence(
}
trustedBlockID := factory.MakeBlockIDWithHash(trustedHeader.Hash())
trustedVals, privVals := factory.ValidatorSet(ctx, t, totalVals, defaultVotingPower)
trustedVoteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), trustedVals)
trustedCommit, err := factory.MakeCommit(ctx, trustedBlockID, height, 1, trustedVoteSet, privVals, defaultEvidenceTime)
trustedVoteSet := types.NewVoteSet(evidenceChainID, height, 1, tmproto.SignedMsgType(2), trustedVals, false)
trustedExtCommit, err := factory.MakeExtendedCommit(ctx, trustedBlockID, height, 1, trustedVoteSet, privVals, defaultEvidenceTime)
require.NoError(t, err)
trustedCommit := trustedExtCommit.StripExtensions()
trusted = &types.LightBlock{
SignedHeader: &types.SignedHeader{

View File

@@ -1,13 +1,14 @@
package state
import (
"bytes"
"context"
"errors"
"fmt"
"time"
abciclient "github.com/tendermint/tendermint/abci/client"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/encoding"
"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/internal/eventbus"
@@ -87,9 +88,8 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
ctx context.Context,
height int64,
state State,
commit *types.Commit,
lastExtCommit *types.ExtendedCommit,
proposerAddr []byte,
votes []*types.Vote,
) (*types.Block, error) {
maxBytes := state.ConsensusParams.Block.MaxBytes
@@ -101,15 +101,14 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
maxDataBytes := types.MaxDataBytes(maxBytes, evSize, state.Validators.Size())
txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGas)
commit := lastExtCommit.StripExtensions()
block := state.MakeBlock(height, txs, commit, evidence, proposerAddr)
localLastCommit := buildLastCommitInfo(block, blockExec.store, state.InitialHeight)
rpp, err := blockExec.appClient.PrepareProposal(
ctx,
&abci.RequestPrepareProposal{
MaxTxBytes: maxDataBytes,
Txs: block.Txs.ToSliceOfBytes(),
LocalLastCommit: extendedCommitInfo(localLastCommit, votes),
LocalLastCommit: buildExtendedCommitInfo(lastExtCommit, blockExec.store, state.InitialHeight, state.ConsensusParams.Vote.ExtensionRequireHeight),
ByzantineValidators: block.Evidence.ToABCI(),
Height: block.Height,
Time: block.Time,
@@ -322,7 +321,7 @@ func (blockExec *BlockExecutor) VerifyVoteExtension(ctx context.Context, vote *t
}
if !resp.IsOK() {
return types.ErrVoteInvalidExtension
return errors.New("invalid vote extension")
}
return nil
@@ -381,14 +380,14 @@ func (blockExec *BlockExecutor) Commit(
func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) abci.CommitInfo {
if block.Height == initialHeight {
// there is no last commmit for the initial height.
// there is no last commit for the initial height.
// return an empty value.
return abci.CommitInfo{}
}
lastValSet, err := store.LoadValidators(block.Height - 1)
if err != nil {
panic(err)
panic(fmt.Errorf("failed to load validator set at height %d: %w", block.Height-1, err))
}
var (
@@ -410,7 +409,7 @@ func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) a
commitSig := block.LastCommit.Signatures[i]
votes[i] = abci.VoteInfo{
Validator: types.TM2PB.Validator(val),
SignedLastBlock: !commitSig.Absent(),
SignedLastBlock: commitSig.BlockIDFlag != types.BlockIDFlagAbsent,
}
}
@@ -420,44 +419,74 @@ func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) a
}
}
// extendedCommitInfo expects a CommitInfo struct along with all of the
// original votes relating to that commit, including their vote extensions. The
// order of votes does not matter.
func extendedCommitInfo(c abci.CommitInfo, votes []*types.Vote) abci.ExtendedCommitInfo {
if len(c.Votes) != len(votes) {
panic(fmt.Sprintf("extendedCommitInfo: number of votes from commit differ from the number of votes supplied (%d != %d)", len(c.Votes), len(votes)))
// buildExtendedCommitInfo populates an ABCI extended commit from the
// corresponding Tendermint extended commit ec, using the stored validator set
// from ec. It requires ec to include the original precommit votes along with
// the vote extensions from the last commit.
//
// For heights below the initial height, for which we do not have the required
// data, it returns an empty record.
//
// Assumes that the commit signatures are sorted according to validator index.
func buildExtendedCommitInfo(ec *types.ExtendedCommit, store Store, initialHeight, extensionRequireHeight int64) abci.ExtendedCommitInfo {
if ec.Height < initialHeight {
// There are no extended commits for heights below the initial height.
return abci.ExtendedCommitInfo{}
}
votesByVal := make(map[string]*types.Vote)
for _, vote := range votes {
if vote != nil {
valAddr := vote.ValidatorAddress.String()
if _, ok := votesByVal[valAddr]; ok {
panic(fmt.Sprintf("extendedCommitInfo: found duplicate vote for validator with address %s", valAddr))
}
votesByVal[valAddr] = vote
valSet, err := store.LoadValidators(ec.Height)
if err != nil {
panic(fmt.Errorf("failed to load validator set at height %d, initial height %d: %w", ec.Height, initialHeight, err))
}
var (
ecSize = ec.Size()
valSetLen = len(valSet.Validators)
)
// Ensure that the size of the validator set in the extended commit matches
// the size of the validator set in the state store.
if ecSize != valSetLen {
panic(fmt.Errorf(
"extended commit size (%d) does not match validator set length (%d) at height %d\n\n%v\n\n%v",
ecSize, valSetLen, ec.Height, ec.ExtendedSignatures, valSet.Validators,
))
}
votes := make([]abci.ExtendedVoteInfo, ecSize)
for i, val := range valSet.Validators {
ecs := ec.ExtendedSignatures[i]
// Absent signatures have empty validator addresses, but otherwise we
// expect the validator addresses to be the same.
if ecs.BlockIDFlag != types.BlockIDFlagAbsent && !bytes.Equal(ecs.ValidatorAddress, val.Address) {
panic(fmt.Errorf("validator address of extended commit signature in position %d (%s) does not match the corresponding validator's at height %d (%s)",
i, ecs.ValidatorAddress, ec.Height, val.Address,
))
}
}
vs := make([]abci.ExtendedVoteInfo, len(c.Votes))
for i := range vs {
var ext []byte
// votes[i] will be nil if c.Votes[i].SignedLastBlock is false
if c.Votes[i].SignedLastBlock {
valAddr := crypto.Address(c.Votes[i].Validator.Address).String()
vote, ok := votesByVal[valAddr]
if !ok || vote == nil {
panic(fmt.Sprintf("extendedCommitInfo: validator with address %s signed last block, but could not find vote for it", valAddr))
if ecs.BlockIDFlag == types.BlockIDFlagCommit {
// We only care about vote extensions if a validator has voted to
// commit.
if ecs.Extension == nil && ecs.ExtensionSignature == nil &&
extensionRequireHeight != 0 && ec.Height >= extensionRequireHeight {
// TODO: this error is akward, make it better
panic(fmt.Errorf("commit received with missing vote extension data"))
}
ext = vote.Extension
ext = ecs.Extension
}
vs[i] = abci.ExtendedVoteInfo{
Validator: c.Votes[i].Validator,
SignedLastBlock: c.Votes[i].SignedLastBlock,
votes[i] = abci.ExtendedVoteInfo{
Validator: types.TM2PB.Validator(val),
SignedLastBlock: ecs.BlockIDFlag != types.BlockIDFlagAbsent,
VoteExtension: ext,
}
}
return abci.ExtendedCommitInfo{
Round: c.Round,
Votes: vs,
Round: ec.Round,
Votes: votes,
}
}

View File

@@ -79,9 +79,10 @@ func TestApplyBlock(t *testing.T) {
assert.EqualValues(t, 1, state.Version.Consensus.App, "App version wasn't updated")
}
// TestFinalizeBlockDecidedLastCommit ensures we correctly send the DecidedLastCommit to the
// application. The test ensures that the DecidedLastCommit properly reflects
// which validators signed the preceding block.
// TestFinalizeBlockDecidedLastCommit ensures we correctly send the
// DecidedLastCommit to the application. The test ensures that the
// DecidedLastCommit properly reflects which validators signed the preceding
// block.
func TestFinalizeBlockDecidedLastCommit(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -96,7 +97,7 @@ func TestFinalizeBlockDecidedLastCommit(t *testing.T) {
state, stateDB, privVals := makeState(t, 7, 1)
stateStore := sm.NewStore(stateDB)
absentSig := types.NewCommitSigAbsent()
absentSig := types.NewExtendedCommitSigAbsent()
testCases := []struct {
name string
@@ -134,12 +135,12 @@ func TestFinalizeBlockDecidedLastCommit(t *testing.T) {
for idx, isAbsent := range tc.absentCommitSigs {
if isAbsent {
lastCommit.Signatures[idx] = absentSig
lastCommit.ExtendedSignatures[idx] = absentSig
}
}
// block for height 2
block := sf.MakeBlock(state, 2, lastCommit)
block := sf.MakeBlock(state, 2, lastCommit.StripExtensions())
bps, err := block.MakePartSet(testPartSize)
require.NoError(t, err)
blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()}
@@ -198,12 +199,15 @@ func TestFinalizeBlockByzantineValidators(t *testing.T) {
ConflictingBlock: &types.LightBlock{
SignedHeader: &types.SignedHeader{
Header: header,
Commit: types.NewCommit(10, 0, makeBlockID(header.Hash(), 100, []byte("partshash")), []types.CommitSig{{
BlockIDFlag: types.BlockIDFlagNil,
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
Timestamp: defaultEvidenceTime,
Signature: crypto.CRandBytes(types.MaxSignatureSize),
}}),
Commit: &types.Commit{
Height: 10,
BlockID: makeBlockID(header.Hash(), 100, []byte("partshash")),
Signatures: []types.CommitSig{{
BlockIDFlag: types.BlockIDFlagNil,
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
Timestamp: defaultEvidenceTime,
Signature: crypto.CRandBytes(types.MaxSignatureSize)}},
},
},
ValidatorSet: state.Validators,
},
@@ -324,8 +328,10 @@ func TestProcessProposal(t *testing.T) {
lastCommitSig = append(lastCommitSig, vote.CommitSig())
}
lastCommit := types.NewCommit(height-1, 0, types.BlockID{}, lastCommitSig)
block1 := sf.MakeBlock(state, height, lastCommit)
block1 := sf.MakeBlock(state, height, &types.Commit{
Height: height - 1,
Signatures: lastCommitSig,
})
block1.Txs = txs
expectedRpp := &abci.RequestProcessProposal{
@@ -653,8 +659,8 @@ func TestEmptyPrepareProposal(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
_, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
_, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
require.NoError(t, err)
}
@@ -708,8 +714,8 @@ func TestPrepareProposalErrorOnNonExistingRemoved(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
require.ErrorContains(t, err, "new transaction incorrectly marked as removed")
require.Nil(t, block)
@@ -764,8 +770,8 @@ func TestPrepareProposalRemoveTxs(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
require.NoError(t, err)
require.Len(t, block.Data.Txs.ToSliceOfBytes(), len(trs)-2)
@@ -823,8 +829,8 @@ func TestPrepareProposalAddedTxsIncluded(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
require.NoError(t, err)
require.Equal(t, txs[0], block.Data.Txs[0])
@@ -879,8 +885,8 @@ func TestPrepareProposalReorderTxs(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
require.NoError(t, err)
for i, tx := range block.Data.Txs {
require.Equal(t, types.Tx(trs[i].Tx), tx)
@@ -939,9 +945,8 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
require.ErrorContains(t, err, "transaction data size exceeds maximum")
require.Nil(t, block, "")
@@ -991,9 +996,8 @@ func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) {
sm.NopMetrics(),
)
pa, _ := state.Validators.GetByIndex(0)
commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes)
commit, _ := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals)
block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa)
require.Nil(t, block)
require.ErrorContains(t, err, "an injected error")

View File

@@ -39,7 +39,7 @@ func makeAndCommitGoodBlock(
blockExec *sm.BlockExecutor,
privVals map[string]types.PrivValidator,
evidence []types.Evidence,
) (sm.State, types.BlockID, *types.Commit) {
) (sm.State, types.BlockID, *types.ExtendedCommit) {
t.Helper()
// A good block passes
@@ -82,19 +82,23 @@ func makeValidCommit(
blockID types.BlockID,
vals *types.ValidatorSet,
privVals map[string]types.PrivValidator,
) (*types.Commit, []*types.Vote) {
) (*types.ExtendedCommit, []*types.Vote) {
t.Helper()
sigs := make([]types.CommitSig, vals.Size())
sigs := make([]types.ExtendedCommitSig, vals.Size())
votes := make([]*types.Vote, vals.Size())
for i := 0; i < vals.Size(); i++ {
_, val := vals.GetByIndex(int32(i))
vote, err := factory.MakeVote(ctx, privVals[val.Address.String()], chainID, int32(i), height, 0, 2, blockID, time.Now())
require.NoError(t, err)
sigs[i] = vote.CommitSig()
sigs[i] = vote.ExtendedCommitSig()
votes[i] = vote
}
return types.NewCommit(height, 0, blockID, sigs), votes
return &types.ExtendedCommit{
Height: height,
BlockID: blockID,
ExtendedSignatures: sigs,
}, votes
}
func makeState(t *testing.T, nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) {

View File

@@ -6,6 +6,7 @@ import (
context "context"
mock "github.com/stretchr/testify/mock"
indexer "github.com/tendermint/tendermint/internal/state/indexer"
query "github.com/tendermint/tendermint/internal/pubsub/query"

View File

@@ -107,6 +107,22 @@ func (_m *BlockStore) LoadBlockCommit(height int64) *types.Commit {
return r0
}
// LoadBlockExtendedCommit provides a mock function with given fields: height
func (_m *BlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit {
ret := _m.Called(height)
var r0 *types.ExtendedCommit
if rf, ok := ret.Get(0).(func(int64) *types.ExtendedCommit); ok {
r0 = rf(height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ExtendedCommit)
}
}
return r0
}
// LoadBlockMeta provides a mock function with given fields: height
func (_m *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
ret := _m.Called(height)
@@ -193,7 +209,7 @@ func (_m *BlockStore) PruneBlocks(height int64) (uint64, error) {
}
// SaveBlock provides a mock function with given fields: block, blockParts, seenCommit
func (_m *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
func (_m *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit) {
_m.Called(block, blockParts, seenCommit)
}

View File

@@ -6,6 +6,7 @@ import (
context "context"
mock "github.com/stretchr/testify/mock"
state "github.com/tendermint/tendermint/internal/state"
testing "testing"

View File

@@ -4,6 +4,7 @@ package mocks
import (
mock "github.com/stretchr/testify/mock"
state "github.com/tendermint/tendermint/internal/state"
tendermintstate "github.com/tendermint/tendermint/proto/tendermint/state"

View File

@@ -26,7 +26,7 @@ type BlockStore interface {
LoadBlockMeta(height int64) *types.BlockMeta
LoadBlock(height int64) *types.Block
SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit)
SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit)
PruneBlocks(height int64) (uint64, error)
@@ -36,6 +36,7 @@ type BlockStore interface {
LoadBlockCommit(height int64) *types.Commit
LoadSeenCommit() *types.Commit
LoadBlockExtendedCommit(height int64) *types.ExtendedCommit
}
//-----------------------------------------------------------------------------

View File

@@ -26,6 +26,9 @@ const (
//------------------------------------------------------------------------
// NB: Before modifying these, cross-check them with those in
// internal/store/store.go
// TODO(thane): Move these and the ones in internal/store/store.go to their own package.
const (
// prefixes are unique across all tm db's
prefixValidators = int64(5)

View File

@@ -63,7 +63,7 @@ func makeBlockAndPartSet(
) (*types.Block, *types.PartSet) {
t.Helper()
lastCommit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
lastCommit := &types.Commit{Height: height - 1}
if height > 1 {
vote, err := factory.MakeVote(
ctx,
@@ -73,8 +73,12 @@ func makeBlockAndPartSet(
lastBlockMeta.BlockID,
time.Now())
require.NoError(t, err)
lastCommit = types.NewCommit(vote.Height, vote.Round,
lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()})
lastCommit = &types.Commit{
Height: vote.Height,
Round: vote.Round,
BlockID: lastBlock.LastBlockID,
Signatures: []types.CommitSig{vote.CommitSig()},
}
}
block := state.MakeBlock(height, []types.Tx{}, lastCommit, nil, state.Validators.GetProposer().Address)

View File

@@ -65,7 +65,8 @@ func TestValidateBlockHeader(t *testing.T) {
eventBus,
sm.NopMetrics(),
)
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
lastCommit := &types.Commit{}
var lastExtCommit *types.ExtendedCommit
// some bad values
wrongHash := crypto.Checksum([]byte("this hash is wrong"))
@@ -100,7 +101,7 @@ func TestValidateBlockHeader(t *testing.T) {
{"Proposer invalid", func(block *types.Block) { block.ProposerAddress = []byte("wrong size") }},
{"first LastCommit contains signatures", func(block *types.Block) {
block.LastCommit = types.NewCommit(0, 0, types.BlockID{}, []types.CommitSig{types.NewCommitSigAbsent()})
block.LastCommit = &types.Commit{Signatures: []types.CommitSig{types.NewCommitSigAbsent()}}
block.LastCommitHash = block.LastCommit.Hash()
}},
}
@@ -121,8 +122,9 @@ func TestValidateBlockHeader(t *testing.T) {
/*
A good block passes
*/
state, _, lastCommit = makeAndCommitGoodBlock(ctx, t,
state, _, lastExtCommit = makeAndCommitGoodBlock(ctx, t,
state, height, lastCommit, state.Validators.GetProposer().Address, blockExec, privVals, nil)
lastCommit = lastExtCommit.StripExtensions()
}
nextHeight := validationTestsStopHeight
@@ -169,8 +171,9 @@ func TestValidateBlockCommit(t *testing.T) {
eventBus,
sm.NopMetrics(),
)
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
wrongSigsCommit := types.NewCommit(1, 0, types.BlockID{}, nil)
lastCommit := &types.Commit{}
var lastExtCommit *types.ExtendedCommit
wrongSigsCommit := &types.Commit{Height: 1}
badPrivVal := types.NewMockPV()
for height := int64(1); height < validationTestsStopHeight; height++ {
@@ -192,12 +195,12 @@ func TestValidateBlockCommit(t *testing.T) {
time.Now(),
)
require.NoError(t, err)
wrongHeightCommit := types.NewCommit(
wrongHeightVote.Height,
wrongHeightVote.Round,
state.LastBlockID,
[]types.CommitSig{wrongHeightVote.CommitSig()},
)
wrongHeightCommit := &types.Commit{
Height: wrongHeightVote.Height,
Round: wrongHeightVote.Round,
BlockID: state.LastBlockID,
Signatures: []types.CommitSig{wrongHeightVote.CommitSig()},
}
block := statefactory.MakeBlock(state, height, wrongHeightCommit)
err = blockExec.ValidateBlock(ctx, state, block)
_, isErrInvalidCommitHeight := err.(types.ErrInvalidCommitHeight)
@@ -220,7 +223,7 @@ func TestValidateBlockCommit(t *testing.T) {
A good block passes
*/
var blockID types.BlockID
state, blockID, lastCommit = makeAndCommitGoodBlock(
state, blockID, lastExtCommit = makeAndCommitGoodBlock(
ctx,
t,
state,
@@ -231,6 +234,7 @@ func TestValidateBlockCommit(t *testing.T) {
privVals,
nil,
)
lastCommit = lastExtCommit.StripExtensions()
/*
wrongSigsCommit is fine except for the extra bad precommit
@@ -270,8 +274,12 @@ func TestValidateBlockCommit(t *testing.T) {
goodVote.Signature, badVote.Signature = g.Signature, b.Signature
wrongSigsCommit = types.NewCommit(goodVote.Height, goodVote.Round,
blockID, []types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()})
wrongSigsCommit = &types.Commit{
Height: goodVote.Height,
Round: goodVote.Round,
BlockID: blockID,
Signatures: []types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()},
}
}
}
@@ -319,7 +327,8 @@ func TestValidateBlockEvidence(t *testing.T) {
eventBus,
sm.NopMetrics(),
)
lastCommit := types.NewCommit(0, 0, types.BlockID{}, nil)
lastCommit := &types.Commit{}
var lastExtCommit *types.ExtendedCommit
for height := int64(1); height < validationTestsStopHeight; height++ {
proposerAddr := state.Validators.GetProposer().Address
@@ -364,7 +373,7 @@ func TestValidateBlockEvidence(t *testing.T) {
evidence = append(evidence, newEv)
}
state, _, lastCommit = makeAndCommitGoodBlock(
state, _, lastExtCommit = makeAndCommitGoodBlock(
ctx,
t,
state,
@@ -375,6 +384,7 @@ func TestValidateBlockEvidence(t *testing.T) {
privVals,
evidence,
)
lastCommit = lastExtCommit.StripExtensions()
}
}

View File

@@ -6,6 +6,7 @@ import (
context "context"
mock "github.com/stretchr/testify/mock"
state "github.com/tendermint/tendermint/internal/state"
testing "testing"

View File

@@ -855,13 +855,13 @@ func mockLB(ctx context.Context, t *testing.T, height int64, time time.Time, las
header.NextValidatorsHash = nextVals.Hash()
header.ConsensusHash = types.DefaultConsensusParams().HashConsensusParams()
lastBlockID = factory.MakeBlockIDWithHash(header.Hash())
voteSet := types.NewVoteSet(factory.DefaultTestChainID, height, 0, tmproto.PrecommitType, currentVals)
commit, err := factory.MakeCommit(ctx, lastBlockID, height, 0, voteSet, currentPrivVals, time)
voteSet := types.NewVoteSet(factory.DefaultTestChainID, height, 0, tmproto.PrecommitType, currentVals, false)
extCommit, err := factory.MakeExtendedCommit(ctx, lastBlockID, height, 0, voteSet, currentPrivVals, time)
require.NoError(t, err)
return nextVals, nextPrivVals, &types.LightBlock{
SignedHeader: &types.SignedHeader{
Header: header,
Commit: commit,
Commit: extCommit.StripExtensions(),
},
ValidatorSet: currentVals,
}

View File

@@ -273,11 +273,31 @@ func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
}
commit, err := types.CommitFromProto(pbc)
if err != nil {
panic(fmt.Errorf("error reading block commit: %w", err))
panic(fmt.Errorf("converting commit to proto: %w", err))
}
return commit
}
func (bs *BlockStore) LoadBlockExtendedCommit(height int64) *types.ExtendedCommit {
pbec := new(tmproto.ExtendedCommit)
bz, err := bs.db.Get(extCommitKey(height))
if err != nil {
panic(fmt.Errorf("fetching extended commit: %w", err))
}
if len(bz) == 0 {
return nil
}
err = proto.Unmarshal(bz, pbec)
if err != nil {
panic(fmt.Errorf("decoding extended commit: %w", err))
}
extCommit, err := types.ExtendedCommitFromProto(pbec)
if err != nil {
panic(fmt.Errorf("converting extended commit: %w", err))
}
return extCommit
}
// LoadSeenCommit returns the last locally seen Commit before being
// cannonicalized. This is useful when we've seen a commit, but there
// has not yet been a new block at `height + 1` that includes this
@@ -298,7 +318,7 @@ func (bs *BlockStore) LoadSeenCommit() *types.Commit {
commit, err := types.CommitFromProto(pbc)
if err != nil {
panic(fmt.Errorf("error from proto commit: %w", err))
panic(fmt.Errorf("converting seen commit: %w", err))
}
return commit
}
@@ -446,7 +466,7 @@ func (bs *BlockStore) batchDelete(
// If all the nodes restart after committing a block,
// we need this to reload the precommits to catch-up nodes to the
// most recent height. Otherwise they'd stall at H-1.
func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.ExtendedCommit) {
if block == nil {
panic("BlockStore can only save a non-nil block")
}
@@ -462,6 +482,10 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
if !blockParts.IsComplete() {
panic("BlockStore can only save complete block part sets")
}
if height != seenCommit.Height {
panic(fmt.Sprintf("BlockStore cannot save seen commit of a different height (block: %d, commit: %d)",
height, seenCommit.Height))
}
// Save block parts. This must be done before the block meta, since callers
// typically load the block meta first as an indication that the block exists
@@ -494,12 +518,18 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
}
// Save seen commit (seen +2/3 precommits for block)
pbsc := seenCommit.ToProto()
pbsc := seenCommit.StripExtensions().ToProto()
seenCommitBytes := mustEncode(pbsc)
if err := batch.Set(seenCommitKey(), seenCommitBytes); err != nil {
panic(err)
}
pbec := seenCommit.ToProto()
extCommitBytes := mustEncode(pbec)
if err := batch.Set(extCommitKey(height), extCommitBytes); err != nil {
panic(err)
}
if err := batch.WriteSync(); err != nil {
panic(err)
}
@@ -579,6 +609,9 @@ func (bs *BlockStore) Close() error {
//---------------------------------- KEY ENCODING -----------------------------------------
// key prefixes
// NB: Before modifying these, cross-check them with those in
// internal/state/store.go
// TODO(thane): Move these and the ones in internal/state/store.go to their own package.
const (
// prefixes are unique across all tm db's
prefixBlockMeta = int64(0)
@@ -586,6 +619,7 @@ const (
prefixBlockCommit = int64(2)
prefixSeenCommit = int64(3)
prefixBlockHash = int64(4)
prefixExtCommit = int64(9) // 5..8 are used by state/store
)
func blockMetaKey(height int64) []byte {
@@ -635,6 +669,14 @@ func seenCommitKey() []byte {
return key
}
func extCommitKey(height int64) []byte {
key, err := orderedcode.Append(nil, prefixExtCommit, height)
if err != nil {
panic(err)
}
return key
}
func blockHashKey(hash []byte) []byte {
key, err := orderedcode.Append(nil, prefixBlockHash, string(hash))
if err != nil {

View File

@@ -2,7 +2,6 @@ package store
import (
"fmt"
stdlog "log"
"os"
"runtime/debug"
"strings"
@@ -27,22 +26,25 @@ import (
// test.
type cleanupFunc func()
// make a Commit with a single vote containing just the height and a timestamp
func makeTestCommit(height int64, timestamp time.Time) *types.Commit {
commitSigs := []types.CommitSig{{
BlockIDFlag: types.BlockIDFlagCommit,
ValidatorAddress: tmrand.Bytes(crypto.AddressSize),
Timestamp: timestamp,
Signature: []byte("Signature"),
// make an extended commit with a single vote containing just the height and a
// timestamp
func makeTestExtCommit(height int64, timestamp time.Time) *types.ExtendedCommit {
extCommitSigs := []types.ExtendedCommitSig{{
CommitSig: types.CommitSig{
BlockIDFlag: types.BlockIDFlagCommit,
ValidatorAddress: tmrand.Bytes(crypto.AddressSize),
Timestamp: timestamp,
Signature: []byte("Signature"),
},
}}
return types.NewCommit(
height,
0,
types.BlockID{
return &types.ExtendedCommit{
Height: height,
BlockID: types.BlockID{
Hash: crypto.CRandBytes(32),
PartSetHeader: types.PartSetHeader{Hash: crypto.CRandBytes(32), Total: 2},
},
commitSigs)
ExtendedSignatures: extCommitSigs,
}
}
func makeStateAndBlockStore(dir string) (sm.State, *BlockStore, cleanupFunc, error) {
@@ -59,47 +61,11 @@ func makeStateAndBlockStore(dir string) (sm.State, *BlockStore, cleanupFunc, err
return state, NewBlockStore(blockDB), func() { os.RemoveAll(cfg.RootDir) }, nil
}
func freshBlockStore() (*BlockStore, dbm.DB) {
func newInMemoryBlockStore() (*BlockStore, dbm.DB) {
db := dbm.NewMemDB()
return NewBlockStore(db), db
}
var (
state sm.State
block *types.Block
partSet *types.PartSet
part1 *types.Part
part2 *types.Part
seenCommit1 *types.Commit
)
func TestMain(m *testing.M) {
dir, err := os.MkdirTemp("", "store_test")
if err != nil {
stdlog.Fatal(err)
}
var cleanup cleanupFunc
state, _, cleanup, err = makeStateAndBlockStore(dir)
if err != nil {
stdlog.Fatal(err)
}
block = factory.MakeBlock(state, 1, new(types.Commit))
partSet, err = block.MakePartSet(2)
if err != nil {
stdlog.Fatal(err)
}
part1 = partSet.GetPart(0)
part2 = partSet.GetPart(1)
seenCommit1 = makeTestCommit(10, tmtime.Now())
code := m.Run()
cleanup()
os.RemoveAll(dir) // best-effort
os.Exit(code)
}
// TODO: This test should be simplified ...
func TestBlockStoreSaveLoadBlock(t *testing.T) {
state, bs, cleanup, err := makeStateAndBlockStore(t.TempDir())
@@ -120,8 +86,10 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
block := factory.MakeBlock(state, bs.Height()+1, new(types.Commit))
validPartSet, err := block.MakePartSet(2)
require.NoError(t, err)
seenCommit := makeTestCommit(10, tmtime.Now())
bs.SaveBlock(block, partSet, seenCommit)
part2 := validPartSet.GetPart(1)
seenCommit := makeTestExtCommit(block.Header.Height, tmtime.Now())
bs.SaveBlock(block, validPartSet, seenCommit)
require.EqualValues(t, 1, bs.Base(), "expecting the new height to be changed")
require.EqualValues(t, block.Header.Height, bs.Height(), "expecting the new height to be changed")
@@ -139,11 +107,11 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
}
// End of setup, test data
commitAtH10 := makeTestCommit(10, tmtime.Now())
commitAtH10 := makeTestExtCommit(10, tmtime.Now()).StripExtensions()
tuples := []struct {
block *types.Block
parts *types.PartSet
seenCommit *types.Commit
seenCommit *types.ExtendedCommit
wantPanic string
wantErr bool
@@ -156,7 +124,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
{
block: newBlock(header1, commitAtH10),
parts: validPartSet,
seenCommit: seenCommit1,
seenCommit: seenCommit,
},
{
@@ -172,10 +140,10 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
ChainID: "block_test",
Time: tmtime.Now(),
ProposerAddress: tmrand.Bytes(crypto.AddressSize)},
makeTestCommit(5, tmtime.Now()),
makeTestExtCommit(5, tmtime.Now()).StripExtensions(),
),
parts: validPartSet,
seenCommit: makeTestCommit(5, tmtime.Now()),
seenCommit: makeTestExtCommit(5, tmtime.Now()),
},
{
@@ -187,7 +155,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
{
block: newBlock(header1, commitAtH10),
parts: validPartSet,
seenCommit: seenCommit1,
seenCommit: seenCommit,
corruptCommitInDB: true, // Corrupt the DB's commit entry
wantPanic: "error reading block commit",
},
@@ -195,7 +163,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
{
block: newBlock(header1, commitAtH10),
parts: validPartSet,
seenCommit: seenCommit1,
seenCommit: seenCommit,
wantPanic: "unmarshal to tmproto.BlockMeta",
corruptBlockInDB: true, // Corrupt the DB's block entry
},
@@ -203,7 +171,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
{
block: newBlock(header1, commitAtH10),
parts: validPartSet,
seenCommit: seenCommit1,
seenCommit: seenCommit,
// Expecting no error and we want a nil back
eraseSeenCommitInDB: true,
@@ -212,7 +180,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
{
block: newBlock(header1, commitAtH10),
parts: validPartSet,
seenCommit: seenCommit1,
seenCommit: seenCommit,
corruptSeenCommitInDB: true,
wantPanic: "error reading block seen commit",
@@ -221,7 +189,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
{
block: newBlock(header1, commitAtH10),
parts: validPartSet,
seenCommit: seenCommit1,
seenCommit: seenCommit,
// Expecting no error and we want a nil back
eraseCommitInDB: true,
@@ -238,7 +206,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
for i, tuple := range tuples {
tuple := tuple
bs, db := freshBlockStore()
bs, db := newInMemoryBlockStore()
// SaveBlock
res, err, panicErr := doFn(func() (interface{}, error) {
bs.SaveBlock(tuple.block, tuple.parts, tuple.seenCommit)
@@ -324,7 +292,7 @@ func TestLoadBaseMeta(t *testing.T) {
block := factory.MakeBlock(state, h, new(types.Commit))
partSet, err := block.MakePartSet(2)
require.NoError(t, err)
seenCommit := makeTestCommit(h, tmtime.Now())
seenCommit := makeTestExtCommit(h, tmtime.Now())
bs.SaveBlock(block, partSet, seenCommit)
}
@@ -338,13 +306,19 @@ func TestLoadBaseMeta(t *testing.T) {
}
func TestLoadBlockPart(t *testing.T) {
bs, db := freshBlockStore()
height, index := int64(10), 1
cfg, err := config.ResetTestRoot(t.TempDir(), "blockchain_reactor_test")
require.NoError(t, err)
bs, db := newInMemoryBlockStore()
const height, index = 10, 1
loadPart := func() (interface{}, error) {
part := bs.LoadBlockPart(height, index)
return part, nil
}
state, err := sm.MakeGenesisStateFromFile(cfg.GenesisFile())
require.NoError(t, err)
// Initially no contents.
// 1. Requesting for a non-existent block shouldn't fail
res, _, panicErr := doFn(loadPart)
@@ -352,13 +326,18 @@ func TestLoadBlockPart(t *testing.T) {
require.Nil(t, res, "a non-existent block part should return nil")
// 2. Next save a corrupted block then try to load it
err := db.Set(blockPartKey(height, index), []byte("Tendermint"))
err = db.Set(blockPartKey(height, index), []byte("Tendermint"))
require.NoError(t, err)
res, _, panicErr = doFn(loadPart)
require.NotNil(t, panicErr, "expecting a non-nil panic")
require.Contains(t, panicErr.Error(), "unmarshal to tmproto.Part failed")
// 3. A good block serialized and saved to the DB should be retrievable
block := factory.MakeBlock(state, height, new(types.Commit))
partSet, err := block.MakePartSet(2)
require.NoError(t, err)
part1 := partSet.GetPart(0)
pb1, err := part1.ToProto()
require.NoError(t, err)
err = db.Set(blockPartKey(height, index), mustEncode(pb1))
@@ -391,7 +370,7 @@ func TestPruneBlocks(t *testing.T) {
block := factory.MakeBlock(state, h, new(types.Commit))
partSet, err := block.MakePartSet(2)
require.NoError(t, err)
seenCommit := makeTestCommit(h, tmtime.Now())
seenCommit := makeTestExtCommit(h, tmtime.Now())
bs.SaveBlock(block, partSet, seenCommit)
}
@@ -452,7 +431,7 @@ func TestPruneBlocks(t *testing.T) {
}
func TestLoadBlockMeta(t *testing.T) {
bs, db := freshBlockStore()
bs, db := newInMemoryBlockStore()
height := int64(10)
loadMeta := func() (interface{}, error) {
meta := bs.LoadBlockMeta(height)
@@ -499,7 +478,7 @@ func TestBlockFetchAtHeight(t *testing.T) {
partSet, err := block.MakePartSet(2)
require.NoError(t, err)
seenCommit := makeTestCommit(10, tmtime.Now())
seenCommit := makeTestExtCommit(block.Header.Height, tmtime.Now())
bs.SaveBlock(block, partSet, seenCommit)
require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed")
@@ -521,9 +500,12 @@ func TestBlockFetchAtHeight(t *testing.T) {
}
func TestSeenAndCanonicalCommit(t *testing.T) {
bs, _ := freshBlockStore()
state, store, cleanup, err := makeStateAndBlockStore(t.TempDir())
defer cleanup()
require.NoError(t, err)
loadCommit := func() (interface{}, error) {
meta := bs.LoadSeenCommit()
meta := store.LoadSeenCommit()
return meta, nil
}
@@ -536,19 +518,19 @@ func TestSeenAndCanonicalCommit(t *testing.T) {
// produce a few blocks and check that the correct seen and cannoncial commits
// are persisted.
for h := int64(3); h <= 5; h++ {
blockCommit := makeTestCommit(h-1, tmtime.Now())
blockCommit := makeTestExtCommit(h-1, tmtime.Now()).StripExtensions()
block := factory.MakeBlock(state, h, blockCommit)
partSet, err := block.MakePartSet(2)
require.NoError(t, err)
seenCommit := makeTestCommit(h, tmtime.Now())
bs.SaveBlock(block, partSet, seenCommit)
c3 := bs.LoadSeenCommit()
seenCommit := makeTestExtCommit(h, tmtime.Now())
store.SaveBlock(block, partSet, seenCommit)
c3 := store.LoadSeenCommit()
require.NotNil(t, c3)
require.Equal(t, h, c3.Height)
require.Equal(t, seenCommit.Hash(), c3.Hash())
c5 := bs.LoadBlockCommit(h)
require.Equal(t, seenCommit.StripExtensions().Hash(), c3.Hash())
c5 := store.LoadBlockCommit(h)
require.Nil(t, c5)
c6 := bs.LoadBlockCommit(h - 1)
c6 := store.LoadBlockCommit(h - 1)
require.Equal(t, blockCommit.Hash(), c6.Hash())
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/tendermint/tendermint/types"
)
func MakeCommit(ctx context.Context, blockID types.BlockID, height int64, round int32, voteSet *types.VoteSet, validators []types.PrivValidator, now time.Time) (*types.Commit, error) {
func MakeExtendedCommit(ctx context.Context, blockID types.BlockID, height int64, round int32, voteSet *types.VoteSet, validators []types.PrivValidator, now time.Time) (*types.ExtendedCommit, error) {
// all sign
for i := 0; i < len(validators); i++ {
pubKey, err := validators[i].GetPubKey(ctx)
@@ -37,5 +37,5 @@ func MakeCommit(ctx context.Context, blockID types.BlockID, height int64, round
}
}
return voteSet.MakeCommit(), nil
return voteSet.MakeExtendedCommit(), nil
}

View File

@@ -72,7 +72,12 @@ func (pkz privKeys) signHeader(t testing.TB, header *types.Header, valSet *types
commitSigs[vote.ValidatorIndex] = vote.CommitSig()
}
return types.NewCommit(header.Height, 1, blockID, commitSigs)
return &types.Commit{
Height: header.Height,
Round: 1,
BlockID: blockID,
Signatures: commitSigs,
}
}
func makeVote(t testing.TB, header *types.Header, valset *types.ValidatorSet, key crypto.PrivKey, blockID types.BlockID) *types.Vote {

View File

@@ -35,6 +35,7 @@ import (
"github.com/tendermint/tendermint/libs/service"
tmtime "github.com/tendermint/tendermint/libs/time"
"github.com/tendermint/tendermint/privval"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/types"
)
@@ -339,13 +340,13 @@ func TestCreateProposalBlock(t *testing.T) {
sm.NopMetrics(),
)
commit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
extCommit := &types.ExtendedCommit{Height: height - 1}
block, err := blockExec.CreateProposalBlock(
ctx,
height,
state, commit,
state,
extCommit,
proposerAddr,
nil,
)
require.NoError(t, err)
@@ -419,13 +420,13 @@ func TestMaxTxsProposalBlockSize(t *testing.T) {
sm.NopMetrics(),
)
commit := types.NewCommit(height-1, 0, types.BlockID{}, nil)
extCommit := &types.ExtendedCommit{Height: height - 1}
block, err := blockExec.CreateProposalBlock(
ctx,
height,
state, commit,
state,
extCommit,
proposerAddr,
nil,
)
require.NoError(t, err)
@@ -525,38 +526,41 @@ func TestMaxProposalBlockSize(t *testing.T) {
}
state.ChainID = maxChainID
cs := types.CommitSig{
BlockIDFlag: types.BlockIDFlagNil,
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
Timestamp: timestamp,
Signature: crypto.CRandBytes(types.MaxSignatureSize),
}
commit := &types.Commit{
Height: math.MaxInt64,
Round: math.MaxInt32,
BlockID: blockID,
}
votes := make([]*types.Vote, types.MaxVotesCount)
voteSet := types.NewVoteSet(state.ChainID, math.MaxInt64-1, math.MaxInt32, tmproto.PrecommitType, state.Validators, false)
// add maximum amount of signatures to a single commit
for i := 0; i < types.MaxVotesCount; i++ {
pubKey, err := privVals[i].GetPubKey(ctx)
require.NoError(t, err)
votes[i] = &types.Vote{
ValidatorAddress: pubKey.Address(),
valIdx, val := state.Validators.GetByAddress(pubKey.Address())
require.NotNil(t, val)
vote := &types.Vote{
Type: tmproto.PrecommitType,
Height: math.MaxInt64 - 1,
Round: math.MaxInt32,
BlockID: blockID,
Timestamp: timestamp,
ValidatorAddress: val.Address,
ValidatorIndex: valIdx,
Extension: []byte("extension"),
}
commit.Signatures = append(commit.Signatures, cs)
vpb := vote.ToProto()
require.NoError(t, privVals[i].SignVote(ctx, state.ChainID, vpb))
vote.Signature = vpb.Signature
vote.ExtensionSignature = vpb.ExtensionSignature
added, err := voteSet.AddVote(vote)
require.NoError(t, err)
require.True(t, added)
}
block, err := blockExec.CreateProposalBlock(
ctx,
math.MaxInt64,
state,
commit,
voteSet.MakeExtendedCommit(),
proposerAddr,
votes,
)
require.NoError(t, err)
partSet, err := block.MakePartSet(types.BlockPartSizeBytes)

View File

@@ -375,17 +375,17 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error {
// Vote extensions are non-deterministic, so it is possible that an
// application may have created a different extension. We therefore always
// re-sign the vote extensions of precommits. For prevotes, the extension
// signature will always be empty.
// re-sign the vote extensions of precommits. For prevotes and nil
// precommits, the extension signature will always be empty.
var extSig []byte
if vote.Type == tmproto.PrecommitType {
if vote.Type == tmproto.PrecommitType && !types.ProtoBlockIDIsNil(&vote.BlockID) {
extSignBytes := types.VoteExtensionSignBytes(chainID, vote)
extSig, err = pv.Key.PrivKey.Sign(extSignBytes)
if err != nil {
return err
}
} else if len(vote.Extension) > 0 {
return errors.New("unexpected vote extension - extensions are only allowed in precommits")
return errors.New("unexpected vote extension - extensions are only allowed in non-nil precommits")
}
// We might crash before writing to the wal,

View File

@@ -116,7 +116,8 @@ func (m *NoBlockResponse) GetHeight() int64 {
// BlockResponse returns block to the requested
type BlockResponse struct {
Block *types.Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"`
Block *types.Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"`
ExtCommit *types.ExtendedCommit `protobuf:"bytes,2,opt,name=ext_commit,json=extCommit,proto3" json:"ext_commit,omitempty"`
}
func (m *BlockResponse) Reset() { *m = BlockResponse{} }
@@ -159,6 +160,13 @@ func (m *BlockResponse) GetBlock() *types.Block {
return nil
}
func (m *BlockResponse) GetExtCommit() *types.ExtendedCommit {
if m != nil {
return m.ExtCommit
}
return nil
}
// StatusRequest requests the status of a peer.
type StatusRequest struct {
}
@@ -385,30 +393,33 @@ func init() {
func init() { proto.RegisterFile("tendermint/blocksync/types.proto", fileDescriptor_19b397c236e0fa07) }
var fileDescriptor_19b397c236e0fa07 = []byte{
// 368 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0x4d, 0x4f, 0xfa, 0x40,
0x10, 0xc6, 0xdb, 0x7f, 0x81, 0x7f, 0x32, 0x50, 0x1a, 0x1b, 0xa3, 0xc4, 0x98, 0x86, 0xd4, 0x97,
0xe8, 0xc1, 0x36, 0xc1, 0xa3, 0xc6, 0x03, 0x27, 0x4c, 0x7c, 0x49, 0x4a, 0xbc, 0x78, 0x21, 0x14,
0x37, 0x40, 0x94, 0x2e, 0x32, 0xdb, 0x03, 0xdf, 0xc2, 0x2f, 0xe0, 0xf7, 0xf1, 0xc8, 0xd1, 0xa3,
0x81, 0x2f, 0x62, 0x98, 0x2d, 0x65, 0x69, 0xb0, 0xb7, 0xdd, 0xe9, 0x33, 0xbf, 0x79, 0xfa, 0x64,
0x16, 0xea, 0x82, 0x45, 0x2f, 0x6c, 0x32, 0x1a, 0x46, 0xc2, 0x0f, 0xdf, 0x78, 0xef, 0x15, 0xa7,
0x51, 0xcf, 0x17, 0xd3, 0x31, 0x43, 0x6f, 0x3c, 0xe1, 0x82, 0xdb, 0xbb, 0x6b, 0x85, 0x97, 0x2a,
0x0e, 0x0e, 0x95, 0x3e, 0x52, 0xcb, 0x6e, 0xd9, 0xe3, 0x9e, 0x42, 0xa5, 0xb9, 0xbc, 0x06, 0xec,
0x3d, 0x66, 0x28, 0xec, 0x3d, 0x28, 0x0d, 0xd8, 0xb0, 0x3f, 0x10, 0x35, 0xbd, 0xae, 0x9f, 0x19,
0x41, 0x72, 0x73, 0xcf, 0xc1, 0x7a, 0xe0, 0x89, 0x12, 0xc7, 0x3c, 0x42, 0xf6, 0xa7, 0xf4, 0x06,
0xcc, 0x4d, 0xe1, 0x05, 0x14, 0x69, 0x24, 0xe9, 0xca, 0x8d, 0x7d, 0x4f, 0xf1, 0x29, 0xfd, 0x4b,
0xbd, 0x54, 0xb9, 0x16, 0x98, 0x6d, 0xd1, 0x15, 0x31, 0x26, 0x9e, 0xdc, 0x6b, 0xa8, 0xae, 0x0a,
0xf9, 0xa3, 0x6d, 0x1b, 0x0a, 0x61, 0x17, 0x59, 0xed, 0x1f, 0x55, 0xe9, 0xec, 0x7e, 0x1a, 0xf0,
0xff, 0x9e, 0x21, 0x76, 0xfb, 0xcc, 0xbe, 0x05, 0x93, 0x66, 0x74, 0x26, 0x12, 0x9d, 0x38, 0x72,
0xbd, 0x6d, 0xc9, 0x79, 0x6a, 0x30, 0x2d, 0x2d, 0xa8, 0x84, 0x6a, 0x50, 0x6d, 0xd8, 0x89, 0x78,
0x67, 0x45, 0x93, 0xbe, 0x68, 0x6e, 0xb9, 0x71, 0xb2, 0x1d, 0x97, 0xc9, 0xaf, 0xa5, 0x05, 0x56,
0x94, 0x89, 0xf4, 0x0e, 0xaa, 0x19, 0xa2, 0x41, 0xc4, 0xa3, 0x5c, 0x83, 0x29, 0xcf, 0x0c, 0xb3,
0x34, 0xa4, 0xdc, 0xd2, 0xdf, 0x2d, 0xe4, 0xd1, 0x36, 0x42, 0x5f, 0xd2, 0x50, 0x2d, 0xd8, 0x8f,
0x60, 0xa5, 0xb4, 0xc4, 0x5c, 0x91, 0x70, 0xc7, 0xf9, 0xb8, 0xd4, 0x5d, 0x15, 0x37, 0x2a, 0xcd,
0x22, 0x18, 0x18, 0x8f, 0x9a, 0x4f, 0x5f, 0x73, 0x47, 0x9f, 0xcd, 0x1d, 0xfd, 0x67, 0xee, 0xe8,
0x1f, 0x0b, 0x47, 0x9b, 0x2d, 0x1c, 0xed, 0x7b, 0xe1, 0x68, 0xcf, 0x57, 0xfd, 0xa1, 0x18, 0xc4,
0xa1, 0xd7, 0xe3, 0x23, 0x5f, 0x5d, 0xe2, 0xf5, 0x91, 0x76, 0xd8, 0xdf, 0xf6, 0x30, 0xc2, 0x12,
0x7d, 0xbb, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x1c, 0xa3, 0x45, 0x37, 0x03, 0x00, 0x00,
// 404 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0xcd, 0x4a, 0xeb, 0x50,
0x10, 0xc7, 0x93, 0x9b, 0xb6, 0x97, 0x3b, 0xb7, 0x69, 0xb8, 0xe1, 0xa2, 0x45, 0x24, 0x94, 0xf8,
0x81, 0x2e, 0x4c, 0x40, 0x97, 0x0a, 0x42, 0x45, 0xa8, 0xe0, 0x07, 0xa4, 0xb8, 0x71, 0x53, 0x9a,
0xf4, 0xd0, 0x06, 0x4d, 0x4e, 0xed, 0x39, 0x81, 0x76, 0xe5, 0x2b, 0xf8, 0x02, 0xbe, 0x8f, 0xcb,
0x2e, 0x5d, 0x4a, 0xfb, 0x22, 0xd2, 0x39, 0x69, 0x9a, 0xc6, 0x98, 0xdd, 0x64, 0xce, 0x7f, 0x7e,
0xf9, 0xcf, 0x0c, 0x03, 0x0d, 0x4e, 0xc2, 0x1e, 0x19, 0x05, 0x7e, 0xc8, 0x6d, 0xf7, 0x89, 0x7a,
0x8f, 0x6c, 0x12, 0x7a, 0x36, 0x9f, 0x0c, 0x09, 0xb3, 0x86, 0x23, 0xca, 0xa9, 0xfe, 0x7f, 0xa5,
0xb0, 0x12, 0xc5, 0xd6, 0x76, 0xaa, 0x0e, 0xd5, 0xa2, 0x5a, 0xd4, 0xe4, 0xbc, 0xa6, 0x88, 0xe6,
0x3e, 0x54, 0x9b, 0x0b, 0xb1, 0x43, 0x9e, 0x23, 0xc2, 0xb8, 0xbe, 0x01, 0x95, 0x01, 0xf1, 0xfb,
0x03, 0x5e, 0x97, 0x1b, 0xf2, 0x81, 0xe2, 0xc4, 0x5f, 0xe6, 0x21, 0x68, 0xb7, 0x34, 0x56, 0xb2,
0x21, 0x0d, 0x19, 0xf9, 0x51, 0xfa, 0x02, 0xea, 0xba, 0xf0, 0x08, 0xca, 0x68, 0x08, 0x75, 0x7f,
0x8f, 0x37, 0xad, 0x54, 0x17, 0xc2, 0x8b, 0xd0, 0x0b, 0x95, 0x7e, 0x0e, 0x40, 0xc6, 0xbc, 0xe3,
0xd1, 0x20, 0xf0, 0x79, 0xfd, 0x17, 0xd6, 0x34, 0xbe, 0xd7, 0x5c, 0x8e, 0x31, 0xd5, 0xbb, 0x40,
0x9d, 0xf3, 0x87, 0x8c, 0xb9, 0x08, 0x4d, 0x0d, 0xd4, 0x36, 0xef, 0xf2, 0x88, 0xc5, 0x4d, 0x99,
0x67, 0x50, 0x5b, 0x26, 0x8a, 0xbd, 0xeb, 0x3a, 0x94, 0xdc, 0x2e, 0x23, 0xf8, 0x57, 0xc5, 0xc1,
0xd8, 0x7c, 0x53, 0xe0, 0xf7, 0x0d, 0x61, 0xac, 0xdb, 0x27, 0xfa, 0x15, 0xa8, 0x68, 0xb2, 0x33,
0x12, 0xe8, 0xb8, 0x25, 0xd3, 0xca, 0x5b, 0x8c, 0x95, 0x9e, 0x6c, 0x4b, 0x72, 0xaa, 0x6e, 0x7a,
0xd2, 0x6d, 0xf8, 0x17, 0xd2, 0xce, 0x92, 0x26, 0x7c, 0xc5, 0xdd, 0xee, 0xe5, 0xe3, 0x32, 0x0b,
0x68, 0x49, 0x8e, 0x16, 0x66, 0x76, 0x72, 0x0d, 0xb5, 0x0c, 0x51, 0x41, 0xe2, 0x4e, 0xa1, 0xc1,
0x84, 0xa7, 0xba, 0x59, 0x1a, 0xc3, 0xb9, 0x25, 0xed, 0x96, 0x8a, 0x68, 0x6b, 0x43, 0x5f, 0xd0,
0x58, 0x3a, 0xa1, 0xdf, 0x81, 0x96, 0xd0, 0x62, 0x73, 0x65, 0xc4, 0xed, 0x16, 0xe3, 0x12, 0x77,
0x35, 0xb6, 0x96, 0x69, 0x96, 0x41, 0x61, 0x51, 0xd0, 0xbc, 0x7f, 0x9f, 0x19, 0xf2, 0x74, 0x66,
0xc8, 0x9f, 0x33, 0x43, 0x7e, 0x9d, 0x1b, 0xd2, 0x74, 0x6e, 0x48, 0x1f, 0x73, 0x43, 0x7a, 0x38,
0xed, 0xfb, 0x7c, 0x10, 0xb9, 0x96, 0x47, 0x03, 0x3b, 0x7d, 0x05, 0xab, 0x10, 0x8f, 0xc0, 0xce,
0xbb, 0x3b, 0xb7, 0x82, 0x6f, 0x27, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x13, 0x4f, 0x42,
0x96, 0x03, 0x00, 0x00,
}
func (m *BlockRequest) Marshal() (dAtA []byte, err error) {
@@ -487,6 +498,18 @@ func (m *BlockResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.ExtCommit != nil {
{
size, err := m.ExtCommit.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintTypes(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
if m.Block != nil {
{
size, err := m.Block.MarshalToSizedBuffer(dAtA[:i])
@@ -740,6 +763,10 @@ func (m *BlockResponse) Size() (n int) {
l = m.Block.Size()
n += 1 + l + sovTypes(uint64(l))
}
if m.ExtCommit != nil {
l = m.ExtCommit.Size()
n += 1 + l + sovTypes(uint64(l))
}
return n
}
@@ -1049,6 +1076,42 @@ func (m *BlockResponse) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ExtCommit", 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.ExtCommit == nil {
m.ExtCommit = &types.ExtendedCommit{}
}
if err := m.ExtCommit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipTypes(dAtA[iNdEx:])

View File

@@ -4,6 +4,7 @@ package tendermint.blocksync;
option go_package = "github.com/tendermint/tendermint/proto/tendermint/blocksync";
import "tendermint/types/block.proto";
import "tendermint/types/types.proto";
// BlockRequest requests a block for a specific height
message BlockRequest {
@@ -19,6 +20,7 @@ message NoBlockResponse {
// BlockResponse returns block to the requested
message BlockResponse {
tendermint.types.Block block = 1;
tendermint.types.ExtendedCommit ext_commit = 2;
}
// StatusRequest requests the status of a peer.

View File

@@ -726,6 +726,162 @@ func (m *CommitSig) GetSignature() []byte {
return nil
}
type ExtendedCommit 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"`
BlockID BlockID `protobuf:"bytes,3,opt,name=block_id,json=blockId,proto3" json:"block_id"`
ExtendedSignatures []ExtendedCommitSig `protobuf:"bytes,4,rep,name=extended_signatures,json=extendedSignatures,proto3" json:"extended_signatures"`
}
func (m *ExtendedCommit) Reset() { *m = ExtendedCommit{} }
func (m *ExtendedCommit) String() string { return proto.CompactTextString(m) }
func (*ExtendedCommit) ProtoMessage() {}
func (*ExtendedCommit) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{8}
}
func (m *ExtendedCommit) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *ExtendedCommit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_ExtendedCommit.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 *ExtendedCommit) XXX_Merge(src proto.Message) {
xxx_messageInfo_ExtendedCommit.Merge(m, src)
}
func (m *ExtendedCommit) XXX_Size() int {
return m.Size()
}
func (m *ExtendedCommit) XXX_DiscardUnknown() {
xxx_messageInfo_ExtendedCommit.DiscardUnknown(m)
}
var xxx_messageInfo_ExtendedCommit proto.InternalMessageInfo
func (m *ExtendedCommit) GetHeight() int64 {
if m != nil {
return m.Height
}
return 0
}
func (m *ExtendedCommit) GetRound() int32 {
if m != nil {
return m.Round
}
return 0
}
func (m *ExtendedCommit) GetBlockID() BlockID {
if m != nil {
return m.BlockID
}
return BlockID{}
}
func (m *ExtendedCommit) GetExtendedSignatures() []ExtendedCommitSig {
if m != nil {
return m.ExtendedSignatures
}
return nil
}
// ExtendedCommitSig retains all the same fields as CommitSig but adds vote
// extension-related fields.
type ExtendedCommitSig struct {
BlockIdFlag BlockIDFlag `protobuf:"varint,1,opt,name=block_id_flag,json=blockIdFlag,proto3,enum=tendermint.types.BlockIDFlag" json:"block_id_flag,omitempty"`
ValidatorAddress []byte `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"`
Timestamp time.Time `protobuf:"bytes,3,opt,name=timestamp,proto3,stdtime" json:"timestamp"`
Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"`
// Vote extension data
Extension []byte `protobuf:"bytes,5,opt,name=extension,proto3" json:"extension,omitempty"`
// Vote extension signature
ExtensionSignature []byte `protobuf:"bytes,6,opt,name=extension_signature,json=extensionSignature,proto3" json:"extension_signature,omitempty"`
}
func (m *ExtendedCommitSig) Reset() { *m = ExtendedCommitSig{} }
func (m *ExtendedCommitSig) String() string { return proto.CompactTextString(m) }
func (*ExtendedCommitSig) ProtoMessage() {}
func (*ExtendedCommitSig) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{9}
}
func (m *ExtendedCommitSig) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *ExtendedCommitSig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_ExtendedCommitSig.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 *ExtendedCommitSig) XXX_Merge(src proto.Message) {
xxx_messageInfo_ExtendedCommitSig.Merge(m, src)
}
func (m *ExtendedCommitSig) XXX_Size() int {
return m.Size()
}
func (m *ExtendedCommitSig) XXX_DiscardUnknown() {
xxx_messageInfo_ExtendedCommitSig.DiscardUnknown(m)
}
var xxx_messageInfo_ExtendedCommitSig proto.InternalMessageInfo
func (m *ExtendedCommitSig) GetBlockIdFlag() BlockIDFlag {
if m != nil {
return m.BlockIdFlag
}
return BlockIDFlagUnknown
}
func (m *ExtendedCommitSig) GetValidatorAddress() []byte {
if m != nil {
return m.ValidatorAddress
}
return nil
}
func (m *ExtendedCommitSig) GetTimestamp() time.Time {
if m != nil {
return m.Timestamp
}
return time.Time{}
}
func (m *ExtendedCommitSig) GetSignature() []byte {
if m != nil {
return m.Signature
}
return nil
}
func (m *ExtendedCommitSig) GetExtension() []byte {
if m != nil {
return m.Extension
}
return nil
}
func (m *ExtendedCommitSig) GetExtensionSignature() []byte {
if m != nil {
return m.ExtensionSignature
}
return nil
}
type Proposal struct {
Type SignedMsgType `protobuf:"varint,1,opt,name=type,proto3,enum=tendermint.types.SignedMsgType" json:"type,omitempty"`
Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"`
@@ -740,7 +896,7 @@ func (m *Proposal) Reset() { *m = Proposal{} }
func (m *Proposal) String() string { return proto.CompactTextString(m) }
func (*Proposal) ProtoMessage() {}
func (*Proposal) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{8}
return fileDescriptor_d3a6e55e2345de56, []int{10}
}
func (m *Proposal) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -827,7 +983,7 @@ func (m *SignedHeader) Reset() { *m = SignedHeader{} }
func (m *SignedHeader) String() string { return proto.CompactTextString(m) }
func (*SignedHeader) ProtoMessage() {}
func (*SignedHeader) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{9}
return fileDescriptor_d3a6e55e2345de56, []int{11}
}
func (m *SignedHeader) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -879,7 +1035,7 @@ func (m *LightBlock) Reset() { *m = LightBlock{} }
func (m *LightBlock) String() string { return proto.CompactTextString(m) }
func (*LightBlock) ProtoMessage() {}
func (*LightBlock) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{10}
return fileDescriptor_d3a6e55e2345de56, []int{12}
}
func (m *LightBlock) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -933,7 +1089,7 @@ func (m *BlockMeta) Reset() { *m = BlockMeta{} }
func (m *BlockMeta) String() string { return proto.CompactTextString(m) }
func (*BlockMeta) ProtoMessage() {}
func (*BlockMeta) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{11}
return fileDescriptor_d3a6e55e2345de56, []int{13}
}
func (m *BlockMeta) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1002,7 +1158,7 @@ func (m *TxProof) Reset() { *m = TxProof{} }
func (m *TxProof) String() string { return proto.CompactTextString(m) }
func (*TxProof) ProtoMessage() {}
func (*TxProof) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{12}
return fileDescriptor_d3a6e55e2345de56, []int{14}
}
func (m *TxProof) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -1063,6 +1219,8 @@ func init() {
proto.RegisterType((*Vote)(nil), "tendermint.types.Vote")
proto.RegisterType((*Commit)(nil), "tendermint.types.Commit")
proto.RegisterType((*CommitSig)(nil), "tendermint.types.CommitSig")
proto.RegisterType((*ExtendedCommit)(nil), "tendermint.types.ExtendedCommit")
proto.RegisterType((*ExtendedCommitSig)(nil), "tendermint.types.ExtendedCommitSig")
proto.RegisterType((*Proposal)(nil), "tendermint.types.Proposal")
proto.RegisterType((*SignedHeader)(nil), "tendermint.types.SignedHeader")
proto.RegisterType((*LightBlock)(nil), "tendermint.types.LightBlock")
@@ -1073,91 +1231,95 @@ func init() {
func init() { proto.RegisterFile("tendermint/types/types.proto", fileDescriptor_d3a6e55e2345de56) }
var fileDescriptor_d3a6e55e2345de56 = []byte{
// 1341 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcf, 0x73, 0xdb, 0xc4,
0x17, 0x8f, 0x62, 0x25, 0xb6, 0x9f, 0xed, 0xc4, 0xd9, 0x6f, 0xda, 0xba, 0x6e, 0xe3, 0x68, 0xfc,
0x1d, 0x20, 0x2d, 0x8c, 0x52, 0x52, 0x86, 0x1f, 0x07, 0x0e, 0xb6, 0x93, 0xb6, 0x9e, 0x26, 0x8e,
0x91, 0xdd, 0x32, 0x70, 0xd1, 0xc8, 0xd6, 0xd6, 0x16, 0x95, 0x25, 0x8d, 0x76, 0x1d, 0x92, 0xfe,
0x05, 0x4c, 0x4e, 0x3d, 0x71, 0xcb, 0x09, 0x0e, 0xdc, 0x39, 0x70, 0x65, 0x38, 0xf5, 0xd8, 0x1b,
0x5c, 0x28, 0x4c, 0x3a, 0xc3, 0xdf, 0xc1, 0xec, 0x0f, 0xc9, 0x72, 0x9c, 0x40, 0xa7, 0xd3, 0xe1,
0xe2, 0xd1, 0xbe, 0xf7, 0x79, 0x6f, 0xdf, 0x8f, 0xcf, 0xee, 0x5b, 0xc3, 0x75, 0x8a, 0x3d, 0x1b,
0x87, 0x23, 0xc7, 0xa3, 0x9b, 0xf4, 0x28, 0xc0, 0x44, 0xfc, 0xea, 0x41, 0xe8, 0x53, 0x1f, 0x15,
0x27, 0x5a, 0x9d, 0xcb, 0xcb, 0xab, 0x03, 0x7f, 0xe0, 0x73, 0xe5, 0x26, 0xfb, 0x12, 0xb8, 0xf2,
0xfa, 0xc0, 0xf7, 0x07, 0x2e, 0xde, 0xe4, 0xab, 0xde, 0xf8, 0xd1, 0x26, 0x75, 0x46, 0x98, 0x50,
0x6b, 0x14, 0x48, 0xc0, 0x5a, 0x62, 0x9b, 0x7e, 0x78, 0x14, 0x50, 0x9f, 0x61, 0xfd, 0x47, 0x52,
0x5d, 0x49, 0xa8, 0x0f, 0x70, 0x48, 0x1c, 0xdf, 0x4b, 0xc6, 0x51, 0xd6, 0x66, 0xa2, 0x3c, 0xb0,
0x5c, 0xc7, 0xb6, 0xa8, 0x1f, 0x0a, 0x44, 0xf5, 0x13, 0x28, 0xb4, 0xad, 0x90, 0x76, 0x30, 0xbd,
0x87, 0x2d, 0x1b, 0x87, 0x68, 0x15, 0x16, 0xa8, 0x4f, 0x2d, 0xb7, 0xa4, 0x68, 0xca, 0x46, 0xc1,
0x10, 0x0b, 0x84, 0x40, 0x1d, 0x5a, 0x64, 0x58, 0x9a, 0xd7, 0x94, 0x8d, 0xbc, 0xc1, 0xbf, 0xab,
0x43, 0x50, 0x99, 0x29, 0xb3, 0x70, 0x3c, 0x1b, 0x1f, 0x46, 0x16, 0x7c, 0xc1, 0xa4, 0xbd, 0x23,
0x8a, 0x89, 0x34, 0x11, 0x0b, 0xf4, 0x01, 0x2c, 0xf0, 0xf8, 0x4b, 0x29, 0x4d, 0xd9, 0xc8, 0x6d,
0x95, 0xf4, 0x44, 0xa1, 0x44, 0x7e, 0x7a, 0x9b, 0xe9, 0xeb, 0xea, 0xb3, 0x17, 0xeb, 0x73, 0x86,
0x00, 0x57, 0x5d, 0x48, 0xd7, 0x5d, 0xbf, 0xff, 0xb8, 0xb9, 0x1d, 0x07, 0xa2, 0x4c, 0x02, 0x41,
0x7b, 0xb0, 0x1c, 0x58, 0x21, 0x35, 0x09, 0xa6, 0xe6, 0x90, 0x67, 0xc1, 0x37, 0xcd, 0x6d, 0xad,
0xeb, 0x67, 0xfb, 0xa0, 0x4f, 0x25, 0x2b, 0x77, 0x29, 0x04, 0x49, 0x61, 0xf5, 0x2f, 0x15, 0x16,
0x65, 0x31, 0x3e, 0x85, 0xb4, 0x2c, 0x2b, 0xdf, 0x30, 0xb7, 0xb5, 0x96, 0xf4, 0x28, 0x55, 0x7a,
0xc3, 0xf7, 0x08, 0xf6, 0xc8, 0x98, 0x48, 0x7f, 0x91, 0x0d, 0x7a, 0x1b, 0x32, 0xfd, 0xa1, 0xe5,
0x78, 0xa6, 0x63, 0xf3, 0x88, 0xb2, 0xf5, 0xdc, 0xe9, 0x8b, 0xf5, 0x74, 0x83, 0xc9, 0x9a, 0xdb,
0x46, 0x9a, 0x2b, 0x9b, 0x36, 0xba, 0x0c, 0x8b, 0x43, 0xec, 0x0c, 0x86, 0x94, 0x97, 0x25, 0x65,
0xc8, 0x15, 0xfa, 0x18, 0x54, 0x46, 0x88, 0x92, 0xca, 0xf7, 0x2e, 0xeb, 0x82, 0x2d, 0x7a, 0xc4,
0x16, 0xbd, 0x1b, 0xb1, 0xa5, 0x9e, 0x61, 0x1b, 0x3f, 0xfd, 0x63, 0x5d, 0x31, 0xb8, 0x05, 0x6a,
0x40, 0xc1, 0xb5, 0x08, 0x35, 0x7b, 0xac, 0x6c, 0x6c, 0xfb, 0x05, 0xee, 0xe2, 0xea, 0x6c, 0x41,
0x64, 0x61, 0x65, 0xe8, 0x39, 0x66, 0x25, 0x44, 0x36, 0xda, 0x80, 0x22, 0x77, 0xd2, 0xf7, 0x47,
0x23, 0x87, 0x9a, 0xbc, 0xee, 0x8b, 0xbc, 0xee, 0x4b, 0x4c, 0xde, 0xe0, 0xe2, 0x7b, 0xac, 0x03,
0xd7, 0x20, 0x6b, 0x5b, 0xd4, 0x12, 0x90, 0x34, 0x87, 0x64, 0x98, 0x80, 0x2b, 0xdf, 0x81, 0xe5,
0x98, 0x75, 0x44, 0x40, 0x32, 0xc2, 0xcb, 0x44, 0xcc, 0x81, 0xb7, 0x60, 0xd5, 0xc3, 0x87, 0xd4,
0x3c, 0x8b, 0xce, 0x72, 0x34, 0x62, 0xba, 0x87, 0xd3, 0x16, 0x6f, 0xc1, 0x52, 0x3f, 0x2a, 0xbe,
0xc0, 0x02, 0xc7, 0x16, 0x62, 0x29, 0x87, 0x5d, 0x85, 0x8c, 0x15, 0x04, 0x02, 0x90, 0xe3, 0x80,
0xb4, 0x15, 0x04, 0x5c, 0x75, 0x13, 0x56, 0x78, 0x8e, 0x21, 0x26, 0x63, 0x97, 0x4a, 0x27, 0x79,
0x8e, 0x59, 0x66, 0x0a, 0x43, 0xc8, 0x39, 0xf6, 0xff, 0x50, 0xc0, 0x07, 0x8e, 0x8d, 0xbd, 0x3e,
0x16, 0xb8, 0x02, 0xc7, 0xe5, 0x23, 0x21, 0x07, 0xdd, 0x80, 0x62, 0x10, 0xfa, 0x81, 0x4f, 0x70,
0x68, 0x5a, 0xb6, 0x1d, 0x62, 0x42, 0x4a, 0x4b, 0xc2, 0x5f, 0x24, 0xaf, 0x09, 0x71, 0xb5, 0x04,
0xea, 0xb6, 0x45, 0x2d, 0x54, 0x84, 0x14, 0x3d, 0x24, 0x25, 0x45, 0x4b, 0x6d, 0xe4, 0x0d, 0xf6,
0x59, 0xfd, 0x29, 0x05, 0xea, 0x43, 0x9f, 0x62, 0x74, 0x1b, 0x54, 0xd6, 0x26, 0xce, 0xbe, 0xa5,
0xf3, 0xf8, 0xdc, 0x71, 0x06, 0x1e, 0xb6, 0xf7, 0xc8, 0xa0, 0x7b, 0x14, 0x60, 0x83, 0x83, 0x13,
0x74, 0x9a, 0x9f, 0xa2, 0xd3, 0x2a, 0x2c, 0x84, 0xfe, 0xd8, 0xb3, 0x39, 0xcb, 0x16, 0x0c, 0xb1,
0x40, 0x3b, 0x90, 0x89, 0x59, 0xa2, 0xfe, 0x1b, 0x4b, 0x96, 0x19, 0x4b, 0x18, 0x87, 0xa5, 0xc0,
0x48, 0xf7, 0x24, 0x59, 0xea, 0x90, 0x8d, 0x2f, 0x2f, 0xc9, 0xb6, 0x57, 0x23, 0xec, 0xc4, 0x0c,
0xbd, 0x0b, 0x2b, 0x71, 0xef, 0xe3, 0xe2, 0x09, 0xc6, 0x15, 0x63, 0x85, 0xac, 0xde, 0x14, 0xad,
0x4c, 0x71, 0x01, 0xa5, 0x79, 0x5e, 0x13, 0x5a, 0x35, 0xf9, 0x4d, 0x74, 0x1d, 0xb2, 0xc4, 0x19,
0x78, 0x16, 0x1d, 0x87, 0x58, 0x32, 0x6f, 0x22, 0x60, 0x5a, 0x7c, 0x48, 0xb1, 0xc7, 0x0f, 0xb9,
0x60, 0xda, 0x44, 0x80, 0x36, 0xe1, 0x7f, 0xf1, 0xc2, 0x9c, 0x78, 0x11, 0x2c, 0x43, 0xb1, 0xaa,
0x13, 0x69, 0xaa, 0x3f, 0x2b, 0xb0, 0x28, 0x0e, 0x46, 0xa2, 0x0d, 0xca, 0xf9, 0x6d, 0x98, 0xbf,
0xa8, 0x0d, 0xa9, 0xd7, 0x6f, 0x43, 0x0d, 0x20, 0x0e, 0x93, 0x94, 0x54, 0x2d, 0xb5, 0x91, 0xdb,
0xba, 0x36, 0xeb, 0x48, 0x84, 0xd8, 0x71, 0x06, 0xf2, 0xdc, 0x27, 0x8c, 0xaa, 0xbf, 0x2b, 0x90,
0x8d, 0xf5, 0xa8, 0x06, 0x85, 0x28, 0x2e, 0xf3, 0x91, 0x6b, 0x0d, 0x24, 0x15, 0xd7, 0x2e, 0x0c,
0xee, 0x8e, 0x6b, 0x0d, 0x8c, 0x9c, 0x8c, 0x87, 0x2d, 0xce, 0x6f, 0xeb, 0xfc, 0x05, 0x6d, 0x9d,
0xe2, 0x51, 0xea, 0xf5, 0x78, 0x34, 0xd5, 0x71, 0xf5, 0x4c, 0xc7, 0xab, 0x3f, 0xce, 0x43, 0xa6,
0xcd, 0x8f, 0xa2, 0xe5, 0xfe, 0x17, 0x07, 0xec, 0x1a, 0x64, 0x03, 0xdf, 0x35, 0x85, 0x46, 0xe5,
0x9a, 0x4c, 0xe0, 0xbb, 0xc6, 0x4c, 0xdb, 0x17, 0xde, 0xd0, 0xe9, 0x5b, 0x7c, 0x03, 0x55, 0x4b,
0x9f, 0xad, 0x5a, 0x08, 0x79, 0x51, 0x0a, 0x39, 0x1a, 0x6f, 0xb1, 0x1a, 0xf0, 0x59, 0xab, 0xcc,
0x8e, 0x72, 0x11, 0xb6, 0x40, 0x1a, 0x12, 0xc7, 0x2c, 0xc4, 0x24, 0x91, 0xd3, 0xb9, 0x74, 0x11,
0x2d, 0x0d, 0x89, 0xab, 0x7e, 0xab, 0x00, 0xec, 0xb2, 0xca, 0xf2, 0x7c, 0xd9, 0x50, 0x23, 0x3c,
0x04, 0x73, 0x6a, 0xe7, 0xca, 0x45, 0x4d, 0x93, 0xfb, 0xe7, 0x49, 0x32, 0xee, 0x06, 0x14, 0x26,
0x64, 0x24, 0x38, 0x0a, 0xe6, 0x1c, 0x27, 0xf1, 0xac, 0xe9, 0x60, 0x6a, 0xe4, 0x0f, 0x12, 0xab,
0xea, 0x2f, 0x0a, 0x64, 0x79, 0x4c, 0x7b, 0x98, 0x5a, 0x53, 0x3d, 0x54, 0x5e, 0xbf, 0x87, 0x6b,
0x00, 0xc2, 0x0d, 0x71, 0x9e, 0x60, 0xc9, 0xac, 0x2c, 0x97, 0x74, 0x9c, 0x27, 0x18, 0x7d, 0x18,
0x17, 0x3c, 0xf5, 0xcf, 0x05, 0x97, 0x47, 0x3a, 0x2a, 0xfb, 0x15, 0x48, 0x7b, 0xe3, 0x91, 0xc9,
0x26, 0x8c, 0x2a, 0xd8, 0xea, 0x8d, 0x47, 0xdd, 0x43, 0x52, 0xfd, 0x0a, 0xd2, 0xdd, 0x43, 0xfe,
0xda, 0x62, 0x14, 0x0d, 0x7d, 0x5f, 0x8e, 0x78, 0xf1, 0xb4, 0xca, 0x30, 0x01, 0x9f, 0x68, 0x08,
0x54, 0x36, 0xcb, 0xa3, 0xb7, 0x1f, 0xfb, 0x46, 0xfa, 0x2b, 0xbe, 0xe3, 0xe4, 0x0b, 0xee, 0xe6,
0xaf, 0x0a, 0xe4, 0x12, 0xf7, 0x03, 0x7a, 0x1f, 0x2e, 0xd5, 0x77, 0xf7, 0x1b, 0xf7, 0xcd, 0xe6,
0xb6, 0x79, 0x67, 0xb7, 0x76, 0xd7, 0x7c, 0xd0, 0xba, 0xdf, 0xda, 0xff, 0xbc, 0x55, 0x9c, 0x2b,
0x5f, 0x3e, 0x3e, 0xd1, 0x50, 0x02, 0xfb, 0xc0, 0x7b, 0xec, 0xf9, 0x5f, 0xb3, 0xab, 0x78, 0x75,
0xda, 0xa4, 0x56, 0xef, 0xec, 0xb4, 0xba, 0x45, 0xa5, 0x7c, 0xe9, 0xf8, 0x44, 0x5b, 0x49, 0x58,
0xd4, 0x7a, 0x04, 0x7b, 0x74, 0xd6, 0xa0, 0xb1, 0xbf, 0xb7, 0xd7, 0xec, 0x16, 0xe7, 0x67, 0x0c,
0xe4, 0x85, 0x7d, 0x03, 0x56, 0xa6, 0x0d, 0x5a, 0xcd, 0xdd, 0x62, 0xaa, 0x8c, 0x8e, 0x4f, 0xb4,
0xa5, 0x04, 0xba, 0xe5, 0xb8, 0xe5, 0xcc, 0x37, 0xdf, 0x55, 0xe6, 0x7e, 0xf8, 0xbe, 0xa2, 0xb0,
0xcc, 0x0a, 0x53, 0x77, 0x04, 0x7a, 0x0f, 0xae, 0x74, 0x9a, 0x77, 0x5b, 0x3b, 0xdb, 0xe6, 0x5e,
0xe7, 0xae, 0xd9, 0xfd, 0xa2, 0xbd, 0x93, 0xc8, 0x6e, 0xf9, 0xf8, 0x44, 0xcb, 0xc9, 0x94, 0x2e,
0x42, 0xb7, 0x8d, 0x9d, 0x87, 0xfb, 0xdd, 0x9d, 0xa2, 0x22, 0xd0, 0xed, 0x10, 0x1f, 0xf8, 0x14,
0x73, 0xf4, 0x2d, 0xb8, 0x7a, 0x0e, 0x3a, 0x4e, 0x6c, 0xe5, 0xf8, 0x44, 0x2b, 0xb4, 0x43, 0x2c,
0xce, 0x0f, 0xb7, 0xd0, 0xa1, 0x34, 0x6b, 0xb1, 0xdf, 0xde, 0xef, 0xd4, 0x76, 0x8b, 0x5a, 0xb9,
0x78, 0x7c, 0xa2, 0xe5, 0xa3, 0xcb, 0x90, 0xe1, 0x27, 0x99, 0xd5, 0x3f, 0x7b, 0x76, 0x5a, 0x51,
0x9e, 0x9f, 0x56, 0x94, 0x3f, 0x4f, 0x2b, 0xca, 0xd3, 0x97, 0x95, 0xb9, 0xe7, 0x2f, 0x2b, 0x73,
0xbf, 0xbd, 0xac, 0xcc, 0x7d, 0xf9, 0xd1, 0xc0, 0xa1, 0xc3, 0x71, 0x4f, 0xef, 0xfb, 0xa3, 0xcd,
0xe4, 0x3f, 0x8c, 0xc9, 0xa7, 0xf8, 0xa7, 0x73, 0xf6, 0xdf, 0x47, 0x6f, 0x91, 0xcb, 0x6f, 0xff,
0x1d, 0x00, 0x00, 0xff, 0xff, 0xbb, 0xc0, 0x81, 0x37, 0x3e, 0x0d, 0x00, 0x00,
// 1396 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x57, 0x4b, 0x6f, 0x1b, 0xd5,
0x17, 0xcf, 0xd8, 0xe3, 0xd7, 0xb1, 0x9d, 0x38, 0xf7, 0x9f, 0xb6, 0xae, 0xdb, 0x38, 0x96, 0xab,
0x3f, 0xa4, 0x05, 0x39, 0x25, 0x45, 0x3c, 0x16, 0x2c, 0x6c, 0xc7, 0x6d, 0xad, 0x26, 0x8e, 0x19,
0xbb, 0x45, 0x74, 0x33, 0x1a, 0x7b, 0x6e, 0xed, 0xa1, 0xf6, 0xcc, 0x68, 0xe6, 0x3a, 0x38, 0xfd,
0x04, 0x28, 0xab, 0xae, 0xd8, 0x65, 0x05, 0x0b, 0xf6, 0x20, 0xb1, 0x45, 0xac, 0xba, 0xec, 0x0e,
0x36, 0x14, 0x48, 0x25, 0x3e, 0x07, 0xba, 0x8f, 0x19, 0xcf, 0xc4, 0x31, 0x54, 0x51, 0x05, 0x12,
0x9b, 0x68, 0xee, 0x39, 0xbf, 0x73, 0xee, 0x79, 0xfc, 0xee, 0xc9, 0x31, 0x5c, 0x25, 0xd8, 0xd4,
0xb1, 0x33, 0x36, 0x4c, 0xb2, 0x45, 0x0e, 0x6d, 0xec, 0xf2, 0xbf, 0x15, 0xdb, 0xb1, 0x88, 0x85,
0x72, 0x33, 0x6d, 0x85, 0xc9, 0x0b, 0x6b, 0x03, 0x6b, 0x60, 0x31, 0xe5, 0x16, 0xfd, 0xe2, 0xb8,
0xc2, 0xc6, 0xc0, 0xb2, 0x06, 0x23, 0xbc, 0xc5, 0x4e, 0xbd, 0xc9, 0xa3, 0x2d, 0x62, 0x8c, 0xb1,
0x4b, 0xb4, 0xb1, 0x2d, 0x00, 0xeb, 0x81, 0x6b, 0xfa, 0xce, 0xa1, 0x4d, 0x2c, 0x8a, 0xb5, 0x1e,
0x09, 0x75, 0x31, 0xa0, 0x3e, 0xc0, 0x8e, 0x6b, 0x58, 0x66, 0x30, 0x8e, 0x42, 0x69, 0x2e, 0xca,
0x03, 0x6d, 0x64, 0xe8, 0x1a, 0xb1, 0x1c, 0x8e, 0x28, 0x7f, 0x08, 0xd9, 0xb6, 0xe6, 0x90, 0x0e,
0x26, 0x77, 0xb1, 0xa6, 0x63, 0x07, 0xad, 0x41, 0x8c, 0x58, 0x44, 0x1b, 0xe5, 0xa5, 0x92, 0xb4,
0x99, 0x55, 0xf8, 0x01, 0x21, 0x90, 0x87, 0x9a, 0x3b, 0xcc, 0x47, 0x4a, 0xd2, 0x66, 0x46, 0x61,
0xdf, 0xe5, 0x21, 0xc8, 0xd4, 0x94, 0x5a, 0x18, 0xa6, 0x8e, 0xa7, 0x9e, 0x05, 0x3b, 0x50, 0x69,
0xef, 0x90, 0x60, 0x57, 0x98, 0xf0, 0x03, 0x7a, 0x17, 0x62, 0x2c, 0xfe, 0x7c, 0xb4, 0x24, 0x6d,
0xa6, 0xb7, 0xf3, 0x95, 0x40, 0xa1, 0x78, 0x7e, 0x95, 0x36, 0xd5, 0xd7, 0xe4, 0x67, 0x2f, 0x36,
0x96, 0x14, 0x0e, 0x2e, 0x8f, 0x20, 0x51, 0x1b, 0x59, 0xfd, 0xc7, 0xcd, 0x1d, 0x3f, 0x10, 0x69,
0x16, 0x08, 0xda, 0x83, 0x15, 0x5b, 0x73, 0x88, 0xea, 0x62, 0xa2, 0x0e, 0x59, 0x16, 0xec, 0xd2,
0xf4, 0xf6, 0x46, 0xe5, 0x74, 0x1f, 0x2a, 0xa1, 0x64, 0xc5, 0x2d, 0x59, 0x3b, 0x28, 0x2c, 0xff,
0x21, 0x43, 0x5c, 0x14, 0xe3, 0x23, 0x48, 0x88, 0xb2, 0xb2, 0x0b, 0xd3, 0xdb, 0xeb, 0x41, 0x8f,
0x42, 0x55, 0xa9, 0x5b, 0xa6, 0x8b, 0x4d, 0x77, 0xe2, 0x0a, 0x7f, 0x9e, 0x0d, 0x7a, 0x03, 0x92,
0xfd, 0xa1, 0x66, 0x98, 0xaa, 0xa1, 0xb3, 0x88, 0x52, 0xb5, 0xf4, 0xc9, 0x8b, 0x8d, 0x44, 0x9d,
0xca, 0x9a, 0x3b, 0x4a, 0x82, 0x29, 0x9b, 0x3a, 0xba, 0x08, 0xf1, 0x21, 0x36, 0x06, 0x43, 0xc2,
0xca, 0x12, 0x55, 0xc4, 0x09, 0x7d, 0x00, 0x32, 0x25, 0x44, 0x5e, 0x66, 0x77, 0x17, 0x2a, 0x9c,
0x2d, 0x15, 0x8f, 0x2d, 0x95, 0xae, 0xc7, 0x96, 0x5a, 0x92, 0x5e, 0xfc, 0xf4, 0xd7, 0x0d, 0x49,
0x61, 0x16, 0xa8, 0x0e, 0xd9, 0x91, 0xe6, 0x12, 0xb5, 0x47, 0xcb, 0x46, 0xaf, 0x8f, 0x31, 0x17,
0x97, 0xe7, 0x0b, 0x22, 0x0a, 0x2b, 0x42, 0x4f, 0x53, 0x2b, 0x2e, 0xd2, 0xd1, 0x26, 0xe4, 0x98,
0x93, 0xbe, 0x35, 0x1e, 0x1b, 0x44, 0x65, 0x75, 0x8f, 0xb3, 0xba, 0x2f, 0x53, 0x79, 0x9d, 0x89,
0xef, 0xd2, 0x0e, 0x5c, 0x81, 0x94, 0xae, 0x11, 0x8d, 0x43, 0x12, 0x0c, 0x92, 0xa4, 0x02, 0xa6,
0x7c, 0x13, 0x56, 0x7c, 0xd6, 0xb9, 0x1c, 0x92, 0xe4, 0x5e, 0x66, 0x62, 0x06, 0xbc, 0x09, 0x6b,
0x26, 0x9e, 0x12, 0xf5, 0x34, 0x3a, 0xc5, 0xd0, 0x88, 0xea, 0x1e, 0x84, 0x2d, 0xfe, 0x0f, 0xcb,
0x7d, 0xaf, 0xf8, 0x1c, 0x0b, 0x0c, 0x9b, 0xf5, 0xa5, 0x0c, 0x76, 0x19, 0x92, 0x9a, 0x6d, 0x73,
0x40, 0x9a, 0x01, 0x12, 0x9a, 0x6d, 0x33, 0xd5, 0x0d, 0x58, 0x65, 0x39, 0x3a, 0xd8, 0x9d, 0x8c,
0x88, 0x70, 0x92, 0x61, 0x98, 0x15, 0xaa, 0x50, 0xb8, 0x9c, 0x61, 0xaf, 0x41, 0x16, 0x1f, 0x18,
0x3a, 0x36, 0xfb, 0x98, 0xe3, 0xb2, 0x0c, 0x97, 0xf1, 0x84, 0x0c, 0x74, 0x1d, 0x72, 0xb6, 0x63,
0xd9, 0x96, 0x8b, 0x1d, 0x55, 0xd3, 0x75, 0x07, 0xbb, 0x6e, 0x7e, 0x99, 0xfb, 0xf3, 0xe4, 0x55,
0x2e, 0x2e, 0xe7, 0x41, 0xde, 0xd1, 0x88, 0x86, 0x72, 0x10, 0x25, 0x53, 0x37, 0x2f, 0x95, 0xa2,
0x9b, 0x19, 0x85, 0x7e, 0x96, 0xbf, 0x8f, 0x82, 0xfc, 0xc0, 0x22, 0x18, 0xdd, 0x02, 0x99, 0xb6,
0x89, 0xb1, 0x6f, 0xf9, 0x2c, 0x3e, 0x77, 0x8c, 0x81, 0x89, 0xf5, 0x3d, 0x77, 0xd0, 0x3d, 0xb4,
0xb1, 0xc2, 0xc0, 0x01, 0x3a, 0x45, 0x42, 0x74, 0x5a, 0x83, 0x98, 0x63, 0x4d, 0x4c, 0x9d, 0xb1,
0x2c, 0xa6, 0xf0, 0x03, 0x6a, 0x40, 0xd2, 0x67, 0x89, 0xfc, 0x77, 0x2c, 0x59, 0xa1, 0x2c, 0xa1,
0x1c, 0x16, 0x02, 0x25, 0xd1, 0x13, 0x64, 0xa9, 0x41, 0xca, 0x1f, 0x5e, 0x82, 0x6d, 0xaf, 0x46,
0xd8, 0x99, 0x19, 0x7a, 0x0b, 0x56, 0xfd, 0xde, 0xfb, 0xc5, 0xe3, 0x8c, 0xcb, 0xf9, 0x0a, 0x51,
0xbd, 0x10, 0xad, 0x54, 0x3e, 0x80, 0x12, 0x2c, 0xaf, 0x19, 0xad, 0x9a, 0x6c, 0x12, 0x5d, 0x85,
0x94, 0x6b, 0x0c, 0x4c, 0x8d, 0x4c, 0x1c, 0x2c, 0x98, 0x37, 0x13, 0x50, 0x2d, 0x9e, 0x12, 0x6c,
0xb2, 0x47, 0xce, 0x99, 0x36, 0x13, 0xa0, 0x2d, 0xf8, 0x9f, 0x7f, 0x50, 0x67, 0x5e, 0x38, 0xcb,
0x90, 0xaf, 0xea, 0x78, 0x9a, 0xf2, 0x0f, 0x12, 0xc4, 0xf9, 0xc3, 0x08, 0xb4, 0x41, 0x3a, 0xbb,
0x0d, 0x91, 0x45, 0x6d, 0x88, 0x9e, 0xbf, 0x0d, 0x55, 0x00, 0x3f, 0x4c, 0x37, 0x2f, 0x97, 0xa2,
0x9b, 0xe9, 0xed, 0x2b, 0xf3, 0x8e, 0x78, 0x88, 0x1d, 0x63, 0x20, 0xde, 0x7d, 0xc0, 0xa8, 0xfc,
0x8b, 0x04, 0x29, 0x5f, 0x8f, 0xaa, 0x90, 0xf5, 0xe2, 0x52, 0x1f, 0x8d, 0xb4, 0x81, 0xa0, 0xe2,
0xfa, 0xc2, 0xe0, 0x6e, 0x8f, 0xb4, 0x81, 0x92, 0x16, 0xf1, 0xd0, 0xc3, 0xd9, 0x6d, 0x8d, 0x2c,
0x68, 0x6b, 0x88, 0x47, 0xd1, 0xf3, 0xf1, 0x28, 0xd4, 0x71, 0xf9, 0x54, 0xc7, 0xcb, 0xbf, 0x4b,
0xb0, 0xdc, 0x98, 0xb2, 0xf0, 0xf5, 0x7f, 0xb3, 0x55, 0x0f, 0x05, 0xb7, 0x74, 0xac, 0xab, 0x73,
0x3d, 0xbb, 0x36, 0xef, 0x31, 0x1c, 0xf3, 0xac, 0x77, 0xc8, 0xf3, 0xd2, 0x99, 0xf5, 0xf0, 0xbb,
0x08, 0xac, 0xce, 0xe1, 0xff, 0x7b, 0xbd, 0x0c, 0xbf, 0xde, 0xd8, 0x2b, 0xbe, 0xde, 0xf8, 0xc2,
0xd7, 0xfb, 0x6d, 0x04, 0x92, 0x6d, 0x36, 0xa5, 0xb5, 0xd1, 0x3f, 0x31, 0x7b, 0xaf, 0x40, 0xca,
0xb6, 0x46, 0x2a, 0xd7, 0xc8, 0x4c, 0x93, 0xb4, 0xad, 0x91, 0x32, 0x47, 0xb3, 0xd8, 0x6b, 0x1a,
0xcc, 0xf1, 0xd7, 0xd0, 0x84, 0xc4, 0xe9, 0x07, 0xe5, 0x40, 0x86, 0x97, 0x42, 0x6c, 0x4d, 0x37,
0x69, 0x0d, 0xd8, 0x1a, 0x26, 0xcd, 0x6f, 0x79, 0x3c, 0x6c, 0x8e, 0x54, 0x04, 0x8e, 0x5a, 0xf0,
0x25, 0x43, 0x2c, 0x6e, 0xf9, 0x45, 0x13, 0x4b, 0x11, 0xb8, 0xf2, 0x97, 0x12, 0xc0, 0x2e, 0xad,
0x2c, 0xcb, 0x97, 0xee, 0x3b, 0x2e, 0x0b, 0x41, 0x0d, 0xdd, 0x5c, 0x5c, 0xd4, 0x34, 0x71, 0x7f,
0xc6, 0x0d, 0xc6, 0x5d, 0x87, 0xec, 0x8c, 0xdb, 0x2e, 0xf6, 0x82, 0x39, 0xc3, 0x89, 0xbf, 0x86,
0x74, 0x30, 0x51, 0x32, 0x07, 0x81, 0x53, 0xf9, 0x47, 0x09, 0x52, 0x2c, 0xa6, 0x3d, 0x4c, 0xb4,
0x50, 0x0f, 0xa5, 0xf3, 0xf7, 0x70, 0x1d, 0x80, 0xbb, 0x71, 0x8d, 0x27, 0x58, 0x30, 0x2b, 0xc5,
0x24, 0x1d, 0xe3, 0x09, 0x46, 0xef, 0xf9, 0x05, 0x8f, 0xfe, 0x75, 0xc1, 0xc5, 0xc4, 0xf0, 0xca,
0x7e, 0x09, 0x12, 0xe6, 0x64, 0xac, 0xd2, 0xe5, 0x43, 0xe6, 0x6c, 0x35, 0x27, 0xe3, 0xee, 0xd4,
0x2d, 0x7f, 0x06, 0x89, 0xee, 0x94, 0x2d, 0xe2, 0x94, 0xa2, 0x8e, 0x65, 0x89, 0xed, 0x8f, 0x6f,
0xdd, 0x49, 0x2a, 0x60, 0xcb, 0x0e, 0x02, 0x99, 0xae, 0x79, 0xde, 0xcf, 0x02, 0xfa, 0x8d, 0x2a,
0xaf, 0xb8, 0xe2, 0x8b, 0xe5, 0xfe, 0xc6, 0x4f, 0x12, 0xa4, 0x03, 0xe3, 0x06, 0xbd, 0x03, 0x17,
0x6a, 0xbb, 0xfb, 0xf5, 0x7b, 0x6a, 0x73, 0x47, 0xbd, 0xbd, 0x5b, 0xbd, 0xa3, 0xde, 0x6f, 0xdd,
0x6b, 0xed, 0x7f, 0xd2, 0xca, 0x2d, 0x15, 0x2e, 0x1e, 0x1d, 0x97, 0x50, 0x00, 0x7b, 0xdf, 0x7c,
0x6c, 0x5a, 0x9f, 0xd3, 0x77, 0xbe, 0x16, 0x36, 0xa9, 0xd6, 0x3a, 0x8d, 0x56, 0x37, 0x27, 0x15,
0x2e, 0x1c, 0x1d, 0x97, 0x56, 0x03, 0x16, 0xd5, 0x9e, 0x8b, 0x4d, 0x32, 0x6f, 0x50, 0xdf, 0xdf,
0xdb, 0x6b, 0x76, 0x73, 0x91, 0x39, 0x03, 0xf1, 0x0f, 0xe2, 0x3a, 0xac, 0x86, 0x0d, 0x5a, 0xcd,
0xdd, 0x5c, 0xb4, 0x80, 0x8e, 0x8e, 0x4b, 0xcb, 0x01, 0x74, 0xcb, 0x18, 0x15, 0x92, 0x5f, 0x7c,
0x55, 0x5c, 0xfa, 0xe6, 0xeb, 0xa2, 0x44, 0x33, 0xcb, 0x86, 0x66, 0x04, 0x7a, 0x1b, 0x2e, 0x75,
0x9a, 0x77, 0x5a, 0x8d, 0x1d, 0x75, 0xaf, 0x73, 0x47, 0xed, 0x7e, 0xda, 0x6e, 0x04, 0xb2, 0x5b,
0x39, 0x3a, 0x2e, 0xa5, 0x45, 0x4a, 0x8b, 0xd0, 0x6d, 0xa5, 0xf1, 0x60, 0xbf, 0xdb, 0xc8, 0x49,
0x1c, 0xdd, 0x76, 0xf0, 0x81, 0x45, 0x30, 0x43, 0xdf, 0x84, 0xcb, 0x67, 0xa0, 0xfd, 0xc4, 0x56,
0x8f, 0x8e, 0x4b, 0xd9, 0xb6, 0x83, 0xf9, 0xfb, 0x61, 0x16, 0x15, 0xc8, 0xcf, 0x5b, 0xec, 0xb7,
0xf7, 0x3b, 0xd5, 0xdd, 0x5c, 0xa9, 0x90, 0x3b, 0x3a, 0x2e, 0x65, 0xbc, 0x61, 0x48, 0xf1, 0xb3,
0xcc, 0x6a, 0x1f, 0x3f, 0x3b, 0x29, 0x4a, 0xcf, 0x4f, 0x8a, 0xd2, 0x6f, 0x27, 0x45, 0xe9, 0xe9,
0xcb, 0xe2, 0xd2, 0xf3, 0x97, 0xc5, 0xa5, 0x9f, 0x5f, 0x16, 0x97, 0x1e, 0xbe, 0x3f, 0x30, 0xc8,
0x70, 0xd2, 0xab, 0xf4, 0xad, 0xf1, 0x56, 0xf0, 0xc7, 0xe7, 0xec, 0x93, 0xff, 0x08, 0x3e, 0xfd,
0xc3, 0xb4, 0x17, 0x67, 0xf2, 0x5b, 0x7f, 0x06, 0x00, 0x00, 0xff, 0xff, 0x46, 0xcf, 0x37, 0x28,
0x59, 0x0f, 0x00, 0x00,
}
func (m *PartSetHeader) Marshal() (dAtA []byte, err error) {
@@ -1634,6 +1796,127 @@ func (m *CommitSig) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *ExtendedCommit) 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 *ExtendedCommit) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ExtendedCommit) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.ExtendedSignatures) > 0 {
for iNdEx := len(m.ExtendedSignatures) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.ExtendedSignatures[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintTypes(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x22
}
}
{
size, err := m.BlockID.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintTypes(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
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 *ExtendedCommitSig) 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 *ExtendedCommitSig) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *ExtendedCommitSig) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.ExtensionSignature) > 0 {
i -= len(m.ExtensionSignature)
copy(dAtA[i:], m.ExtensionSignature)
i = encodeVarintTypes(dAtA, i, uint64(len(m.ExtensionSignature)))
i--
dAtA[i] = 0x32
}
if len(m.Extension) > 0 {
i -= len(m.Extension)
copy(dAtA[i:], m.Extension)
i = encodeVarintTypes(dAtA, i, uint64(len(m.Extension)))
i--
dAtA[i] = 0x2a
}
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] = 0x22
}
n11, err11 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):])
if err11 != nil {
return 0, err11
}
i -= n11
i = encodeVarintTypes(dAtA, i, uint64(n11))
i--
dAtA[i] = 0x1a
if len(m.ValidatorAddress) > 0 {
i -= len(m.ValidatorAddress)
copy(dAtA[i:], m.ValidatorAddress)
i = encodeVarintTypes(dAtA, i, uint64(len(m.ValidatorAddress)))
i--
dAtA[i] = 0x12
}
if m.BlockIdFlag != 0 {
i = encodeVarintTypes(dAtA, i, uint64(m.BlockIdFlag))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *Proposal) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -1661,12 +1944,12 @@ func (m *Proposal) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i--
dAtA[i] = 0x3a
}
n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):])
if err10 != nil {
return 0, err10
n12, err12 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):])
if err12 != nil {
return 0, err12
}
i -= n10
i = encodeVarintTypes(dAtA, i, uint64(n10))
i -= n12
i = encodeVarintTypes(dAtA, i, uint64(n12))
i--
dAtA[i] = 0x32
{
@@ -2117,6 +2400,59 @@ func (m *CommitSig) Size() (n int) {
return n
}
func (m *ExtendedCommit) 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))
}
l = m.BlockID.Size()
n += 1 + l + sovTypes(uint64(l))
if len(m.ExtendedSignatures) > 0 {
for _, e := range m.ExtendedSignatures {
l = e.Size()
n += 1 + l + sovTypes(uint64(l))
}
}
return n
}
func (m *ExtendedCommitSig) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.BlockIdFlag != 0 {
n += 1 + sovTypes(uint64(m.BlockIdFlag))
}
l = len(m.ValidatorAddress)
if l > 0 {
n += 1 + l + sovTypes(uint64(l))
}
l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp)
n += 1 + l + sovTypes(uint64(l))
l = len(m.Signature)
if l > 0 {
n += 1 + l + sovTypes(uint64(l))
}
l = len(m.Extension)
if l > 0 {
n += 1 + l + sovTypes(uint64(l))
}
l = len(m.ExtensionSignature)
if l > 0 {
n += 1 + l + sovTypes(uint64(l))
}
return n
}
func (m *Proposal) Size() (n int) {
if m == nil {
return 0
@@ -3823,6 +4159,399 @@ func (m *CommitSig) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *ExtendedCommit) 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: ExtendedCommit: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ExtendedCommit: 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 != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field BlockID", 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 := m.BlockID.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ExtendedSignatures", 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
}
m.ExtendedSignatures = append(m.ExtendedSignatures, ExtendedCommitSig{})
if err := m.ExtendedSignatures[len(m.ExtendedSignatures)-1].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 *ExtendedCommitSig) 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: ExtendedCommitSig: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ExtendedCommitSig: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field BlockIdFlag", wireType)
}
m.BlockIdFlag = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTypes
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.BlockIdFlag |= BlockIDFlag(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", 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.ValidatorAddress = append(m.ValidatorAddress[:0], dAtA[iNdEx:postIndex]...)
if m.ValidatorAddress == nil {
m.ValidatorAddress = []byte{}
}
iNdEx = postIndex
case 3:
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 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_gogo_protobuf_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 4:
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 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Extension", 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.Extension = append(m.Extension[:0], dAtA[iNdEx:postIndex]...)
if m.Extension == nil {
m.Extension = []byte{}
}
iNdEx = postIndex
case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ExtensionSignature", 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.ExtensionSignature = append(m.ExtensionSignature[:0], dAtA[iNdEx:postIndex]...)
if m.ExtensionSignature == nil {
m.ExtensionSignature = []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 *Proposal) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0

View File

@@ -142,6 +142,28 @@ message CommitSig {
bytes signature = 4;
}
message ExtendedCommit {
int64 height = 1;
int32 round = 2;
BlockID block_id = 3
[(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"];
repeated ExtendedCommitSig extended_signatures = 4 [(gogoproto.nullable) = false];
}
// ExtendedCommitSig retains all the same fields as CommitSig but adds vote
// extension-related fields.
message ExtendedCommitSig {
BlockIDFlag block_id_flag = 1;
bytes validator_address = 2;
google.protobuf.Timestamp timestamp = 3
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
bytes signature = 4;
// Vote extension data
bytes extension = 5;
// Vote extension signature
bytes extension_signature = 6;
}
message Proposal {
SignedMsgType type = 1;
int64 height = 2;

View File

@@ -17,6 +17,7 @@ import (
"github.com/creachadair/tomledit"
"github.com/creachadair/tomledit/transform"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/config"
)

View File

@@ -165,9 +165,9 @@ func generateLightClientAttackEvidence(
// create a commit for the forged header
blockID := makeBlockID(header.Hash(), 1000, []byte("partshash"))
voteSet := types.NewVoteSet(chainID, forgedHeight, 0, tmproto.SignedMsgType(2), conflictingVals)
voteSet := types.NewVoteSet(chainID, forgedHeight, 0, tmproto.SignedMsgType(2), conflictingVals, false)
commit, err := factory.MakeCommit(ctx, blockID, forgedHeight, 0, voteSet, pv, forgedTime)
commit, err := factory.MakeExtendedCommit(ctx, blockID, forgedHeight, 0, voteSet, pv, forgedTime)
if err != nil {
return nil, err
}
@@ -176,7 +176,7 @@ func generateLightClientAttackEvidence(
ConflictingBlock: &types.LightBlock{
SignedHeader: &types.SignedHeader{
Header: header,
Commit: commit,
Commit: commit.StripExtensions(),
},
ValidatorSet: conflictingVals,
},

View File

@@ -608,16 +608,6 @@ type CommitSig struct {
Signature []byte `json:"signature"`
}
// NewCommitSigForBlock returns new CommitSig with BlockIDFlagCommit.
func NewCommitSigForBlock(signature []byte, valAddr Address, ts time.Time) CommitSig {
return CommitSig{
BlockIDFlag: BlockIDFlagCommit,
ValidatorAddress: valAddr,
Timestamp: ts,
Signature: signature,
}
}
func MaxCommitBytes(valCount int) int64 {
// From the repeated commit sig field
var protoEncodingOverhead int64 = 2
@@ -632,16 +622,6 @@ func NewCommitSigAbsent() CommitSig {
}
}
// ForBlock returns true if CommitSig is for the block.
func (cs CommitSig) ForBlock() bool {
return cs.BlockIDFlag == BlockIDFlagCommit
}
// Absent returns true if CommitSig is absent.
func (cs CommitSig) Absent() bool {
return cs.BlockIDFlag == BlockIDFlagAbsent
}
// CommitSig returns a string representation of CommitSig.
//
// 1. first 6 bytes of signature
@@ -730,7 +710,6 @@ func (cs *CommitSig) ToProto() *tmproto.CommitSig {
// FromProto sets a protobuf CommitSig to the given pointer.
// It returns an error if the CommitSig is invalid.
func (cs *CommitSig) FromProto(csp tmproto.CommitSig) error {
cs.BlockIDFlag = BlockIDFlag(csp.BlockIdFlag)
cs.ValidatorAddress = csp.ValidatorAddress
cs.Timestamp = csp.Timestamp
@@ -741,6 +720,99 @@ func (cs *CommitSig) FromProto(csp tmproto.CommitSig) error {
//-------------------------------------
// ExtendedCommitSig contains a commit signature along with its corresponding
// vote extension and vote extension signature.
type ExtendedCommitSig struct {
CommitSig // Commit signature
Extension []byte // Vote extension
ExtensionSignature []byte // Vote extension signature
}
// NewExtendedCommitSigAbsent returns new ExtendedCommitSig with
// BlockIDFlagAbsent. Other fields are all empty.
func NewExtendedCommitSigAbsent() ExtendedCommitSig {
return ExtendedCommitSig{CommitSig: NewCommitSigAbsent()}
}
// String returns a string representation of an ExtendedCommitSig.
//
// 1. commit sig
// 2. first 6 bytes of vote extension
// 3. first 6 bytes of vote extension signature
func (ecs ExtendedCommitSig) String() string {
return fmt.Sprintf("ExtendedCommitSig{%s with %X %X}",
ecs.CommitSig,
tmbytes.Fingerprint(ecs.Extension),
tmbytes.Fingerprint(ecs.ExtensionSignature),
)
}
// ValidateBasic checks whether the structure is well-formed.
func (ecs ExtendedCommitSig) ValidateBasic() error {
if err := ecs.CommitSig.ValidateBasic(); err != nil {
return err
}
if ecs.BlockIDFlag == BlockIDFlagCommit {
if len(ecs.Extension) > MaxVoteExtensionSize {
return fmt.Errorf("vote extension is too big (max: %d)", MaxVoteExtensionSize)
}
if len(ecs.ExtensionSignature) > MaxSignatureSize {
return fmt.Errorf("vote extension signature is too big (max: %d)", MaxSignatureSize)
}
return nil
}
// We expect there to not be any vote extension or vote extension signature
// on nil or absent votes.
if len(ecs.Extension) != 0 {
return fmt.Errorf("vote extension is present for commit sig with block ID flag %v", ecs.BlockIDFlag)
}
if len(ecs.ExtensionSignature) != 0 {
return fmt.Errorf("vote extension signature is present for commit sig with block ID flag %v", ecs.BlockIDFlag)
}
return nil
}
func (ecs ExtendedCommitSig) ValidateExtension() error {
if len(ecs.ExtensionSignature) == 0 {
return errors.New("vote extension signature is missing")
}
return nil
}
// ToProto converts the ExtendedCommitSig to its Protobuf representation.
func (ecs *ExtendedCommitSig) ToProto() *tmproto.ExtendedCommitSig {
if ecs == nil {
return nil
}
return &tmproto.ExtendedCommitSig{
BlockIdFlag: tmproto.BlockIDFlag(ecs.BlockIDFlag),
ValidatorAddress: ecs.ValidatorAddress,
Timestamp: ecs.Timestamp,
Signature: ecs.Signature,
Extension: ecs.Extension,
ExtensionSignature: ecs.ExtensionSignature,
}
}
// FromProto populates the ExtendedCommitSig with values from the given
// Protobuf representation. Returns an error if the ExtendedCommitSig is
// invalid.
func (ecs *ExtendedCommitSig) FromProto(ecsp tmproto.ExtendedCommitSig) error {
ecs.BlockIDFlag = BlockIDFlag(ecsp.BlockIdFlag)
ecs.ValidatorAddress = ecsp.ValidatorAddress
ecs.Timestamp = ecsp.Timestamp
ecs.Signature = ecsp.Signature
ecs.Extension = ecsp.Extension
ecs.ExtensionSignature = ecsp.ExtensionSignature
return ecs.ValidateBasic()
}
//-------------------------------------
// Commit contains the evidence that a block was committed by a set of validators.
// NOTE: Commit is empty for height 1, but never nil.
type Commit struct {
@@ -756,42 +828,12 @@ type Commit struct {
// Memoized in first call to corresponding method.
// NOTE: can't memoize in constructor because constructor isn't used for
// unmarshaling.
hash tmbytes.HexBytes
bitArray *bits.BitArray
hash tmbytes.HexBytes
}
// NewCommit returns a new Commit.
func NewCommit(height int64, round int32, blockID BlockID, commitSigs []CommitSig) *Commit {
return &Commit{
Height: height,
Round: round,
BlockID: blockID,
Signatures: commitSigs,
}
}
// CommitToVoteSet constructs a VoteSet from the Commit and validator set.
// Panics if signatures from the commit can't be added to the voteset.
// Inverse of VoteSet.MakeCommit().
func CommitToVoteSet(chainID string, commit *Commit, vals *ValidatorSet) *VoteSet {
voteSet := NewVoteSet(chainID, commit.Height, commit.Round, tmproto.PrecommitType, vals)
for idx, commitSig := range commit.Signatures {
if commitSig.Absent() {
continue // OK, some precommits can be missing.
}
vote := commit.GetVote(int32(idx))
if err := vote.ValidateBasic(); err != nil {
panic(fmt.Errorf("failed to validate vote reconstructed from LastCommit: %w", err))
}
added, err := voteSet.AddVote(vote)
if !added || err != nil {
panic(fmt.Errorf("failed to reconstruct LastCommit: %w", err))
}
}
return voteSet
}
// GetVote converts the CommitSig for the given valIdx to a Vote.
// 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.
// Returns nil if the precommit at valIdx is nil.
// Panics if valIdx >= commit.Size().
func (commit *Commit) GetVote(valIdx int32) *Vote {
@@ -822,26 +864,7 @@ func (commit *Commit) VoteSignBytes(chainID string, valIdx int32) []byte {
return VoteSignBytes(chainID, v)
}
// Type returns the vote type of the commit, which is always VoteTypePrecommit
// Implements VoteSetReader.
func (commit *Commit) Type() byte {
return byte(tmproto.PrecommitType)
}
// GetHeight returns height of the commit.
// Implements VoteSetReader.
func (commit *Commit) GetHeight() int64 {
return commit.Height
}
// GetRound returns height of the commit.
// Implements VoteSetReader.
func (commit *Commit) GetRound() int32 {
return commit.Round
}
// Size returns the number of signatures in the commit.
// Implements VoteSetReader.
func (commit *Commit) Size() int {
if commit == nil {
return 0
@@ -849,33 +872,6 @@ func (commit *Commit) Size() int {
return len(commit.Signatures)
}
// BitArray returns a BitArray of which validators voted for BlockID or nil in this commit.
// Implements VoteSetReader.
func (commit *Commit) BitArray() *bits.BitArray {
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.Absent())
}
}
return commit.bitArray
}
// GetByIndex returns the vote corresponding to a given validator index.
// Panics if `index >= commit.Size()`.
// Implements VoteSetReader.
func (commit *Commit) GetByIndex(valIdx int32) *Vote {
return commit.GetVote(valIdx)
}
// IsCommit returns true if there is at least one signature.
// Implements VoteSetReader.
func (commit *Commit) IsCommit() bool {
return len(commit.Signatures) != 0
}
// ValidateBasic performs basic validation that doesn't involve state data.
// Does not actually check the cryptographic signatures.
func (commit *Commit) ValidateBasic() error {
@@ -999,7 +995,246 @@ func CommitFromProto(cp *tmproto.Commit) (*Commit, error) {
return commit, commit.ValidateBasic()
}
//-----------------------------------------------------------------------------
//-------------------------------------
// ExtendedCommit is similar to Commit, except that its signatures also retain
// their corresponding vote extensions and vote extension signatures.
type ExtendedCommit struct {
Height int64
Round int32
BlockID BlockID
ExtendedSignatures []ExtendedCommitSig
bitArray *bits.BitArray
}
// Clone creates a deep copy of this extended commit.
func (ec *ExtendedCommit) Clone() *ExtendedCommit {
sigs := make([]ExtendedCommitSig, len(ec.ExtendedSignatures))
copy(sigs, ec.ExtendedSignatures)
ecc := *ec
ecc.ExtendedSignatures = sigs
return &ecc
}
// ToVoteSet constructs a VoteSet from the Commit and validator set.
// Panics if signatures from the commit can't be added to the voteset.
// Inverse of VoteSet.MakeExtendedCommit().
func (ec *ExtendedCommit) ToVoteSet(chainID string, vals *ValidatorSet, requireExtensions bool) *VoteSet {
voteSet := NewVoteSet(chainID, ec.Height, ec.Round, tmproto.PrecommitType, vals, requireExtensions)
for idx, ecs := range ec.ExtendedSignatures {
if ecs.BlockIDFlag == BlockIDFlagAbsent {
continue // OK, some precommits can be missing.
}
vote := ec.GetExtendedVote(int32(idx))
if err := vote.ValidateBasic(); err != nil {
panic(fmt.Errorf("failed to validate vote reconstructed from LastCommit: %w", err))
}
added, err := voteSet.AddVote(vote)
if !added || err != nil {
panic(fmt.Errorf("failed to reconstruct vote set from extended commit: %w", err))
}
}
return voteSet
}
// ToVoteSet constructs a VoteSet from the Commit and validator set.
// Panics if signatures from the commit can't be added to the voteset.
// Inverse of VoteSet.MakeCommit().
func (c *Commit) ToVoteSet(chainID string, vals *ValidatorSet) *VoteSet {
voteSet := NewVoteSet(chainID, c.Height, c.Round, tmproto.PrecommitType, vals, false)
for idx, cs := range c.Signatures {
if cs.BlockIDFlag == BlockIDFlagAbsent {
continue // OK, some precommits can be missing.
}
vote := c.GetVote(int32(idx))
if err := vote.ValidateBasic(); err != nil {
panic(fmt.Errorf("failed to validate vote reconstructed from commit: %w", err))
}
added, err := voteSet.AddVote(vote)
if !added || err != nil {
panic(fmt.Errorf("failed to reconstruct vote set from commit: %w", err))
}
}
return voteSet
}
// TODO Comment
// this should probably also verify the signature
// probably want to change to just verify when present.
func (ec *ExtendedCommit) EnsureExtensions() error {
for idx, ecs := range ec.ExtendedSignatures {
if ecs.BlockIDFlag == BlockIDFlagAbsent {
continue
}
vote := ec.GetExtendedVote(int32(idx))
if err := vote.EnsureExtension(); err != nil {
return err
}
}
return nil
}
// StripExtensions converts an ExtendedCommit to a Commit by removing all vote
// extension-related fields.
func (ec *ExtendedCommit) StripExtensions() *Commit {
cs := make([]CommitSig, len(ec.ExtendedSignatures))
for idx, ecs := range ec.ExtendedSignatures {
cs[idx] = ecs.CommitSig
}
return &Commit{
Height: ec.Height,
Round: ec.Round,
BlockID: ec.BlockID,
Signatures: cs,
}
}
// GetExtendedVote converts the ExtendedCommitSig for the given validator
// index to a Vote with a vote extensions.
// It panics if valIndex is out of range.
func (ec *ExtendedCommit) GetExtendedVote(valIndex int32) *Vote {
ecs := ec.ExtendedSignatures[valIndex]
return &Vote{
Type: tmproto.PrecommitType,
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,
}
}
// Type returns the vote type of the extended commit, which is always
// VoteTypePrecommit
// Implements VoteSetReader.
func (ec *ExtendedCommit) Type() byte { return byte(tmproto.PrecommitType) }
// GetHeight returns height of the extended commit.
// Implements VoteSetReader.
func (ec *ExtendedCommit) GetHeight() int64 { return ec.Height }
// GetRound returns height of the extended commit.
// Implements VoteSetReader.
func (ec *ExtendedCommit) GetRound() int32 { return ec.Round }
// Size returns the number of signatures in the extended commit.
// Implements VoteSetReader.
func (ec *ExtendedCommit) Size() int {
if ec == nil {
return 0
}
return len(ec.ExtendedSignatures)
}
// BitArray returns a BitArray of which validators voted for BlockID or nil in
// this extended commit.
// Implements VoteSetReader.
func (ec *ExtendedCommit) BitArray() *bits.BitArray {
if ec.bitArray == nil {
ec.bitArray = bits.NewBitArray(len(ec.ExtendedSignatures))
for i, extCommitSig := range ec.ExtendedSignatures {
// TODO: need to check the BlockID otherwise we could be counting conflicts,
// not just the one with +2/3 !
ec.bitArray.SetIndex(i, extCommitSig.BlockIDFlag != BlockIDFlagAbsent)
}
}
return ec.bitArray
}
// GetByIndex returns the vote corresponding to a given validator index.
// Panics if `index >= extCommit.Size()`.
// Implements VoteSetReader.
func (ec *ExtendedCommit) GetByIndex(valIdx int32) *Vote {
return ec.GetExtendedVote(valIdx)
}
// IsCommit returns true if there is at least one signature.
// Implements VoteSetReader.
func (ec *ExtendedCommit) IsCommit() bool {
return len(ec.ExtendedSignatures) != 0
}
// ValidateBasic checks whether the extended commit is well-formed. Does not
// actually check the cryptographic signatures.
func (ec *ExtendedCommit) ValidateBasic() error {
if ec.Height < 0 {
return errors.New("negative Height")
}
if ec.Round < 0 {
return errors.New("negative Round")
}
if ec.Height >= 1 {
if ec.BlockID.IsNil() {
return errors.New("commit cannot be for nil block")
}
if len(ec.ExtendedSignatures) == 0 {
return errors.New("no signatures in commit")
}
for i, extCommitSig := range ec.ExtendedSignatures {
if err := extCommitSig.ValidateBasic(); err != nil {
return fmt.Errorf("wrong ExtendedCommitSig #%d: %v", i, err)
}
}
}
return nil
}
// ToProto converts ExtendedCommit to protobuf
func (ec *ExtendedCommit) ToProto() *tmproto.ExtendedCommit {
if ec == nil {
return nil
}
c := new(tmproto.ExtendedCommit)
sigs := make([]tmproto.ExtendedCommitSig, len(ec.ExtendedSignatures))
for i := range ec.ExtendedSignatures {
sigs[i] = *ec.ExtendedSignatures[i].ToProto()
}
c.ExtendedSignatures = sigs
c.Height = ec.Height
c.Round = ec.Round
c.BlockID = ec.BlockID.ToProto()
return c
}
// ExtendedCommitFromProto constructs an ExtendedCommit from the given Protobuf
// representation. It returns an error if the extended commit is invalid.
func ExtendedCommitFromProto(ecp *tmproto.ExtendedCommit) (*ExtendedCommit, error) {
if ecp == nil {
return nil, errors.New("nil ExtendedCommit")
}
extCommit := new(ExtendedCommit)
bi, err := BlockIDFromProto(&ecp.BlockID)
if err != nil {
return nil, err
}
sigs := make([]ExtendedCommitSig, len(ecp.ExtendedSignatures))
for i := range ecp.ExtendedSignatures {
if err := sigs[i].FromProto(ecp.ExtendedSignatures[i]); err != nil {
return nil, err
}
}
extCommit.ExtendedSignatures = sigs
extCommit.Height = ecp.Height
extCommit.Round = ecp.Round
extCommit.BlockID = *bi
return extCommit, extCommit.ValidateBasic()
}
//-------------------------------------
// Data contains the set of transactions included in the block
type Data struct {
@@ -1170,3 +1405,9 @@ func BlockIDFromProto(bID *tmproto.BlockID) (*BlockID, error) {
return blockID, blockID.ValidateBasic()
}
// ProtoBlockIDIsNil is similar to the IsNil function on BlockID, but for the
// Protobuf representation.
func ProtoBlockIDIsNil(bID *tmproto.BlockID) bool {
return len(bID.Hash) == 0 && ProtoPartSetHeaderIsZero(&bID.PartSetHeader)
}

View File

@@ -42,14 +42,14 @@ func TestBlockAddEvidence(t *testing.T) {
h := int64(3)
voteSet, _, vals := randVoteSet(ctx, t, h-1, 1, tmproto.PrecommitType, 10, 1)
commit, err := makeCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
extCommit, err := makeExtCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
require.NoError(t, err)
ev, err := NewMockDuplicateVoteEvidenceWithValidator(ctx, h, time.Now(), vals[0], "block-test-chain")
require.NoError(t, err)
evList := []Evidence{ev}
block := MakeBlock(h, txs, commit, evList)
block := MakeBlock(h, txs, extCommit.StripExtensions(), evList)
require.NotNil(t, block)
require.Equal(t, 1, len(block.Evidence))
require.NotNil(t, block.EvidenceHash)
@@ -66,9 +66,9 @@ func TestBlockValidateBasic(t *testing.T) {
h := int64(3)
voteSet, valSet, vals := randVoteSet(ctx, t, h-1, 1, tmproto.PrecommitType, 10, 1)
commit, err := makeCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
extCommit, err := makeExtCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
require.NoError(t, err)
commit := extCommit.StripExtensions()
ev, err := NewMockDuplicateVoteEvidenceWithValidator(ctx, h, time.Now(), vals[0], "block-test-chain")
require.NoError(t, err)
@@ -104,7 +104,10 @@ func TestBlockValidateBasic(t *testing.T) {
blk.LastCommit = nil
}, true},
{"Invalid LastCommit", func(blk *Block) {
blk.LastCommit = NewCommit(-1, 0, *voteSet.maj23, nil)
blk.LastCommit = &Commit{
Height: -1,
BlockID: *voteSet.maj23,
}
}, true},
{"Invalid Evidence", func(blk *Block) {
emptyEv := &DuplicateVoteEvidence{}
@@ -153,15 +156,14 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) {
h := int64(3)
voteSet, _, vals := randVoteSet(ctx, t, h-1, 1, tmproto.PrecommitType, 10, 1)
commit, err := makeCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
extCommit, err := makeExtCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
require.NoError(t, err)
ev, err := NewMockDuplicateVoteEvidenceWithValidator(ctx, h, time.Now(), vals[0], "block-test-chain")
require.NoError(t, err)
evList := []Evidence{ev}
partSet, err := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList).MakePartSet(512)
partSet, err := MakeBlock(h, []Tx{Tx("Hello World")}, extCommit.StripExtensions(), evList).MakePartSet(512)
require.NoError(t, err)
assert.NotNil(t, partSet)
@@ -178,14 +180,14 @@ func TestBlockHashesTo(t *testing.T) {
h := int64(3)
voteSet, valSet, vals := randVoteSet(ctx, t, h-1, 1, tmproto.PrecommitType, 10, 1)
commit, err := makeCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
extCommit, err := makeExtCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
require.NoError(t, err)
ev, err := NewMockDuplicateVoteEvidenceWithValidator(ctx, h, time.Now(), vals[0], "block-test-chain")
require.NoError(t, err)
evList := []Evidence{ev}
block := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList)
block := MakeBlock(h, []Tx{Tx("Hello World")}, extCommit.StripExtensions(), evList)
block.ValidatorsHash = valSet.Hash()
assert.False(t, block.HashesTo([]byte{}))
assert.False(t, block.HashesTo([]byte("something else")))
@@ -260,7 +262,7 @@ func TestCommit(t *testing.T) {
lastID := makeBlockIDRandom()
h := int64(3)
voteSet, _, vals := randVoteSet(ctx, t, h-1, 1, tmproto.PrecommitType, 10, 1)
commit, err := makeCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
commit, err := makeExtCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
require.NoError(t, err)
assert.Equal(t, h-1, commit.Height)
@@ -273,7 +275,7 @@ func TestCommit(t *testing.T) {
require.NotNil(t, commit.BitArray())
assert.Equal(t, bits.NewBitArray(10).Size(), commit.BitArray().Size())
assert.Equal(t, voteWithoutExtension(voteSet.GetByIndex(0)), commit.GetByIndex(0))
assert.Equal(t, voteSet.GetByIndex(0), commit.GetByIndex(0))
assert.True(t, commit.IsCommit())
}
@@ -477,11 +479,11 @@ func randCommit(ctx context.Context, t *testing.T, now time.Time) *Commit {
lastID := makeBlockIDRandom()
h := int64(3)
voteSet, _, vals := randVoteSet(ctx, t, h-1, 1, tmproto.PrecommitType, 10, 1)
commit, err := makeCommit(ctx, lastID, h-1, 1, voteSet, vals, now)
commit, err := makeExtCommit(ctx, lastID, h-1, 1, voteSet, vals, now)
require.NoError(t, err)
return commit
return commit.StripExtensions()
}
func hexBytesFromString(t *testing.T, s string) bytes.HexBytes {
@@ -554,34 +556,127 @@ func TestBlockMaxDataBytesNoEvidence(t *testing.T) {
}
}
func TestCommitToVoteSet(t *testing.T) {
lastID := makeBlockIDRandom()
h := int64(3)
// TestVoteSetToExtendedCommit tests that the extended commit produced from a
// vote set contains the same vote information as the vote set. The test ensures
// that the MakeExtendedCommit method behaves as expected, whether vote extensions
// are present in the original votes or not.
func TestVoteSetToExtendedCommit(t *testing.T) {
for _, testCase := range []struct {
name string
includeExtension bool
}{
{
name: "no extensions",
includeExtension: false,
},
{
name: "with extensions",
includeExtension: true,
},
} {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
t.Run(testCase.name, func(t *testing.T) {
blockID := makeBlockIDRandom()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
voteSet, valSet, vals := randVoteSet(ctx, t, h-1, 1, tmproto.PrecommitType, 10, 1)
commit, err := makeCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
voteSet, _, vals := randVoteSet(ctx, t, 3, 1, tmproto.PrecommitType, 10, 1)
for i := 0; i < len(vals); i++ {
pubKey, err := vals[i].GetPubKey(ctx)
require.NoError(t, err)
vote := &Vote{
ValidatorAddress: pubKey.Address(),
ValidatorIndex: int32(i),
Height: 3,
Round: 1,
Type: tmproto.PrecommitType,
BlockID: blockID,
Timestamp: time.Now(),
}
v := vote.ToProto()
err = vals[i].SignVote(ctx, voteSet.ChainID(), v)
require.NoError(t, err)
vote.Signature = v.Signature
if testCase.includeExtension {
vote.ExtensionSignature = v.ExtensionSignature
}
added, err := voteSet.AddVote(vote)
require.NoError(t, err)
require.True(t, added)
}
ec := voteSet.MakeExtendedCommit()
assert.NoError(t, err)
for i := int32(0); int(i) < len(vals); i++ {
vote1 := voteSet.GetByIndex(i)
vote2 := ec.GetExtendedVote(i)
chainID := voteSet.ChainID()
voteSet2 := CommitToVoteSet(chainID, commit, valSet)
vote1bz, err := vote1.ToProto().Marshal()
require.NoError(t, err)
vote2bz, err := vote2.ToProto().Marshal()
require.NoError(t, err)
assert.Equal(t, vote1bz, vote2bz)
}
})
}
}
for i := int32(0); int(i) < len(vals); i++ {
vote1 := voteWithoutExtension(voteSet.GetByIndex(i))
vote2 := voteSet2.GetByIndex(i)
vote3 := commit.GetVote(i)
// TestExtendedCommitToVoteSet tests that the vote set produced from an extended commit
// contains the same vote information as the extended commit. The test ensures
// that the ToVoteSet method behaves as expected, whether vote extensions
// are present in the original votes or not.
func TestExtendedCommitToVoteSet(t *testing.T) {
for _, testCase := range []struct {
name string
includeExtension bool
}{
{
name: "no extensions",
includeExtension: false,
},
{
name: "with extensions",
includeExtension: true,
},
} {
t.Run(testCase.name, func(t *testing.T) {
lastID := makeBlockIDRandom()
h := int64(3)
vote1bz, err := vote1.ToProto().Marshal()
require.NoError(t, err)
vote2bz, err := vote2.ToProto().Marshal()
require.NoError(t, err)
vote3bz, err := vote3.ToProto().Marshal()
require.NoError(t, err)
assert.Equal(t, vote1bz, vote2bz)
assert.Equal(t, vote1bz, vote3bz)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
voteSet, valSet, vals := randVoteSet(ctx, t, h-1, 1, tmproto.PrecommitType, 10, 1)
extCommit, err := makeExtCommit(ctx, lastID, h-1, 1, voteSet, vals, time.Now())
assert.NoError(t, err)
if !testCase.includeExtension {
for i := 0; i < len(vals); i++ {
v := voteSet.GetByIndex(int32(i))
v.Extension = nil
v.ExtensionSignature = nil
extCommit.ExtendedSignatures[i].Extension = nil
extCommit.ExtendedSignatures[i].ExtensionSignature = nil
}
}
chainID := voteSet.ChainID()
voteSet2 := extCommit.ToVoteSet(chainID, valSet, true)
for i := int32(0); int(i) < len(vals); i++ {
vote1 := voteSet.GetByIndex(i)
vote2 := voteSet2.GetByIndex(i)
vote3 := extCommit.GetExtendedVote(i)
vote1bz, err := vote1.ToProto().Marshal()
require.NoError(t, err)
vote2bz, err := vote2.ToProto().Marshal()
require.NoError(t, err)
vote3bz, err := vote3.ToProto().Marshal()
require.NoError(t, err)
assert.Equal(t, vote1bz, vote2bz)
assert.Equal(t, vote1bz, vote3bz)
}
})
}
}
@@ -634,12 +729,12 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) {
}
if tc.valid {
commit := voteSet.MakeCommit() // panics without > 2/3 valid votes
assert.NotNil(t, commit)
err := valSet.VerifyCommit(voteSet.ChainID(), blockID, height-1, commit)
extCommit := voteSet.MakeExtendedCommit() // panics without > 2/3 valid votes
assert.NotNil(t, extCommit)
err := valSet.VerifyCommit(voteSet.ChainID(), blockID, height-1, extCommit.StripExtensions())
assert.NoError(t, err)
} else {
assert.Panics(t, func() { voteSet.MakeCommit() })
assert.Panics(t, func() { voteSet.MakeExtendedCommit() })
}
}
}

View File

@@ -309,7 +309,7 @@ func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *Validator
// validators who are in the commonVals and voted for the lunatic header
if l.ConflictingHeaderIsInvalid(trusted.Header) {
for _, commitSig := range l.ConflictingBlock.Commit.Signatures {
if !commitSig.ForBlock() {
if commitSig.BlockIDFlag != BlockIDFlagCommit {
continue
}
@@ -329,12 +329,12 @@ func (l *LightClientAttackEvidence) GetByzantineValidators(commonVals *Validator
// only need a single loop to find the validators that voted twice.
for i := 0; i < len(l.ConflictingBlock.Commit.Signatures); i++ {
sigA := l.ConflictingBlock.Commit.Signatures[i]
if !sigA.ForBlock() {
if sigA.BlockIDFlag != BlockIDFlagCommit {
continue
}
sigB := trusted.Commit.Signatures[i]
if !sigB.ForBlock() {
if sigB.BlockIDFlag != BlockIDFlagCommit {
continue
}

View File

@@ -153,8 +153,10 @@ func TestLightClientAttackEvidenceBasic(t *testing.T) {
header := makeHeaderRandom()
header.Height = height
blockID := makeBlockID(crypto.Checksum([]byte("blockhash")), math.MaxInt32, crypto.Checksum([]byte("partshash")))
commit, err := makeCommit(ctx, blockID, height, 1, voteSet, privVals, defaultVoteTime)
extCommit, err := makeExtCommit(ctx, blockID, height, 1, voteSet, privVals, defaultVoteTime)
require.NoError(t, err)
commit := extCommit.StripExtensions()
lcae := &LightClientAttackEvidence{
ConflictingBlock: &LightBlock{
SignedHeader: &SignedHeader{
@@ -217,8 +219,10 @@ func TestLightClientAttackEvidenceValidation(t *testing.T) {
header.Height = height
header.ValidatorsHash = valSet.Hash()
blockID := makeBlockID(header.Hash(), math.MaxInt32, crypto.Checksum([]byte("partshash")))
commit, err := makeCommit(ctx, blockID, height, 1, voteSet, privVals, time.Now())
extCommit, err := makeExtCommit(ctx, blockID, height, 1, voteSet, privVals, time.Now())
require.NoError(t, err)
commit := extCommit.StripExtensions()
lcae := &LightClientAttackEvidence{
ConflictingBlock: &LightBlock{
SignedHeader: &SignedHeader{
@@ -424,13 +428,13 @@ func TestEvidenceVectors(t *testing.T) {
ProposerAddress: []byte("2915b7b15f979e48ebc61774bb1d86ba3136b7eb"),
}
blockID3 := makeBlockID(header.Hash(), math.MaxInt32, crypto.Checksum([]byte("partshash")))
commit, err := makeCommit(ctx, blockID3, height, 1, voteSet, privVals, defaultVoteTime)
extCommit, err := makeExtCommit(ctx, blockID3, height, 1, voteSet, privVals, defaultVoteTime)
require.NoError(t, err)
lcae := &LightClientAttackEvidence{
ConflictingBlock: &LightBlock{
SignedHeader: &SignedHeader{
Header: header,
Commit: commit,
Commit: extCommit.StripExtensions(),
},
ValidatorSet: valSet,
},

View File

@@ -43,6 +43,7 @@ type ConsensusParams struct {
Version VersionParams `json:"version"`
Synchrony SynchronyParams `json:"synchrony"`
Timeout TimeoutParams `json:"timeout"`
Vote VoteParams `json:"vote"`
}
// HashedParams is a subset of ConsensusParams.
@@ -96,6 +97,11 @@ type TimeoutParams struct {
BypassCommitTimeout bool `json:"bypass_commit_timeout"`
}
// VoteParams configure validity rules of the votes within Tendermint consensus.
type VoteParams struct {
ExtensionRequireHeight int64 `json:"extension_require_height"`
}
// DefaultConsensusParams returns a default ConsensusParams.
func DefaultConsensusParams() *ConsensusParams {
return &ConsensusParams{
@@ -105,6 +111,7 @@ func DefaultConsensusParams() *ConsensusParams {
Version: DefaultVersionParams(),
Synchrony: DefaultSynchronyParams(),
Timeout: DefaultTimeoutParams(),
Vote: DefaultVoteParams(),
}
}
@@ -176,6 +183,13 @@ func DefaultTimeoutParams() TimeoutParams {
}
}
func DefaultVoteParams() VoteParams {
return VoteParams{
// When set to 0, vote extensions are not required.
ExtensionRequireHeight: 0,
}
}
// TimeoutParamsOrDefaults returns the SynchronyParams, filling in any zero values
// with the Tendermint defined default values.
func (t TimeoutParams) TimeoutParamsOrDefaults() TimeoutParams {

View File

@@ -145,6 +145,12 @@ func PartSetHeaderFromProto(ppsh *tmproto.PartSetHeader) (*PartSetHeader, error)
return psh, psh.ValidateBasic()
}
// ProtoPartSetHeaderIsZero is similar to the IsZero function for
// PartSetHeader, but for the Protobuf representation.
func ProtoPartSetHeaderIsZero(ppsh *tmproto.PartSetHeader) bool {
return ppsh.Total == 0 && len(ppsh.Hash) == 0
}
//-------------------------------------
type PartSet struct {

View File

@@ -90,7 +90,6 @@ func (pv MockPV) SignVote(ctx context.Context, chainID string, vote *tmproto.Vot
}
signBytes := VoteSignBytes(useChainID, vote)
extSignBytes := VoteExtensionSignBytes(useChainID, vote)
sig, err := pv.PrivKey.Sign(signBytes)
if err != nil {
return err
@@ -98,14 +97,15 @@ func (pv MockPV) SignVote(ctx context.Context, chainID string, vote *tmproto.Vot
vote.Signature = sig
var extSig []byte
// We only sign vote extensions for precommits
if vote.Type == tmproto.PrecommitType {
// We only sign vote extensions for non-nil precommits
if vote.Type == tmproto.PrecommitType && !ProtoBlockIDIsNil(&vote.BlockID) {
extSignBytes := VoteExtensionSignBytes(useChainID, vote)
extSig, err = pv.PrivKey.Sign(extSignBytes)
if err != nil {
return err
}
} else if len(vote.Extension) > 0 {
return errors.New("unexpected vote extension - vote extensions are only allowed in precommits")
return errors.New("unexpected vote extension - vote extensions are only allowed in non-nil precommits")
}
vote.ExtensionSignature = extSig
return nil

View File

@@ -8,8 +8,8 @@ import (
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)
func makeCommit(ctx context.Context, blockID BlockID, height int64, round int32,
voteSet *VoteSet, validators []PrivValidator, now time.Time) (*Commit, error) {
func makeExtCommit(ctx context.Context, blockID BlockID, height int64, round int32,
voteSet *VoteSet, validators []PrivValidator, now time.Time) (*ExtendedCommit, error) {
// all sign
for i := 0; i < len(validators); i++ {
@@ -33,7 +33,7 @@ func makeCommit(ctx context.Context, blockID BlockID, height int64, round int32,
}
}
return voteSet.MakeCommit(), nil
return voteSet.MakeExtendedCommit(), nil
}
func signAddVote(ctx context.Context, privVal PrivValidator, vote *Vote, voteSet *VoteSet) (signed bool, err error) {
@@ -46,13 +46,3 @@ func signAddVote(ctx context.Context, privVal PrivValidator, vote *Vote, voteSet
vote.ExtensionSignature = v.ExtensionSignature
return voteSet.AddVote(vote)
}
// Votes constructed from commits don't have extensions, because we don't store
// the extensions themselves in the commit. This method is used to construct a
// copy of a vote, but nil its extension and signature.
func voteWithoutExtension(v *Vote) *Vote {
vc := v.Copy()
vc.Extension = nil
vc.ExtensionSignature = nil
return vc
}

View File

@@ -36,10 +36,10 @@ func VerifyCommit(chainID string, vals *ValidatorSet, blockID BlockID,
votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
// ignore all absent signatures
ignore := func(c CommitSig) bool { return c.Absent() }
ignore := func(c CommitSig) bool { return c.BlockIDFlag == BlockIDFlagAbsent }
// only count the signatures that are for the block
count := func(c CommitSig) bool { return c.ForBlock() }
count := func(c CommitSig) bool { return c.BlockIDFlag == BlockIDFlagCommit }
// attempt to batch verify
if shouldBatchVerify(vals, commit) {
@@ -69,7 +69,7 @@ func VerifyCommitLight(chainID string, vals *ValidatorSet, blockID BlockID,
votingPowerNeeded := vals.TotalVotingPower() * 2 / 3
// ignore all commit signatures that are not for the block
ignore := func(c CommitSig) bool { return !c.ForBlock() }
ignore := func(c CommitSig) bool { return c.BlockIDFlag != BlockIDFlagCommit }
// count all the remaining signatures
count := func(c CommitSig) bool { return true }
@@ -113,7 +113,7 @@ func VerifyCommitLightTrusting(chainID string, vals *ValidatorSet, commit *Commi
votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator)
// ignore all commit signatures that are not for the block
ignore := func(c CommitSig) bool { return !c.ForBlock() }
ignore := func(c CommitSig) bool { return c.BlockIDFlag != BlockIDFlagCommit }
// count all the remaining signatures
count := func(c CommitSig) bool { return true }

View File

@@ -99,7 +99,12 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) {
vi++
}
commit := NewCommit(tc.height, round, tc.blockID, sigs)
commit := &Commit{
Height: tc.height,
Round: round,
BlockID: tc.blockID,
Signatures: sigs,
}
err := valSet.VerifyCommit(chainID, blockID, height, commit)
if tc.expErr {
@@ -146,9 +151,10 @@ func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) {
defer cancel()
voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
extCommit, err := makeExtCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
require.NoError(t, err)
commit := extCommit.StripExtensions()
require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
// malleate 4th signature
@@ -176,9 +182,10 @@ func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSign
defer cancel()
voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
extCommit, err := makeExtCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
require.NoError(t, err)
commit := extCommit.StripExtensions()
require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
// malleate 4th signature (3 signatures are enough for 2/3+)
@@ -203,9 +210,10 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin
defer cancel()
voteSet, valSet, vals := randVoteSet(ctx, t, h, 0, tmproto.PrecommitType, 4, 10)
commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
extCommit, err := makeExtCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
require.NoError(t, err)
commit := extCommit.StripExtensions()
require.NoError(t, valSet.VerifyCommit(chainID, blockID, h, commit))
// malleate 3rd signature (2 signatures are enough for 1/3+ trust level)
@@ -227,10 +235,11 @@ func TestValidatorSet_VerifyCommitLightTrusting(t *testing.T) {
var (
blockID = makeBlockIDRandom()
voteSet, originalValset, vals = randVoteSet(ctx, t, 1, 1, tmproto.PrecommitType, 6, 1)
commit, err = makeCommit(ctx, blockID, 1, 1, voteSet, vals, time.Now())
extCommit, err = makeExtCommit(ctx, blockID, 1, 1, voteSet, vals, time.Now())
newValSet, _ = randValidatorPrivValSet(ctx, t, 2, 1)
)
require.NoError(t, err)
commit := extCommit.StripExtensions()
testCases := []struct {
valSet *ValidatorSet
@@ -271,11 +280,11 @@ func TestValidatorSet_VerifyCommitLightTrustingErrorsOnOverflow(t *testing.T) {
var (
blockID = makeBlockIDRandom()
voteSet, valSet, vals = randVoteSet(ctx, t, 1, 1, tmproto.PrecommitType, 1, MaxTotalVotingPower)
commit, err = makeCommit(ctx, blockID, 1, 1, voteSet, vals, time.Now())
extCommit, err = makeExtCommit(ctx, blockID, 1, 1, voteSet, vals, time.Now())
)
require.NoError(t, err)
err = valSet.VerifyCommitLightTrusting("test_chain_id", commit,
err = valSet.VerifyCommitLightTrusting("test_chain_id", extCommit.StripExtensions(),
tmmath.Fraction{Numerator: 25, Denominator: 55})
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "int64 overflow")

View File

@@ -1539,8 +1539,9 @@ func BenchmarkValidatorSet_VerifyCommit_Ed25519(b *testing.B) { // nolint
// generate n validators
voteSet, valSet, vals := randVoteSet(ctx, b, h, 0, tmproto.PrecommitType, n, int64(n*5))
// create a commit with n validators
commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
extCommit, err := makeExtCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
require.NoError(b, err)
commit := extCommit.StripExtensions()
for i := 0; i < b.N/n; i++ {
err = valSet.VerifyCommit(chainID, blockID, h, commit)
@@ -1567,8 +1568,9 @@ func BenchmarkValidatorSet_VerifyCommitLight_Ed25519(b *testing.B) { // nolint
voteSet, valSet, vals := randVoteSet(ctx, b, h, 0, tmproto.PrecommitType, n, int64(n*5))
// create a commit with n validators
commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
extCommit, err := makeExtCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
require.NoError(b, err)
commit := extCommit.StripExtensions()
for i := 0; i < b.N/n; i++ {
err = valSet.VerifyCommitLight(chainID, blockID, h, commit)
@@ -1594,8 +1596,9 @@ func BenchmarkValidatorSet_VerifyCommitLightTrusting_Ed25519(b *testing.B) {
// generate n validators
voteSet, valSet, vals := randVoteSet(ctx, b, h, 0, tmproto.PrecommitType, n, int64(n*5))
// create a commit with n validators
commit, err := makeCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
extCommit, err := makeExtCommit(ctx, blockID, h, 0, voteSet, vals, time.Now())
require.NoError(b, err)
commit := extCommit.StripExtensions()
for i := 0; i < b.N/n; i++ {
err = valSet.VerifyCommitLightTrusting(chainID, commit, tmmath.Fraction{Numerator: 1, Denominator: 3})

View File

@@ -14,6 +14,9 @@ import (
const (
nilVoteStr string = "nil-Vote"
// The maximum supported number of bytes in a vote extension.
MaxVoteExtensionSize int = 1024 * 1024
)
var (
@@ -24,7 +27,7 @@ var (
ErrVoteInvalidBlockHash = errors.New("invalid block hash")
ErrVoteNonDeterministicSignature = errors.New("non-deterministic signature")
ErrVoteNil = errors.New("nil vote")
ErrVoteInvalidExtension = errors.New("invalid vote extension")
ErrVoteExtensionAbsent = errors.New("vote extension absent")
)
type ErrVoteConflictingVotes struct {
@@ -109,6 +112,22 @@ func (vote *Vote) CommitSig() CommitSig {
}
}
// ExtendedCommitSig attempts to construct an ExtendedCommitSig from this vote.
// Panics if either the vote extension signature is missing or if the block ID
// is not either empty or complete.
func (vote *Vote) ExtendedCommitSig() ExtendedCommitSig {
if vote == nil {
return NewExtendedCommitSigAbsent()
}
cs := vote.CommitSig()
return ExtendedCommitSig{
CommitSig: cs,
Extension: vote.Extension,
ExtensionSignature: vote.ExtensionSignature,
}
}
// VoteSignBytes returns the proto-encoding of the canonicalized Vote, for
// signing. Panics if the marshaling fails.
//
@@ -216,12 +235,10 @@ func (vote *Vote) VerifyWithExtension(chainID string, pubKey crypto.PubKey) erro
if err != nil {
return err
}
// We only verify vote extension signatures for precommits.
if vote.Type == tmproto.PrecommitType {
// We only verify vote extension signatures for non-nil precommits.
if vote.Type == tmproto.PrecommitType && !ProtoBlockIDIsNil(&v.BlockID) {
extSignBytes := VoteExtensionSignBytes(chainID, v)
// TODO: Remove extension signature nil check to enforce vote extension
// signing once we resolve https://github.com/tendermint/tendermint/issues/8272
if vote.ExtensionSignature != nil && !pubKey.VerifySignature(extSignBytes, vote.ExtensionSignature) {
if !pubKey.VerifySignature(extSignBytes, vote.ExtensionSignature) {
return ErrVoteInvalidSignature
}
}
@@ -273,8 +290,10 @@ func (vote *Vote) ValidateBasic() error {
return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize)
}
// We should only ever see vote extensions in precommits.
if vote.Type != tmproto.PrecommitType {
// We should only ever see vote extensions in non-nil precommits, otherwise
// this is a violation of the specification.
// https://github.com/tendermint/tendermint/issues/8487
if vote.Type != tmproto.PrecommitType || (vote.Type == tmproto.PrecommitType && vote.BlockID.IsNil()) {
if len(vote.Extension) > 0 {
return errors.New("unexpected vote extension")
}
@@ -294,22 +313,24 @@ func (vote *Vote) ValidateWithExtension() error {
return err
}
// We should always see vote extension signatures in precommits
if vote.Type == tmproto.PrecommitType {
// TODO(thane): Remove extension length check once
// https://github.com/tendermint/tendermint/issues/8272 is
// resolved.
if len(vote.Extension) > 0 && len(vote.ExtensionSignature) == 0 {
return errors.New("vote extension signature is missing")
}
if len(vote.ExtensionSignature) > MaxSignatureSize {
return fmt.Errorf("vote extension signature is too big (max: %d)", MaxSignatureSize)
}
if err := vote.EnsureExtension(); err != nil {
return err
}
return nil
}
//
func (vote *Vote) EnsureExtension() error {
// We should always see vote extension signatures in non-nil precommits
if vote.Type == tmproto.PrecommitType && !vote.BlockID.IsNil() {
if len(vote.ExtensionSignature) == 0 {
return ErrVoteExtensionAbsent
}
}
return nil
}
// ToProto converts the handwritten type to proto generated type
// return type, nil if everything converts safely, otherwise nil, error
func (vote *Vote) ToProto() *tmproto.Vote {

View File

@@ -53,11 +53,12 @@ const (
NOTE: Assumes that the sum total of voting power does not exceed MaxUInt64.
*/
type VoteSet struct {
chainID string
height int64
round int32
signedMsgType tmproto.SignedMsgType
valSet *ValidatorSet
chainID string
height int64
round int32
signedMsgType tmproto.SignedMsgType
valSet *ValidatorSet
requireExtensions bool
mtx sync.Mutex
votesBitArray *bits.BitArray
@@ -70,22 +71,23 @@ type VoteSet struct {
// Constructs a new VoteSet struct used to accumulate votes for given height/round.
func NewVoteSet(chainID string, height int64, round int32,
signedMsgType tmproto.SignedMsgType, valSet *ValidatorSet) *VoteSet {
signedMsgType tmproto.SignedMsgType, valSet *ValidatorSet, requireExtensions bool) *VoteSet {
if height == 0 {
panic("Cannot make VoteSet for height == 0, doesn't make sense.")
}
return &VoteSet{
chainID: chainID,
height: height,
round: round,
signedMsgType: signedMsgType,
valSet: valSet,
votesBitArray: bits.NewBitArray(valSet.Size()),
votes: make([]*Vote, valSet.Size()),
sum: 0,
maj23: nil,
votesByBlock: make(map[string]*blockVotes, valSet.Size()),
peerMaj23s: make(map[string]BlockID),
chainID: chainID,
height: height,
round: round,
signedMsgType: signedMsgType,
valSet: valSet,
requireExtensions: requireExtensions,
votesBitArray: bits.NewBitArray(valSet.Size()),
votes: make([]*Vote, valSet.Size()),
sum: 0,
maj23: nil,
votesByBlock: make(map[string]*blockVotes, valSet.Size()),
peerMaj23s: make(map[string]BlockID),
}
}
@@ -194,8 +196,20 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
}
// Check signature.
if err := vote.VerifyWithExtension(voteSet.chainID, val.PubKey); err != nil {
return false, fmt.Errorf("failed to verify vote with ChainID %s and PubKey %s: %w", voteSet.chainID, val.PubKey, err)
if voteSet.requireExtensions {
if err := vote.VerifyWithExtension(voteSet.chainID, val.PubKey); err != nil {
return false, fmt.Errorf("failed to verify vote with ChainID %s and PubKey %s: %w", voteSet.chainID, val.PubKey, err)
}
} else {
if len(vote.ExtensionSignature) != 0 {
if err := vote.VerifyWithExtension(voteSet.chainID, val.PubKey); err != nil {
return false, fmt.Errorf("failed to verify vote with ChainID %s and PubKey %s: %w", voteSet.chainID, val.PubKey, err)
}
} else {
if err := vote.Verify(voteSet.chainID, val.PubKey); err != nil {
return false, fmt.Errorf("failed to verify vote with ChainID %s and PubKey %s: %w", voteSet.chainID, val.PubKey, err)
}
}
}
// Add vote and get conflicting vote if any.
@@ -220,13 +234,6 @@ func (voteSet *VoteSet) getVote(valIndex int32, blockKey string) (vote *Vote, ok
return nil, false
}
func (voteSet *VoteSet) GetVotes() []*Vote {
if voteSet == nil {
return nil
}
return voteSet.votes
}
// Assumes signature is valid.
// If conflicting vote exists, returns it.
func (voteSet *VoteSet) addVerifiedVote(
@@ -606,36 +613,41 @@ func (voteSet *VoteSet) sumTotalFrac() (int64, int64, float64) {
//--------------------------------------------------------------------------------
// Commit
// MakeCommit constructs a Commit from the VoteSet. It only includes precommits
// for the block, which has 2/3+ majority, and nil.
// MakeExtendedCommit constructs a Commit from the VoteSet. It only includes
// precommits for the block, which has 2/3+ majority, and nil.
//
// Panics if the vote type is not PrecommitType or if there's no +2/3 votes for
// a single block.
func (voteSet *VoteSet) MakeCommit() *Commit {
func (voteSet *VoteSet) MakeExtendedCommit() *ExtendedCommit {
if voteSet.signedMsgType != tmproto.PrecommitType {
panic("Cannot MakeCommit() unless VoteSet.Type is PrecommitType")
panic("Cannot MakeExtendCommit() unless VoteSet.Type is PrecommitType")
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
// Make sure we have a 2/3 majority
if voteSet.maj23 == nil {
panic("Cannot MakeCommit() unless a blockhash has +2/3")
panic("Cannot MakeExtendCommit() unless a blockhash has +2/3")
}
// For every validator, get the precommit
commitSigs := make([]CommitSig, len(voteSet.votes))
// For every validator, get the precommit with extensions
sigs := make([]ExtendedCommitSig, len(voteSet.votes))
for i, v := range voteSet.votes {
commitSig := v.CommitSig()
sig := v.ExtendedCommitSig()
// if block ID exists but doesn't match, exclude sig
if commitSig.ForBlock() && !v.BlockID.Equals(*voteSet.maj23) {
commitSig = NewCommitSigAbsent()
if sig.BlockIDFlag == BlockIDFlagCommit && !v.BlockID.Equals(*voteSet.maj23) {
sig = NewExtendedCommitSigAbsent()
}
commitSigs[i] = commitSig
sigs[i] = sig
}
return NewCommit(voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, commitSigs)
return &ExtendedCommit{
Height: voteSet.GetHeight(),
Round: voteSet.GetRound(),
BlockID: *voteSet.maj23,
ExtendedSignatures: sigs,
}
}
//--------------------------------------------------------------------------------

View File

@@ -450,7 +450,7 @@ func TestVoteSet_MakeCommit(t *testing.T) {
}
// MakeCommit should fail.
assert.Panics(t, func() { voteSet.MakeCommit() }, "Doesn't have +2/3 majority")
assert.Panics(t, func() { voteSet.MakeExtendedCommit() }, "Doesn't have +2/3 majority")
// 7th voted for some other block.
{
@@ -487,13 +487,13 @@ func TestVoteSet_MakeCommit(t *testing.T) {
require.NoError(t, err)
}
commit := voteSet.MakeCommit()
extCommit := voteSet.MakeExtendedCommit()
// Commit should have 10 elements
assert.Equal(t, 10, len(commit.Signatures))
assert.Equal(t, 10, len(extCommit.ExtendedSignatures))
// Ensure that Commit is good.
if err := commit.ValidateBasic(); err != nil {
if err := extCommit.ValidateBasic(); err != nil {
t.Errorf("error in Commit.ValidateBasic(): %v", err)
}
}
@@ -510,7 +510,7 @@ func randVoteSet(
) (*VoteSet, *ValidatorSet, []PrivValidator) {
t.Helper()
valSet, privValidators := randValidatorPrivValSet(ctx, t, numValidators, votingPower)
return NewVoteSet("test_chain_id", height, round, signedMsgType, valSet), valSet, privValidators
return NewVoteSet("test_chain_id", height, round, signedMsgType, valSet, false), valSet, privValidators
}
func deterministicVoteSet(
@@ -523,7 +523,7 @@ func deterministicVoteSet(
) (*VoteSet, *ValidatorSet, []PrivValidator) {
t.Helper()
valSet, privValidators := deterministicValidatorSet(ctx, t)
return NewVoteSet("test_chain_id", height, round, signedMsgType, valSet), valSet, privValidators
return NewVoteSet("test_chain_id", height, round, signedMsgType, valSet, false), valSet, privValidators
}
func randValidatorPrivValSet(ctx context.Context, t testing.TB, numValidators int, votingPower int64) (*ValidatorSet, []PrivValidator) {

View File

@@ -223,26 +223,22 @@ func TestVoteExtension(t *testing.T) {
includeSignature: true,
expectError: false,
},
// TODO(thane): Re-enable once
// https://github.com/tendermint/tendermint/issues/8272 is resolved
//{
// name: "no extension signature",
// extension: []byte("extension"),
// includeSignature: false,
// expectError: true,
//},
{
name: "no extension signature",
extension: []byte("extension"),
includeSignature: false,
expectError: true,
},
{
name: "empty extension",
includeSignature: true,
expectError: false,
},
// TODO: Re-enable once
// https://github.com/tendermint/tendermint/issues/8272 is resolved.
//{
// name: "no extension and no signature",
// includeSignature: false,
// expectError: true,
//},
{
name: "no extension and no signature",
includeSignature: false,
expectError: true,
},
}
for _, tc := range testCases {
@@ -497,11 +493,11 @@ func getSampleCommit(ctx context.Context, t testing.TB) *Commit {
lastID := makeBlockIDRandom()
voteSet, _, vals := randVoteSet(ctx, t, 2, 1, tmproto.PrecommitType, 10, 1)
commit, err := makeCommit(ctx, lastID, 2, 1, voteSet, vals, time.Now())
commit, err := makeExtCommit(ctx, lastID, 2, 1, voteSet, vals, time.Now())
require.NoError(t, err)
return commit
return commit.StripExtensions()
}
func BenchmarkVoteSignBytes(b *testing.B) {