Vote extensions: base implementation (#9836)

* [cherry-picked] abci: Vote Extension 1 (#6646)

* add proto, add boilerplates

* add canonical

* fix tests

* add vote signing test

* Update internal/consensus/msgs_test.go

* modify state execution in progress

* add extension signing

* VoteExtension -> ExtendVote

* apply review

* update data structures

* Add comments

* Apply suggestions from code review

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>

* *Signed -> *ToSign

* add Vote to RequestExtendVote

* apply reviews

* Apply suggestions from code review

Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>

* fix typo, modify proto

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>

* [cherry-picked] ABCI Vote Extension 2 (#6885)

* add proto, add boilerplates

* add canonical

* fix tests

* add vote signing test

* Update internal/consensus/msgs_test.go

* modify state execution in progress

* add extension signing

* add extension signing

* VoteExtension -> ExtendVote

* modify state execution in progress

* add extension signing

* verify in progress

* modify CommitSig

* fix test

* apply review

* update data structures

* Apply suggestions from code review

* Add comments

* fix test

* VoteExtensionSigned => VoteExtensionToSigned

* Apply suggestions from code review

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>

* *Signed -> *ToSign

* add Vote to RequestExtendVote

* add example VoteExtension

* apply reviews

* fix vote

* Apply suggestions from code review

Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>

* fix typo, modify proto

* add abcipp_kvstore.go

* add extension test

* fix test

* fix test

* fix test

* fit lint

* uncomment test

* refactor test in progress

* gofmt

* apply review

* fix lint

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>

* [cheryy-picked] abci: PrepareProposal-VoteExtension integration [2nd try] (#7821)

* PrepareProposal-VoteExtension integration (#6915)

* make proto-gen

* Fix protobuf crash in e2e nightly tests

* Update types/vote.go

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

* Addressed @creachadair's comments

Co-authored-by: mconcat <monoidconcat@gmail.com>
Co-authored-by: M. J. Fromberger <fromberger@interchain.io>

* [cherry-picked] Vote extensions: new design (#8031)

* Changed the spec text to agreed VoteExtension solution

* Revert "Removed protobufs related to vote extensions"

This reverts commit 4566f1e302.

* Changes to ABCI protocol buffers

* Update spec/core/data_structures.md

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

* Update spec/core/data_structures.md

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

* Fix dangling link in ABCI++ readme

* Addressed comments

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

* [cherry-picked] abci++: Sync implementation and spec for vote extensions (#8141)

* Refactor so building and linting works

This is the first step towards implementing vote extensions: generating
the relevant proto stubs and getting the build and linter to pass.

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

* Fix typo

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

* Better describe method given vote extensions

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

* Fix types tests

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

* Move CanonicalVoteExtension to canonical types proto defs

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

* Regenerate protos including latest PBTS synchrony params update

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

* Inject vote extensions into proposal

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

* Thread vote extensions through code and fix tests

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

* Remove extraneous empty value initialization

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

* Fix lint

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

* Fix missing VerifyVoteExtension request data

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

* Explicitly ensure length > 0 to sign vote extension

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

* Explicitly ensure length > 0 to sign vote extension

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

* Remove extraneous comment

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

* Update privval/file.go

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

* Update types/vote_test.go

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

* Format

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

* Fix ABCI proto generation scripts for Linux

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

* Sync intermediate and goal protos

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

* Update internal/consensus/common_test.go

Co-authored-by: Sergio Mena <sergio@informal.systems>

* Use dummy value with clearer meaning

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

* Rewrite loop for clarity

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

* Panic on ABCI++ method call failure

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

* Add strong correctness guarantees when constructing extended commit info for ABCI++

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

* Add strong guarantee in extendedCommitInfo that the number of votes corresponds

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

* Make extendedCommitInfo function more robust

At first extendedCommitInfo expected votes to be in the same order as
their corresponding validators in the supplied CommitInfo struct, but
this proved to be rather difficult since when a validator set's loaded
from state it's first sorted by voting power and then by address.

Instead of sorting the votes in the same way, this approach simply maps
votes to their corresponding validator's address prior to constructing
the extended commit info. This way it's easy to look up the
corresponding vote and we don't need to care about vote order.

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

* Remove extraneous validator address assignment

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

* Sign over canonical vote extension

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

* Validate vote extension signature against canonical vote extension

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

* Update privval tests for more meaningful dummy value

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

* Add vote extension capability to E2E test app

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

* Disable lint for weak RNG usage for test app

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

* Use parseVoteExtension instead of custom parsing in PrepareProposal

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

* Only include extension if we have received txs

It's unclear at this point why this is necessary to ensure that the
application's local app_hash matches that committed in the previous
block.

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

* Require app_hash from app to match that from last block

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

* Add contrived (possibly flaky) test to check that vote extensions code works

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

* Remove workaround for problem now solved by #8229

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

* add tests for vote extension cases

* Fix spelling mistake to appease linter

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

* Collapse redundant if statement

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

* Formatting

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

* Always expect an extension signature, regardless of whether an extension is present

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

* Votes constructed from commits cannot include extensions or signatures

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

* Pass through vote extension in test helpers

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

* Temporarily disable vote extension signature requirement

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

* Expand on vote equality test errors for clarity

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

* Expand on vote matching error messages in testing

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

* Allow for selective subscription by vote type

This is an attempt to fix the intermittently failing
`TestPrepareProposalReceivesVoteExtensions` test in the internal
consensus package.

Occasionally we get prevote messages via the subscription channel, and
we're not interested in those. This change allows us to specify what
types of votes we're interested in (i.e. precommits) and discard the
rest.

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

* Read lock consensus state mutex in test helper to avoid data race

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

* Revert BlockIDFlag parameter in node test

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

* Perform additional check in ProcessProposal for special txs generated by vote extensions

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

* e2e: check that our added tx does not cause all txs to exceed req.MaxTxBytes

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

* Only set vote extension signatures when signing is successful

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

* Remove channel capacity constraint in test helper to avoid missing messages

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

* Add TODO to always require extension signatures in vote validation

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

* e2e: reject vote extensions if the request height does not match what we expect

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

* types: remove extraneous call to voteWithoutExtension in test

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

* Remove unnecessary address parameter from CanonicalVoteExtension

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

* privval: change test vote type to precommit since we use an extension

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

* privval: update signing logic to cater for vote extensions

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

* proto: update field descriptions for vote message

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

* proto: update field description for vote extension sig in vote message

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

* proto/types: use fixed-length 64-bit integers for rounds in CanonicalVoteExtension

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

* consensus: fix flaky TestPrepareProposalReceivesVoteExtensions

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

* consensus: remove previously added test helper functionality

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

* e2e: add error logs when we get an unexpected height in ExtendVote or VerifyVoteExtension requests

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

* node_test: get validator addresses from privvals

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

* privval/file_test: optimize filepv creation in tests

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

* privval: add test to check that vote extensions are always signed

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

* Add a script to check documentation for ToC entries. (#8356)

This script verifies that each document in the docs and architecture directory
has a corresponding table-of-contents entry in its README file.  It can be run
manually from the command line.

- Hook up this script to run in CI (optional workflow).
- Update ADR ToC to include missing entries this script found.

* build(deps): Bump async from 2.6.3 to 2.6.4 in /docs (#8357)

Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

---
updated-dependencies:
- dependency-name: async
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* privval/file_test: reset vote ext sig before signing

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

Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
Co-authored-by: Sergio Mena <sergio@informal.systems>
Co-authored-by: William Banfield <wbanfield@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix cherry-picks

* make proto-gen

* make mockery

* fix build

* All units tests passing

* linter error

* Update consensus/state_test.go

Co-authored-by: William Banfield <4561443+williambanfield@users.noreply.github.com>

* Addressed @williambanfield's comments

* Go, not C!

Co-authored-by: mconcat <monoidconcat@gmail.com>
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
Co-authored-by: M. J. Fromberger <fromberger@interchain.io>
Co-authored-by: Thane Thomson <connect@thanethomson.com>
Co-authored-by: William Banfield <wbanfield@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: William Banfield <4561443+williambanfield@users.noreply.github.com>
This commit is contained in:
Sergio Mena
2022-12-06 21:03:46 +01:00
committed by GitHub
parent 1d911818e6
commit 67d3ce03a4
50 changed files with 3916 additions and 635 deletions
+8
View File
@@ -234,6 +234,14 @@ func (cli *grpcClient) ProcessProposal(ctx context.Context, req *types.RequestPr
return cli.client.ProcessProposal(ctx, types.ToRequestProcessProposal(req).GetProcessProposal(), grpc.WaitForReady(true))
}
func (cli *grpcClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
return cli.client.ExtendVote(ctx, types.ToRequestExtendVote(req).GetExtendVote(), grpc.WaitForReady(true))
}
func (cli *grpcClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
return cli.client.VerifyVoteExtension(ctx, types.ToRequestVerifyVoteExtension(req).GetVerifyVoteExtension(), grpc.WaitForReady(true))
}
func (cli *grpcClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
return cli.client.FinalizeBlock(ctx, types.ToRequestFinalizeBlock(req).GetFinalizeBlock(), grpc.WaitForReady(true))
}
+14
View File
@@ -164,6 +164,20 @@ func (app *localClient) ProcessProposal(ctx context.Context, req *types.RequestP
return app.Application.ProcessProposal(ctx, req)
}
func (app *localClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.Application.ExtendVote(ctx, req)
}
func (app *localClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.Application.VerifyVoteExtension(ctx, req)
}
func (app *localClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
+46
View File
@@ -148,6 +148,29 @@ func (_m *Client) Error() error {
return r0
}
// ExtendVote provides a mock function with given fields: _a0, _a1
func (_m *Client) ExtendVote(_a0 context.Context, _a1 *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseExtendVote
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestExtendVote) *types.ResponseExtendVote); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseExtendVote)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestExtendVote) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// FinalizeBlock provides a mock function with given fields: _a0, _a1
func (_m *Client) FinalizeBlock(_a0 context.Context, _a1 *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
ret := _m.Called(_a0, _a1)
@@ -498,6 +521,29 @@ func (_m *Client) String() string {
return r0
}
// VerifyVoteExtension provides a mock function with given fields: _a0, _a1
func (_m *Client) VerifyVoteExtension(_a0 context.Context, _a1 *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseVerifyVoteExtension
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestVerifyVoteExtension) *types.ResponseVerifyVoteExtension); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseVerifyVoteExtension)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestVerifyVoteExtension) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewClient interface {
mock.TestingT
Cleanup(func())
+26
View File
@@ -379,6 +379,28 @@ func (cli *socketClient) ProcessProposal(ctx context.Context, req *types.Request
return reqRes.Response.GetProcessProposal(), cli.Error()
}
func (cli *socketClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestExtendVote(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetExtendVote(), cli.Error()
}
func (cli *socketClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestVerifyVoteExtension(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetVerifyVoteExtension(), cli.Error()
}
func (cli *socketClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestFinalizeBlock(req))
if err != nil {
@@ -461,6 +483,10 @@ func resMatchesReq(req *types.Request, res *types.Response) (ok bool) {
_, ok = res.Value.(*types.Response_ListSnapshots)
case *types.Request_OfferSnapshot:
_, ok = res.Value.(*types.Response_OfferSnapshot)
case *types.Request_ExtendVote:
_, ok = res.Value.(*types.Response_ExtendVote)
case *types.Request_VerifyVoteExtension:
_, ok = res.Value.(*types.Response_VerifyVoteExtension)
case *types.Request_PrepareProposal:
_, ok = res.Value.(*types.Response_PrepareProposal)
case *types.Request_ProcessProposal:
+1 -1
View File
@@ -1,6 +1,6 @@
# KVStore
The KVStoreApplication is a simple merkle key-value store.
The KVStoreApplication is a simple merkle key-value store.
Transactions of the form `key=value` are stored as key-value pairs in the tree.
Transactions without an `=` sign set the value to the key.
The app has no replay protection (other than what the mempool provides).
+12
View File
@@ -288,6 +288,18 @@ func (s *SocketServer) handleRequest(ctx context.Context, req *types.Request) (*
return nil, err
}
return types.ToResponseApplySnapshotChunk(res), nil
case *types.Request_ExtendVote:
res, err := s.app.ExtendVote(ctx, r.ExtendVote)
if err != nil {
return nil, err
}
return types.ToResponseExtendVote(res), nil
case *types.Request_VerifyVoteExtension:
res, err := s.app.VerifyVoteExtension(ctx, r.VerifyVoteExtension)
if err != nil {
return nil, err
}
return types.ToResponseVerifyVoteExtension(res), nil
default:
return nil, fmt.Errorf("unknown request from client: %T", req)
}
+14
View File
@@ -20,6 +20,10 @@ type Application interface {
ProcessProposal(context.Context, *RequestProcessProposal) (*ResponseProcessProposal, error)
// Deliver the decided block with its txs to the Application
FinalizeBlock(context.Context, *RequestFinalizeBlock) (*ResponseFinalizeBlock, error)
// Create application specific vote extension
ExtendVote(context.Context, *RequestExtendVote) (*ResponseExtendVote, error)
// Verify application's vote extension data
VerifyVoteExtension(context.Context, *RequestVerifyVoteExtension) (*ResponseVerifyVoteExtension, error)
// Commit the state and return the application Merkle root hash
Commit(context.Context, *RequestCommit) (*ResponseCommit, error)
@@ -94,6 +98,16 @@ func (BaseApplication) ProcessProposal(_ context.Context, req *RequestProcessPro
return &ResponseProcessProposal{Status: ResponseProcessProposal_ACCEPT}, nil
}
func (BaseApplication) ExtendVote(_ context.Context, req *RequestExtendVote) (*ResponseExtendVote, error) {
return &ResponseExtendVote{}, nil
}
func (BaseApplication) VerifyVoteExtension(_ context.Context, req *RequestVerifyVoteExtension) (*ResponseVerifyVoteExtension, error) {
return &ResponseVerifyVoteExtension{
Status: ResponseVerifyVoteExtension_ACCEPT,
}, nil
}
func (BaseApplication) FinalizeBlock(_ context.Context, req *RequestFinalizeBlock) (*ResponseFinalizeBlock, error) {
txs := make([]*ExecTxResult, len(req.Txs))
for i := range req.Txs {
+24
View File
@@ -105,6 +105,18 @@ func ToRequestProcessProposal(req *RequestProcessProposal) *Request {
}
}
func ToRequestExtendVote(req *RequestExtendVote) *Request {
return &Request{
Value: &Request_ExtendVote{req},
}
}
func ToRequestVerifyVoteExtension(req *RequestVerifyVoteExtension) *Request {
return &Request{
Value: &Request_VerifyVoteExtension{req},
}
}
func ToRequestFinalizeBlock(req *RequestFinalizeBlock) *Request {
return &Request{
Value: &Request_FinalizeBlock{req},
@@ -197,6 +209,18 @@ func ToResponseProcessProposal(res *ResponseProcessProposal) *Response {
}
}
func ToResponseExtendVote(res *ResponseExtendVote) *Response {
return &Response{
Value: &Response_ExtendVote{res},
}
}
func ToResponseVerifyVoteExtension(res *ResponseVerifyVoteExtension) *Response {
return &Response{
Value: &Response_VerifyVoteExtension{res},
}
}
func ToResponseFinalizeBlock(res *ResponseFinalizeBlock) *Response {
return &Response{
Value: &Response_FinalizeBlock{res},
+46
View File
@@ -83,6 +83,29 @@ func (_m *Application) Commit(_a0 context.Context, _a1 *types.RequestCommit) (*t
return r0, r1
}
// ExtendVote provides a mock function with given fields: _a0, _a1
func (_m *Application) ExtendVote(_a0 context.Context, _a1 *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseExtendVote
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestExtendVote) *types.ResponseExtendVote); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseExtendVote)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestExtendVote) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// FinalizeBlock provides a mock function with given fields: _a0, _a1
func (_m *Application) FinalizeBlock(_a0 context.Context, _a1 *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
ret := _m.Called(_a0, _a1)
@@ -290,6 +313,29 @@ func (_m *Application) Query(_a0 context.Context, _a1 *types.RequestQuery) (*typ
return r0, r1
}
// VerifyVoteExtension provides a mock function with given fields: _a0, _a1
func (_m *Application) VerifyVoteExtension(_a0 context.Context, _a1 *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseVerifyVoteExtension
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestVerifyVoteExtension) *types.ResponseVerifyVoteExtension); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseVerifyVoteExtension)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestVerifyVoteExtension) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewApplication interface {
mock.TestingT
Cleanup(func())
+7
View File
@@ -51,6 +51,10 @@ func (r ResponseProcessProposal) IsStatusUnknown() bool {
return r.Status == ResponseProcessProposal_UNKNOWN
}
func (r ResponseVerifyVoteExtension) IsAccepted() bool {
return r.Status == ResponseVerifyVoteExtension_ACCEPT
}
//---------------------------------------------------------------------------
// override JSON marshaling so we emit defaults (ie. disable omitempty)
@@ -156,3 +160,6 @@ func MarshalTxResults(r []*ExecTxResult) ([][]byte, error) {
}
return s, nil
}
// -----------------------------------------------
// construct Result data
+1583 -288
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -198,6 +198,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
}
var commit *types.Commit
var votes []*types.Vote
switch {
case lazyProposer.Height == lazyProposer.state.InitialHeight:
// We're creating a proposal for the first block.
@@ -206,6 +207,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
case lazyProposer.LastCommit.HasTwoThirdsMajority():
// Make the commit from LastCommit
commit = lazyProposer.LastCommit.MakeCommit()
votes = lazyProposer.LastCommit.GetVotes()
default: // This shouldn't happen.
lazyProposer.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block")
return
@@ -223,7 +225,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
proposerAddr := lazyProposer.privValidatorPubKey.Address()
block, err := lazyProposer.blockExec.CreateProposalBlock(
lazyProposer.Height, lazyProposer.state, commit, proposerAddr, nil)
lazyProposer.Height, lazyProposer.state, commit, proposerAddr, votes)
require.NoError(t, err)
blockParts, err := block.MakePartSet(types.BlockPartSizeBytes)
require.NoError(t, err)
+26 -32
View File
@@ -93,7 +93,8 @@ func newValidatorStub(privValidator types.PrivValidator, valIndex int32) *valida
func (vs *validatorStub) signVote(
voteType tmproto.SignedMsgType,
hash []byte,
header types.PartSetHeader) (*types.Vote, error) {
header types.PartSetHeader,
voteExtension []byte) (*types.Vote, error) {
pubKey, err := vs.PrivValidator.GetPubKey()
if err != nil {
@@ -101,34 +102,37 @@ func (vs *validatorStub) signVote(
}
vote := &types.Vote{
ValidatorIndex: vs.Index,
ValidatorAddress: pubKey.Address(),
Type: voteType,
Height: vs.Height,
Round: vs.Round,
Timestamp: tmtime.Now(),
Type: voteType,
BlockID: types.BlockID{Hash: hash, PartSetHeader: header},
Timestamp: tmtime.Now(),
ValidatorAddress: pubKey.Address(),
ValidatorIndex: vs.Index,
Extension: voteExtension,
}
v := vote.ToProto()
if err := vs.PrivValidator.SignVote(test.DefaultTestChainID, v); err != nil {
if err = vs.PrivValidator.SignVote(test.DefaultTestChainID, v); err != nil {
return nil, fmt.Errorf("sign vote failed: %w", err)
}
// ref: signVote in FilePV, the vote should use the privious vote info when the sign data is the same.
// ref: signVote in FilePV, the vote should use the previous vote info when the sign data is the same.
if signDataIsEqual(vs.lastVote, v) {
v.Signature = vs.lastVote.Signature
v.Timestamp = vs.lastVote.Timestamp
v.ExtensionSignature = vs.lastVote.ExtensionSignature
}
vote.Signature = v.Signature
vote.Timestamp = v.Timestamp
vote.ExtensionSignature = v.ExtensionSignature
return vote, err
}
// Sign vote for type/hash/header
func signVote(vs *validatorStub, voteType tmproto.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote {
v, err := vs.signVote(voteType, hash, header)
v, err := vs.signVote(voteType, hash, header, []byte("extension"))
if err != nil {
panic(fmt.Errorf("failed to sign vote: %v", err))
}
@@ -312,43 +316,27 @@ func validatePrecommit(
}
}
rs := cs.GetRoundState()
if lockedBlockHash == nil {
if cs.LockedRound != lockRound || cs.LockedBlock != nil {
if rs.LockedRound != lockRound || rs.LockedBlock != nil {
panic(fmt.Sprintf(
"Expected to be locked on nil at round %d. Got locked at round %d with block %v",
lockRound,
cs.LockedRound,
cs.LockedBlock))
rs.LockedRound,
rs.LockedBlock))
}
} else {
if cs.LockedRound != lockRound || !bytes.Equal(cs.LockedBlock.Hash(), lockedBlockHash) {
if rs.LockedRound != lockRound || !bytes.Equal(rs.LockedBlock.Hash(), lockedBlockHash) {
panic(fmt.Sprintf(
"Expected block to be locked on round %d, got %d. Got locked block %X, expected %X",
lockRound,
cs.LockedRound,
cs.LockedBlock.Hash(),
rs.LockedRound,
rs.LockedBlock.Hash(),
lockedBlockHash))
}
}
}
func validatePrevoteAndPrecommit(
t *testing.T,
cs *State,
thisRound,
lockRound int32,
privVal *validatorStub,
votedBlockHash,
lockedBlockHash []byte,
) {
// verify the prevote
validatePrevote(t, cs, thisRound, privVal, votedBlockHash)
// verify precommit
cs.mtx.Lock()
validatePrecommit(t, cs, thisRound, lockRound, privVal, votedBlockHash, lockedBlockHash)
cs.mtx.Unlock()
}
func subscribeToVoter(cs *State, addr []byte) <-chan tmpubsub.Message {
votesSub, err := cs.eventBus.SubscribeUnbuffered(context.Background(), testSubscriber, types.EventQueryVote)
if err != nil {
@@ -695,6 +683,11 @@ func ensurePrevoteMatch(t *testing.T, voteCh <-chan tmpubsub.Message, height int
ensureVoteMatch(t, voteCh, height, round, hash, tmproto.PrevoteType)
}
func ensurePrecommitMatch(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, round int32, hash []byte) {
t.Helper()
ensureVoteMatch(t, voteCh, height, round, hash, tmproto.PrecommitType)
}
func ensureVoteMatch(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, round int32, hash []byte, voteType tmproto.SignedMsgType) {
t.Helper()
select {
@@ -960,5 +953,6 @@ func signDataIsEqual(v1 *types.Vote, v2 *tmproto.Vote) bool {
v1.Height == v2.GetHeight() &&
v1.Round == v2.Round &&
bytes.Equal(v1.ValidatorAddress.Bytes(), v2.GetValidatorAddress()) &&
v1.ValidatorIndex == v2.GetValidatorIndex()
v1.ValidatorIndex == v2.GetValidatorIndex() &&
bytes.Equal(v1.Extension, v2.Extension)
}
+6 -1
View File
@@ -349,6 +349,8 @@ func TestConsMsgsVectors(t *testing.T) {
BlockID: bi,
}
vpb := v.ToProto()
v.Extension = []byte("extension")
vextPb := v.ToProto()
testCases := []struct {
testName string
@@ -381,9 +383,12 @@ func TestConsMsgsVectors(t *testing.T) {
{"BlockPart", &tmcons.Message{Sum: &tmcons.Message_BlockPart{
BlockPart: &tmcons.BlockPart{Height: 1, Round: 1, Part: *pbParts}}},
"2a36080110011a3008011204746573741a26080110011a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d"},
{"Vote", &tmcons.Message{Sum: &tmcons.Message_Vote{
{"Vote_without_ext", &tmcons.Message{Sum: &tmcons.Message_Vote{
Vote: &tmcons.Vote{Vote: vpb}}},
"32700a6e0802100122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d2a0608c0b89fdc0532146164645f6d6f72655f6578636c616d6174696f6e3801"},
{"Vote_with_ext", &tmcons.Message{Sum: &tmcons.Message_Vote{
Vote: &tmcons.Vote{Vote: vextPb}}},
"327b0a790802100122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d2a0608c0b89fdc0532146164645f6d6f72655f6578636c616d6174696f6e38014a09657874656e73696f6e"},
{"HasVote", &tmcons.Message{Sum: &tmcons.Message_HasVote{
HasVote: &tmcons.HasVote{Height: 1, Round: 1, Type: tmproto.PrevoteType, Index: 1}}},
"3a080801100118012001"},
+23 -2
View File
@@ -765,7 +765,6 @@ func (cs *State) receiveRoutine(maxSteps int) {
if err := cs.wal.Write(mi); err != nil {
cs.Logger.Error("failed writing to WAL", "err", err)
}
// handles proposals, block parts, votes
// may generate internal events (votes, complete proposals, 2/3 majorities)
cs.handleMsg(mi)
@@ -1971,6 +1970,7 @@ func (cs *State) handleCompleteProposal(blockHeight int64) {
// Attempt to add the vote. if its a duplicate signature, dupeout the validator
func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) {
added, err := cs.addVote(vote, peerID)
if err != nil {
// If the vote height is off, we'll just ignore it,
// But if it's a conflicting sig, add it to the cs.evpool.
@@ -2068,6 +2068,13 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error
return
}
// Verify VoteExtension if precommit
if vote.Type == tmproto.PrecommitType {
if err = cs.blockExec.VerifyVoteExtension(vote); err != nil {
return false, err
}
}
height := cs.Height
added, err = cs.Votes.AddVote(vote, peerID)
if !added {
@@ -2229,9 +2236,19 @@ func (cs *State) signVote(
BlockID: types.BlockID{Hash: hash, PartSetHeader: header},
}
switch msgType {
case tmproto.PrecommitType:
// if the signedMessage type is for a precommit, add VoteExtension
ext, err := cs.blockExec.ExtendVote(vote)
if err != nil {
return nil, err
}
vote.Extension = ext
}
v := vote.ToProto()
err := cs.privValidator.SignVote(cs.state.ChainID, v)
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
vote.Timestamp = v.Timestamp
return vote, err
@@ -2259,7 +2276,11 @@ func (cs *State) voteTime() time.Time {
}
// sign the vote and publish on internalMsgQueue
func (cs *State) signAddVote(msgType tmproto.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote {
func (cs *State) signAddVote(
msgType tmproto.SignedMsgType,
hash []byte,
header types.PartSetHeader,
) *types.Vote {
if cs.privValidator == nil { // the node does not have a key
return nil
}
+246 -6
View File
@@ -363,7 +363,7 @@ func TestStateFullRound1(t *testing.T) {
// nil is proposed, so prevote and precommit nil
func TestStateFullRoundNil(t *testing.T) {
cs, vss := randState(1)
cs, _ := randState(1)
height, round := cs.Height, cs.Round
voteCh := subscribeUnBuffered(cs.eventBus, types.EventQueryVote)
@@ -371,11 +371,8 @@ func TestStateFullRoundNil(t *testing.T) {
cs.enterPrevote(height, round)
cs.startRoutines(4)
ensurePrevote(voteCh, height, round) // prevote
ensurePrecommit(voteCh, height, round) // precommit
// should prevote and precommit nil
validatePrevoteAndPrecommit(t, cs, round, -1, vss[0], nil, nil)
ensurePrevoteMatch(t, voteCh, height, round, nil) // prevote
ensurePrecommitMatch(t, voteCh, height, round, nil) // precommit
}
// run through propose, prevote, precommit commit with two validators
@@ -1421,6 +1418,233 @@ func TestProcessProposalAccept(t *testing.T) {
}
}
// TestExtendVoteCalled tests that the vote extension methods are called at the
// correct point in the consensus algorithm.
func TestExtendVoteCalled(t *testing.T) {
m := abcimocks.NewApplication(t)
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{
VoteExtension: []byte("extension"),
}, nil)
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
}, nil)
m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe()
cs1, vss := randStateWithApp(4, m)
height, round := cs1.Height, cs1.Round
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
pv1, err := cs1.privValidator.GetPubKey()
require.NoError(t, err)
addr := pv1.Address()
voteCh := subscribeToVoter(cs1, addr)
startTestRound(cs1, cs1.Height, round)
ensureNewRound(newRoundCh, height, round)
ensureNewProposal(proposalCh, height, round)
m.AssertNotCalled(t, "ExtendVote", mock.Anything)
rs := cs1.GetRoundState()
blockID := types.BlockID{
Hash: rs.ProposalBlock.Hash(),
PartSetHeader: rs.ProposalBlockParts.Header(),
}
signAddVotes(cs1, tmproto.PrevoteType, blockID.Hash, blockID.PartSetHeader, vss[1:]...)
ensurePrevoteMatch(t, voteCh, height, round, blockID.Hash)
ensurePrecommit(voteCh, height, round)
m.AssertCalled(t, "ExtendVote", context.TODO(), &abci.RequestExtendVote{
Height: height,
Hash: blockID.Hash,
})
m.AssertCalled(t, "VerifyVoteExtension", context.TODO(), &abci.RequestVerifyVoteExtension{
Hash: blockID.Hash,
ValidatorAddress: addr,
Height: height,
VoteExtension: []byte("extension"),
})
signAddVotes(cs1, tmproto.PrecommitType, blockID.Hash, blockID.PartSetHeader, vss[1:]...)
ensureNewRound(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] {
pv, err := pv.GetPubKey()
require.NoError(t, err)
addr := pv.Address()
m.AssertCalled(t, "VerifyVoteExtension", context.TODO(), &abci.RequestVerifyVoteExtension{
Hash: blockID.Hash,
ValidatorAddress: addr,
Height: height,
VoteExtension: []byte("extension"),
})
}
}
// TestVerifyVoteExtensionNotCalledOnAbsentPrecommit tests that the VerifyVoteExtension
// method is not called for a validator's vote that is never delivered.
func TestVerifyVoteExtensionNotCalledOnAbsentPrecommit(t *testing.T) {
m := abcimocks.NewApplication(t)
m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil)
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{
VoteExtension: []byte("extension"),
}, 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("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
cs1, vss := randStateWithApp(4, m)
height, round := cs1.Height, cs1.Round
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
pv1, err := cs1.privValidator.GetPubKey()
require.NoError(t, err)
addr := pv1.Address()
voteCh := subscribeToVoter(cs1, addr)
startTestRound(cs1, cs1.Height, round)
ensureNewRound(newRoundCh, height, round)
ensureNewProposal(proposalCh, height, round)
rs := cs1.GetRoundState()
blockID := types.BlockID{
Hash: rs.ProposalBlock.Hash(),
PartSetHeader: rs.ProposalBlockParts.Header(),
}
signAddVotes(cs1, tmproto.PrevoteType, blockID.Hash, blockID.PartSetHeader, vss[2:]...)
ensurePrevoteMatch(t, voteCh, height, round, blockID.Hash)
ensurePrecommit(voteCh, height, round)
m.AssertCalled(t, "ExtendVote", context.TODO(), &abci.RequestExtendVote{
Height: height,
Hash: blockID.Hash,
})
m.AssertCalled(t, "VerifyVoteExtension", context.TODO(), &abci.RequestVerifyVoteExtension{
Hash: blockID.Hash,
ValidatorAddress: addr,
Height: height,
VoteExtension: []byte("extension"),
})
signAddVotes(cs1, tmproto.PrecommitType, blockID.Hash, blockID.PartSetHeader, vss[2:]...)
ensureNewRound(newRoundCh, height+1, 0)
m.AssertExpectations(t)
// vss[1] did not issue a precommit for the block, ensure that a vote extension
// for its address was not sent to the application.
pv, err := vss[1].GetPubKey()
require.NoError(t, err)
addr = pv.Address()
m.AssertNotCalled(t, "VerifyVoteExtension", context.TODO(), &abci.RequestVerifyVoteExtension{
Hash: blockID.Hash,
ValidatorAddress: addr,
Height: height,
VoteExtension: []byte("extension"),
})
}
// TestPrepareProposalReceivesVoteExtensions tests that the PrepareProposal method
// is called with the vote extensions from the previous height. The test functions
// by completing a consensus height with a mock application as the proposer. The
// test then proceeds to fail sever rounds of consensus until the mock application
// is the proposer again and ensures that the mock application receives the set of
// vote extensions from the previous consensus instance.
func TestPrepareProposalReceivesVoteExtensions(t *testing.T) {
// create a list of vote extensions, one for each validator.
voteExtensions := [][]byte{
[]byte("extension 0"),
[]byte("extension 1"),
[]byte("extension 2"),
[]byte("extension 3"),
}
m := abcimocks.NewApplication(t)
m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{
VoteExtension: voteExtensions[0],
}, nil)
m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil)
// capture the prepare proposal request.
rpp := &abci.RequestPrepareProposal{}
m.On("PrepareProposal", mock.Anything, mock.MatchedBy(func(r *abci.RequestPrepareProposal) bool {
rpp = r
return true
})).Return(&abci.ResponsePrepareProposal{}, nil)
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT}, nil)
m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil)
cs1, vss := randStateWithApp(4, m)
height, round := cs1.Height, cs1.Round
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
pv1, err := cs1.privValidator.GetPubKey()
require.NoError(t, err)
addr := pv1.Address()
voteCh := subscribeToVoter(cs1, addr)
startTestRound(cs1, height, round)
ensureNewRound(newRoundCh, height, round)
ensureNewProposal(proposalCh, height, round)
rs := cs1.GetRoundState()
blockID := types.BlockID{
Hash: rs.ProposalBlock.Hash(),
PartSetHeader: rs.ProposalBlockParts.Header(),
}
signAddVotes(cs1, tmproto.PrevoteType, blockID.Hash, blockID.PartSetHeader, vss[1:]...)
// create a precommit for each validator with the associated vote extension.
for i, vs := range vss[1:] {
signAddPrecommitWithExtension(t, cs1, blockID.Hash, blockID.PartSetHeader, voteExtensions[i+1], vs)
}
ensurePrevote(voteCh, height, round)
// ensure that the height is committed.
ensurePrecommitMatch(t, voteCh, height, round, blockID.Hash)
incrementHeight(vss[1:]...)
height++
round = 0
ensureNewRound(newRoundCh, height, round)
incrementRound(vss[1:]...)
incrementRound(vss[1:]...)
incrementRound(vss[1:]...)
round = 3
blockID2 := types.BlockID{}
signAddVotes(cs1, tmproto.PrecommitType, blockID2.Hash, blockID2.PartSetHeader, vss[1:]...)
ensureNewRound(newRoundCh, height, round)
ensureNewProposal(proposalCh, height, round)
// ensure that the proposer received the list of vote extensions from the
// previous height.
require.Len(t, rpp.LocalLastCommit.Votes, len(vss))
for i := range vss {
require.Equal(t, rpp.LocalLastCommit.Votes[i].VoteExtension, voteExtensions[i])
}
}
func TestFinalizeBlockCalled(t *testing.T) {
for _, testCase := range []struct {
name string
@@ -1444,6 +1668,10 @@ func TestFinalizeBlockCalled(t *testing.T) {
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)
m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
}, nil)
r := &abci.ResponseFinalizeBlock{AgreedAppData: []byte("the_hash")}
m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(r, nil).Maybe()
m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe()
@@ -2090,3 +2318,15 @@ func subscribeUnBuffered(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpu
}
return sub.Out()
}
func signAddPrecommitWithExtension(
t *testing.T,
cs *State,
hash []byte,
header types.PartSetHeader,
extension []byte,
stub *validatorStub) {
v, err := stub.signVote(tmproto.PrecommitType, hash, header, extension)
require.NoError(t, err, "failed to sign vote")
addVotes(cs, v)
}
+1
View File
@@ -82,6 +82,7 @@ func makeVoteHR(t *testing.T, height int64, valIndex, round int32, privVals []ty
}
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
return vote
}
+1
View File
@@ -32,6 +32,7 @@ func MakeCommitFromVoteSet(blockID types.BlockID, voteSet *types.VoteSet, valida
return nil, err
}
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
if _, err := voteSet.AddVote(vote); err != nil {
return nil, err
}
+1
View File
@@ -38,5 +38,6 @@ func MakeVote(
}
v.Signature = vpb.Signature
v.ExtensionSignature = vpb.ExtensionSignature
return v, nil
}
+29 -8
View File
@@ -82,6 +82,14 @@ type FilePVLastSignState struct {
filePath string
}
func (lss *FilePVLastSignState) reset() {
lss.Height = 0
lss.Round = 0
lss.Step = 0
lss.Signature = nil
lss.SignBytes = nil
}
// CheckHRS checks the given height, round, step (HRS) against that of the
// FilePVLastSignState. It returns an error if the arguments constitute a regression,
// or if they match but the SignBytes are empty.
@@ -276,12 +284,7 @@ func (pv *FilePV) Save() {
// Reset resets all fields in the FilePV.
// NOTE: Unsafe!
func (pv *FilePV) Reset() {
var sig []byte
pv.LastSignState.Height = 0
pv.LastSignState.Round = 0
pv.LastSignState.Step = 0
pv.LastSignState.Signature = sig
pv.LastSignState.SignBytes = nil
pv.LastSignState.reset()
pv.Save()
}
@@ -312,6 +315,12 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error {
}
signBytes := types.VoteSignBytes(chainID, vote)
extSignBytes := types.VoteExtensionSignBytes(chainID, vote)
// We always sign the vote extension. See below for details.
extSig, err := pv.Key.PrivKey.Sign(extSignBytes)
if err != nil {
return err
}
// We might crash before writing to the wal,
// causing us to try to re-sign for the same HRS.
@@ -322,11 +331,19 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error {
if bytes.Equal(signBytes, lss.SignBytes) {
vote.Signature = lss.Signature
} else if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok {
// Compares the canonicalized votes (i.e. without vote extensions
// or vote extension signatures).
vote.Timestamp = timestamp
vote.Signature = lss.Signature
} else {
err = fmt.Errorf("conflicting data")
}
// Vote extensions are non-deterministic, so it's possible that an
// application may have created a different extension. We therefore
// always re-sign the vote extension.
vote.ExtensionSignature = extSig
return err
}
@@ -337,6 +354,8 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error {
}
pv.saveSigned(height, round, step, signBytes, sig)
vote.Signature = sig
vote.ExtensionSignature = extSig
return nil
}
@@ -396,8 +415,10 @@ func (pv *FilePV) saveSigned(height int64, round int32, step int8,
//-----------------------------------------------------------------------------------------
// returns the timestamp from the lastSignBytes.
// returns true if the only difference in the votes is their timestamp.
// Returns the timestamp from the lastSignBytes.
// Returns true if the only difference in the votes is their timestamp.
// Performs these checks on the canonical votes (excluding the vote extension
// and vote extension signatures).
func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
var lastVote, newVote tmproto.CanonicalVote
if err := protoio.UnmarshalDelimited(lastSignBytes, &lastVote); err != nil {
+92 -41
View File
@@ -20,33 +20,21 @@ import (
)
func TestGenLoadValidator(t *testing.T) {
assert := assert.New(t)
tempKeyFile, err := os.CreateTemp("", "priv_validator_key_")
require.Nil(t, err)
tempStateFile, err := os.CreateTemp("", "priv_validator_state_")
require.Nil(t, err)
privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
privVal, tempKeyFileName, tempStateFileName := newTestFilePV(t)
height := int64(100)
privVal.LastSignState.Height = height
privVal.Save()
addr := privVal.GetAddress()
privVal = LoadFilePV(tempKeyFile.Name(), tempStateFile.Name())
assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same")
assert.Equal(height, privVal.LastSignState.Height, "expected privval.LastHeight to have been saved")
privVal = LoadFilePV(tempKeyFileName, tempStateFileName)
assert.Equal(t, addr, privVal.GetAddress(), "expected privval addr to be the same")
assert.Equal(t, height, privVal.LastSignState.Height, "expected privval.LastHeight to have been saved")
}
func TestResetValidator(t *testing.T) {
tempKeyFile, err := os.CreateTemp("", "priv_validator_key_")
require.Nil(t, err)
tempStateFile, err := os.CreateTemp("", "priv_validator_state_")
require.Nil(t, err)
privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
emptyState := FilePVLastSignState{filePath: tempStateFile.Name()}
privVal, _, tempStateFileName := newTestFilePV(t)
emptyState := FilePVLastSignState{filePath: tempStateFileName}
// new priv val has empty state
assert.Equal(t, privVal.LastSignState, emptyState)
@@ -56,8 +44,8 @@ func TestResetValidator(t *testing.T) {
voteType := tmproto.PrevoteType
randBytes := tmrand.Bytes(tmhash.Size)
blockID := types.BlockID{Hash: randBytes, PartSetHeader: types.PartSetHeader{}}
vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID)
err = privVal.SignVote("mychainid", vote.ToProto())
vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID, nil)
err := privVal.SignVote("mychainid", vote.ToProto())
assert.NoError(t, err, "expected no error signing vote")
// priv val after signing is not same as empty
@@ -158,12 +146,7 @@ func TestUnmarshalValidatorKey(t *testing.T) {
func TestSignVote(t *testing.T) {
assert := assert.New(t)
tempKeyFile, err := os.CreateTemp("", "priv_validator_key_")
require.Nil(t, err)
tempStateFile, err := os.CreateTemp("", "priv_validator_state_")
require.Nil(t, err)
privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
privVal, _, _ := newTestFilePV(t)
randbytes := tmrand.Bytes(tmhash.Size)
randbytes2 := tmrand.Bytes(tmhash.Size)
@@ -177,9 +160,9 @@ func TestSignVote(t *testing.T) {
voteType := tmproto.PrevoteType
// sign a vote for first time
vote := newVote(privVal.Key.Address, 0, height, round, voteType, block1)
vote := newVote(privVal.Key.Address, 0, height, round, voteType, block1, nil)
v := vote.ToProto()
err = privVal.SignVote("mychainid", v)
err := privVal.SignVote("mychainid", v)
assert.NoError(err, "expected no error signing vote")
// try to sign the same vote again; should be fine
@@ -188,10 +171,10 @@ func TestSignVote(t *testing.T) {
// now try some bad votes
cases := []*types.Vote{
newVote(privVal.Key.Address, 0, height, round-1, voteType, block1), // round regression
newVote(privVal.Key.Address, 0, height-1, round, voteType, block1), // height regression
newVote(privVal.Key.Address, 0, height-2, round+4, voteType, block1), // height regression and different round
newVote(privVal.Key.Address, 0, height, round, voteType, block2), // different block
newVote(privVal.Key.Address, 0, height, round-1, voteType, block1, nil), // round regression
newVote(privVal.Key.Address, 0, height-1, round, voteType, block1, nil), // height regression
newVote(privVal.Key.Address, 0, height-2, round+4, voteType, block1, nil), // height regression and different round
newVote(privVal.Key.Address, 0, height, round, voteType, block2, nil), // different block
}
for _, c := range cases {
@@ -211,12 +194,7 @@ func TestSignVote(t *testing.T) {
func TestSignProposal(t *testing.T) {
assert := assert.New(t)
tempKeyFile, err := os.CreateTemp("", "priv_validator_key_")
require.Nil(t, err)
tempStateFile, err := os.CreateTemp("", "priv_validator_state_")
require.Nil(t, err)
privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
privVal, _, _ := newTestFilePV(t)
randbytes := tmrand.Bytes(tmhash.Size)
randbytes2 := tmrand.Bytes(tmhash.Size)
@@ -230,7 +208,7 @@ func TestSignProposal(t *testing.T) {
// sign a proposal for first time
proposal := newProposal(height, round, block1)
pbp := proposal.ToProto()
err = privVal.SignProposal("mychainid", pbp)
err := privVal.SignProposal("mychainid", pbp)
assert.NoError(err, "expected no error signing proposal")
// try to sign the same proposal again; should be fine
@@ -297,30 +275,91 @@ func TestDifferByTimestamp(t *testing.T) {
{
voteType := tmproto.PrevoteType
blockID := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{}}
vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID)
vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID, nil)
v := vote.ToProto()
err := privVal.SignVote("mychainid", v)
assert.NoError(t, err, "expected no error signing vote")
signBytes := types.VoteSignBytes(chainID, v)
sig := v.Signature
extSig := v.ExtensionSignature
timeStamp := vote.Timestamp
// manipulate the timestamp. should get changed back
v.Timestamp = v.Timestamp.Add(time.Millisecond)
var emptySig []byte
v.Signature = emptySig
v.ExtensionSignature = emptySig
err = privVal.SignVote("mychainid", v)
assert.NoError(t, err, "expected no error on signing same vote")
assert.Equal(t, timeStamp, v.Timestamp)
assert.Equal(t, signBytes, types.VoteSignBytes(chainID, v))
assert.Equal(t, sig, v.Signature)
assert.Equal(t, extSig, v.ExtensionSignature)
}
}
func TestVoteExtensionsAreAlwaysSigned(t *testing.T) {
privVal, _, _ := newTestFilePV(t)
pubKey, err := privVal.GetPubKey()
assert.NoError(t, err)
block := types.BlockID{
Hash: tmrand.Bytes(tmhash.Size),
PartSetHeader: types.PartSetHeader{Total: 5, Hash: tmrand.Bytes(tmhash.Size)},
}
height, round := int64(10), int32(1)
voteType := tmproto.PrecommitType
// We initially sign this vote without an extension
vote1 := newVote(privVal.Key.Address, 0, height, round, voteType, block, nil)
vpb1 := vote1.ToProto()
err = privVal.SignVote("mychainid", vpb1)
assert.NoError(t, err, "expected no error signing vote")
assert.NotNil(t, vpb1.ExtensionSignature)
vesb1 := types.VoteExtensionSignBytes("mychainid", vpb1)
assert.True(t, pubKey.VerifySignature(vesb1, vpb1.ExtensionSignature))
// We duplicate this vote precisely, including its timestamp, but change
// its extension
vote2 := vote1.Copy()
vote2.Extension = []byte("new extension")
vpb2 := vote2.ToProto()
err = privVal.SignVote("mychainid", vpb2)
assert.NoError(t, err, "expected no error signing same vote with manipulated vote extension")
// We need to ensure that a valid new extension signature has been created
// that validates against the vote extension sign bytes with the new
// extension, and does not validate against the vote extension sign bytes
// with the old extension.
vesb2 := types.VoteExtensionSignBytes("mychainid", vpb2)
assert.True(t, pubKey.VerifySignature(vesb2, vpb2.ExtensionSignature))
assert.False(t, pubKey.VerifySignature(vesb1, vpb2.ExtensionSignature))
// We now manipulate the timestamp of the vote with the extension, as per
// TestDifferByTimestamp
expectedTimestamp := vpb2.Timestamp
vpb2.Timestamp = vpb2.Timestamp.Add(time.Millisecond)
vpb2.Signature = nil
vpb2.ExtensionSignature = nil
err = privVal.SignVote("mychainid", vpb2)
assert.NoError(t, err, "expected no error signing same vote with manipulated timestamp and vote extension")
assert.Equal(t, expectedTimestamp, vpb2.Timestamp)
vesb3 := types.VoteExtensionSignBytes("mychainid", vpb2)
assert.True(t, pubKey.VerifySignature(vesb3, vpb2.ExtensionSignature))
assert.False(t, pubKey.VerifySignature(vesb1, vpb2.ExtensionSignature))
}
func newVote(addr types.Address, idx int32, height int64, round int32,
typ tmproto.SignedMsgType, blockID types.BlockID) *types.Vote {
typ tmproto.SignedMsgType, blockID types.BlockID, extension []byte) *types.Vote {
return &types.Vote{
ValidatorAddress: addr,
ValidatorIndex: idx,
@@ -329,6 +368,7 @@ func newVote(addr types.Address, idx int32, height int64, round int32,
Type: typ,
Timestamp: tmtime.Now(),
BlockID: blockID,
Extension: extension,
}
}
@@ -340,3 +380,14 @@ func newProposal(height int64, round int32, blockID types.BlockID) *types.Propos
Timestamp: tmtime.Now(),
}
}
func newTestFilePV(t *testing.T) (*FilePV, string, string) {
tempKeyFile, err := os.CreateTemp(t.TempDir(), "priv_validator_key_")
require.NoError(t, err)
tempStateFile, err := os.CreateTemp(t.TempDir(), "priv_validator_state_")
require.NoError(t, err)
privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
return privVal, tempKeyFile.Name(), tempStateFile.Name()
}
+8 -13
View File
@@ -22,19 +22,14 @@ var stamp = time.Date(2019, 10, 13, 16, 14, 44, 0, time.UTC)
func exampleVote() *types.Vote {
return &types.Vote{
Type: tmproto.SignedMsgType(1),
Height: 3,
Round: 2,
Timestamp: stamp,
BlockID: types.BlockID{
Hash: tmhash.Sum([]byte("blockID_hash")),
PartSetHeader: types.PartSetHeader{
Total: 1000000,
Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")),
},
},
Type: tmproto.PrecommitType,
Height: 3,
Round: 2,
BlockID: types.BlockID{Hash: tmhash.Sum([]byte("blockID_hash")), PartSetHeader: types.PartSetHeader{Total: 1000000, Hash: tmhash.Sum([]byte("blockID_part_set_header_hash"))}},
Timestamp: stamp,
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
ValidatorIndex: 56789,
Extension: []byte("extension"),
}
}
@@ -84,8 +79,8 @@ func TestPrivvalVectors(t *testing.T) {
{"pubKey request", &privproto.PubKeyRequest{}, "0a00"},
{"pubKey response", &privproto.PubKeyResponse{PubKey: ppk, Error: nil}, "12240a220a20556a436f1218d30942efe798420f51dc9b6a311b929c578257457d05c5fcf230"},
{"pubKey response with error", &privproto.PubKeyResponse{PubKey: cryptoproto.PublicKey{}, Error: remoteError}, "12140a0012100801120c697427732061206572726f72"},
{"Vote Request", &privproto.SignVoteRequest{Vote: votepb}, "1a760a74080110031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a2a0608f49a8ded0532146af1f4111082efb388211bc72c55bcd61e9ac3d538d5bb03"},
{"Vote Response", &privproto.SignedVoteResponse{Vote: *votepb, Error: nil}, "22760a74080110031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a2a0608f49a8ded0532146af1f4111082efb388211bc72c55bcd61e9ac3d538d5bb03"},
{"Vote Request", &privproto.SignVoteRequest{Vote: votepb}, "1a81010a7f080210031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a2a0608f49a8ded0532146af1f4111082efb388211bc72c55bcd61e9ac3d538d5bb034a09657874656e73696f6e"},
{"Vote Response", &privproto.SignedVoteResponse{Vote: *votepb, Error: nil}, "2281010a7f080210031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a2a0608f49a8ded0532146af1f4111082efb388211bc72c55bcd61e9ac3d538d5bb034a09657874656e73696f6e"},
{"Vote Response with error", &privproto.SignedVoteResponse{Vote: tmproto.Vote{}, Error: remoteError}, "22250a11220212002a0b088092b8c398feffffff0112100801120c697427732061206572726f72"},
{"Proposal Request", &privproto.SignProposalRequest{Proposal: proposalpb}, "2a700a6e08011003180220022a4a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a320608f49a8ded053a10697427732061207369676e6174757265"},
{"Proposal Response", &privproto.SignedProposalResponse{Proposal: *proposalpb, Error: nil}, "32700a6e08011003180220022a4a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a320608f49a8ded053a10697427732061207369676e6174757265"},
+72 -35
View File
@@ -30,6 +30,8 @@ service ABCI {
returns (ResponseApplySnapshotChunk);
rpc PrepareProposal(RequestPrepareProposal) returns (ResponsePrepareProposal);
rpc ProcessProposal(RequestProcessProposal) returns (ResponseProcessProposal);
rpc ExtendVote(RequestExtendVote) returns (ResponseExtendVote);
rpc VerifyVoteExtension(RequestVerifyVoteExtension) returns (ResponseVerifyVoteExtension);
rpc FinalizeBlock(RequestFinalizeBlock) returns (ResponseFinalizeBlock);
}
@@ -38,20 +40,22 @@ service ABCI {
message Request {
oneof value {
RequestEcho echo = 1;
RequestFlush flush = 2;
RequestInfo info = 3;
RequestInitChain init_chain = 5;
RequestQuery query = 6;
RequestCheckTx check_tx = 8;
RequestCommit commit = 11;
RequestListSnapshots list_snapshots = 12;
RequestOfferSnapshot offer_snapshot = 13;
RequestLoadSnapshotChunk load_snapshot_chunk = 14;
RequestApplySnapshotChunk apply_snapshot_chunk = 15;
RequestPrepareProposal prepare_proposal = 16;
RequestProcessProposal process_proposal = 17;
RequestFinalizeBlock finalize_block = 20;
RequestEcho echo = 1;
RequestFlush flush = 2;
RequestInfo info = 3;
RequestInitChain init_chain = 5;
RequestQuery query = 6;
RequestCheckTx check_tx = 8;
RequestCommit commit = 11;
RequestListSnapshots list_snapshots = 12;
RequestOfferSnapshot offer_snapshot = 13;
RequestLoadSnapshotChunk load_snapshot_chunk = 14;
RequestApplySnapshotChunk apply_snapshot_chunk = 15;
RequestPrepareProposal prepare_proposal = 16;
RequestProcessProposal process_proposal = 17;
RequestExtendVote extend_vote = 18;
RequestVerifyVoteExtension verify_vote_extension = 19;
RequestFinalizeBlock finalize_block = 20;
}
reserved 4, 7, 9, 10; // SetOption, BeginBlock, DeliverTx, EndBlock
}
@@ -149,6 +153,20 @@ message RequestProcessProposal {
bytes proposer_address = 8;
}
// Extends a vote with application-side injection
message RequestExtendVote {
bytes hash = 1;
int64 height = 2;
}
// Verify the vote extension
message RequestVerifyVoteExtension {
bytes hash = 1;
bytes validator_address = 2;
int64 height = 3;
bytes vote_extension = 4;
}
message RequestFinalizeBlock {
repeated bytes txs = 1;
CommitInfo decided_last_commit = 2 [(gogoproto.nullable) = false];
@@ -167,21 +185,23 @@ message RequestFinalizeBlock {
message Response {
oneof value {
ResponseException exception = 1;
ResponseEcho echo = 2;
ResponseFlush flush = 3;
ResponseInfo info = 4;
ResponseInitChain init_chain = 6;
ResponseQuery query = 7;
ResponseCheckTx check_tx = 9;
ResponseCommit commit = 12;
ResponseListSnapshots list_snapshots = 13;
ResponseOfferSnapshot offer_snapshot = 14;
ResponseLoadSnapshotChunk load_snapshot_chunk = 15;
ResponseApplySnapshotChunk apply_snapshot_chunk = 16;
ResponsePrepareProposal prepare_proposal = 17;
ResponseProcessProposal process_proposal = 18;
ResponseFinalizeBlock finalize_block = 21;
ResponseException exception = 1;
ResponseEcho echo = 2;
ResponseFlush flush = 3;
ResponseInfo info = 4;
ResponseInitChain init_chain = 6;
ResponseQuery query = 7;
ResponseCheckTx check_tx = 9;
ResponseCommit commit = 12;
ResponseListSnapshots list_snapshots = 13;
ResponseOfferSnapshot offer_snapshot = 14;
ResponseLoadSnapshotChunk load_snapshot_chunk = 15;
ResponseApplySnapshotChunk apply_snapshot_chunk = 16;
ResponsePrepareProposal prepare_proposal = 17;
ResponseProcessProposal process_proposal = 18;
ResponseExtendVote extend_vote = 19;
ResponseVerifyVoteExtension verify_vote_extension = 20;
ResponseFinalizeBlock finalize_block = 21;
}
reserved 5, 8, 10, 11; // SetOption, BeginBlock, DeliverTx, EndBlock
}
@@ -299,6 +319,20 @@ message ResponseProcessProposal {
}
}
message ResponseExtendVote {
bytes vote_extension = 1;
}
message ResponseVerifyVoteExtension {
VerifyStatus status = 1;
enum VerifyStatus {
UNKNOWN = 0;
ACCEPT = 1;
REJECT = 2;
}
}
message ResponseFinalizeBlock {
// set of block events emmitted as part of executing the block
repeated Event events = 1
@@ -323,6 +357,9 @@ message CommitInfo {
repeated VoteInfo votes = 2 [(gogoproto.nullable) = false];
}
// ExtendedCommitInfo is similar to CommitInfo except that it is only used in
// the PrepareProposal request such that Tendermint can provide vote extensions
// to the application.
message ExtendedCommitInfo {
// The round at which the block proposer decided in the previous height.
int32 round = 1;
@@ -377,29 +414,29 @@ message TxResult {
//----------------------------------------
// Blockchain Types
// Validator
message Validator {
bytes address = 1; // The first 20 bytes of SHA256(public key)
// PubKey pub_key = 2 [(gogoproto.nullable)=false];
int64 power = 3; // The voting power
}
// ValidatorUpdate
message ValidatorUpdate {
tendermint.crypto.PublicKey pub_key = 1 [(gogoproto.nullable) = false];
int64 power = 2;
}
// VoteInfo
message VoteInfo {
Validator validator = 1 [(gogoproto.nullable) = false];
bool signed_last_block = 2;
}
message ExtendedVoteInfo {
Validator validator = 1 [(gogoproto.nullable) = false];
bool signed_last_block = 2;
bytes vote_extension = 3; // Reserved for future use
// The validator that sent the vote.
Validator validator = 1 [(gogoproto.nullable) = false];
// Indicates whether the validator signed the last block, allowing for rewards based on validator availability.
bool signed_last_block = 2;
// Non-deterministic extension provided by the sending validator's application.
bytes vote_extension = 3;
}
enum MisbehaviorType {
-1
View File
@@ -27,7 +27,6 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// PublicKey defines the keys available for use with Tendermint Validators
type PublicKey struct {
// Types that are valid to be assigned to Sum:
//
// *PublicKey_Ed25519
// *PublicKey_Secp256K1
Sum isPublicKey_Sum `protobuf_oneof:"sum"`
-1
View File
@@ -68,7 +68,6 @@ func (m *Txs) GetTxs() [][]byte {
type Message struct {
// Types that are valid to be assigned to Sum:
//
// *Message_Txs
Sum isMessage_Sum `protobuf_oneof:"sum"`
}
-1
View File
@@ -158,7 +158,6 @@ func (m *PacketMsg) GetData() []byte {
type Packet struct {
// Types that are valid to be assigned to Sum:
//
// *Packet_PacketPing
// *Packet_PacketPong
// *Packet_PacketMsg
-1
View File
@@ -24,7 +24,6 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type Message struct {
// Types that are valid to be assigned to Sum:
//
// *Message_SnapshotsRequest
// *Message_SnapshotsResponse
// *Message_ChunkRequest
+386 -43
View File
@@ -225,12 +225,13 @@ func (m *CanonicalProposal) GetChainID() string {
}
type CanonicalVote struct {
Type SignedMsgType `protobuf:"varint,1,opt,name=type,proto3,enum=tendermint.types.SignedMsgType" json:"type,omitempty"`
Height int64 `protobuf:"fixed64,2,opt,name=height,proto3" json:"height,omitempty"`
Round int64 `protobuf:"fixed64,3,opt,name=round,proto3" json:"round,omitempty"`
BlockID *CanonicalBlockID `protobuf:"bytes,4,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"`
Timestamp time.Time `protobuf:"bytes,5,opt,name=timestamp,proto3,stdtime" json:"timestamp"`
ChainID string `protobuf:"bytes,6,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"`
Type SignedMsgType `protobuf:"varint,1,opt,name=type,proto3,enum=tendermint.types.SignedMsgType" json:"type,omitempty"`
Height int64 `protobuf:"fixed64,2,opt,name=height,proto3" json:"height,omitempty"`
Round int64 `protobuf:"fixed64,3,opt,name=round,proto3" json:"round,omitempty"`
BlockID *CanonicalBlockID `protobuf:"bytes,4,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"`
Timestamp time.Time `protobuf:"bytes,5,opt,name=timestamp,proto3,stdtime" json:"timestamp"`
ChainID string `protobuf:"bytes,6,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"`
VoteExtension *VoteExtensionToSign `protobuf:"bytes,7,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,omitempty"`
}
func (m *CanonicalVote) Reset() { *m = CanonicalVote{} }
@@ -308,48 +309,130 @@ func (m *CanonicalVote) GetChainID() string {
return ""
}
func (m *CanonicalVote) GetVoteExtension() *VoteExtensionToSign {
if m != nil {
return m.VoteExtension
}
return nil
}
// CanonicalVoteExtension provides us a way to serialize a vote extension from
// a particular validator such that we can sign over those serialized bytes.
type CanonicalVoteExtension struct {
Extension []byte `protobuf:"bytes,1,opt,name=extension,proto3" json:"extension,omitempty"`
Height int64 `protobuf:"fixed64,2,opt,name=height,proto3" json:"height,omitempty"`
Round int64 `protobuf:"fixed64,3,opt,name=round,proto3" json:"round,omitempty"`
ChainId string `protobuf:"bytes,4,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"`
}
func (m *CanonicalVoteExtension) Reset() { *m = CanonicalVoteExtension{} }
func (m *CanonicalVoteExtension) String() string { return proto.CompactTextString(m) }
func (*CanonicalVoteExtension) ProtoMessage() {}
func (*CanonicalVoteExtension) Descriptor() ([]byte, []int) {
return fileDescriptor_8d1a1a84ff7267ed, []int{4}
}
func (m *CanonicalVoteExtension) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *CanonicalVoteExtension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_CanonicalVoteExtension.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 *CanonicalVoteExtension) XXX_Merge(src proto.Message) {
xxx_messageInfo_CanonicalVoteExtension.Merge(m, src)
}
func (m *CanonicalVoteExtension) XXX_Size() int {
return m.Size()
}
func (m *CanonicalVoteExtension) XXX_DiscardUnknown() {
xxx_messageInfo_CanonicalVoteExtension.DiscardUnknown(m)
}
var xxx_messageInfo_CanonicalVoteExtension proto.InternalMessageInfo
func (m *CanonicalVoteExtension) GetExtension() []byte {
if m != nil {
return m.Extension
}
return nil
}
func (m *CanonicalVoteExtension) GetHeight() int64 {
if m != nil {
return m.Height
}
return 0
}
func (m *CanonicalVoteExtension) GetRound() int64 {
if m != nil {
return m.Round
}
return 0
}
func (m *CanonicalVoteExtension) GetChainId() string {
if m != nil {
return m.ChainId
}
return ""
}
func init() {
proto.RegisterType((*CanonicalBlockID)(nil), "tendermint.types.CanonicalBlockID")
proto.RegisterType((*CanonicalPartSetHeader)(nil), "tendermint.types.CanonicalPartSetHeader")
proto.RegisterType((*CanonicalProposal)(nil), "tendermint.types.CanonicalProposal")
proto.RegisterType((*CanonicalVote)(nil), "tendermint.types.CanonicalVote")
proto.RegisterType((*CanonicalVoteExtension)(nil), "tendermint.types.CanonicalVoteExtension")
}
func init() { proto.RegisterFile("tendermint/types/canonical.proto", fileDescriptor_8d1a1a84ff7267ed) }
var fileDescriptor_8d1a1a84ff7267ed = []byte{
// 487 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x53, 0x3d, 0x6f, 0xd3, 0x40,
0x18, 0xce, 0xa5, 0x4e, 0xe2, 0x5c, 0x1b, 0x08, 0xa7, 0xaa, 0xb2, 0x22, 0x64, 0x5b, 0x1e, 0x90,
0x59, 0x6c, 0xa9, 0x1d, 0xd8, 0x5d, 0x06, 0x82, 0x40, 0x94, 0x6b, 0xd5, 0x81, 0x25, 0xba, 0xd8,
0x87, 0x6d, 0xe1, 0xf8, 0x4e, 0xf6, 0x65, 0xe8, 0xc2, 0x6f, 0xe8, 0xef, 0xe0, 0x97, 0x74, 0xec,
0x08, 0x4b, 0x40, 0xce, 0x1f, 0x41, 0x77, 0x4e, 0xec, 0xa8, 0x01, 0x16, 0x10, 0xcb, 0xe9, 0xfd,
0x78, 0xee, 0x79, 0x1f, 0x3d, 0xaf, 0x5e, 0x68, 0x0b, 0x9a, 0x47, 0xb4, 0x58, 0xa4, 0xb9, 0xf0,
0xc5, 0x0d, 0xa7, 0xa5, 0x1f, 0x92, 0x9c, 0xe5, 0x69, 0x48, 0x32, 0x8f, 0x17, 0x4c, 0x30, 0x34,
0x6e, 0x11, 0x9e, 0x42, 0x4c, 0x8e, 0x63, 0x16, 0x33, 0xd5, 0xf4, 0x65, 0x54, 0xe3, 0x26, 0x4f,
0xf7, 0x98, 0xd4, 0xbb, 0xe9, 0x5a, 0x31, 0x63, 0x71, 0x46, 0x7d, 0x95, 0xcd, 0x97, 0x1f, 0x7d,
0x91, 0x2e, 0x68, 0x29, 0xc8, 0x82, 0xd7, 0x00, 0xe7, 0x33, 0x1c, 0x9f, 0x6f, 0x27, 0x07, 0x19,
0x0b, 0x3f, 0x4d, 0x5f, 0x22, 0x04, 0xb5, 0x84, 0x94, 0x89, 0x01, 0x6c, 0xe0, 0x1e, 0x61, 0x15,
0xa3, 0x6b, 0xf8, 0x98, 0x93, 0x42, 0xcc, 0x4a, 0x2a, 0x66, 0x09, 0x25, 0x11, 0x2d, 0x8c, 0xae,
0x0d, 0xdc, 0xc3, 0x53, 0xd7, 0x7b, 0x28, 0xd4, 0x6b, 0x08, 0x2f, 0x48, 0x21, 0x2e, 0xa9, 0x78,
0xa5, 0xf0, 0x81, 0x76, 0xb7, 0xb2, 0x3a, 0x78, 0xc4, 0x77, 0x8b, 0x4e, 0x00, 0x4f, 0x7e, 0x0d,
0x47, 0xc7, 0xb0, 0x27, 0x98, 0x20, 0x99, 0x92, 0x31, 0xc2, 0x75, 0xd2, 0x68, 0xeb, 0xb6, 0xda,
0x9c, 0x6f, 0x5d, 0xf8, 0xa4, 0x25, 0x29, 0x18, 0x67, 0x25, 0xc9, 0xd0, 0x19, 0xd4, 0xa4, 0x1c,
0xf5, 0xfd, 0xd1, 0xa9, 0xb5, 0x2f, 0xf3, 0x32, 0x8d, 0x73, 0x1a, 0xbd, 0x2d, 0xe3, 0xab, 0x1b,
0x4e, 0xb1, 0x02, 0xa3, 0x13, 0xd8, 0x4f, 0x68, 0x1a, 0x27, 0x42, 0x0d, 0x18, 0xe3, 0x4d, 0x26,
0xc5, 0x14, 0x6c, 0x99, 0x47, 0xc6, 0x81, 0x2a, 0xd7, 0x09, 0x7a, 0x0e, 0x87, 0x9c, 0x65, 0xb3,
0xba, 0xa3, 0xd9, 0xc0, 0x3d, 0x08, 0x8e, 0xaa, 0x95, 0xa5, 0x5f, 0xbc, 0x7b, 0x83, 0x65, 0x0d,
0xeb, 0x9c, 0x65, 0x2a, 0x42, 0xaf, 0xa1, 0x3e, 0x97, 0xf6, 0xce, 0xd2, 0xc8, 0xe8, 0x29, 0xe3,
0x9c, 0x3f, 0x18, 0xb7, 0xd9, 0x44, 0x70, 0x58, 0xad, 0xac, 0xc1, 0x26, 0xc1, 0x03, 0x45, 0x30,
0x8d, 0x50, 0x00, 0x87, 0xcd, 0x1a, 0x8d, 0xbe, 0x22, 0x9b, 0x78, 0xf5, 0xa2, 0xbd, 0xed, 0xa2,
0xbd, 0xab, 0x2d, 0x22, 0xd0, 0xa5, 0xef, 0xb7, 0xdf, 0x2d, 0x80, 0xdb, 0x6f, 0xe8, 0x19, 0xd4,
0xc3, 0x84, 0xa4, 0xb9, 0xd4, 0x33, 0xb0, 0x81, 0x3b, 0xac, 0x67, 0x9d, 0xcb, 0x9a, 0x9c, 0xa5,
0x9a, 0xd3, 0xc8, 0xf9, 0xd2, 0x85, 0xa3, 0x46, 0xd6, 0x35, 0x13, 0xf4, 0x7f, 0xf8, 0xba, 0x6b,
0x96, 0xf6, 0x2f, 0xcd, 0xea, 0xfd, 0xbd, 0x59, 0xfd, 0xdf, 0x9b, 0x15, 0xbc, 0xbf, 0xab, 0x4c,
0x70, 0x5f, 0x99, 0xe0, 0x47, 0x65, 0x82, 0xdb, 0xb5, 0xd9, 0xb9, 0x5f, 0x9b, 0x9d, 0xaf, 0x6b,
0xb3, 0xf3, 0xe1, 0x45, 0x9c, 0x8a, 0x64, 0x39, 0xf7, 0x42, 0xb6, 0xf0, 0x77, 0x0f, 0xb6, 0x0d,
0xeb, 0xc3, 0x7e, 0x78, 0xcc, 0xf3, 0xbe, 0xaa, 0x9f, 0xfd, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x6d,
0xdd, 0x12, 0x5d, 0x31, 0x04, 0x00, 0x00,
// 550 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0x4d, 0x6f, 0xd3, 0x40,
0x10, 0x8d, 0xd3, 0x34, 0x1f, 0x9b, 0xa6, 0x84, 0x55, 0x55, 0x99, 0xa8, 0xb2, 0x2d, 0x4b, 0x20,
0x73, 0xb1, 0xa5, 0xf4, 0xc0, 0xdd, 0x05, 0x89, 0xa0, 0x22, 0xca, 0x36, 0xea, 0x81, 0x8b, 0xb5,
0xb1, 0x17, 0xdb, 0xc2, 0xf1, 0x5a, 0xf6, 0xa6, 0xa2, 0x17, 0xf8, 0x0b, 0xfd, 0x59, 0x3d, 0xf6,
0x08, 0x97, 0x80, 0x9c, 0x3f, 0xc1, 0x11, 0xed, 0x3a, 0xb1, 0x4d, 0x03, 0x48, 0x08, 0xc4, 0xc5,
0x9a, 0x79, 0xf3, 0x3c, 0xf3, 0xf6, 0x8d, 0x76, 0x81, 0xc6, 0x48, 0xec, 0x91, 0x74, 0x1e, 0xc6,
0xcc, 0x62, 0x57, 0x09, 0xc9, 0x2c, 0x17, 0xc7, 0x34, 0x0e, 0x5d, 0x1c, 0x99, 0x49, 0x4a, 0x19,
0x85, 0xc3, 0x8a, 0x61, 0x0a, 0xc6, 0xe8, 0xc0, 0xa7, 0x3e, 0x15, 0x45, 0x8b, 0x47, 0x05, 0x6f,
0x74, 0xb4, 0xd5, 0x49, 0x7c, 0xd7, 0x55, 0xd5, 0xa7, 0xd4, 0x8f, 0x88, 0x25, 0xb2, 0xd9, 0xe2,
0xad, 0xc5, 0xc2, 0x39, 0xc9, 0x18, 0x9e, 0x27, 0x05, 0x41, 0xff, 0x00, 0x86, 0x27, 0x9b, 0xc9,
0x76, 0x44, 0xdd, 0x77, 0x93, 0xa7, 0x10, 0x82, 0x56, 0x80, 0xb3, 0x40, 0x96, 0x34, 0xc9, 0xd8,
0x43, 0x22, 0x86, 0x17, 0xe0, 0x5e, 0x82, 0x53, 0xe6, 0x64, 0x84, 0x39, 0x01, 0xc1, 0x1e, 0x49,
0xe5, 0xa6, 0x26, 0x19, 0xfd, 0xb1, 0x61, 0xde, 0x15, 0x6a, 0x96, 0x0d, 0xcf, 0x70, 0xca, 0xce,
0x09, 0x7b, 0x2e, 0xf8, 0x76, 0xeb, 0x66, 0xa9, 0x36, 0xd0, 0x20, 0xa9, 0x83, 0xba, 0x0d, 0x0e,
0x7f, 0x4e, 0x87, 0x07, 0x60, 0x97, 0x51, 0x86, 0x23, 0x21, 0x63, 0x80, 0x8a, 0xa4, 0xd4, 0xd6,
0xac, 0xb4, 0xe9, 0x9f, 0x9b, 0xe0, 0x7e, 0xd5, 0x24, 0xa5, 0x09, 0xcd, 0x70, 0x04, 0x8f, 0x41,
0x8b, 0xcb, 0x11, 0xbf, 0xef, 0x8f, 0xd5, 0x6d, 0x99, 0xe7, 0xa1, 0x1f, 0x13, 0xef, 0x65, 0xe6,
0x4f, 0xaf, 0x12, 0x82, 0x04, 0x19, 0x1e, 0x82, 0x76, 0x40, 0x42, 0x3f, 0x60, 0x62, 0xc0, 0x10,
0xad, 0x33, 0x2e, 0x26, 0xa5, 0x8b, 0xd8, 0x93, 0x77, 0x04, 0x5c, 0x24, 0xf0, 0x31, 0xe8, 0x25,
0x34, 0x72, 0x8a, 0x4a, 0x4b, 0x93, 0x8c, 0x1d, 0x7b, 0x2f, 0x5f, 0xaa, 0xdd, 0xb3, 0x57, 0xa7,
0x88, 0x63, 0xa8, 0x9b, 0xd0, 0x48, 0x44, 0xf0, 0x05, 0xe8, 0xce, 0xb8, 0xbd, 0x4e, 0xe8, 0xc9,
0xbb, 0xc2, 0x38, 0xfd, 0x37, 0xc6, 0xad, 0x37, 0x61, 0xf7, 0xf3, 0xa5, 0xda, 0x59, 0x27, 0xa8,
0x23, 0x1a, 0x4c, 0x3c, 0x68, 0x83, 0x5e, 0xb9, 0x46, 0xb9, 0x2d, 0x9a, 0x8d, 0xcc, 0x62, 0xd1,
0xe6, 0x66, 0xd1, 0xe6, 0x74, 0xc3, 0xb0, 0xbb, 0xdc, 0xf7, 0xeb, 0x2f, 0xaa, 0x84, 0xaa, 0xdf,
0xe0, 0x23, 0xd0, 0x75, 0x03, 0x1c, 0xc6, 0x5c, 0x4f, 0x47, 0x93, 0x8c, 0x5e, 0x31, 0xeb, 0x84,
0x63, 0x7c, 0x96, 0x28, 0x4e, 0x3c, 0xfd, 0x5b, 0x13, 0x0c, 0x4a, 0x59, 0x17, 0x94, 0x91, 0xff,
0xe1, 0x6b, 0xdd, 0xac, 0xd6, 0xbf, 0x34, 0x6b, 0xf7, 0xef, 0xcd, 0x6a, 0xff, 0xda, 0x2c, 0x78,
0x0a, 0xf6, 0x2f, 0x29, 0x23, 0x0e, 0x79, 0xcf, 0x48, 0x9c, 0x85, 0x34, 0x16, 0xd6, 0xf6, 0xc7,
0x0f, 0xb7, 0xd5, 0x73, 0x2b, 0x9f, 0x6d, 0x68, 0x53, 0xca, 0x3d, 0x43, 0x83, 0xcb, 0x3a, 0xa8,
0x7f, 0xac, 0x5d, 0x8d, 0x1f, 0xe8, 0xf0, 0x08, 0xf4, 0xaa, 0x11, 0xc5, 0x2d, 0xad, 0x80, 0x3f,
0xf4, 0xfa, 0x41, 0xed, 0x6c, 0xdc, 0xeb, 0x5e, 0x79, 0x1c, 0xfb, 0xf5, 0x4d, 0xae, 0x48, 0xb7,
0xb9, 0x22, 0x7d, 0xcd, 0x15, 0xe9, 0x7a, 0xa5, 0x34, 0x6e, 0x57, 0x4a, 0xe3, 0xd3, 0x4a, 0x69,
0xbc, 0x79, 0xe2, 0x87, 0x2c, 0x58, 0xcc, 0x4c, 0x97, 0xce, 0xad, 0xfa, 0xfb, 0x53, 0x85, 0xc5,
0x3b, 0x75, 0xf7, 0x6d, 0x9a, 0xb5, 0x05, 0x7e, 0xfc, 0x3d, 0x00, 0x00, 0xff, 0xff, 0x59, 0x81,
0xfb, 0xaf, 0x00, 0x05, 0x00, 0x00,
}
func (m *CanonicalBlockID) Marshal() (dAtA []byte, err error) {
@@ -519,6 +602,18 @@ func (m *CanonicalVote) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.VoteExtension != nil {
{
size, err := m.VoteExtension.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintCanonical(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x3a
}
if len(m.ChainID) > 0 {
i -= len(m.ChainID)
copy(dAtA[i:], m.ChainID)
@@ -526,12 +621,12 @@ func (m *CanonicalVote) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i--
dAtA[i] = 0x32
}
n4, err4 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):])
if err4 != nil {
return 0, err4
n5, err5 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):])
if err5 != nil {
return 0, err5
}
i -= n4
i = encodeVarintCanonical(dAtA, i, uint64(n4))
i -= n5
i = encodeVarintCanonical(dAtA, i, uint64(n5))
i--
dAtA[i] = 0x2a
if m.BlockID != nil {
@@ -566,6 +661,55 @@ func (m *CanonicalVote) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *CanonicalVoteExtension) 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 *CanonicalVoteExtension) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *CanonicalVoteExtension) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.ChainId) > 0 {
i -= len(m.ChainId)
copy(dAtA[i:], m.ChainId)
i = encodeVarintCanonical(dAtA, i, uint64(len(m.ChainId)))
i--
dAtA[i] = 0x22
}
if m.Round != 0 {
i -= 8
encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.Round))
i--
dAtA[i] = 0x19
}
if m.Height != 0 {
i -= 8
encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.Height))
i--
dAtA[i] = 0x11
}
if len(m.Extension) > 0 {
i -= len(m.Extension)
copy(dAtA[i:], m.Extension)
i = encodeVarintCanonical(dAtA, i, uint64(len(m.Extension)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintCanonical(dAtA []byte, offset int, v uint64) int {
offset -= sovCanonical(v)
base := offset
@@ -664,6 +808,33 @@ func (m *CanonicalVote) Size() (n int) {
if l > 0 {
n += 1 + l + sovCanonical(uint64(l))
}
if m.VoteExtension != nil {
l = m.VoteExtension.Size()
n += 1 + l + sovCanonical(uint64(l))
}
return n
}
func (m *CanonicalVoteExtension) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Extension)
if l > 0 {
n += 1 + l + sovCanonical(uint64(l))
}
if m.Height != 0 {
n += 9
}
if m.Round != 0 {
n += 9
}
l = len(m.ChainId)
if l > 0 {
n += 1 + l + sovCanonical(uint64(l))
}
return n
}
@@ -1271,6 +1442,178 @@ func (m *CanonicalVote) Unmarshal(dAtA []byte) error {
}
m.ChainID = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 7:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field VoteExtension", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCanonical
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthCanonical
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthCanonical
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.VoteExtension == nil {
m.VoteExtension = &VoteExtensionToSign{}
}
if err := m.VoteExtension.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipCanonical(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthCanonical
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *CanonicalVoteExtension) 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 ErrIntOverflowCanonical
}
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: CanonicalVoteExtension: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: CanonicalVoteExtension: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
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 ErrIntOverflowCanonical
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthCanonical
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthCanonical
}
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 2:
if wireType != 1 {
return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType)
}
m.Height = 0
if (iNdEx + 8) > l {
return io.ErrUnexpectedEOF
}
m.Height = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:]))
iNdEx += 8
case 3:
if wireType != 1 {
return fmt.Errorf("proto: wrong wireType = %d for field Round", wireType)
}
m.Round = 0
if (iNdEx + 8) > l {
return io.ErrUnexpectedEOF
}
m.Round = int64(encoding_binary.LittleEndian.Uint64(dAtA[iNdEx:]))
iNdEx += 8
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowCanonical
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthCanonical
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthCanonical
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ChainId = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipCanonical(dAtA[iNdEx:])
+10
View File
@@ -34,4 +34,14 @@ message CanonicalVote {
CanonicalBlockID block_id = 4 [(gogoproto.customname) = "BlockID"];
google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
string chain_id = 6 [(gogoproto.customname) = "ChainID"];
VoteExtensionToSign vote_extension = 7;
}
// CanonicalVoteExtension provides us a way to serialize a vote extension from
// a particular validator such that we can sign over those serialized bytes.
message CanonicalVoteExtension {
bytes extension = 1;
sfixed64 height = 2;
sfixed64 round = 3;
string chain_id = 4;
}
+685 -106
View File
@@ -473,7 +473,15 @@ type Vote struct {
Timestamp time.Time `protobuf:"bytes,5,opt,name=timestamp,proto3,stdtime" json:"timestamp"`
ValidatorAddress []byte `protobuf:"bytes,6,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"`
ValidatorIndex int32 `protobuf:"varint,7,opt,name=validator_index,json=validatorIndex,proto3" json:"validator_index,omitempty"`
Signature []byte `protobuf:"bytes,8,opt,name=signature,proto3" json:"signature,omitempty"`
// Vote signature by the validator if they participated in consensus for the
// associated block.
Signature []byte `protobuf:"bytes,8,opt,name=signature,proto3" json:"signature,omitempty"`
// Vote extension provided by the application. Only valid for precommit
// messages.
Extension []byte `protobuf:"bytes,9,opt,name=extension,proto3" json:"extension,omitempty"`
// Vote extension signature by the validator if they participated in
// consensus for the associated block.
ExtensionSignature []byte `protobuf:"bytes,10,opt,name=extension_signature,json=extensionSignature,proto3" json:"extension_signature,omitempty"`
}
func (m *Vote) Reset() { *m = Vote{} }
@@ -565,6 +573,119 @@ func (m *Vote) GetSignature() []byte {
return nil
}
func (m *Vote) GetExtension() []byte {
if m != nil {
return m.Extension
}
return nil
}
func (m *Vote) GetExtensionSignature() []byte {
if m != nil {
return m.ExtensionSignature
}
return nil
}
// VoteExtension is app-defined additional information to the validator votes.
type VoteExtension struct {
AppDataToSign []byte `protobuf:"bytes,1,opt,name=app_data_to_sign,json=appDataToSign,proto3" json:"app_data_to_sign,omitempty"`
AppDataSelfAuthenticating []byte `protobuf:"bytes,2,opt,name=app_data_self_authenticating,json=appDataSelfAuthenticating,proto3" json:"app_data_self_authenticating,omitempty"`
}
func (m *VoteExtension) Reset() { *m = VoteExtension{} }
func (m *VoteExtension) String() string { return proto.CompactTextString(m) }
func (*VoteExtension) ProtoMessage() {}
func (*VoteExtension) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{6}
}
func (m *VoteExtension) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *VoteExtension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_VoteExtension.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 *VoteExtension) XXX_Merge(src proto.Message) {
xxx_messageInfo_VoteExtension.Merge(m, src)
}
func (m *VoteExtension) XXX_Size() int {
return m.Size()
}
func (m *VoteExtension) XXX_DiscardUnknown() {
xxx_messageInfo_VoteExtension.DiscardUnknown(m)
}
var xxx_messageInfo_VoteExtension proto.InternalMessageInfo
func (m *VoteExtension) GetAppDataToSign() []byte {
if m != nil {
return m.AppDataToSign
}
return nil
}
func (m *VoteExtension) GetAppDataSelfAuthenticating() []byte {
if m != nil {
return m.AppDataSelfAuthenticating
}
return nil
}
// VoteExtensionToSign is a subset of VoteExtension that is signed by the validators private key.
// VoteExtensionToSign is extracted from an existing VoteExtension.
type VoteExtensionToSign struct {
AppDataToSign []byte `protobuf:"bytes,1,opt,name=app_data_to_sign,json=appDataToSign,proto3" json:"app_data_to_sign,omitempty"`
}
func (m *VoteExtensionToSign) Reset() { *m = VoteExtensionToSign{} }
func (m *VoteExtensionToSign) String() string { return proto.CompactTextString(m) }
func (*VoteExtensionToSign) ProtoMessage() {}
func (*VoteExtensionToSign) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{7}
}
func (m *VoteExtensionToSign) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *VoteExtensionToSign) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_VoteExtensionToSign.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 *VoteExtensionToSign) XXX_Merge(src proto.Message) {
xxx_messageInfo_VoteExtensionToSign.Merge(m, src)
}
func (m *VoteExtensionToSign) XXX_Size() int {
return m.Size()
}
func (m *VoteExtensionToSign) XXX_DiscardUnknown() {
xxx_messageInfo_VoteExtensionToSign.DiscardUnknown(m)
}
var xxx_messageInfo_VoteExtensionToSign proto.InternalMessageInfo
func (m *VoteExtensionToSign) GetAppDataToSign() []byte {
if m != nil {
return m.AppDataToSign
}
return nil
}
// Commit contains the evidence that a block was committed by a set of validators.
type Commit struct {
Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"`
@@ -577,7 +698,7 @@ func (m *Commit) Reset() { *m = Commit{} }
func (m *Commit) String() string { return proto.CompactTextString(m) }
func (*Commit) ProtoMessage() {}
func (*Commit) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{6}
return fileDescriptor_d3a6e55e2345de56, []int{8}
}
func (m *Commit) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -636,17 +757,18 @@ func (m *Commit) GetSignatures() []CommitSig {
// CommitSig is a part of the Vote included in a Commit.
type CommitSig 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"`
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"`
VoteExtension *VoteExtensionToSign `protobuf:"bytes,5,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,omitempty"`
}
func (m *CommitSig) Reset() { *m = CommitSig{} }
func (m *CommitSig) String() string { return proto.CompactTextString(m) }
func (*CommitSig) ProtoMessage() {}
func (*CommitSig) Descriptor() ([]byte, []int) {
return fileDescriptor_d3a6e55e2345de56, []int{7}
return fileDescriptor_d3a6e55e2345de56, []int{9}
}
func (m *CommitSig) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -703,6 +825,13 @@ func (m *CommitSig) GetSignature() []byte {
return nil
}
func (m *CommitSig) GetVoteExtension() *VoteExtensionToSign {
if m != nil {
return m.VoteExtension
}
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"`
@@ -717,7 +846,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)
@@ -804,7 +933,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)
@@ -856,7 +985,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)
@@ -910,7 +1039,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)
@@ -978,7 +1107,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)
@@ -1037,6 +1166,8 @@ func init() {
proto.RegisterType((*Header)(nil), "tendermint.types.Header")
proto.RegisterType((*Data)(nil), "tendermint.types.Data")
proto.RegisterType((*Vote)(nil), "tendermint.types.Vote")
proto.RegisterType((*VoteExtension)(nil), "tendermint.types.VoteExtension")
proto.RegisterType((*VoteExtensionToSign)(nil), "tendermint.types.VoteExtensionToSign")
proto.RegisterType((*Commit)(nil), "tendermint.types.Commit")
proto.RegisterType((*CommitSig)(nil), "tendermint.types.CommitSig")
proto.RegisterType((*Proposal)(nil), "tendermint.types.Proposal")
@@ -1049,90 +1180,97 @@ func init() {
func init() { proto.RegisterFile("tendermint/types/types.proto", fileDescriptor_d3a6e55e2345de56) }
var fileDescriptor_d3a6e55e2345de56 = []byte{
// 1314 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x4f, 0x6f, 0x1b, 0x45,
0x14, 0xcf, 0xda, 0x9b, 0xd8, 0x7e, 0xb6, 0x13, 0x67, 0x95, 0xb6, 0xae, 0xdb, 0x38, 0x2b, 0x23,
0x20, 0x2d, 0x68, 0x53, 0x52, 0xc4, 0x9f, 0x03, 0x07, 0xdb, 0x49, 0x5b, 0xab, 0x89, 0x63, 0xd6,
0x6e, 0x11, 0x5c, 0x56, 0x6b, 0xef, 0xd4, 0x5e, 0xba, 0xde, 0x59, 0xed, 0x8c, 0x43, 0xd2, 0x4f,
0x80, 0x72, 0xea, 0x89, 0x5b, 0x4e, 0x70, 0xe0, 0xce, 0x17, 0x40, 0x9c, 0x7a, 0xec, 0x0d, 0x2e,
0x14, 0x94, 0x4a, 0x88, 0x8f, 0x81, 0xe6, 0x8f, 0xd7, 0xeb, 0x38, 0x86, 0xaa, 0xaa, 0xb8, 0x58,
0x3b, 0xef, 0xfd, 0xde, 0xcc, 0x7b, 0xbf, 0xf7, 0x9b, 0x3f, 0x86, 0xeb, 0x14, 0xf9, 0x0e, 0x0a,
0x87, 0xae, 0x4f, 0xb7, 0xe8, 0x71, 0x80, 0x88, 0xf8, 0x35, 0x82, 0x10, 0x53, 0xac, 0x15, 0x26,
0x5e, 0x83, 0xdb, 0x4b, 0x6b, 0x7d, 0xdc, 0xc7, 0xdc, 0xb9, 0xc5, 0xbe, 0x04, 0xae, 0xb4, 0xd1,
0xc7, 0xb8, 0xef, 0xa1, 0x2d, 0x3e, 0xea, 0x8e, 0x1e, 0x6d, 0x51, 0x77, 0x88, 0x08, 0xb5, 0x87,
0x81, 0x04, 0xac, 0xc7, 0x96, 0xe9, 0x85, 0xc7, 0x01, 0xc5, 0x0c, 0x8b, 0x1f, 0x49, 0x77, 0x39,
0xe6, 0x3e, 0x44, 0x21, 0x71, 0xb1, 0x1f, 0xcf, 0xa3, 0xa4, 0xcf, 0x64, 0x79, 0x68, 0x7b, 0xae,
0x63, 0x53, 0x1c, 0x0a, 0x44, 0xe5, 0x53, 0xc8, 0xb7, 0xec, 0x90, 0xb6, 0x11, 0xbd, 0x87, 0x6c,
0x07, 0x85, 0xda, 0x1a, 0x2c, 0x52, 0x4c, 0x6d, 0xaf, 0xa8, 0xe8, 0xca, 0x66, 0xde, 0x14, 0x03,
0x4d, 0x03, 0x75, 0x60, 0x93, 0x41, 0x31, 0xa1, 0x2b, 0x9b, 0x39, 0x93, 0x7f, 0x57, 0x06, 0xa0,
0xb2, 0x50, 0x16, 0xe1, 0xfa, 0x0e, 0x3a, 0x1a, 0x47, 0xf0, 0x01, 0xb3, 0x76, 0x8f, 0x29, 0x22,
0x32, 0x44, 0x0c, 0xb4, 0x0f, 0x61, 0x91, 0xe7, 0x5f, 0x4c, 0xea, 0xca, 0x66, 0x76, 0xbb, 0x68,
0xc4, 0x88, 0x12, 0xf5, 0x19, 0x2d, 0xe6, 0xaf, 0xa9, 0xcf, 0x5e, 0x6c, 0x2c, 0x98, 0x02, 0x5c,
0xf1, 0x20, 0x55, 0xf3, 0x70, 0xef, 0x71, 0x63, 0x27, 0x4a, 0x44, 0x99, 0x24, 0xa2, 0xed, 0xc3,
0x4a, 0x60, 0x87, 0xd4, 0x22, 0x88, 0x5a, 0x03, 0x5e, 0x05, 0x5f, 0x34, 0xbb, 0xbd, 0x61, 0x9c,
0xef, 0x83, 0x31, 0x55, 0xac, 0x5c, 0x25, 0x1f, 0xc4, 0x8d, 0x95, 0xbf, 0x54, 0x58, 0x92, 0x64,
0x7c, 0x06, 0x29, 0x49, 0x2b, 0x5f, 0x30, 0xbb, 0xbd, 0x1e, 0x9f, 0x51, 0xba, 0x8c, 0x3a, 0xf6,
0x09, 0xf2, 0xc9, 0x88, 0xc8, 0xf9, 0xc6, 0x31, 0xda, 0x3b, 0x90, 0xee, 0x0d, 0x6c, 0xd7, 0xb7,
0x5c, 0x87, 0x67, 0x94, 0xa9, 0x65, 0xcf, 0x5e, 0x6c, 0xa4, 0xea, 0xcc, 0xd6, 0xd8, 0x31, 0x53,
0xdc, 0xd9, 0x70, 0xb4, 0xcb, 0xb0, 0x34, 0x40, 0x6e, 0x7f, 0x40, 0x39, 0x2d, 0x49, 0x53, 0x8e,
0xb4, 0x4f, 0x40, 0x65, 0x82, 0x28, 0xaa, 0x7c, 0xed, 0x92, 0x21, 0xd4, 0x62, 0x8c, 0xd5, 0x62,
0x74, 0xc6, 0x6a, 0xa9, 0xa5, 0xd9, 0xc2, 0x4f, 0xff, 0xd8, 0x50, 0x4c, 0x1e, 0xa1, 0xd5, 0x21,
0xef, 0xd9, 0x84, 0x5a, 0x5d, 0x46, 0x1b, 0x5b, 0x7e, 0x91, 0x4f, 0x71, 0x75, 0x96, 0x10, 0x49,
0xac, 0x4c, 0x3d, 0xcb, 0xa2, 0x84, 0xc9, 0xd1, 0x36, 0xa1, 0xc0, 0x27, 0xe9, 0xe1, 0xe1, 0xd0,
0xa5, 0x16, 0xe7, 0x7d, 0x89, 0xf3, 0xbe, 0xcc, 0xec, 0x75, 0x6e, 0xbe, 0xc7, 0x3a, 0x70, 0x0d,
0x32, 0x8e, 0x4d, 0x6d, 0x01, 0x49, 0x71, 0x48, 0x9a, 0x19, 0xb8, 0xf3, 0x5d, 0x58, 0x89, 0x54,
0x47, 0x04, 0x24, 0x2d, 0x66, 0x99, 0x98, 0x39, 0xf0, 0x16, 0xac, 0xf9, 0xe8, 0x88, 0x5a, 0xe7,
0xd1, 0x19, 0x8e, 0xd6, 0x98, 0xef, 0xe1, 0x74, 0xc4, 0xdb, 0xb0, 0xdc, 0x1b, 0x93, 0x2f, 0xb0,
0xc0, 0xb1, 0xf9, 0xc8, 0xca, 0x61, 0x57, 0x21, 0x6d, 0x07, 0x81, 0x00, 0x64, 0x39, 0x20, 0x65,
0x07, 0x01, 0x77, 0xdd, 0x84, 0x55, 0x5e, 0x63, 0x88, 0xc8, 0xc8, 0xa3, 0x72, 0x92, 0x1c, 0xc7,
0xac, 0x30, 0x87, 0x29, 0xec, 0x1c, 0xfb, 0x16, 0xe4, 0xd1, 0xa1, 0xeb, 0x20, 0xbf, 0x87, 0x04,
0x2e, 0xcf, 0x71, 0xb9, 0xb1, 0x91, 0x83, 0x6e, 0x40, 0x21, 0x08, 0x71, 0x80, 0x09, 0x0a, 0x2d,
0xdb, 0x71, 0x42, 0x44, 0x48, 0x71, 0x59, 0xcc, 0x37, 0xb6, 0x57, 0x85, 0xb9, 0x52, 0x04, 0x75,
0xc7, 0xa6, 0xb6, 0x56, 0x80, 0x24, 0x3d, 0x22, 0x45, 0x45, 0x4f, 0x6e, 0xe6, 0x4c, 0xf6, 0x59,
0xf9, 0x3b, 0x01, 0xea, 0x43, 0x4c, 0x91, 0x76, 0x1b, 0x54, 0xd6, 0x26, 0xae, 0xbe, 0xe5, 0x8b,
0xf4, 0xdc, 0x76, 0xfb, 0x3e, 0x72, 0xf6, 0x49, 0xbf, 0x73, 0x1c, 0x20, 0x93, 0x83, 0x63, 0x72,
0x4a, 0x4c, 0xc9, 0x69, 0x0d, 0x16, 0x43, 0x3c, 0xf2, 0x1d, 0xae, 0xb2, 0x45, 0x53, 0x0c, 0xb4,
0x5d, 0x48, 0x47, 0x2a, 0x51, 0xff, 0x4b, 0x25, 0x2b, 0x4c, 0x25, 0x4c, 0xc3, 0xd2, 0x60, 0xa6,
0xba, 0x52, 0x2c, 0x35, 0xc8, 0x44, 0x87, 0x97, 0x54, 0xdb, 0xab, 0x09, 0x76, 0x12, 0xa6, 0xbd,
0x07, 0xab, 0x51, 0xef, 0x23, 0xf2, 0x84, 0xe2, 0x0a, 0x91, 0x43, 0xb2, 0x37, 0x25, 0x2b, 0x4b,
0x1c, 0x40, 0x29, 0x5e, 0xd7, 0x44, 0x56, 0x0d, 0x7e, 0x12, 0x5d, 0x87, 0x0c, 0x71, 0xfb, 0xbe,
0x4d, 0x47, 0x21, 0x92, 0xca, 0x9b, 0x18, 0x2a, 0x3f, 0x2b, 0xb0, 0x24, 0x94, 0x1c, 0xe3, 0x4d,
0xb9, 0x98, 0xb7, 0xc4, 0x3c, 0xde, 0x92, 0xaf, 0xcf, 0x5b, 0x15, 0x20, 0x4a, 0x86, 0x14, 0x55,
0x3d, 0xb9, 0x99, 0xdd, 0xbe, 0x36, 0x3b, 0x91, 0x48, 0xb1, 0xed, 0xf6, 0xe5, 0x46, 0x8d, 0x05,
0x55, 0x7e, 0x57, 0x20, 0x13, 0xf9, 0xb5, 0x2a, 0xe4, 0xc7, 0x79, 0x59, 0x8f, 0x3c, 0xbb, 0x2f,
0xb5, 0xb3, 0x3e, 0x37, 0xb9, 0x3b, 0x9e, 0xdd, 0x37, 0xb3, 0x32, 0x1f, 0x36, 0xb8, 0xb8, 0x0f,
0x89, 0x39, 0x7d, 0x98, 0x6a, 0x7c, 0xf2, 0xf5, 0x1a, 0x3f, 0xd5, 0x22, 0xf5, 0x7c, 0x8b, 0x7e,
0x4a, 0x40, 0xba, 0xc5, 0xf7, 0x8e, 0xed, 0xfd, 0x1f, 0x3b, 0xe2, 0x1a, 0x64, 0x02, 0xec, 0x59,
0xc2, 0xa3, 0x72, 0x4f, 0x3a, 0xc0, 0x9e, 0x39, 0xd3, 0xf6, 0xc5, 0x37, 0xb4, 0x5d, 0x96, 0xde,
0x00, 0x6b, 0xa9, 0xf3, 0xac, 0x85, 0x90, 0x13, 0x54, 0xc8, 0xbb, 0xec, 0x16, 0xe3, 0x80, 0x5f,
0x8e, 0xca, 0xec, 0xdd, 0x2b, 0xd2, 0x16, 0x48, 0x53, 0xe2, 0x58, 0x84, 0x38, 0xfa, 0xe5, 0x75,
0x5a, 0x9c, 0x27, 0x4b, 0x53, 0xe2, 0x2a, 0xdf, 0x29, 0x00, 0x7b, 0x8c, 0x59, 0x5e, 0x2f, 0xbb,
0x85, 0x08, 0x4f, 0xc1, 0x9a, 0x5a, 0xb9, 0x3c, 0xaf, 0x69, 0x72, 0xfd, 0x1c, 0x89, 0xe7, 0x5d,
0x87, 0xfc, 0x44, 0x8c, 0x04, 0x8d, 0x93, 0xb9, 0x60, 0x92, 0xe8, 0x72, 0x68, 0x23, 0x6a, 0xe6,
0x0e, 0x63, 0xa3, 0xca, 0x2f, 0x0a, 0x64, 0x78, 0x4e, 0xfb, 0x88, 0xda, 0x53, 0x3d, 0x54, 0x5e,
0xbf, 0x87, 0xeb, 0x00, 0x62, 0x1a, 0xe2, 0x3e, 0x41, 0x52, 0x59, 0x19, 0x6e, 0x69, 0xbb, 0x4f,
0x90, 0xf6, 0x51, 0x44, 0x78, 0xf2, 0xdf, 0x09, 0x97, 0x5b, 0x7a, 0x4c, 0xfb, 0x15, 0x48, 0xf9,
0xa3, 0xa1, 0xc5, 0xae, 0x04, 0x55, 0xa8, 0xd5, 0x1f, 0x0d, 0x3b, 0x47, 0xa4, 0xf2, 0x35, 0xa4,
0x3a, 0x47, 0xfc, 0x79, 0xc4, 0x24, 0x1a, 0x62, 0x2c, 0xef, 0x64, 0xf1, 0x16, 0x4a, 0x33, 0x03,
0xbf, 0x82, 0x34, 0x50, 0xd9, 0xe5, 0x3b, 0x7e, 0xac, 0xb1, 0x6f, 0xcd, 0x78, 0xc5, 0x87, 0x97,
0x7c, 0x72, 0xdd, 0xfc, 0x55, 0x81, 0x6c, 0xec, 0x7c, 0xd0, 0x3e, 0x80, 0x4b, 0xb5, 0xbd, 0x83,
0xfa, 0x7d, 0xab, 0xb1, 0x63, 0xdd, 0xd9, 0xab, 0xde, 0xb5, 0x1e, 0x34, 0xef, 0x37, 0x0f, 0xbe,
0x68, 0x16, 0x16, 0x4a, 0x97, 0x4f, 0x4e, 0x75, 0x2d, 0x86, 0x7d, 0xe0, 0x3f, 0xf6, 0xf1, 0x37,
0xbe, 0xb6, 0x05, 0x6b, 0xd3, 0x21, 0xd5, 0x5a, 0x7b, 0xb7, 0xd9, 0x29, 0x28, 0xa5, 0x4b, 0x27,
0xa7, 0xfa, 0x6a, 0x2c, 0xa2, 0xda, 0x25, 0xc8, 0xa7, 0xb3, 0x01, 0xf5, 0x83, 0xfd, 0xfd, 0x46,
0xa7, 0x90, 0x98, 0x09, 0x90, 0x07, 0xf6, 0x0d, 0x58, 0x9d, 0x0e, 0x68, 0x36, 0xf6, 0x0a, 0xc9,
0x92, 0x76, 0x72, 0xaa, 0x2f, 0xc7, 0xd0, 0x4d, 0xd7, 0x2b, 0xa5, 0xbf, 0xfd, 0xbe, 0xbc, 0xf0,
0xe3, 0x0f, 0x65, 0x85, 0x55, 0x96, 0x9f, 0x3a, 0x23, 0xb4, 0xf7, 0xe1, 0x4a, 0xbb, 0x71, 0xb7,
0xb9, 0xbb, 0x63, 0xed, 0xb7, 0xef, 0x5a, 0x9d, 0x2f, 0x5b, 0xbb, 0xb1, 0xea, 0x56, 0x4e, 0x4e,
0xf5, 0xac, 0x2c, 0x69, 0x1e, 0xba, 0x65, 0xee, 0x3e, 0x3c, 0xe8, 0xec, 0x16, 0x14, 0x81, 0x6e,
0x85, 0xe8, 0x10, 0x53, 0xc4, 0xd1, 0xb7, 0xe0, 0xea, 0x05, 0xe8, 0xa8, 0xb0, 0xd5, 0x93, 0x53,
0x3d, 0xdf, 0x0a, 0x91, 0xd8, 0x3f, 0x3c, 0xc2, 0x80, 0xe2, 0x6c, 0xc4, 0x41, 0xeb, 0xa0, 0x5d,
0xdd, 0x2b, 0xe8, 0xa5, 0xc2, 0xc9, 0xa9, 0x9e, 0x1b, 0x1f, 0x86, 0x0c, 0x3f, 0xa9, 0xac, 0xf6,
0xf9, 0xb3, 0xb3, 0xb2, 0xf2, 0xfc, 0xac, 0xac, 0xfc, 0x79, 0x56, 0x56, 0x9e, 0xbe, 0x2c, 0x2f,
0x3c, 0x7f, 0x59, 0x5e, 0xf8, 0xed, 0x65, 0x79, 0xe1, 0xab, 0x8f, 0xfb, 0x2e, 0x1d, 0x8c, 0xba,
0x46, 0x0f, 0x0f, 0xb7, 0xe2, 0x7f, 0x09, 0x26, 0x9f, 0xe2, 0xaf, 0xc9, 0xf9, 0xbf, 0x0b, 0xdd,
0x25, 0x6e, 0xbf, 0xfd, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4c, 0x78, 0x43, 0xdf, 0xef, 0x0c,
0x00, 0x00,
// 1435 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcd, 0x6f, 0x1a, 0xd7,
0x16, 0xf7, 0xc0, 0xd8, 0xc0, 0x81, 0xb1, 0xf1, 0x8d, 0x93, 0x60, 0x12, 0x63, 0x34, 0x4f, 0x79,
0x71, 0xf2, 0x9e, 0x70, 0xea, 0x54, 0xfd, 0x58, 0xb4, 0x15, 0x60, 0x92, 0xa0, 0xd8, 0x98, 0x0e,
0x24, 0x55, 0xbb, 0x19, 0x0d, 0x70, 0x0d, 0xd3, 0x0c, 0x73, 0x47, 0x33, 0x17, 0xd7, 0xce, 0x5f,
0x50, 0x79, 0x95, 0x55, 0x77, 0x5e, 0xb5, 0x8b, 0x4a, 0x5d, 0x76, 0xd1, 0x6d, 0xd5, 0x55, 0x96,
0xd9, 0xb5, 0xab, 0xb4, 0x72, 0xa4, 0xfe, 0x1d, 0xd5, 0xfd, 0x60, 0x18, 0x8c, 0xdd, 0x46, 0x51,
0xd4, 0x0d, 0x9a, 0x7b, 0xce, 0xef, 0x9c, 0x7b, 0x3e, 0x7e, 0xf7, 0xdc, 0x0b, 0x5c, 0xa7, 0xd8,
0xed, 0x61, 0x7f, 0x68, 0xbb, 0x74, 0x93, 0x1e, 0x79, 0x38, 0x10, 0xbf, 0x25, 0xcf, 0x27, 0x94,
0xa0, 0xec, 0x44, 0x5b, 0xe2, 0xf2, 0xfc, 0x4a, 0x9f, 0xf4, 0x09, 0x57, 0x6e, 0xb2, 0x2f, 0x81,
0xcb, 0xaf, 0xf7, 0x09, 0xe9, 0x3b, 0x78, 0x93, 0xaf, 0x3a, 0xa3, 0xfd, 0x4d, 0x6a, 0x0f, 0x71,
0x40, 0xad, 0xa1, 0x27, 0x01, 0x6b, 0x91, 0x6d, 0xba, 0xfe, 0x91, 0x47, 0x09, 0xc3, 0x92, 0x7d,
0xa9, 0x2e, 0x44, 0xd4, 0x07, 0xd8, 0x0f, 0x6c, 0xe2, 0x46, 0xe3, 0xc8, 0x17, 0x67, 0xa2, 0x3c,
0xb0, 0x1c, 0xbb, 0x67, 0x51, 0xe2, 0x0b, 0x84, 0xfe, 0x21, 0x68, 0x4d, 0xcb, 0xa7, 0x2d, 0x4c,
0x1f, 0x60, 0xab, 0x87, 0x7d, 0xb4, 0x02, 0xf3, 0x94, 0x50, 0xcb, 0xc9, 0x29, 0x45, 0x65, 0x43,
0x33, 0xc4, 0x02, 0x21, 0x50, 0x07, 0x56, 0x30, 0xc8, 0xc5, 0x8a, 0xca, 0x46, 0xc6, 0xe0, 0xdf,
0xfa, 0x00, 0x54, 0x66, 0xca, 0x2c, 0x6c, 0xb7, 0x87, 0x0f, 0xc7, 0x16, 0x7c, 0xc1, 0xa4, 0x9d,
0x23, 0x8a, 0x03, 0x69, 0x22, 0x16, 0xe8, 0x5d, 0x98, 0xe7, 0xf1, 0xe7, 0xe2, 0x45, 0x65, 0x23,
0xbd, 0x95, 0x2b, 0x45, 0x0a, 0x25, 0xf2, 0x2b, 0x35, 0x99, 0xbe, 0xa2, 0x3e, 0x7f, 0xb9, 0x3e,
0x67, 0x08, 0xb0, 0xee, 0x40, 0xa2, 0xe2, 0x90, 0xee, 0x93, 0xfa, 0x76, 0x18, 0x88, 0x32, 0x09,
0x04, 0xed, 0xc2, 0x92, 0x67, 0xf9, 0xd4, 0x0c, 0x30, 0x35, 0x07, 0x3c, 0x0b, 0xbe, 0x69, 0x7a,
0x6b, 0xbd, 0x74, 0xb6, 0x0f, 0xa5, 0xa9, 0x64, 0xe5, 0x2e, 0x9a, 0x17, 0x15, 0xea, 0x7f, 0xaa,
0xb0, 0x20, 0x8b, 0xf1, 0x11, 0x24, 0x64, 0x59, 0xf9, 0x86, 0xe9, 0xad, 0xb5, 0xa8, 0x47, 0xa9,
0x2a, 0x55, 0x89, 0x1b, 0x60, 0x37, 0x18, 0x05, 0xd2, 0xdf, 0xd8, 0x06, 0xfd, 0x17, 0x92, 0xdd,
0x81, 0x65, 0xbb, 0xa6, 0xdd, 0xe3, 0x11, 0xa5, 0x2a, 0xe9, 0xd3, 0x97, 0xeb, 0x89, 0x2a, 0x93,
0xd5, 0xb7, 0x8d, 0x04, 0x57, 0xd6, 0x7b, 0xe8, 0x0a, 0x2c, 0x0c, 0xb0, 0xdd, 0x1f, 0x50, 0x5e,
0x96, 0xb8, 0x21, 0x57, 0xe8, 0x03, 0x50, 0x19, 0x21, 0x72, 0x2a, 0xdf, 0x3b, 0x5f, 0x12, 0x6c,
0x29, 0x8d, 0xd9, 0x52, 0x6a, 0x8f, 0xd9, 0x52, 0x49, 0xb2, 0x8d, 0x9f, 0xfd, 0xbe, 0xae, 0x18,
0xdc, 0x02, 0x55, 0x41, 0x73, 0xac, 0x80, 0x9a, 0x1d, 0x56, 0x36, 0xb6, 0xfd, 0x3c, 0x77, 0xb1,
0x3a, 0x5b, 0x10, 0x59, 0x58, 0x19, 0x7a, 0x9a, 0x59, 0x09, 0x51, 0x0f, 0x6d, 0x40, 0x96, 0x3b,
0xe9, 0x92, 0xe1, 0xd0, 0xa6, 0x26, 0xaf, 0xfb, 0x02, 0xaf, 0xfb, 0x22, 0x93, 0x57, 0xb9, 0xf8,
0x01, 0xeb, 0xc0, 0x35, 0x48, 0xf5, 0x2c, 0x6a, 0x09, 0x48, 0x82, 0x43, 0x92, 0x4c, 0xc0, 0x95,
0x37, 0x61, 0x29, 0x64, 0x5d, 0x20, 0x20, 0x49, 0xe1, 0x65, 0x22, 0xe6, 0xc0, 0x3b, 0xb0, 0xe2,
0xe2, 0x43, 0x6a, 0x9e, 0x45, 0xa7, 0x38, 0x1a, 0x31, 0xdd, 0xe3, 0x69, 0x8b, 0x1b, 0xb0, 0xd8,
0x1d, 0x17, 0x5f, 0x60, 0x81, 0x63, 0xb5, 0x50, 0xca, 0x61, 0xab, 0x90, 0xb4, 0x3c, 0x4f, 0x00,
0xd2, 0x1c, 0x90, 0xb0, 0x3c, 0x8f, 0xab, 0x6e, 0xc3, 0x32, 0xcf, 0xd1, 0xc7, 0xc1, 0xc8, 0xa1,
0xd2, 0x49, 0x86, 0x63, 0x96, 0x98, 0xc2, 0x10, 0x72, 0x8e, 0xfd, 0x0f, 0x68, 0xf8, 0xc0, 0xee,
0x61, 0xb7, 0x8b, 0x05, 0x4e, 0xe3, 0xb8, 0xcc, 0x58, 0xc8, 0x41, 0xb7, 0x20, 0xeb, 0xf9, 0xc4,
0x23, 0x01, 0xf6, 0x4d, 0xab, 0xd7, 0xf3, 0x71, 0x10, 0xe4, 0x16, 0x85, 0xbf, 0xb1, 0xbc, 0x2c,
0xc4, 0x7a, 0x0e, 0xd4, 0x6d, 0x8b, 0x5a, 0x28, 0x0b, 0x71, 0x7a, 0x18, 0xe4, 0x94, 0x62, 0x7c,
0x23, 0x63, 0xb0, 0x4f, 0xfd, 0xa7, 0x38, 0xa8, 0x8f, 0x09, 0xc5, 0xe8, 0x2e, 0xa8, 0xac, 0x4d,
0x9c, 0x7d, 0x8b, 0xe7, 0xf1, 0xb9, 0x65, 0xf7, 0x5d, 0xdc, 0xdb, 0x0d, 0xfa, 0xed, 0x23, 0x0f,
0x1b, 0x1c, 0x1c, 0xa1, 0x53, 0x6c, 0x8a, 0x4e, 0x2b, 0x30, 0xef, 0x93, 0x91, 0xdb, 0xe3, 0x2c,
0x9b, 0x37, 0xc4, 0x02, 0xd5, 0x20, 0x19, 0xb2, 0x44, 0xfd, 0x27, 0x96, 0x2c, 0x31, 0x96, 0x30,
0x0e, 0x4b, 0x81, 0x91, 0xe8, 0x48, 0xb2, 0x54, 0x20, 0x15, 0x0e, 0x2f, 0xc9, 0xb6, 0xd7, 0x23,
0xec, 0xc4, 0x0c, 0xfd, 0x0f, 0x96, 0xc3, 0xde, 0x87, 0xc5, 0x13, 0x8c, 0xcb, 0x86, 0x0a, 0x59,
0xbd, 0x29, 0x5a, 0x99, 0x62, 0x00, 0x25, 0x78, 0x5e, 0x13, 0x5a, 0xd5, 0xf9, 0x24, 0xba, 0x0e,
0xa9, 0xc0, 0xee, 0xbb, 0x16, 0x1d, 0xf9, 0x58, 0x32, 0x6f, 0x22, 0x60, 0x5a, 0x7c, 0x48, 0xb1,
0xcb, 0x0f, 0xb9, 0x60, 0xda, 0x44, 0x80, 0x36, 0xe1, 0x52, 0xb8, 0x30, 0x27, 0x5e, 0x04, 0xcb,
0x50, 0xa8, 0x6a, 0x8d, 0x35, 0xfa, 0x11, 0x68, 0xac, 0x71, 0xb5, 0xd0, 0xc3, 0x4d, 0xc8, 0x32,
0xee, 0xf1, 0xe3, 0x41, 0x09, 0xf7, 0x21, 0x87, 0x97, 0x66, 0x79, 0x1e, 0xeb, 0x7f, 0x9b, 0x30,
0x73, 0xf4, 0x09, 0x5c, 0x0f, 0x81, 0x01, 0x76, 0xf6, 0x4d, 0x6b, 0x44, 0x07, 0xd8, 0xa5, 0x76,
0xd7, 0xa2, 0xb6, 0xdb, 0x97, 0x73, 0x74, 0x55, 0x1a, 0xb5, 0xb0, 0xb3, 0x5f, 0x9e, 0x02, 0xe8,
0x1f, 0xc3, 0xa5, 0xa9, 0xad, 0xa5, 0xdf, 0xd7, 0x0d, 0x40, 0xff, 0x59, 0x81, 0x05, 0x71, 0xa6,
0x23, 0x0c, 0x52, 0xce, 0x67, 0x50, 0xec, 0x22, 0x06, 0xc5, 0xdf, 0x9c, 0x41, 0x65, 0x80, 0xb0,
0xc2, 0x41, 0x4e, 0x2d, 0xc6, 0x37, 0xd2, 0x5b, 0xd7, 0x66, 0x1d, 0x89, 0x10, 0x5b, 0x76, 0x5f,
0x8e, 0xac, 0x88, 0x91, 0xfe, 0x43, 0x0c, 0x52, 0xa1, 0x1e, 0x95, 0x41, 0x1b, 0xc7, 0x65, 0xee,
0x3b, 0x56, 0x5f, 0x9e, 0xa2, 0xb5, 0x0b, 0x83, 0xbb, 0xe7, 0x58, 0x7d, 0x23, 0x2d, 0xe3, 0x61,
0x8b, 0xf3, 0x19, 0x19, 0xbb, 0x80, 0x91, 0x53, 0x47, 0x20, 0xfe, 0x66, 0x47, 0x60, 0x8a, 0xac,
0xea, 0x59, 0xb2, 0xee, 0xc0, 0xe2, 0x01, 0xa1, 0xd8, 0x9c, 0x30, 0x56, 0x9c, 0xb4, 0x1b, 0xb3,
0x29, 0x9d, 0x43, 0x05, 0x43, 0x3b, 0x88, 0x0a, 0xf5, 0x1f, 0x63, 0x90, 0x6c, 0xf2, 0x99, 0x64,
0x39, 0xff, 0xc6, 0xa4, 0xb9, 0x06, 0x29, 0x8f, 0x38, 0xa6, 0xd0, 0xa8, 0x5c, 0x93, 0xf4, 0x88,
0x63, 0xcc, 0x90, 0x68, 0xfe, 0x2d, 0x8d, 0xa1, 0x85, 0xb7, 0xd0, 0x83, 0xc4, 0x99, 0x1e, 0xe8,
0x3e, 0x64, 0x44, 0x29, 0xe4, 0x1b, 0xe1, 0x0e, 0xab, 0x01, 0x7f, 0x74, 0x28, 0xb3, 0x6f, 0x1a,
0x11, 0xb6, 0x40, 0x1a, 0x12, 0xc7, 0x2c, 0xc4, 0x95, 0x2a, 0x9f, 0x29, 0xb9, 0x8b, 0x48, 0x6e,
0x48, 0x9c, 0xfe, 0x8d, 0x02, 0xb0, 0xc3, 0x2a, 0xcb, 0xf3, 0x65, 0xb7, 0x7b, 0xc0, 0x43, 0x30,
0xa7, 0x76, 0x2e, 0x5c, 0xd4, 0x34, 0xb9, 0x7f, 0x26, 0x88, 0xc6, 0x5d, 0x05, 0x6d, 0x42, 0xed,
0x00, 0x8f, 0x83, 0x39, 0xc7, 0x49, 0x78, 0xe9, 0xb6, 0x30, 0x35, 0x32, 0x07, 0x91, 0x95, 0xfe,
0x8b, 0x02, 0x29, 0x1e, 0xd3, 0x2e, 0xa6, 0xd6, 0x54, 0x0f, 0x95, 0x37, 0xef, 0xe1, 0x1a, 0x80,
0x70, 0x13, 0xd8, 0x4f, 0xb1, 0x64, 0x56, 0x8a, 0x4b, 0x5a, 0xf6, 0x53, 0x8c, 0xde, 0x0b, 0x0b,
0x1e, 0xff, 0xfb, 0x82, 0xcb, 0x01, 0x31, 0x2e, 0xfb, 0x55, 0x48, 0xb8, 0xa3, 0xa1, 0xc9, 0xae,
0x5a, 0x55, 0xb0, 0xd5, 0x1d, 0x0d, 0xdb, 0x87, 0x81, 0xfe, 0x25, 0x24, 0xda, 0x87, 0xfc, 0xd9,
0xc9, 0x28, 0xea, 0x13, 0x22, 0xdf, 0x3a, 0x62, 0x4a, 0x26, 0x99, 0x80, 0x5f, 0xed, 0x08, 0x54,
0x36, 0x45, 0xc7, 0x8f, 0x60, 0xf6, 0x8d, 0x4a, 0xaf, 0xf9, 0xa0, 0x95, 0x4f, 0xd9, 0xdb, 0xbf,
0x2a, 0x90, 0x8e, 0x4c, 0x1b, 0xf4, 0x0e, 0x5c, 0xae, 0xec, 0xec, 0x55, 0x1f, 0x9a, 0xf5, 0x6d,
0xf3, 0xde, 0x4e, 0xf9, 0xbe, 0xf9, 0xa8, 0xf1, 0xb0, 0xb1, 0xf7, 0x59, 0x23, 0x3b, 0x97, 0xbf,
0x72, 0x7c, 0x52, 0x44, 0x11, 0xec, 0x23, 0xf7, 0x89, 0x4b, 0xbe, 0x62, 0x77, 0xd2, 0xca, 0xb4,
0x49, 0xb9, 0xd2, 0xaa, 0x35, 0xda, 0x59, 0x25, 0x7f, 0xf9, 0xf8, 0xa4, 0xb8, 0x1c, 0xb1, 0x28,
0x77, 0x02, 0xec, 0xd2, 0x59, 0x83, 0xea, 0xde, 0xee, 0x6e, 0xbd, 0x9d, 0x8d, 0xcd, 0x18, 0xc8,
0xf1, 0x7f, 0x0b, 0x96, 0xa7, 0x0d, 0x1a, 0xf5, 0x9d, 0x6c, 0x3c, 0x8f, 0x8e, 0x4f, 0x8a, 0x8b,
0x11, 0x74, 0xc3, 0x76, 0xf2, 0xc9, 0xaf, 0xbf, 0x2d, 0xcc, 0x7d, 0xff, 0x5d, 0x41, 0x61, 0x99,
0x69, 0x53, 0x33, 0x02, 0xfd, 0x1f, 0xae, 0xb6, 0xea, 0xf7, 0x1b, 0xb5, 0x6d, 0x73, 0xb7, 0x75,
0xdf, 0x6c, 0x7f, 0xde, 0xac, 0x45, 0xb2, 0x5b, 0x3a, 0x3e, 0x29, 0xa6, 0x65, 0x4a, 0x17, 0xa1,
0x9b, 0x46, 0xed, 0xf1, 0x5e, 0xbb, 0x96, 0x55, 0x04, 0xba, 0xe9, 0x63, 0x36, 0xc0, 0x38, 0xfa,
0x0e, 0xac, 0x9e, 0x83, 0x0e, 0x13, 0x5b, 0x3e, 0x3e, 0x29, 0x6a, 0x4d, 0x1f, 0x8b, 0xf3, 0xc3,
0x2d, 0x4a, 0x90, 0x9b, 0xb5, 0xd8, 0x6b, 0xee, 0xb5, 0xca, 0x3b, 0xd9, 0x62, 0x3e, 0x7b, 0x7c,
0x52, 0xcc, 0x8c, 0x87, 0x21, 0xc3, 0x4f, 0x32, 0xab, 0x7c, 0xfa, 0xfc, 0xb4, 0xa0, 0xbc, 0x38,
0x2d, 0x28, 0x7f, 0x9c, 0x16, 0x94, 0x67, 0xaf, 0x0a, 0x73, 0x2f, 0x5e, 0x15, 0xe6, 0x7e, 0x7b,
0x55, 0x98, 0xfb, 0xe2, 0xfd, 0xbe, 0x4d, 0x07, 0xa3, 0x4e, 0xa9, 0x4b, 0x86, 0x9b, 0xd1, 0xbf,
0x5a, 0x93, 0x4f, 0xf1, 0x97, 0xef, 0xec, 0xdf, 0xb0, 0xce, 0x02, 0x97, 0xdf, 0xfd, 0x2b, 0x00,
0x00, 0xff, 0xff, 0x4f, 0xab, 0x7f, 0x9d, 0x47, 0x0e, 0x00, 0x00,
}
func (m *PartSetHeader) Marshal() (dAtA []byte, err error) {
@@ -1433,6 +1571,20 @@ func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = 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] = 0x52
}
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] = 0x4a
}
if len(m.Signature) > 0 {
i -= len(m.Signature)
copy(dAtA[i:], m.Signature)
@@ -1488,6 +1640,73 @@ func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *VoteExtension) 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 *VoteExtension) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *VoteExtension) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.AppDataSelfAuthenticating) > 0 {
i -= len(m.AppDataSelfAuthenticating)
copy(dAtA[i:], m.AppDataSelfAuthenticating)
i = encodeVarintTypes(dAtA, i, uint64(len(m.AppDataSelfAuthenticating)))
i--
dAtA[i] = 0x12
}
if len(m.AppDataToSign) > 0 {
i -= len(m.AppDataToSign)
copy(dAtA[i:], m.AppDataToSign)
i = encodeVarintTypes(dAtA, i, uint64(len(m.AppDataToSign)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *VoteExtensionToSign) 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 *VoteExtensionToSign) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *VoteExtensionToSign) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.AppDataToSign) > 0 {
i -= len(m.AppDataToSign)
copy(dAtA[i:], m.AppDataToSign)
i = encodeVarintTypes(dAtA, i, uint64(len(m.AppDataToSign)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Commit) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -1565,6 +1784,18 @@ func (m *CommitSig) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.VoteExtension != nil {
{
size, err := m.VoteExtension.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintTypes(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x2a
}
if len(m.Signature) > 0 {
i -= len(m.Signature)
copy(dAtA[i:], m.Signature)
@@ -1572,12 +1803,12 @@ func (m *CommitSig) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i--
dAtA[i] = 0x22
}
n9, err9 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):])
if err9 != nil {
return 0, err9
n10, err10 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):])
if err10 != nil {
return 0, err10
}
i -= n9
i = encodeVarintTypes(dAtA, i, uint64(n9))
i -= n10
i = encodeVarintTypes(dAtA, i, uint64(n10))
i--
dAtA[i] = 0x1a
if len(m.ValidatorAddress) > 0 {
@@ -1622,12 +1853,12 @@ func (m *Proposal) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i--
dAtA[i] = 0x3a
}
n10, err10 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):])
if err10 != nil {
return 0, err10
n11, err11 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):])
if err11 != nil {
return 0, err11
}
i -= n10
i = encodeVarintTypes(dAtA, i, uint64(n10))
i -= n11
i = encodeVarintTypes(dAtA, i, uint64(n11))
i--
dAtA[i] = 0x32
{
@@ -2022,6 +2253,44 @@ func (m *Vote) Size() (n int) {
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 *VoteExtension) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.AppDataToSign)
if l > 0 {
n += 1 + l + sovTypes(uint64(l))
}
l = len(m.AppDataSelfAuthenticating)
if l > 0 {
n += 1 + l + sovTypes(uint64(l))
}
return n
}
func (m *VoteExtensionToSign) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.AppDataToSign)
if l > 0 {
n += 1 + l + sovTypes(uint64(l))
}
return n
}
@@ -2067,6 +2336,10 @@ func (m *CommitSig) Size() (n int) {
if l > 0 {
n += 1 + l + sovTypes(uint64(l))
}
if m.VoteExtension != nil {
l = m.VoteExtension.Size()
n += 1 + l + sovTypes(uint64(l))
}
return n
}
@@ -3362,6 +3635,276 @@ func (m *Vote) Unmarshal(dAtA []byte) error {
m.Signature = []byte{}
}
iNdEx = postIndex
case 9:
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 10:
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 *VoteExtension) 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: VoteExtension: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: VoteExtension: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AppDataToSign", 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.AppDataToSign = append(m.AppDataToSign[:0], dAtA[iNdEx:postIndex]...)
if m.AppDataToSign == nil {
m.AppDataToSign = []byte{}
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AppDataSelfAuthenticating", 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.AppDataSelfAuthenticating = append(m.AppDataSelfAuthenticating[:0], dAtA[iNdEx:postIndex]...)
if m.AppDataSelfAuthenticating == nil {
m.AppDataSelfAuthenticating = []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 *VoteExtensionToSign) 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: VoteExtensionToSign: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: VoteExtensionToSign: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AppDataToSign", 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.AppDataToSign = append(m.AppDataToSign[:0], dAtA[iNdEx:postIndex]...)
if m.AppDataToSign == nil {
m.AppDataToSign = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipTypes(dAtA[iNdEx:])
@@ -3687,6 +4230,42 @@ func (m *CommitSig) Unmarshal(dAtA []byte) error {
m.Signature = []byte{}
}
iNdEx = postIndex
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field VoteExtension", 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.VoteExtension == nil {
m.VoteExtension = &VoteExtensionToSign{}
}
if err := m.VoteExtension.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipTypes(dAtA[iNdEx:])
+22 -1
View File
@@ -101,7 +101,27 @@ message Vote {
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
bytes validator_address = 6;
int32 validator_index = 7;
bytes signature = 8;
// Vote signature by the validator if they participated in consensus for the
// associated block.
bytes signature = 8;
// Vote extension provided by the application. Only valid for precommit
// messages.
bytes extension = 9;
// Vote extension signature by the validator if they participated in
// consensus for the associated block.
bytes extension_signature = 10;
}
// VoteExtension is app-defined additional information to the validator votes.
message VoteExtension {
bytes app_data_to_sign = 1;
bytes app_data_self_authenticating = 2;
}
// VoteExtensionToSign is a subset of VoteExtension that is signed by the validators private key.
// VoteExtensionToSign is extracted from an existing VoteExtension.
message VoteExtensionToSign {
bytes app_data_to_sign = 1;
}
// Commit contains the evidence that a block was committed by a set of validators.
@@ -119,6 +139,7 @@ message CommitSig {
google.protobuf.Timestamp timestamp = 3
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
bytes signature = 4;
VoteExtensionToSign vote_extension = 5;
}
message Proposal {
+12
View File
@@ -20,6 +20,8 @@ type AppConnConsensus interface {
InitChain(context.Context, *types.RequestInitChain) (*types.ResponseInitChain, error)
PrepareProposal(context.Context, *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error)
ProcessProposal(context.Context, *types.RequestProcessProposal) (*types.ResponseProcessProposal, error)
ExtendVote(context.Context, *types.RequestExtendVote) (*types.ResponseExtendVote, error)
VerifyVoteExtension(context.Context, *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error)
FinalizeBlock(context.Context, *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error)
Commit(context.Context) (*types.ResponseCommit, error)
}
@@ -87,6 +89,16 @@ func (app *appConnConsensus) ProcessProposal(ctx context.Context, req *types.Req
return app.appConn.ProcessProposal(ctx, req)
}
func (app *appConnConsensus) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "deliver_tx", "type", "async"))()
return app.appConn.ExtendVote(ctx, req)
}
func (app *appConnConsensus) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "deliver_tx", "type", "async"))()
return app.appConn.VerifyVoteExtension(ctx, req)
}
func (app *appConnConsensus) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "deliver_tx", "type", "async"))()
return app.appConn.FinalizeBlock(ctx, req)
+46
View File
@@ -52,6 +52,29 @@ func (_m *AppConnConsensus) Error() error {
return r0
}
// ExtendVote provides a mock function with given fields: _a0, _a1
func (_m *AppConnConsensus) ExtendVote(_a0 context.Context, _a1 *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseExtendVote
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestExtendVote) *types.ResponseExtendVote); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseExtendVote)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestExtendVote) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// FinalizeBlock provides a mock function with given fields: _a0, _a1
func (_m *AppConnConsensus) FinalizeBlock(_a0 context.Context, _a1 *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
ret := _m.Called(_a0, _a1)
@@ -144,6 +167,29 @@ func (_m *AppConnConsensus) ProcessProposal(_a0 context.Context, _a1 *types.Requ
return r0, r1
}
// VerifyVoteExtension provides a mock function with given fields: _a0, _a1
func (_m *AppConnConsensus) VerifyVoteExtension(_a0 context.Context, _a1 *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseVerifyVoteExtension
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestVerifyVoteExtension) *types.ResponseVerifyVoteExtension); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseVerifyVoteExtension)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestVerifyVoteExtension) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewAppConnConsensus interface {
mock.TestingT
Cleanup(func())
+5
View File
@@ -91,6 +91,11 @@ func (_m *AppConnMempool) Flush(_a0 context.Context) error {
return r0
}
// SetResponseCallback provides a mock function with given fields: _a0
func (_m *AppConnMempool) SetResponseCallback(_a0 abcicli.Callback) {
_m.Called(_a0)
}
type mockConstructorTestingTNewAppConnMempool interface {
mock.TestingT
Cleanup(func())
+62 -5
View File
@@ -6,6 +6,7 @@ import (
"time"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
"github.com/tendermint/tendermint/libs/fail"
"github.com/tendermint/tendermint/libs/log"
@@ -293,6 +294,39 @@ func (blockExec *BlockExecutor) ApplyBlock(
return state, nil
}
func (blockExec *BlockExecutor) ExtendVote(vote *types.Vote) ([]byte, error) {
req := abci.RequestExtendVote{
Hash: vote.BlockID.Hash,
Height: vote.Height,
}
resp, err := blockExec.proxyApp.ExtendVote(context.TODO(), &req)
if err != nil {
panic(fmt.Errorf("ExtendVote call failed: %w", err))
}
return resp.VoteExtension, nil
}
func (blockExec *BlockExecutor) VerifyVoteExtension(vote *types.Vote) error {
req := abci.RequestVerifyVoteExtension{
Hash: vote.BlockID.Hash,
ValidatorAddress: vote.ValidatorAddress,
Height: vote.Height,
VoteExtension: vote.Extension,
}
resp, err := blockExec.proxyApp.VerifyVoteExtension(context.TODO(), &req)
if err != nil {
panic(fmt.Errorf("VerifyVoteExtension call failed: %w", err))
}
if !resp.IsAccepted() {
return types.ErrVoteInvalidExtension
}
return nil
}
// Commit locks the mempool, runs the ABCI Commit message, and updates the
// mempool.
// It returns the result of calling abci.Commit (the AppHash) and the height to retain (if any).
@@ -384,16 +418,39 @@ 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)))
}
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
}
}
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))
}
ext = vote.Extension
}
vs[i] = abci.ExtendedVoteInfo{
Validator: c.Votes[i].Validator,
SignedLastBlock: c.Votes[i].SignedLastBlock,
/*
TODO: Include vote extensions information when implementing vote extensions.
VoteExtension: []byte{},
*/
VoteExtension: ext,
}
}
return abci.ExtendedCommitInfo{
@@ -473,7 +530,7 @@ func updateState(
nextVersion := state.Version
// NOTE: the AppHash has not been populated.
// NOTE: the AppHash and the VoteExtension has not been populated.
// It will be filled on state.Save.
return State{
Version: nextVersion,
+14 -12
View File
@@ -178,11 +178,13 @@ func TestFinalizeBlockValidators(t *testing.T) {
commitSig0 = types.NewCommitSigForBlock(
[]byte("Signature1"),
state.Validators.Validators[0].Address,
now)
now,
)
commitSig1 = types.NewCommitSigForBlock(
[]byte("Signature2"),
state.Validators.Validators[1].Address,
now)
now,
)
absentSig = types.NewCommitSigAbsent()
)
@@ -711,9 +713,9 @@ func TestEmptyPrepareProposal(t *testing.T) {
blockStore,
)
pa, _ := state.Validators.GetByIndex(0)
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
commit, votes, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
require.NoError(t, err)
_, err = blockExec.CreateProposalBlock(height, state, commit, pa, nil)
_, err = blockExec.CreateProposalBlock(height, state, commit, pa, votes)
require.NoError(t, err)
}
@@ -754,9 +756,9 @@ func TestPrepareProposalTxsAllIncluded(t *testing.T) {
blockStore,
)
pa, _ := state.Validators.GetByIndex(0)
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
commit, votes, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
require.NoError(t, err)
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, votes)
require.NoError(t, err)
for i, tx := range block.Data.Txs {
@@ -807,9 +809,9 @@ func TestPrepareProposalReorderTxs(t *testing.T) {
blockStore,
)
pa, _ := state.Validators.GetByIndex(0)
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
commit, votes, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
require.NoError(t, err)
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, votes)
require.NoError(t, err)
for i, tx := range block.Data.Txs {
require.Equal(t, txs[i], tx)
@@ -862,10 +864,10 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) {
blockStore,
)
pa, _ := state.Validators.GetByIndex(0)
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
commit, votes, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
require.NoError(t, err)
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, votes)
require.Nil(t, block)
require.ErrorContains(t, err, "transaction data size exceeds maximum")
@@ -912,10 +914,10 @@ func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) {
blockStore,
)
pa, _ := state.Validators.GetByIndex(0)
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
commit, votes, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
require.NoError(t, err)
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, votes)
require.Nil(t, block)
require.ErrorContains(t, err, "an injected error")
+8 -6
View File
@@ -46,7 +46,7 @@ func makeAndCommitGoodBlock(
}
// Simulate a lastCommit for this block from all validators for the next height
commit, err := makeValidCommit(height, blockID, state.Validators, privVals)
commit, _, err := makeValidCommit(height, blockID, state.Validators, privVals)
if err != nil {
return state, types.BlockID{}, nil, err
}
@@ -88,17 +88,19 @@ func makeValidCommit(
blockID types.BlockID,
vals *types.ValidatorSet,
privVals map[string]types.PrivValidator,
) (*types.Commit, error) {
sigs := make([]types.CommitSig, 0)
) (*types.Commit, []*types.Vote, error) {
sigs := make([]types.CommitSig, vals.Size())
votes := make([]*types.Vote, vals.Size())
for i := 0; i < vals.Size(); i++ {
_, val := vals.GetByIndex(int32(i))
vote, err := types.MakeVote(height, blockID, vals, privVals[val.Address.String()], chainID, time.Now())
if err != nil {
return nil, err
return nil, nil, err
}
sigs = append(sigs, vote.CommitSig())
sigs[i] = vote.CommitSig()
votes[i] = vote
}
return types.NewCommit(height, 0, blockID, sigs), nil
return types.NewCommit(height, 0, blockID, sigs), votes, nil
}
func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) {
+156 -6
View File
@@ -4,20 +4,28 @@ import (
"bytes"
"context"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"math/rand"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/tendermint/tendermint/abci/example/kvstore"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/version"
)
const appVersion = 1
const (
appVersion = 1
voteExtensionKey string = "extensionSum"
voteExtensionMaxVal int64 = 128
)
// Application is an ABCI application for use by end-to-end tests. It is a
// simple key/value store for strings, storing data in memory and persisting
@@ -282,8 +290,69 @@ func (app *Application) ApplySnapshotChunk(_ context.Context, req *abci.RequestA
return &abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil
}
// PrepareProposal will take the given transactions and attempt to prepare a
// proposal from them when it's our turn to do so. In the process, vote
// extensions from the previous round of consensus, if present, will be used to
// construct a special transaction whose value is the sum of all of the vote
// extensions from the previous round.
//
// NB: Assumes that the supplied transactions do not exceed `req.MaxTxBytes`.
// If adding a special vote extension-generated transaction would cause the
// total number of transaction bytes to exceed `req.MaxTxBytes`, we will not
// append our special vote extension transaction.
func (app *Application) PrepareProposal(
_ context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) {
var sum int64
var extCount int
for _, vote := range req.LocalLastCommit.Votes {
if !vote.SignedLastBlock || len(vote.VoteExtension) == 0 {
continue
}
extValue, err := parseVoteExtension(vote.VoteExtension)
// This should have been verified in VerifyVoteExtension
if err != nil {
panic(fmt.Errorf("failed to parse vote extension in PrepareProposal: %w", err))
}
valAddr := crypto.Address(vote.Validator.Address)
app.logger.Info("got vote extension value in PrepareProposal", "valAddr", valAddr, "value", extValue)
sum += extValue
extCount++
}
if app.cfg.PrepareProposalDelay != 0 {
time.Sleep(app.cfg.PrepareProposalDelay)
}
// We only generate our special transaction if we have vote extensions
if extCount > 0 {
var totalBytes int64
extTxPrefix := fmt.Sprintf("%s=", voteExtensionKey)
extTx := []byte(fmt.Sprintf("%s%d", extTxPrefix, sum))
app.logger.Info("preparing proposal with custom transaction from vote extensions", "tx", extTx)
// Our generated transaction takes precedence over any supplied
// transaction that attempts to modify the "extensionSum" value.
txs := make([][]byte, len(req.Txs)+1)
for i, tx := range req.Txs {
if strings.HasPrefix(string(tx), extTxPrefix) {
continue
}
txs[i] = tx
totalBytes += int64(len(tx))
}
if totalBytes+int64(len(extTx)) < req.MaxTxBytes {
txs[len(req.Txs)] = extTx
} else {
app.logger.Info(
"too many txs to include special vote extension-generated tx",
"totalBytes", totalBytes,
"MaxTxBytes", req.MaxTxBytes,
"extTx", extTx,
"extTxLen", len(extTx),
)
}
return &abci.ResponsePrepareProposal{Txs: txs}, nil
}
// None of the transactions are modified by this application.
txs := make([][]byte, 0, len(req.Txs))
var totalBytes int64
for _, tx := range req.Txs {
@@ -294,10 +363,6 @@ func (app *Application) PrepareProposal(
txs = append(txs, tx)
}
if app.cfg.PrepareProposalDelay != 0 {
time.Sleep(app.cfg.PrepareProposalDelay)
}
return &abci.ResponsePrepareProposal{Txs: txs}, nil
}
@@ -305,10 +370,19 @@ func (app *Application) PrepareProposal(
// It accepts any proposal that does not contain a malformed transaction.
func (app *Application) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
for _, tx := range req.Txs {
_, _, err := parseTx(tx)
k, v, err := parseTx(tx)
if err != nil {
app.logger.Error("malformed transaction in ProcessProposal", "tx", tx, "err", err)
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
}
// Additional check for vote extension-related txs
if k == voteExtensionKey {
_, err := strconv.Atoi(v)
if err != nil {
app.logger.Error("malformed vote extension transaction", k, v, "err", err)
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
}
}
}
if app.cfg.ProcessProposalDelay != 0 {
@@ -318,6 +392,66 @@ func (app *Application) ProcessProposal(_ context.Context, req *abci.RequestProc
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
}
// ExtendVote will produce vote extensions in the form of random numbers to
// demonstrate vote extension nondeterminism.
//
// In the next block, if there are any vote extensions from the previous block,
// a new transaction will be proposed that updates a special value in the
// key/value store ("extensionSum") with the sum of all of the numbers collected
// from the vote extensions.
func (app *Application) ExtendVote(_ context.Context, req *abci.RequestExtendVote) (*abci.ResponseExtendVote, error) {
// We ignore any requests for vote extensions that don't match our expected
// next height.
if req.Height != int64(app.state.Height)+1 {
app.logger.Error(
"got unexpected height in ExtendVote request",
"expectedHeight", app.state.Height+1,
"requestHeight", req.Height,
)
return &abci.ResponseExtendVote{}, nil
}
ext := make([]byte, binary.MaxVarintLen64)
// We don't care that these values are generated by a weak random number
// generator. It's just for test purposes.
//nolint:gosec // G404: Use of weak random number generator
num := rand.Int63n(voteExtensionMaxVal)
extLen := binary.PutVarint(ext, num)
app.logger.Info("generated vote extension", "num", num, "ext", fmt.Sprintf("%x", ext[:extLen]), "state.Height", app.state.Height)
return &abci.ResponseExtendVote{
VoteExtension: ext[:extLen],
}, nil
}
// VerifyVoteExtension simply validates vote extensions from other validators
// without doing anything about them. In this case, it just makes sure that the
// vote extension is a well-formed integer value.
func (app *Application) VerifyVoteExtension(_ context.Context, req *abci.RequestVerifyVoteExtension) (*abci.ResponseVerifyVoteExtension, error) {
// We allow vote extensions to be optional
if len(req.VoteExtension) == 0 {
return &abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
}, nil
}
if req.Height != int64(app.state.Height)+1 {
panic(fmt.Errorf(
"got unexpected height in VerifyVoteExtension request; expected %d, actual %d",
app.state.Height, req.Height,
))
}
num, err := parseVoteExtension(req.VoteExtension)
if err != nil {
app.logger.Error("failed to verify vote extension", "req", req, "err", err)
return &abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_REJECT,
}, nil
}
app.logger.Info("verified vote extension value", "req", req, "num", num)
return &abci.ResponseVerifyVoteExtension{
Status: abci.ResponseVerifyVoteExtension_ACCEPT,
}, nil
}
func (app *Application) Rollback() error {
return app.state.Rollback()
}
@@ -352,3 +486,19 @@ func parseTx(tx []byte) (string, string, error) {
}
return string(parts[0]), string(parts[1]), nil
}
// parseVoteExtension attempts to parse the given extension data into a positive
// integer value.
func parseVoteExtension(ext []byte) (int64, error) {
num, errVal := binary.Varint(ext)
if errVal == 0 {
return 0, errors.New("vote extension is too small to parse")
}
if errVal < 0 {
return 0, errors.New("vote extension value is too large")
}
if num >= voteExtensionMaxVal {
return 0, fmt.Errorf("vote extension value must be smaller than %d (was %d)", voteExtensionMaxVal, num)
}
return num, nil
}
+12
View File
@@ -59,6 +59,18 @@ func (app *SyncApplication) ProcessProposal(ctx context.Context, req *abci.Reque
return app.app.ProcessProposal(ctx, req)
}
func (app *SyncApplication) ExtendVote(ctx context.Context, req *abci.RequestExtendVote) (*abci.ResponseExtendVote, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.app.ExtendVote(ctx, req)
}
func (app *SyncApplication) VerifyVoteExtension(ctx context.Context, req *abci.RequestVerifyVoteExtension) (*abci.ResponseVerifyVoteExtension, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.app.VerifyVoteExtension(ctx, req)
}
func (app *SyncApplication) FinalizeBlock(ctx context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
+17
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"math/rand"
"strconv"
"testing"
"time"
@@ -38,6 +39,7 @@ func TestApp_Hash(t *testing.T) {
testNode(t, func(t *testing.T, node e2e.Node) {
client, err := node.Client()
require.NoError(t, err)
info, err := client.ABCIInfo(ctx)
require.NoError(t, err)
require.NotEmpty(t, info.Response.LastBlockAppHash, "expected app to return app hash")
@@ -102,3 +104,18 @@ func TestApp_Tx(t *testing.T) {
})
}
func TestApp_VoteExtensions(t *testing.T) {
testNode(t, func(t *testing.T, node e2e.Node) {
client, err := node.Client()
require.NoError(t, err)
// This special value should have been created by way of vote extensions
resp, err := client.ABCIQuery(ctx, "", []byte("extensionSum"))
require.NoError(t, err)
extSum, err := strconv.Atoi(string(resp.Response.Value))
require.NoError(t, err)
require.GreaterOrEqual(t, extSum, 0)
})
}
@@ -286,6 +286,7 @@ func (th *TestHarness) TestSignVote() error {
return newTestHarnessError(ErrTestSignVoteFailed, err, fmt.Sprintf("voteType=%d", voteType))
}
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
th.logger.Debug("Signed vote", "vote", vote)
// validate the contents of the vote
if err := vote.ValidateBasic(); err != nil {
+2 -2
View File
@@ -243,7 +243,7 @@ func TestCommit(t *testing.T) {
require.NotNil(t, commit.BitArray())
assert.Equal(t, bits.NewBitArray(10).Size(), commit.BitArray().Size())
assert.Equal(t, voteSet.GetByIndex(0), commit.GetByIndex(0))
assert.Equal(t, voteWithoutExtension(voteSet.GetByIndex(0)), commit.GetByIndex(0))
assert.True(t, commit.IsCommit())
}
@@ -526,7 +526,7 @@ func TestCommitToVoteSet(t *testing.T) {
voteSet2 := CommitToVoteSet(chainID, commit, valSet)
for i := int32(0); int(i) < len(vals); i++ {
vote1 := voteSet.GetByIndex(i)
vote1 := voteWithoutExtension(voteSet.GetByIndex(i))
vote2 := voteSet2.GetByIndex(i)
vote3 := commit.GetVote(i)
+14 -1
View File
@@ -52,7 +52,8 @@ func CanonicalizeProposal(chainID string, proposal *tmproto.Proposal) tmproto.Ca
}
// CanonicalizeVote transforms the given Vote to a CanonicalVote, which does
// not contain ValidatorIndex and ValidatorAddress fields.
// not contain ValidatorIndex and ValidatorAddress fields, or any fields
// relating to vote extensions.
func CanonicalizeVote(chainID string, vote *tmproto.Vote) tmproto.CanonicalVote {
return tmproto.CanonicalVote{
Type: vote.Type,
@@ -64,6 +65,18 @@ func CanonicalizeVote(chainID string, vote *tmproto.Vote) tmproto.CanonicalVote
}
}
// CanonicalizeVoteExtension extracts the vote extension from the given vote
// and constructs a CanonicalizeVoteExtension struct, whose representation in
// bytes is what is signed in order to produce the vote extension's signature.
func CanonicalizeVoteExtension(chainID string, vote *tmproto.Vote) tmproto.CanonicalVoteExtension {
return tmproto.CanonicalVoteExtension{
Extension: vote.Extension,
Height: vote.Height,
Round: int64(vote.Round),
ChainId: chainID,
}
}
// CanonicalTime can be used to stringify time in a canonical way.
func CanonicalTime(t time.Time) string {
// Note that sending time over amino resets it to
+1
View File
@@ -251,6 +251,7 @@ func makeVote(
require.NoError(t, err)
v.Signature = vpb.Signature
v.ExtensionSignature = vpb.ExtensionSignature
return v
}
+6
View File
@@ -77,11 +77,17 @@ func (pv MockPV) SignVote(chainID string, vote *tmproto.Vote) error {
}
signBytes := VoteSignBytes(useChainID, vote)
extSignBytes := VoteExtensionSignBytes(useChainID, vote)
sig, err := pv.PrivKey.Sign(signBytes)
if err != nil {
return err
}
vote.Signature = sig
extSig, err := pv.PrivKey.Sign(extSignBytes)
if err != nil {
return err
}
vote.ExtensionSignature = extSig
return nil
}
+12
View File
@@ -44,6 +44,7 @@ func signAddVote(privVal PrivValidator, vote *Vote, voteSet *VoteSet) (signed bo
return false, err
}
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
return voteSet.AddVote(vote)
}
@@ -77,6 +78,7 @@ func MakeVote(
}
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
return vote, nil
}
@@ -99,3 +101,13 @@ func MakeBlock(height int64, txs []Tx, lastCommit *Commit, evidence []Evidence)
block.fillHeader()
return block
}
// 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
}
+3
View File
@@ -148,6 +148,7 @@ func TestValidatorSet_VerifyCommit_CheckAllSignatures(t *testing.T) {
err = vals[3].SignVote("CentaurusA", v)
require.NoError(t, err)
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
commit.Signatures[3] = vote.CommitSig()
err = valSet.VerifyCommit(chainID, blockID, h, commit)
@@ -174,6 +175,7 @@ func TestValidatorSet_VerifyCommitLight_ReturnsAsSoonAsMajorityOfVotingPowerSign
err = vals[3].SignVote("CentaurusA", v)
require.NoError(t, err)
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
commit.Signatures[3] = vote.CommitSig()
err = valSet.VerifyCommitLight(chainID, blockID, h, commit)
@@ -198,6 +200,7 @@ func TestValidatorSet_VerifyCommitLightTrusting_ReturnsAsSoonAsTrustLevelOfVotin
err = vals[2].SignVote("CentaurusA", v)
require.NoError(t, err)
vote.Signature = v.Signature
vote.ExtensionSignature = v.ExtensionSignature
commit.Signatures[2] = vote.CommitSig()
err = valSet.VerifyCommitLightTrusting(chainID, commit, tmmath.Fraction{Numerator: 1, Denominator: 3})
+55 -19
View File
@@ -24,6 +24,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")
)
type ErrVoteConflictingVotes struct {
@@ -48,14 +49,16 @@ type Address = crypto.Address
// Vote represents a prevote, precommit, or commit vote from validators for
// consensus.
type Vote struct {
Type tmproto.SignedMsgType `json:"type"`
Height int64 `json:"height"`
Round int32 `json:"round"` // assume there will not be greater than 2_147_483_647 rounds
BlockID BlockID `json:"block_id"` // zero if vote is nil.
Timestamp time.Time `json:"timestamp"`
ValidatorAddress Address `json:"validator_address"`
ValidatorIndex int32 `json:"validator_index"`
Signature []byte `json:"signature"`
Type tmproto.SignedMsgType `json:"type"`
Height int64 `json:"height"`
Round int32 `json:"round"` // assume there will not be greater than 2_147_483_647 rounds
BlockID BlockID `json:"block_id"` // zero if vote is nil.
Timestamp time.Time `json:"timestamp"`
ValidatorAddress Address `json:"validator_address"`
ValidatorIndex int32 `json:"validator_index"`
Signature []byte `json:"signature"`
Extension []byte `json:"extension"`
ExtensionSignature []byte `json:"extension_signature"`
}
// CommitSig converts the Vote to a CommitSig.
@@ -83,7 +86,7 @@ func (vote *Vote) CommitSig() CommitSig {
}
// VoteSignBytes returns the proto-encoding of the canonicalized Vote, for
// signing. Panics is the marshaling fails.
// signing. Panics if the marshaling fails.
//
// The encoded Protobuf message is varint length-prefixed (using MarshalDelimited)
// for backwards-compatibility with the Amino encoding, due to e.g. hardware
@@ -100,6 +103,21 @@ func VoteSignBytes(chainID string, vote *tmproto.Vote) []byte {
return bz
}
// VoteExtensionSignBytes returns the proto-encoding of the canonicalized vote
// extension for signing. Panics if the marshaling fails.
//
// Similar to VoteSignBytes, the encoded Protobuf message is varint
// length-prefixed for backwards-compatibility with the Amino encoding.
func VoteExtensionSignBytes(chainID string, vote *tmproto.Vote) []byte {
pb := CanonicalizeVoteExtension(chainID, vote)
bz, err := protoio.MarshalDelimited(&pb)
if err != nil {
panic(err)
}
return bz
}
func (vote *Vote) Copy() *Vote {
voteCopy := *vote
return &voteCopy
@@ -115,7 +133,8 @@ func (vote *Vote) Copy() *Vote {
// 6. type string
// 7. first 6 bytes of block hash
// 8. first 6 bytes of signature
// 9. timestamp
// 9. first 6 bytes of vote extension
// 10. timestamp
func (vote *Vote) String() string {
if vote == nil {
return nilVoteStr
@@ -131,7 +150,7 @@ func (vote *Vote) String() string {
panic("Unknown vote type")
}
return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}",
return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X %X @ %s}",
vote.ValidatorIndex,
tmbytes.Fingerprint(vote.ValidatorAddress),
vote.Height,
@@ -140,6 +159,7 @@ func (vote *Vote) String() string {
typeString,
tmbytes.Fingerprint(vote.BlockID.Hash),
tmbytes.Fingerprint(vote.Signature),
tmbytes.Fingerprint(vote.Extension),
CanonicalTime(vote.Timestamp),
)
}
@@ -152,6 +172,12 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
if !pubKey.VerifySignature(VoteSignBytes(chainID, v), vote.Signature) {
return ErrVoteInvalidSignature
}
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) {
return ErrVoteInvalidSignature
}
return nil
}
@@ -198,6 +224,12 @@ func (vote *Vote) ValidateBasic() error {
return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize)
}
// TODO: Remove the extension length check such that we always require
// extension signatures to be present.
if len(vote.Extension) > 0 && len(vote.ExtensionSignature) == 0 {
return errors.New("vote extension signature is missing")
}
return nil
}
@@ -209,14 +241,16 @@ func (vote *Vote) ToProto() *tmproto.Vote {
}
return &tmproto.Vote{
Type: vote.Type,
Height: vote.Height,
Round: vote.Round,
BlockID: vote.BlockID.ToProto(),
Timestamp: vote.Timestamp,
ValidatorAddress: vote.ValidatorAddress,
ValidatorIndex: vote.ValidatorIndex,
Signature: vote.Signature,
Type: vote.Type,
Height: vote.Height,
Round: vote.Round,
BlockID: vote.BlockID.ToProto(),
Timestamp: vote.Timestamp,
ValidatorAddress: vote.ValidatorAddress,
ValidatorIndex: vote.ValidatorIndex,
Signature: vote.Signature,
Extension: vote.Extension,
ExtensionSignature: vote.ExtensionSignature,
}
}
@@ -257,6 +291,8 @@ func VoteFromProto(pv *tmproto.Vote) (*Vote, error) {
vote.ValidatorAddress = pv.ValidatorAddress
vote.ValidatorIndex = pv.ValidatorIndex
vote.Signature = pv.Signature
vote.Extension = pv.Extension
vote.ExtensionSignature = pv.ExtensionSignature
return vote, vote.ValidateBasic()
}
+1
View File
@@ -118,6 +118,7 @@ func TestVoteSet_AddVote_Bad(t *testing.T) {
t.Errorf("expected VoteSet.Add to fail, wrong type")
}
}
}
func TestVoteSet_2_3Majority(t *testing.T) {
+97 -2
View File
@@ -13,6 +13,7 @@ import (
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/libs/protoio"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
func examplePrevote() *Vote {
@@ -127,6 +128,27 @@ func TestVoteSignBytesTestVectors(t *testing.T) {
0x32,
0xd, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64}, // chainID
},
// containing vote extension
5: {
"test_chain_id", &Vote{
Height: 1,
Round: 1,
Extension: []byte("extension"),
},
[]byte{
0x2e, // length
0x11, // (field_number << 3) | wire_type
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height
0x19, // (field_number << 3) | wire_type
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round
// remaning fields:
0x2a, // (field_number << 3) | wire_type
0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, // timestamp
// (field_number << 3) | wire_type
0x32,
0xd, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, // chainID
}, // chainID
},
}
for i, tc := range tests {
v := tc.vote.ToProto()
@@ -177,6 +199,79 @@ func TestVoteVerifySignature(t *testing.T) {
require.True(t, valid)
}
// TestVoteExtension tests that the vote verification behaves correctly in each case
// of vote extension being set on the vote.
func TestVoteExtension(t *testing.T) {
testCases := []struct {
name string
extension []byte
includeSignature bool
expectError bool
}{
{
name: "all fields present",
extension: []byte("extension"),
includeSignature: true,
expectError: false,
},
// TODO: Re-enable once
// https://github.com/tendermint/tendermint/issues/8272 is resolved.
//{
// 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,
//},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
height, round := int64(1), int32(0)
privVal := NewMockPV()
pk, err := privVal.GetPubKey()
require.NoError(t, err)
blk := Block{}
ps, err := blk.MakePartSet(BlockPartSizeBytes)
require.NoError(t, err)
vote := &Vote{
ValidatorAddress: pk.Address(),
ValidatorIndex: 0,
Height: height,
Round: round,
Timestamp: tmtime.Now(),
Type: tmproto.PrecommitType,
BlockID: BlockID{blk.Hash(), ps.Header()},
}
v := vote.ToProto()
err = privVal.SignVote("test_chain_id", v)
require.NoError(t, err)
vote.Signature = v.Signature
if tc.includeSignature {
vote.ExtensionSignature = v.ExtensionSignature
}
err = vote.Verify("test_chain_id", pk)
if tc.expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
func TestIsVoteTypeValid(t *testing.T) {
tc := []struct {
name string
@@ -219,13 +314,13 @@ func TestVoteVerify(t *testing.T) {
func TestVoteString(t *testing.T) {
str := examplePrecommit().String()
expected := `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PRECOMMIT(Precommit) 8B01023386C3 000000000000 @ 2017-12-25T03:00:01.234Z}` //nolint:lll //ignore line length for tests
expected := `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PRECOMMIT(Precommit) 8B01023386C3 000000000000 000000000000 @ 2017-12-25T03:00:01.234Z}` //nolint:lll //ignore line length for tests
if str != expected {
t.Errorf("got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str)
}
str2 := examplePrevote().String()
expected = `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PREVOTE(Prevote) 8B01023386C3 000000000000 @ 2017-12-25T03:00:01.234Z}` //nolint:lll //ignore line length for tests
expected = `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PREVOTE(Prevote) 8B01023386C3 000000000000 000000000000 @ 2017-12-25T03:00:01.234Z}` //nolint:lll //ignore line length for tests
if str2 != expected {
t.Errorf("got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str2)
}