From 67d3ce03a4b0d7694c29ea8c1a68de4d3d5e5d75 Mon Sep 17 00:00:00 2001 From: Sergio Mena Date: Tue, 6 Dec 2022 21:03:46 +0100 Subject: [PATCH] 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 * *Signed -> *ToSign * add Vote to RequestExtendVote * apply reviews * Apply suggestions from code review Co-authored-by: Dev Ojha Co-authored-by: Aleksandr Bezobchuk * fix typo, modify proto Co-authored-by: Aleksandr Bezobchuk Co-authored-by: Dev Ojha * [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 * *Signed -> *ToSign * add Vote to RequestExtendVote * add example VoteExtension * apply reviews * fix vote * Apply suggestions from code review Co-authored-by: Dev Ojha Co-authored-by: Aleksandr Bezobchuk * 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 Co-authored-by: Dev Ojha * [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 * Addressed @creachadair's comments Co-authored-by: mconcat Co-authored-by: M. J. Fromberger * [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 4566f1e3028278c5b3eca27b53254a48771b152b. * Changes to ABCI protocol buffers * Update spec/core/data_structures.md Co-authored-by: M. J. Fromberger * Update spec/core/data_structures.md Co-authored-by: M. J. Fromberger * Fix dangling link in ABCI++ readme * Addressed comments Co-authored-by: M. J. Fromberger * [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 * Fix typo Signed-off-by: Thane Thomson * Better describe method given vote extensions Signed-off-by: Thane Thomson * Fix types tests Signed-off-by: Thane Thomson * Move CanonicalVoteExtension to canonical types proto defs Signed-off-by: Thane Thomson * Regenerate protos including latest PBTS synchrony params update Signed-off-by: Thane Thomson * Inject vote extensions into proposal Signed-off-by: Thane Thomson * Thread vote extensions through code and fix tests Signed-off-by: Thane Thomson * Remove extraneous empty value initialization Signed-off-by: Thane Thomson * Fix lint Signed-off-by: Thane Thomson * Fix missing VerifyVoteExtension request data Signed-off-by: Thane Thomson * Explicitly ensure length > 0 to sign vote extension Signed-off-by: Thane Thomson * Explicitly ensure length > 0 to sign vote extension Signed-off-by: Thane Thomson * Remove extraneous comment Signed-off-by: Thane Thomson * Update privval/file.go Co-authored-by: M. J. Fromberger * Update types/vote_test.go Co-authored-by: M. J. Fromberger * Format Signed-off-by: Thane Thomson * Fix ABCI proto generation scripts for Linux Signed-off-by: Thane Thomson * Sync intermediate and goal protos Signed-off-by: Thane Thomson * Update internal/consensus/common_test.go Co-authored-by: Sergio Mena * Use dummy value with clearer meaning Signed-off-by: Thane Thomson * Rewrite loop for clarity Signed-off-by: Thane Thomson * Panic on ABCI++ method call failure Signed-off-by: Thane Thomson * Add strong correctness guarantees when constructing extended commit info for ABCI++ Signed-off-by: Thane Thomson * Add strong guarantee in extendedCommitInfo that the number of votes corresponds Signed-off-by: Thane Thomson * 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 * Remove extraneous validator address assignment Signed-off-by: Thane Thomson * Sign over canonical vote extension Signed-off-by: Thane Thomson * Validate vote extension signature against canonical vote extension Signed-off-by: Thane Thomson * Update privval tests for more meaningful dummy value Signed-off-by: Thane Thomson * Add vote extension capability to E2E test app Signed-off-by: Thane Thomson * Disable lint for weak RNG usage for test app Signed-off-by: Thane Thomson * Use parseVoteExtension instead of custom parsing in PrepareProposal Signed-off-by: Thane Thomson * 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 * Require app_hash from app to match that from last block Signed-off-by: Thane Thomson * Add contrived (possibly flaky) test to check that vote extensions code works Signed-off-by: Thane Thomson * Remove workaround for problem now solved by #8229 Signed-off-by: Thane Thomson * add tests for vote extension cases * Fix spelling mistake to appease linter Signed-off-by: Thane Thomson * Collapse redundant if statement Signed-off-by: Thane Thomson * Formatting Signed-off-by: Thane Thomson * Always expect an extension signature, regardless of whether an extension is present Signed-off-by: Thane Thomson * Votes constructed from commits cannot include extensions or signatures Signed-off-by: Thane Thomson * Pass through vote extension in test helpers Signed-off-by: Thane Thomson * Temporarily disable vote extension signature requirement Signed-off-by: Thane Thomson * Expand on vote equality test errors for clarity Signed-off-by: Thane Thomson * Expand on vote matching error messages in testing Signed-off-by: Thane Thomson * 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 * Read lock consensus state mutex in test helper to avoid data race Signed-off-by: Thane Thomson * Revert BlockIDFlag parameter in node test Signed-off-by: Thane Thomson * Perform additional check in ProcessProposal for special txs generated by vote extensions Signed-off-by: Thane Thomson * e2e: check that our added tx does not cause all txs to exceed req.MaxTxBytes Signed-off-by: Thane Thomson * Only set vote extension signatures when signing is successful Signed-off-by: Thane Thomson * Remove channel capacity constraint in test helper to avoid missing messages Signed-off-by: Thane Thomson * Add TODO to always require extension signatures in vote validation Signed-off-by: Thane Thomson * e2e: reject vote extensions if the request height does not match what we expect Signed-off-by: Thane Thomson * types: remove extraneous call to voteWithoutExtension in test Signed-off-by: Thane Thomson * Remove unnecessary address parameter from CanonicalVoteExtension Signed-off-by: Thane Thomson * privval: change test vote type to precommit since we use an extension Signed-off-by: Thane Thomson * privval: update signing logic to cater for vote extensions Signed-off-by: Thane Thomson * proto: update field descriptions for vote message Signed-off-by: Thane Thomson * proto: update field description for vote extension sig in vote message Signed-off-by: Thane Thomson * proto/types: use fixed-length 64-bit integers for rounds in CanonicalVoteExtension Signed-off-by: Thane Thomson * consensus: fix flaky TestPrepareProposalReceivesVoteExtensions Signed-off-by: Thane Thomson * consensus: remove previously added test helper functionality Signed-off-by: Thane Thomson * e2e: add error logs when we get an unexpected height in ExtendVote or VerifyVoteExtension requests Signed-off-by: Thane Thomson * node_test: get validator addresses from privvals Signed-off-by: Thane Thomson * privval/file_test: optimize filepv creation in tests Signed-off-by: Thane Thomson * privval: add test to check that vote extensions are always signed Signed-off-by: Thane Thomson * 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] 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 Co-authored-by: M. J. Fromberger Co-authored-by: Sergio Mena Co-authored-by: William Banfield 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 Co-authored-by: Aleksandr Bezobchuk Co-authored-by: Dev Ojha Co-authored-by: M. J. Fromberger Co-authored-by: Thane Thomson Co-authored-by: William Banfield Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: William Banfield <4561443+williambanfield@users.noreply.github.com> --- abci/client/grpc_client.go | 8 + abci/client/local_client.go | 14 + abci/client/mocks/client.go | 46 + abci/client/socket_client.go | 26 + abci/example/kvstore/README.md | 2 +- abci/server/socket_server.go | 12 + abci/types/application.go | 14 + abci/types/messages.go | 24 + abci/types/mocks/application.go | 46 + abci/types/types.go | 7 + abci/types/types.pb.go | 1871 ++++++++++++++--- consensus/byzantine_test.go | 4 +- consensus/common_test.go | 58 +- consensus/msgs_test.go | 7 +- consensus/state.go | 25 +- consensus/state_test.go | 252 ++- consensus/types/height_vote_set_test.go | 1 + internal/test/commit.go | 1 + internal/test/vote.go | 1 + privval/file.go | 37 +- privval/file_test.go | 133 +- privval/msgs_test.go | 21 +- proto/tendermint/abci/types.proto | 107 +- proto/tendermint/crypto/keys.pb.go | 1 - proto/tendermint/mempool/types.pb.go | 1 - proto/tendermint/p2p/conn.pb.go | 1 - proto/tendermint/statesync/types.pb.go | 1 - proto/tendermint/types/canonical.pb.go | 429 +++- proto/tendermint/types/canonical.proto | 10 + proto/tendermint/types/types.pb.go | 791 ++++++- proto/tendermint/types/types.proto | 23 +- proxy/app_conn.go | 12 + proxy/mocks/app_conn_consensus.go | 46 + proxy/mocks/app_conn_mempool.go | 5 + state/execution.go | 67 +- state/execution_test.go | 26 +- state/helpers_test.go | 14 +- test/e2e/app/app.go | 162 +- test/e2e/app/sync_app.go | 12 + test/e2e/tests/app_test.go | 17 + .../internal/test_harness.go | 1 + types/block_test.go | 4 +- types/canonical.go | 15 +- types/evidence_test.go | 1 + types/priv_validator.go | 6 + types/test_util.go | 12 + types/validation_test.go | 3 + types/vote.go | 74 +- types/vote_set_test.go | 1 + types/vote_test.go | 99 +- 50 files changed, 3916 insertions(+), 635 deletions(-) diff --git a/abci/client/grpc_client.go b/abci/client/grpc_client.go index 26fbf6337..962b5698f 100644 --- a/abci/client/grpc_client.go +++ b/abci/client/grpc_client.go @@ -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)) } diff --git a/abci/client/local_client.go b/abci/client/local_client.go index fe6e0cfd9..fec449321 100644 --- a/abci/client/local_client.go +++ b/abci/client/local_client.go @@ -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() diff --git a/abci/client/mocks/client.go b/abci/client/mocks/client.go index 8892712ab..9e0e074b1 100644 --- a/abci/client/mocks/client.go +++ b/abci/client/mocks/client.go @@ -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()) diff --git a/abci/client/socket_client.go b/abci/client/socket_client.go index 061cfc0a5..3817dafbb 100644 --- a/abci/client/socket_client.go +++ b/abci/client/socket_client.go @@ -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: diff --git a/abci/example/kvstore/README.md b/abci/example/kvstore/README.md index e97debb1d..e9e38b53c 100644 --- a/abci/example/kvstore/README.md +++ b/abci/example/kvstore/README.md @@ -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). diff --git a/abci/server/socket_server.go b/abci/server/socket_server.go index 1053e0d7f..94cd551f0 100644 --- a/abci/server/socket_server.go +++ b/abci/server/socket_server.go @@ -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) } diff --git a/abci/types/application.go b/abci/types/application.go index b08862647..a0f8a0826 100644 --- a/abci/types/application.go +++ b/abci/types/application.go @@ -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 { diff --git a/abci/types/messages.go b/abci/types/messages.go index 5f863b87b..2ba9cad66 100644 --- a/abci/types/messages.go +++ b/abci/types/messages.go @@ -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}, diff --git a/abci/types/mocks/application.go b/abci/types/mocks/application.go index a39d1f2e6..0457cbda3 100644 --- a/abci/types/mocks/application.go +++ b/abci/types/mocks/application.go @@ -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()) diff --git a/abci/types/types.go b/abci/types/types.go index 414319bac..b84c0639b 100644 --- a/abci/types/types.go +++ b/abci/types/types.go @@ -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 diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index 6bf451e76..8521fae52 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -121,7 +121,7 @@ func (x ResponseOfferSnapshot_Result) String() string { } func (ResponseOfferSnapshot_Result) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{25, 0} + return fileDescriptor_252557cfdd89a31a, []int{27, 0} } type ResponseApplySnapshotChunk_Result int32 @@ -158,7 +158,7 @@ func (x ResponseApplySnapshotChunk_Result) String() string { } func (ResponseApplySnapshotChunk_Result) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{27, 0} + return fileDescriptor_252557cfdd89a31a, []int{29, 0} } type ResponseProcessProposal_ProposalStatus int32 @@ -186,7 +186,35 @@ func (x ResponseProcessProposal_ProposalStatus) String() string { } func (ResponseProcessProposal_ProposalStatus) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{29, 0} + return fileDescriptor_252557cfdd89a31a, []int{31, 0} +} + +type ResponseVerifyVoteExtension_VerifyStatus int32 + +const ( + ResponseVerifyVoteExtension_UNKNOWN ResponseVerifyVoteExtension_VerifyStatus = 0 + ResponseVerifyVoteExtension_ACCEPT ResponseVerifyVoteExtension_VerifyStatus = 1 + ResponseVerifyVoteExtension_REJECT ResponseVerifyVoteExtension_VerifyStatus = 2 +) + +var ResponseVerifyVoteExtension_VerifyStatus_name = map[int32]string{ + 0: "UNKNOWN", + 1: "ACCEPT", + 2: "REJECT", +} + +var ResponseVerifyVoteExtension_VerifyStatus_value = map[string]int32{ + "UNKNOWN": 0, + "ACCEPT": 1, + "REJECT": 2, +} + +func (x ResponseVerifyVoteExtension_VerifyStatus) String() string { + return proto.EnumName(ResponseVerifyVoteExtension_VerifyStatus_name, int32(x)) +} + +func (ResponseVerifyVoteExtension_VerifyStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_252557cfdd89a31a, []int{33, 0} } type Request struct { @@ -204,6 +232,8 @@ type Request struct { // *Request_ApplySnapshotChunk // *Request_PrepareProposal // *Request_ProcessProposal + // *Request_ExtendVote + // *Request_VerifyVoteExtension // *Request_FinalizeBlock Value isRequest_Value `protobuf_oneof:"value"` } @@ -286,24 +316,32 @@ type Request_PrepareProposal struct { type Request_ProcessProposal struct { ProcessProposal *RequestProcessProposal `protobuf:"bytes,17,opt,name=process_proposal,json=processProposal,proto3,oneof" json:"process_proposal,omitempty"` } +type Request_ExtendVote struct { + ExtendVote *RequestExtendVote `protobuf:"bytes,18,opt,name=extend_vote,json=extendVote,proto3,oneof" json:"extend_vote,omitempty"` +} +type Request_VerifyVoteExtension struct { + VerifyVoteExtension *RequestVerifyVoteExtension `protobuf:"bytes,19,opt,name=verify_vote_extension,json=verifyVoteExtension,proto3,oneof" json:"verify_vote_extension,omitempty"` +} type Request_FinalizeBlock struct { FinalizeBlock *RequestFinalizeBlock `protobuf:"bytes,20,opt,name=finalize_block,json=finalizeBlock,proto3,oneof" json:"finalize_block,omitempty"` } -func (*Request_Echo) isRequest_Value() {} -func (*Request_Flush) isRequest_Value() {} -func (*Request_Info) isRequest_Value() {} -func (*Request_InitChain) isRequest_Value() {} -func (*Request_Query) isRequest_Value() {} -func (*Request_CheckTx) isRequest_Value() {} -func (*Request_Commit) isRequest_Value() {} -func (*Request_ListSnapshots) isRequest_Value() {} -func (*Request_OfferSnapshot) isRequest_Value() {} -func (*Request_LoadSnapshotChunk) isRequest_Value() {} -func (*Request_ApplySnapshotChunk) isRequest_Value() {} -func (*Request_PrepareProposal) isRequest_Value() {} -func (*Request_ProcessProposal) isRequest_Value() {} -func (*Request_FinalizeBlock) isRequest_Value() {} +func (*Request_Echo) isRequest_Value() {} +func (*Request_Flush) isRequest_Value() {} +func (*Request_Info) isRequest_Value() {} +func (*Request_InitChain) isRequest_Value() {} +func (*Request_Query) isRequest_Value() {} +func (*Request_CheckTx) isRequest_Value() {} +func (*Request_Commit) isRequest_Value() {} +func (*Request_ListSnapshots) isRequest_Value() {} +func (*Request_OfferSnapshot) isRequest_Value() {} +func (*Request_LoadSnapshotChunk) isRequest_Value() {} +func (*Request_ApplySnapshotChunk) isRequest_Value() {} +func (*Request_PrepareProposal) isRequest_Value() {} +func (*Request_ProcessProposal) isRequest_Value() {} +func (*Request_ExtendVote) isRequest_Value() {} +func (*Request_VerifyVoteExtension) isRequest_Value() {} +func (*Request_FinalizeBlock) isRequest_Value() {} func (m *Request) GetValue() isRequest_Value { if m != nil { @@ -403,6 +441,20 @@ func (m *Request) GetProcessProposal() *RequestProcessProposal { return nil } +func (m *Request) GetExtendVote() *RequestExtendVote { + if x, ok := m.GetValue().(*Request_ExtendVote); ok { + return x.ExtendVote + } + return nil +} + +func (m *Request) GetVerifyVoteExtension() *RequestVerifyVoteExtension { + if x, ok := m.GetValue().(*Request_VerifyVoteExtension); ok { + return x.VerifyVoteExtension + } + return nil +} + func (m *Request) GetFinalizeBlock() *RequestFinalizeBlock { if x, ok := m.GetValue().(*Request_FinalizeBlock); ok { return x.FinalizeBlock @@ -426,6 +478,8 @@ func (*Request) XXX_OneofWrappers() []interface{} { (*Request_ApplySnapshotChunk)(nil), (*Request_PrepareProposal)(nil), (*Request_ProcessProposal)(nil), + (*Request_ExtendVote)(nil), + (*Request_VerifyVoteExtension)(nil), (*Request_FinalizeBlock)(nil), } } @@ -1236,6 +1290,128 @@ func (m *RequestProcessProposal) GetProposerAddress() []byte { return nil } +// Extends a vote with application-side injection +type RequestExtendVote struct { + Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *RequestExtendVote) Reset() { *m = RequestExtendVote{} } +func (m *RequestExtendVote) String() string { return proto.CompactTextString(m) } +func (*RequestExtendVote) ProtoMessage() {} +func (*RequestExtendVote) Descriptor() ([]byte, []int) { + return fileDescriptor_252557cfdd89a31a, []int{14} +} +func (m *RequestExtendVote) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RequestExtendVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RequestExtendVote.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 *RequestExtendVote) XXX_Merge(src proto.Message) { + xxx_messageInfo_RequestExtendVote.Merge(m, src) +} +func (m *RequestExtendVote) XXX_Size() int { + return m.Size() +} +func (m *RequestExtendVote) XXX_DiscardUnknown() { + xxx_messageInfo_RequestExtendVote.DiscardUnknown(m) +} + +var xxx_messageInfo_RequestExtendVote proto.InternalMessageInfo + +func (m *RequestExtendVote) GetHash() []byte { + if m != nil { + return m.Hash + } + return nil +} + +func (m *RequestExtendVote) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +// Verify the vote extension +type RequestVerifyVoteExtension struct { + Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + ValidatorAddress []byte `protobuf:"bytes,2,opt,name=validator_address,json=validatorAddress,proto3" json:"validator_address,omitempty"` + Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` + VoteExtension []byte `protobuf:"bytes,4,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,omitempty"` +} + +func (m *RequestVerifyVoteExtension) Reset() { *m = RequestVerifyVoteExtension{} } +func (m *RequestVerifyVoteExtension) String() string { return proto.CompactTextString(m) } +func (*RequestVerifyVoteExtension) ProtoMessage() {} +func (*RequestVerifyVoteExtension) Descriptor() ([]byte, []int) { + return fileDescriptor_252557cfdd89a31a, []int{15} +} +func (m *RequestVerifyVoteExtension) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RequestVerifyVoteExtension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RequestVerifyVoteExtension.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 *RequestVerifyVoteExtension) XXX_Merge(src proto.Message) { + xxx_messageInfo_RequestVerifyVoteExtension.Merge(m, src) +} +func (m *RequestVerifyVoteExtension) XXX_Size() int { + return m.Size() +} +func (m *RequestVerifyVoteExtension) XXX_DiscardUnknown() { + xxx_messageInfo_RequestVerifyVoteExtension.DiscardUnknown(m) +} + +var xxx_messageInfo_RequestVerifyVoteExtension proto.InternalMessageInfo + +func (m *RequestVerifyVoteExtension) GetHash() []byte { + if m != nil { + return m.Hash + } + return nil +} + +func (m *RequestVerifyVoteExtension) GetValidatorAddress() []byte { + if m != nil { + return m.ValidatorAddress + } + return nil +} + +func (m *RequestVerifyVoteExtension) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *RequestVerifyVoteExtension) GetVoteExtension() []byte { + if m != nil { + return m.VoteExtension + } + return nil +} + type RequestFinalizeBlock struct { Txs [][]byte `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"` DecidedLastCommit CommitInfo `protobuf:"bytes,2,opt,name=decided_last_commit,json=decidedLastCommit,proto3" json:"decided_last_commit"` @@ -1253,7 +1429,7 @@ func (m *RequestFinalizeBlock) Reset() { *m = RequestFinalizeBlock{} } func (m *RequestFinalizeBlock) String() string { return proto.CompactTextString(m) } func (*RequestFinalizeBlock) ProtoMessage() {} func (*RequestFinalizeBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{14} + return fileDescriptor_252557cfdd89a31a, []int{16} } func (m *RequestFinalizeBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1340,7 +1516,6 @@ func (m *RequestFinalizeBlock) GetProposerAddress() []byte { type Response struct { // Types that are valid to be assigned to Value: - // // *Response_Exception // *Response_Echo // *Response_Flush @@ -1355,6 +1530,8 @@ type Response struct { // *Response_ApplySnapshotChunk // *Response_PrepareProposal // *Response_ProcessProposal + // *Response_ExtendVote + // *Response_VerifyVoteExtension // *Response_FinalizeBlock Value isResponse_Value `protobuf_oneof:"value"` } @@ -1363,7 +1540,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{15} + return fileDescriptor_252557cfdd89a31a, []int{17} } func (m *Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1440,25 +1617,33 @@ type Response_PrepareProposal struct { type Response_ProcessProposal struct { ProcessProposal *ResponseProcessProposal `protobuf:"bytes,18,opt,name=process_proposal,json=processProposal,proto3,oneof" json:"process_proposal,omitempty"` } +type Response_ExtendVote struct { + ExtendVote *ResponseExtendVote `protobuf:"bytes,19,opt,name=extend_vote,json=extendVote,proto3,oneof" json:"extend_vote,omitempty"` +} +type Response_VerifyVoteExtension struct { + VerifyVoteExtension *ResponseVerifyVoteExtension `protobuf:"bytes,20,opt,name=verify_vote_extension,json=verifyVoteExtension,proto3,oneof" json:"verify_vote_extension,omitempty"` +} type Response_FinalizeBlock struct { FinalizeBlock *ResponseFinalizeBlock `protobuf:"bytes,21,opt,name=finalize_block,json=finalizeBlock,proto3,oneof" json:"finalize_block,omitempty"` } -func (*Response_Exception) isResponse_Value() {} -func (*Response_Echo) isResponse_Value() {} -func (*Response_Flush) isResponse_Value() {} -func (*Response_Info) isResponse_Value() {} -func (*Response_InitChain) isResponse_Value() {} -func (*Response_Query) isResponse_Value() {} -func (*Response_CheckTx) isResponse_Value() {} -func (*Response_Commit) isResponse_Value() {} -func (*Response_ListSnapshots) isResponse_Value() {} -func (*Response_OfferSnapshot) isResponse_Value() {} -func (*Response_LoadSnapshotChunk) isResponse_Value() {} -func (*Response_ApplySnapshotChunk) isResponse_Value() {} -func (*Response_PrepareProposal) isResponse_Value() {} -func (*Response_ProcessProposal) isResponse_Value() {} -func (*Response_FinalizeBlock) isResponse_Value() {} +func (*Response_Exception) isResponse_Value() {} +func (*Response_Echo) isResponse_Value() {} +func (*Response_Flush) isResponse_Value() {} +func (*Response_Info) isResponse_Value() {} +func (*Response_InitChain) isResponse_Value() {} +func (*Response_Query) isResponse_Value() {} +func (*Response_CheckTx) isResponse_Value() {} +func (*Response_Commit) isResponse_Value() {} +func (*Response_ListSnapshots) isResponse_Value() {} +func (*Response_OfferSnapshot) isResponse_Value() {} +func (*Response_LoadSnapshotChunk) isResponse_Value() {} +func (*Response_ApplySnapshotChunk) isResponse_Value() {} +func (*Response_PrepareProposal) isResponse_Value() {} +func (*Response_ProcessProposal) isResponse_Value() {} +func (*Response_ExtendVote) isResponse_Value() {} +func (*Response_VerifyVoteExtension) isResponse_Value() {} +func (*Response_FinalizeBlock) isResponse_Value() {} func (m *Response) GetValue() isResponse_Value { if m != nil { @@ -1565,6 +1750,20 @@ func (m *Response) GetProcessProposal() *ResponseProcessProposal { return nil } +func (m *Response) GetExtendVote() *ResponseExtendVote { + if x, ok := m.GetValue().(*Response_ExtendVote); ok { + return x.ExtendVote + } + return nil +} + +func (m *Response) GetVerifyVoteExtension() *ResponseVerifyVoteExtension { + if x, ok := m.GetValue().(*Response_VerifyVoteExtension); ok { + return x.VerifyVoteExtension + } + return nil +} + func (m *Response) GetFinalizeBlock() *ResponseFinalizeBlock { if x, ok := m.GetValue().(*Response_FinalizeBlock); ok { return x.FinalizeBlock @@ -1589,6 +1788,8 @@ func (*Response) XXX_OneofWrappers() []interface{} { (*Response_ApplySnapshotChunk)(nil), (*Response_PrepareProposal)(nil), (*Response_ProcessProposal)(nil), + (*Response_ExtendVote)(nil), + (*Response_VerifyVoteExtension)(nil), (*Response_FinalizeBlock)(nil), } } @@ -1602,7 +1803,7 @@ func (m *ResponseException) Reset() { *m = ResponseException{} } func (m *ResponseException) String() string { return proto.CompactTextString(m) } func (*ResponseException) ProtoMessage() {} func (*ResponseException) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{16} + return fileDescriptor_252557cfdd89a31a, []int{18} } func (m *ResponseException) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1646,7 +1847,7 @@ func (m *ResponseEcho) Reset() { *m = ResponseEcho{} } func (m *ResponseEcho) String() string { return proto.CompactTextString(m) } func (*ResponseEcho) ProtoMessage() {} func (*ResponseEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{17} + return fileDescriptor_252557cfdd89a31a, []int{19} } func (m *ResponseEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1689,7 +1890,7 @@ func (m *ResponseFlush) Reset() { *m = ResponseFlush{} } func (m *ResponseFlush) String() string { return proto.CompactTextString(m) } func (*ResponseFlush) ProtoMessage() {} func (*ResponseFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{18} + return fileDescriptor_252557cfdd89a31a, []int{20} } func (m *ResponseFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1730,7 +1931,7 @@ func (m *ResponseInfo) Reset() { *m = ResponseInfo{} } func (m *ResponseInfo) String() string { return proto.CompactTextString(m) } func (*ResponseInfo) ProtoMessage() {} func (*ResponseInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{19} + return fileDescriptor_252557cfdd89a31a, []int{21} } func (m *ResponseInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1804,7 +2005,7 @@ func (m *ResponseInitChain) Reset() { *m = ResponseInitChain{} } func (m *ResponseInitChain) String() string { return proto.CompactTextString(m) } func (*ResponseInitChain) ProtoMessage() {} func (*ResponseInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{20} + return fileDescriptor_252557cfdd89a31a, []int{22} } func (m *ResponseInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1871,7 +2072,7 @@ func (m *ResponseQuery) Reset() { *m = ResponseQuery{} } func (m *ResponseQuery) String() string { return proto.CompactTextString(m) } func (*ResponseQuery) ProtoMessage() {} func (*ResponseQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{21} + return fileDescriptor_252557cfdd89a31a, []int{23} } func (m *ResponseQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1983,7 +2184,7 @@ func (m *ResponseCheckTx) Reset() { *m = ResponseCheckTx{} } func (m *ResponseCheckTx) String() string { return proto.CompactTextString(m) } func (*ResponseCheckTx) ProtoMessage() {} func (*ResponseCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{22} + return fileDescriptor_252557cfdd89a31a, []int{24} } func (m *ResponseCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2097,7 +2298,7 @@ func (m *ResponseCommit) Reset() { *m = ResponseCommit{} } func (m *ResponseCommit) String() string { return proto.CompactTextString(m) } func (*ResponseCommit) ProtoMessage() {} func (*ResponseCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{23} + return fileDescriptor_252557cfdd89a31a, []int{25} } func (m *ResponseCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2141,7 +2342,7 @@ func (m *ResponseListSnapshots) Reset() { *m = ResponseListSnapshots{} } func (m *ResponseListSnapshots) String() string { return proto.CompactTextString(m) } func (*ResponseListSnapshots) ProtoMessage() {} func (*ResponseListSnapshots) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{24} + return fileDescriptor_252557cfdd89a31a, []int{26} } func (m *ResponseListSnapshots) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2185,7 +2386,7 @@ func (m *ResponseOfferSnapshot) Reset() { *m = ResponseOfferSnapshot{} } func (m *ResponseOfferSnapshot) String() string { return proto.CompactTextString(m) } func (*ResponseOfferSnapshot) ProtoMessage() {} func (*ResponseOfferSnapshot) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{25} + return fileDescriptor_252557cfdd89a31a, []int{27} } func (m *ResponseOfferSnapshot) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2229,7 +2430,7 @@ func (m *ResponseLoadSnapshotChunk) Reset() { *m = ResponseLoadSnapshotC func (m *ResponseLoadSnapshotChunk) String() string { return proto.CompactTextString(m) } func (*ResponseLoadSnapshotChunk) ProtoMessage() {} func (*ResponseLoadSnapshotChunk) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{26} + return fileDescriptor_252557cfdd89a31a, []int{28} } func (m *ResponseLoadSnapshotChunk) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2275,7 +2476,7 @@ func (m *ResponseApplySnapshotChunk) Reset() { *m = ResponseApplySnapsho func (m *ResponseApplySnapshotChunk) String() string { return proto.CompactTextString(m) } func (*ResponseApplySnapshotChunk) ProtoMessage() {} func (*ResponseApplySnapshotChunk) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{27} + return fileDescriptor_252557cfdd89a31a, []int{29} } func (m *ResponseApplySnapshotChunk) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2333,7 +2534,7 @@ func (m *ResponsePrepareProposal) Reset() { *m = ResponsePrepareProposal func (m *ResponsePrepareProposal) String() string { return proto.CompactTextString(m) } func (*ResponsePrepareProposal) ProtoMessage() {} func (*ResponsePrepareProposal) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{28} + return fileDescriptor_252557cfdd89a31a, []int{30} } func (m *ResponsePrepareProposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2377,7 +2578,7 @@ func (m *ResponseProcessProposal) Reset() { *m = ResponseProcessProposal func (m *ResponseProcessProposal) String() string { return proto.CompactTextString(m) } func (*ResponseProcessProposal) ProtoMessage() {} func (*ResponseProcessProposal) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{29} + return fileDescriptor_252557cfdd89a31a, []int{31} } func (m *ResponseProcessProposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2413,6 +2614,94 @@ func (m *ResponseProcessProposal) GetStatus() ResponseProcessProposal_ProposalSt return ResponseProcessProposal_UNKNOWN } +type ResponseExtendVote struct { + VoteExtension []byte `protobuf:"bytes,1,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,omitempty"` +} + +func (m *ResponseExtendVote) Reset() { *m = ResponseExtendVote{} } +func (m *ResponseExtendVote) String() string { return proto.CompactTextString(m) } +func (*ResponseExtendVote) ProtoMessage() {} +func (*ResponseExtendVote) Descriptor() ([]byte, []int) { + return fileDescriptor_252557cfdd89a31a, []int{32} +} +func (m *ResponseExtendVote) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResponseExtendVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResponseExtendVote.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 *ResponseExtendVote) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResponseExtendVote.Merge(m, src) +} +func (m *ResponseExtendVote) XXX_Size() int { + return m.Size() +} +func (m *ResponseExtendVote) XXX_DiscardUnknown() { + xxx_messageInfo_ResponseExtendVote.DiscardUnknown(m) +} + +var xxx_messageInfo_ResponseExtendVote proto.InternalMessageInfo + +func (m *ResponseExtendVote) GetVoteExtension() []byte { + if m != nil { + return m.VoteExtension + } + return nil +} + +type ResponseVerifyVoteExtension struct { + Status ResponseVerifyVoteExtension_VerifyStatus `protobuf:"varint,1,opt,name=status,proto3,enum=tendermint.abci.ResponseVerifyVoteExtension_VerifyStatus" json:"status,omitempty"` +} + +func (m *ResponseVerifyVoteExtension) Reset() { *m = ResponseVerifyVoteExtension{} } +func (m *ResponseVerifyVoteExtension) String() string { return proto.CompactTextString(m) } +func (*ResponseVerifyVoteExtension) ProtoMessage() {} +func (*ResponseVerifyVoteExtension) Descriptor() ([]byte, []int) { + return fileDescriptor_252557cfdd89a31a, []int{33} +} +func (m *ResponseVerifyVoteExtension) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResponseVerifyVoteExtension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResponseVerifyVoteExtension.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 *ResponseVerifyVoteExtension) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResponseVerifyVoteExtension.Merge(m, src) +} +func (m *ResponseVerifyVoteExtension) XXX_Size() int { + return m.Size() +} +func (m *ResponseVerifyVoteExtension) XXX_DiscardUnknown() { + xxx_messageInfo_ResponseVerifyVoteExtension.DiscardUnknown(m) +} + +var xxx_messageInfo_ResponseVerifyVoteExtension proto.InternalMessageInfo + +func (m *ResponseVerifyVoteExtension) GetStatus() ResponseVerifyVoteExtension_VerifyStatus { + if m != nil { + return m.Status + } + return ResponseVerifyVoteExtension_UNKNOWN +} + type ResponseFinalizeBlock struct { // set of block events emmitted as part of executing the block Events []Event `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` @@ -2432,7 +2721,7 @@ func (m *ResponseFinalizeBlock) Reset() { *m = ResponseFinalizeBlock{} } func (m *ResponseFinalizeBlock) String() string { return proto.CompactTextString(m) } func (*ResponseFinalizeBlock) ProtoMessage() {} func (*ResponseFinalizeBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{30} + return fileDescriptor_252557cfdd89a31a, []int{34} } func (m *ResponseFinalizeBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2505,7 +2794,7 @@ func (m *CommitInfo) Reset() { *m = CommitInfo{} } func (m *CommitInfo) String() string { return proto.CompactTextString(m) } func (*CommitInfo) ProtoMessage() {} func (*CommitInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{31} + return fileDescriptor_252557cfdd89a31a, []int{35} } func (m *CommitInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2548,6 +2837,9 @@ func (m *CommitInfo) GetVotes() []VoteInfo { return nil } +// 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. type ExtendedCommitInfo struct { // The round at which the block proposer decided in the previous height. Round int32 `protobuf:"varint,1,opt,name=round,proto3" json:"round,omitempty"` @@ -2560,7 +2852,7 @@ func (m *ExtendedCommitInfo) Reset() { *m = ExtendedCommitInfo{} } func (m *ExtendedCommitInfo) String() string { return proto.CompactTextString(m) } func (*ExtendedCommitInfo) ProtoMessage() {} func (*ExtendedCommitInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{32} + return fileDescriptor_252557cfdd89a31a, []int{36} } func (m *ExtendedCommitInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2615,7 +2907,7 @@ func (m *Event) Reset() { *m = Event{} } func (m *Event) String() string { return proto.CompactTextString(m) } func (*Event) ProtoMessage() {} func (*Event) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{33} + return fileDescriptor_252557cfdd89a31a, []int{37} } func (m *Event) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2669,7 +2961,7 @@ func (m *EventAttribute) Reset() { *m = EventAttribute{} } func (m *EventAttribute) String() string { return proto.CompactTextString(m) } func (*EventAttribute) ProtoMessage() {} func (*EventAttribute) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{34} + return fileDescriptor_252557cfdd89a31a, []int{38} } func (m *EventAttribute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2737,7 +3029,7 @@ func (m *ExecTxResult) Reset() { *m = ExecTxResult{} } func (m *ExecTxResult) String() string { return proto.CompactTextString(m) } func (*ExecTxResult) ProtoMessage() {} func (*ExecTxResult) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{35} + return fileDescriptor_252557cfdd89a31a, []int{39} } func (m *ExecTxResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2836,7 +3128,7 @@ func (m *TxResult) Reset() { *m = TxResult{} } func (m *TxResult) String() string { return proto.CompactTextString(m) } func (*TxResult) ProtoMessage() {} func (*TxResult) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{36} + return fileDescriptor_252557cfdd89a31a, []int{40} } func (m *TxResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2893,7 +3185,6 @@ func (m *TxResult) GetResult() ExecTxResult { return ExecTxResult{} } -// Validator type Validator struct { Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // PubKey pub_key = 2 [(gogoproto.nullable)=false]; @@ -2904,7 +3195,7 @@ func (m *Validator) Reset() { *m = Validator{} } func (m *Validator) String() string { return proto.CompactTextString(m) } func (*Validator) ProtoMessage() {} func (*Validator) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{37} + return fileDescriptor_252557cfdd89a31a, []int{41} } func (m *Validator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2947,7 +3238,6 @@ func (m *Validator) GetPower() int64 { return 0 } -// ValidatorUpdate type ValidatorUpdate struct { PubKey crypto.PublicKey `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key"` Power int64 `protobuf:"varint,2,opt,name=power,proto3" json:"power,omitempty"` @@ -2957,7 +3247,7 @@ func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} } func (m *ValidatorUpdate) String() string { return proto.CompactTextString(m) } func (*ValidatorUpdate) ProtoMessage() {} func (*ValidatorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{38} + return fileDescriptor_252557cfdd89a31a, []int{42} } func (m *ValidatorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3000,7 +3290,6 @@ func (m *ValidatorUpdate) GetPower() int64 { return 0 } -// VoteInfo type VoteInfo struct { Validator Validator `protobuf:"bytes,1,opt,name=validator,proto3" json:"validator"` SignedLastBlock bool `protobuf:"varint,2,opt,name=signed_last_block,json=signedLastBlock,proto3" json:"signed_last_block,omitempty"` @@ -3010,7 +3299,7 @@ func (m *VoteInfo) Reset() { *m = VoteInfo{} } func (m *VoteInfo) String() string { return proto.CompactTextString(m) } func (*VoteInfo) ProtoMessage() {} func (*VoteInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{39} + return fileDescriptor_252557cfdd89a31a, []int{43} } func (m *VoteInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3054,16 +3343,19 @@ func (m *VoteInfo) GetSignedLastBlock() bool { } type ExtendedVoteInfo struct { - Validator Validator `protobuf:"bytes,1,opt,name=validator,proto3" json:"validator"` - SignedLastBlock bool `protobuf:"varint,2,opt,name=signed_last_block,json=signedLastBlock,proto3" json:"signed_last_block,omitempty"` - VoteExtension []byte `protobuf:"bytes,3,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,omitempty"` + // The validator that sent the vote. + Validator Validator `protobuf:"bytes,1,opt,name=validator,proto3" json:"validator"` + // Indicates whether the validator signed the last block, allowing for rewards based on validator availability. + SignedLastBlock bool `protobuf:"varint,2,opt,name=signed_last_block,json=signedLastBlock,proto3" json:"signed_last_block,omitempty"` + // Non-deterministic extension provided by the sending validator's application. + VoteExtension []byte `protobuf:"bytes,3,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,omitempty"` } func (m *ExtendedVoteInfo) Reset() { *m = ExtendedVoteInfo{} } func (m *ExtendedVoteInfo) String() string { return proto.CompactTextString(m) } func (*ExtendedVoteInfo) ProtoMessage() {} func (*ExtendedVoteInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{40} + return fileDescriptor_252557cfdd89a31a, []int{44} } func (m *ExtendedVoteInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3131,7 +3423,7 @@ func (m *Misbehavior) Reset() { *m = Misbehavior{} } func (m *Misbehavior) String() string { return proto.CompactTextString(m) } func (*Misbehavior) ProtoMessage() {} func (*Misbehavior) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{41} + return fileDescriptor_252557cfdd89a31a, []int{45} } func (m *Misbehavior) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3207,7 +3499,7 @@ func (m *Snapshot) Reset() { *m = Snapshot{} } func (m *Snapshot) String() string { return proto.CompactTextString(m) } func (*Snapshot) ProtoMessage() {} func (*Snapshot) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{42} + return fileDescriptor_252557cfdd89a31a, []int{46} } func (m *Snapshot) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3277,6 +3569,7 @@ func init() { proto.RegisterEnum("tendermint.abci.ResponseOfferSnapshot_Result", ResponseOfferSnapshot_Result_name, ResponseOfferSnapshot_Result_value) proto.RegisterEnum("tendermint.abci.ResponseApplySnapshotChunk_Result", ResponseApplySnapshotChunk_Result_name, ResponseApplySnapshotChunk_Result_value) proto.RegisterEnum("tendermint.abci.ResponseProcessProposal_ProposalStatus", ResponseProcessProposal_ProposalStatus_name, ResponseProcessProposal_ProposalStatus_value) + proto.RegisterEnum("tendermint.abci.ResponseVerifyVoteExtension_VerifyStatus", ResponseVerifyVoteExtension_VerifyStatus_name, ResponseVerifyVoteExtension_VerifyStatus_value) proto.RegisterType((*Request)(nil), "tendermint.abci.Request") proto.RegisterType((*RequestEcho)(nil), "tendermint.abci.RequestEcho") proto.RegisterType((*RequestFlush)(nil), "tendermint.abci.RequestFlush") @@ -3291,6 +3584,8 @@ func init() { proto.RegisterType((*RequestApplySnapshotChunk)(nil), "tendermint.abci.RequestApplySnapshotChunk") proto.RegisterType((*RequestPrepareProposal)(nil), "tendermint.abci.RequestPrepareProposal") proto.RegisterType((*RequestProcessProposal)(nil), "tendermint.abci.RequestProcessProposal") + proto.RegisterType((*RequestExtendVote)(nil), "tendermint.abci.RequestExtendVote") + proto.RegisterType((*RequestVerifyVoteExtension)(nil), "tendermint.abci.RequestVerifyVoteExtension") proto.RegisterType((*RequestFinalizeBlock)(nil), "tendermint.abci.RequestFinalizeBlock") proto.RegisterType((*Response)(nil), "tendermint.abci.Response") proto.RegisterType((*ResponseException)(nil), "tendermint.abci.ResponseException") @@ -3307,6 +3602,8 @@ func init() { proto.RegisterType((*ResponseApplySnapshotChunk)(nil), "tendermint.abci.ResponseApplySnapshotChunk") proto.RegisterType((*ResponsePrepareProposal)(nil), "tendermint.abci.ResponsePrepareProposal") proto.RegisterType((*ResponseProcessProposal)(nil), "tendermint.abci.ResponseProcessProposal") + proto.RegisterType((*ResponseExtendVote)(nil), "tendermint.abci.ResponseExtendVote") + proto.RegisterType((*ResponseVerifyVoteExtension)(nil), "tendermint.abci.ResponseVerifyVoteExtension") proto.RegisterType((*ResponseFinalizeBlock)(nil), "tendermint.abci.ResponseFinalizeBlock") proto.RegisterType((*CommitInfo)(nil), "tendermint.abci.CommitInfo") proto.RegisterType((*ExtendedCommitInfo)(nil), "tendermint.abci.ExtendedCommitInfo") @@ -3325,192 +3622,204 @@ func init() { func init() { proto.RegisterFile("tendermint/abci/types.proto", fileDescriptor_252557cfdd89a31a) } var fileDescriptor_252557cfdd89a31a = []byte{ - // 2946 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcd, 0x73, 0x23, 0xc5, - 0x15, 0xd7, 0xe8, 0x5b, 0x4f, 0x5f, 0xe3, 0x5e, 0xb3, 0x68, 0xc5, 0xae, 0x6d, 0x86, 0x02, 0x96, - 0x05, 0x6c, 0x62, 0xb2, 0x7c, 0x14, 0x90, 0x2a, 0x59, 0xab, 0x8d, 0xd6, 0x6b, 0x6c, 0x33, 0x96, - 0x97, 0x22, 0x1f, 0x0c, 0x6d, 0xa9, 0x2d, 0x0d, 0x2b, 0x69, 0x86, 0x99, 0x96, 0x91, 0x39, 0xe6, - 0xa3, 0x2a, 0xc5, 0x89, 0x4b, 0xaa, 0xb8, 0x70, 0xc8, 0x21, 0xff, 0x43, 0x4e, 0x39, 0xe5, 0xc0, - 0x21, 0x07, 0x0e, 0x39, 0xe4, 0x90, 0x22, 0x29, 0xb8, 0xa4, 0xf2, 0x0f, 0x70, 0x4b, 0xa5, 0xfa, - 0x63, 0x46, 0x33, 0x92, 0xc6, 0x92, 0x09, 0x45, 0x55, 0xaa, 0x72, 0x9b, 0x7e, 0xf3, 0xde, 0xeb, - 0x9e, 0xd7, 0xaf, 0xdf, 0x7b, 0xbf, 0x37, 0x0d, 0x8f, 0x51, 0x32, 0xec, 0x10, 0x67, 0x60, 0x0e, - 0xe9, 0x16, 0x3e, 0x69, 0x9b, 0x5b, 0xf4, 0xdc, 0x26, 0xee, 0xa6, 0xed, 0x58, 0xd4, 0x42, 0xe5, - 0xc9, 0xcb, 0x4d, 0xf6, 0xb2, 0x7a, 0x23, 0xc0, 0xdd, 0x76, 0xce, 0x6d, 0x6a, 0x6d, 0xd9, 0x8e, - 0x65, 0x9d, 0x0a, 0xfe, 0xea, 0xf5, 0xd9, 0xd7, 0x0f, 0xc9, 0xb9, 0xd4, 0x16, 0x12, 0xe6, 0xb3, - 0x6c, 0xd9, 0xd8, 0xc1, 0x03, 0xef, 0xf5, 0x7a, 0xd7, 0xb2, 0xba, 0x7d, 0xb2, 0xc5, 0x47, 0x27, - 0xa3, 0xd3, 0x2d, 0x6a, 0x0e, 0x88, 0x4b, 0xf1, 0xc0, 0x96, 0x0c, 0xab, 0x5d, 0xab, 0x6b, 0xf1, - 0xc7, 0x2d, 0xf6, 0x24, 0xa8, 0xda, 0x6f, 0xb3, 0x90, 0xd1, 0xc9, 0x07, 0x23, 0xe2, 0x52, 0xb4, - 0x0d, 0x49, 0xd2, 0xee, 0x59, 0x15, 0x65, 0x43, 0xb9, 0x99, 0xdf, 0xbe, 0xbe, 0x39, 0xb5, 0xfc, - 0x4d, 0xc9, 0xd7, 0x68, 0xf7, 0xac, 0x66, 0x4c, 0xe7, 0xbc, 0xe8, 0x36, 0xa4, 0x4e, 0xfb, 0x23, - 0xb7, 0x57, 0x89, 0x73, 0xa1, 0x1b, 0x51, 0x42, 0x77, 0x19, 0x53, 0x33, 0xa6, 0x0b, 0x6e, 0x36, - 0x95, 0x39, 0x3c, 0xb5, 0x2a, 0x89, 0x8b, 0xa7, 0xba, 0x37, 0x3c, 0xe5, 0x53, 0x31, 0x5e, 0xb4, - 0x03, 0x60, 0x0e, 0x4d, 0x6a, 0xb4, 0x7b, 0xd8, 0x1c, 0x56, 0x52, 0x5c, 0xf2, 0xf1, 0x68, 0x49, - 0x93, 0xd6, 0x19, 0x63, 0x33, 0xa6, 0xe7, 0x4c, 0x6f, 0xc0, 0x96, 0xfb, 0xc1, 0x88, 0x38, 0xe7, - 0x95, 0xf4, 0xc5, 0xcb, 0x7d, 0x8b, 0x31, 0xb1, 0xe5, 0x72, 0x6e, 0xf4, 0x3a, 0x64, 0xdb, 0x3d, - 0xd2, 0x7e, 0x68, 0xd0, 0x71, 0x25, 0xcb, 0x25, 0xd7, 0xa3, 0x24, 0xeb, 0x8c, 0xaf, 0x35, 0x6e, - 0xc6, 0xf4, 0x4c, 0x5b, 0x3c, 0xa2, 0x57, 0x20, 0xdd, 0xb6, 0x06, 0x03, 0x93, 0x56, 0xf2, 0x5c, - 0x76, 0x2d, 0x52, 0x96, 0x73, 0x35, 0x63, 0xba, 0xe4, 0x47, 0xfb, 0x50, 0xea, 0x9b, 0x2e, 0x35, - 0xdc, 0x21, 0xb6, 0xdd, 0x9e, 0x45, 0xdd, 0x4a, 0x81, 0x6b, 0x78, 0x32, 0x4a, 0xc3, 0x9e, 0xe9, - 0xd2, 0x23, 0x8f, 0xb9, 0x19, 0xd3, 0x8b, 0xfd, 0x20, 0x81, 0xe9, 0xb3, 0x4e, 0x4f, 0x89, 0xe3, - 0x2b, 0xac, 0x14, 0x2f, 0xd6, 0x77, 0xc0, 0xb8, 0x3d, 0x79, 0xa6, 0xcf, 0x0a, 0x12, 0xd0, 0x4f, - 0xe1, 0x4a, 0xdf, 0xc2, 0x1d, 0x5f, 0x9d, 0xd1, 0xee, 0x8d, 0x86, 0x0f, 0x2b, 0x25, 0xae, 0xf4, - 0x99, 0xc8, 0x45, 0x5a, 0xb8, 0xe3, 0xa9, 0xa8, 0x33, 0x81, 0x66, 0x4c, 0x5f, 0xe9, 0x4f, 0x13, - 0xd1, 0xbb, 0xb0, 0x8a, 0x6d, 0xbb, 0x7f, 0x3e, 0xad, 0xbd, 0xcc, 0xb5, 0xdf, 0x8a, 0xd2, 0x5e, - 0x63, 0x32, 0xd3, 0xea, 0x11, 0x9e, 0xa1, 0xa2, 0x16, 0xa8, 0xb6, 0x43, 0x6c, 0xec, 0x10, 0xc3, - 0x76, 0x2c, 0xdb, 0x72, 0x71, 0xbf, 0xa2, 0x72, 0xdd, 0x4f, 0x47, 0xe9, 0x3e, 0x14, 0xfc, 0x87, - 0x92, 0xbd, 0x19, 0xd3, 0xcb, 0x76, 0x98, 0x24, 0xb4, 0x5a, 0x6d, 0xe2, 0xba, 0x13, 0xad, 0x2b, - 0x8b, 0xb4, 0x72, 0xfe, 0xb0, 0xd6, 0x10, 0x89, 0x6d, 0xdc, 0xa9, 0x39, 0xc4, 0x7d, 0xf3, 0x23, - 0x62, 0x9c, 0xf4, 0xad, 0xf6, 0xc3, 0xca, 0xea, 0xc5, 0x1b, 0x77, 0x57, 0x72, 0xef, 0x30, 0x66, - 0xb6, 0x71, 0xa7, 0x41, 0xc2, 0x4e, 0x06, 0x52, 0x67, 0xb8, 0x3f, 0x22, 0xbb, 0xc9, 0x6c, 0x52, - 0x4d, 0xed, 0x26, 0xb3, 0x19, 0x35, 0xbb, 0x9b, 0xcc, 0xe6, 0x54, 0xd8, 0x4d, 0x66, 0x41, 0xcd, - 0x6b, 0x4f, 0x43, 0x3e, 0x70, 0xdc, 0x51, 0x05, 0x32, 0x03, 0xe2, 0xba, 0xb8, 0x4b, 0x78, 0x74, - 0xc8, 0xe9, 0xde, 0x50, 0x2b, 0x41, 0x21, 0x78, 0xc4, 0xb5, 0x4f, 0x14, 0x5f, 0x92, 0x9d, 0x5e, - 0x26, 0x79, 0x46, 0x1c, 0xd7, 0xb4, 0x86, 0x9e, 0xa4, 0x1c, 0xa2, 0x27, 0xa0, 0xc8, 0x3f, 0xc5, - 0xf0, 0xde, 0xb3, 0x10, 0x92, 0xd4, 0x0b, 0x9c, 0xf8, 0x40, 0x32, 0xad, 0x43, 0xde, 0xde, 0xb6, - 0x7d, 0x96, 0x04, 0x67, 0x01, 0x7b, 0xdb, 0xf6, 0x18, 0x1e, 0x87, 0x02, 0xfb, 0x6e, 0x9f, 0x23, - 0xc9, 0x27, 0xc9, 0x33, 0x9a, 0x64, 0xd1, 0xfe, 0x1c, 0x07, 0x75, 0x3a, 0x2c, 0xa0, 0x57, 0x20, - 0xc9, 0x22, 0xa4, 0x0c, 0x76, 0xd5, 0x4d, 0x11, 0x3e, 0x37, 0xbd, 0xf0, 0xb9, 0xd9, 0xf2, 0xc2, - 0xe7, 0x4e, 0xf6, 0xf3, 0x2f, 0xd7, 0x63, 0x9f, 0xfc, 0x7d, 0x5d, 0xd1, 0xb9, 0x04, 0xba, 0xc6, - 0x82, 0x01, 0x36, 0x87, 0x86, 0xd9, 0xe1, 0x4b, 0xce, 0xb1, 0x93, 0x8e, 0xcd, 0xe1, 0xbd, 0x0e, - 0xda, 0x03, 0xb5, 0x6d, 0x0d, 0x5d, 0x32, 0x74, 0x47, 0xae, 0x21, 0xc2, 0xb3, 0x0c, 0x71, 0xa1, - 0x40, 0x25, 0x92, 0x44, 0xdd, 0xe3, 0x3c, 0xe4, 0x8c, 0x7a, 0xb9, 0x1d, 0x26, 0xa0, 0xbb, 0x00, - 0x67, 0xb8, 0x6f, 0x76, 0x30, 0xb5, 0x1c, 0xb7, 0x92, 0xdc, 0x48, 0xdc, 0xcc, 0x6f, 0x6f, 0xcc, - 0x6c, 0xf8, 0x03, 0x8f, 0xe5, 0xd8, 0xee, 0x60, 0x4a, 0x76, 0x92, 0x6c, 0xb9, 0x7a, 0x40, 0x12, - 0x3d, 0x05, 0x65, 0x6c, 0xdb, 0x86, 0x4b, 0x31, 0x25, 0xc6, 0xc9, 0x39, 0x25, 0x2e, 0x8f, 0x9e, - 0x05, 0xbd, 0x88, 0x6d, 0xfb, 0x88, 0x51, 0x77, 0x18, 0x11, 0x3d, 0x09, 0x25, 0x16, 0x29, 0x4d, - 0xdc, 0x37, 0x7a, 0xc4, 0xec, 0xf6, 0x28, 0x8f, 0x92, 0x09, 0xbd, 0x28, 0xa9, 0x4d, 0x4e, 0xd4, - 0x3a, 0xfe, 0x8e, 0xf3, 0x28, 0x89, 0x10, 0x24, 0x3b, 0x98, 0x62, 0x6e, 0xc9, 0x82, 0xce, 0x9f, - 0x19, 0xcd, 0xc6, 0xb4, 0x27, 0xed, 0xc3, 0x9f, 0xd1, 0x55, 0x48, 0x4b, 0xb5, 0x09, 0xae, 0x56, - 0x8e, 0xd0, 0x2a, 0xa4, 0x6c, 0xc7, 0x3a, 0x23, 0x7c, 0xeb, 0xb2, 0xba, 0x18, 0x68, 0x3a, 0x94, - 0xc2, 0x11, 0x15, 0x95, 0x20, 0x4e, 0xc7, 0x72, 0x96, 0x38, 0x1d, 0xa3, 0x17, 0x20, 0xc9, 0x0c, - 0xc9, 0xe7, 0x28, 0xcd, 0xc9, 0x21, 0x52, 0xae, 0x75, 0x6e, 0x13, 0x9d, 0x73, 0x6a, 0x65, 0x28, - 0x86, 0x22, 0xad, 0x76, 0x15, 0x56, 0xe7, 0x05, 0x4e, 0xad, 0xe7, 0xd3, 0x43, 0x01, 0x10, 0xdd, - 0x86, 0xac, 0x1f, 0x39, 0x85, 0xe3, 0x5c, 0x9b, 0x99, 0xd6, 0x63, 0xd6, 0x7d, 0x56, 0xe6, 0x31, - 0x6c, 0x03, 0x7a, 0x58, 0xe6, 0xc9, 0x82, 0x9e, 0xc1, 0xb6, 0xdd, 0xc4, 0x6e, 0x4f, 0x7b, 0x0f, - 0x2a, 0x51, 0x51, 0x31, 0x60, 0x30, 0x85, 0xbb, 0xbd, 0x67, 0xb0, 0xab, 0x90, 0x3e, 0xb5, 0x9c, - 0x01, 0xa6, 0x5c, 0x59, 0x51, 0x97, 0x23, 0x66, 0x48, 0x11, 0x21, 0x13, 0x9c, 0x2c, 0x06, 0x9a, - 0x01, 0xd7, 0x22, 0x23, 0x23, 0x13, 0x31, 0x87, 0x1d, 0x22, 0xcc, 0x5a, 0xd4, 0xc5, 0x60, 0xa2, - 0x48, 0x2c, 0x56, 0x0c, 0xd8, 0xb4, 0x2e, 0xff, 0x56, 0xae, 0x3f, 0xa7, 0xcb, 0x91, 0xf6, 0x69, - 0x02, 0xae, 0xce, 0x8f, 0x8f, 0x68, 0x03, 0x0a, 0x03, 0x3c, 0x36, 0xe8, 0x58, 0xba, 0x9d, 0xc2, - 0x37, 0x1e, 0x06, 0x78, 0xdc, 0x1a, 0x0b, 0x9f, 0x53, 0x21, 0x41, 0xc7, 0x6e, 0x25, 0xbe, 0x91, - 0xb8, 0x59, 0xd0, 0xd9, 0x23, 0x3a, 0x86, 0x95, 0xbe, 0xd5, 0xc6, 0x7d, 0xa3, 0x8f, 0x5d, 0x6a, - 0xc8, 0xc4, 0x29, 0x0e, 0xd1, 0x13, 0x33, 0xc6, 0x6e, 0x8c, 0x39, 0xa5, 0x23, 0xf6, 0x93, 0x05, - 0x1c, 0xe9, 0xff, 0x65, 0xae, 0x63, 0x0f, 0x7b, 0x5b, 0x8d, 0xee, 0x40, 0x7e, 0x60, 0xba, 0x27, - 0xa4, 0x87, 0xcf, 0x4c, 0xcb, 0x91, 0xa7, 0x69, 0xd6, 0x69, 0xde, 0x9c, 0xf0, 0x48, 0x4d, 0x41, - 0xb1, 0xc0, 0x96, 0xa4, 0x42, 0x3e, 0xec, 0x45, 0x93, 0xf4, 0xa5, 0xa3, 0xc9, 0x0b, 0xb0, 0x3a, - 0x24, 0x63, 0x6a, 0x4c, 0xce, 0xab, 0xf0, 0x93, 0x0c, 0x37, 0x3d, 0x62, 0xef, 0xfc, 0x13, 0xee, - 0x32, 0x97, 0x41, 0xcf, 0xf0, 0x0c, 0x63, 0x5b, 0x2e, 0x71, 0x0c, 0xdc, 0xe9, 0x38, 0xc4, 0x75, - 0x79, 0x51, 0x52, 0xe0, 0x69, 0x83, 0xd3, 0x6b, 0x82, 0xac, 0xfd, 0x26, 0xb8, 0x35, 0xe1, 0x8c, - 0x22, 0x0d, 0xaf, 0x4c, 0x0c, 0x7f, 0x04, 0xab, 0x52, 0xbe, 0x13, 0xb2, 0xbd, 0xa8, 0xec, 0x1e, - 0x9b, 0x3d, 0x5f, 0xd3, 0x36, 0x47, 0x9e, 0x78, 0xb4, 0xd9, 0x13, 0xdf, 0xce, 0xec, 0x08, 0x92, - 0xdc, 0x28, 0x49, 0x11, 0x62, 0xd8, 0xf3, 0xff, 0xda, 0x56, 0xfc, 0x2a, 0xe1, 0xc7, 0x94, 0x50, - 0x6e, 0x9e, 0xb3, 0x11, 0x6f, 0xc1, 0x95, 0x0e, 0x69, 0x9b, 0x9d, 0x6f, 0xbb, 0x0f, 0x2b, 0x52, - 0xfa, 0xff, 0xdb, 0x30, 0xbb, 0x0d, 0xff, 0xcc, 0x42, 0x56, 0x27, 0xae, 0xcd, 0x52, 0x2d, 0xda, - 0x81, 0x1c, 0x19, 0xb7, 0x89, 0x4d, 0xbd, 0xea, 0x24, 0xbf, 0xad, 0xcd, 0x29, 0xa8, 0x04, 0x77, - 0xc3, 0xe3, 0x64, 0x88, 0xc2, 0x17, 0x43, 0x2f, 0x4a, 0xd0, 0x14, 0x8d, 0x7f, 0xa4, 0x78, 0x10, - 0x35, 0xbd, 0xe4, 0xa1, 0xa6, 0x44, 0x24, 0x20, 0x10, 0x52, 0x53, 0xb0, 0xe9, 0x45, 0x09, 0x9b, - 0x92, 0x0b, 0x26, 0x0b, 0xe1, 0xa6, 0x7a, 0x08, 0x37, 0xa5, 0x17, 0x7c, 0x66, 0x04, 0x70, 0x7a, - 0xc9, 0x03, 0x4e, 0x99, 0x05, 0x2b, 0x9e, 0x42, 0x4e, 0x6f, 0x04, 0x90, 0x53, 0x8e, 0x8b, 0x6e, - 0x44, 0x8a, 0xce, 0x81, 0x4e, 0xaf, 0xfa, 0xd0, 0xa9, 0x10, 0x09, 0xbb, 0xa4, 0xf0, 0x34, 0x76, - 0x3a, 0x98, 0xc1, 0x4e, 0x02, 0xeb, 0x3c, 0x15, 0xa9, 0x62, 0x01, 0x78, 0x3a, 0x98, 0x01, 0x4f, - 0xa5, 0x05, 0x0a, 0x17, 0xa0, 0xa7, 0x9f, 0xcd, 0x47, 0x4f, 0xd1, 0xf8, 0x46, 0x2e, 0x73, 0x39, - 0xf8, 0x64, 0x44, 0xc0, 0x27, 0x01, 0x71, 0x9e, 0x8d, 0x54, 0xbf, 0x34, 0x7e, 0x3a, 0x9e, 0x83, - 0x9f, 0x04, 0xd2, 0xb9, 0x19, 0xa9, 0x7c, 0x09, 0x00, 0x75, 0x3c, 0x07, 0x40, 0xa1, 0x85, 0x6a, - 0x17, 0x22, 0xa8, 0x83, 0x19, 0x04, 0xf5, 0xc8, 0x82, 0xdd, 0x5b, 0x1e, 0x42, 0xa5, 0xd4, 0xf4, - 0x6e, 0x32, 0x9b, 0x55, 0x73, 0x02, 0x3c, 0xed, 0x26, 0xb3, 0x79, 0xb5, 0xa0, 0x3d, 0x03, 0x2b, - 0x33, 0xb1, 0x83, 0x95, 0x56, 0xc4, 0x71, 0x2c, 0x47, 0x82, 0x21, 0x31, 0xd0, 0x6e, 0xb2, 0x92, - 0x7a, 0x12, 0x27, 0x2e, 0x80, 0x5b, 0xbc, 0x84, 0x0d, 0xc4, 0x06, 0xed, 0x0f, 0xca, 0x44, 0x96, - 0x03, 0xae, 0x60, 0x39, 0x9e, 0x93, 0xe5, 0x78, 0x00, 0x84, 0xc5, 0xc3, 0x20, 0x6c, 0x1d, 0xf2, - 0xac, 0x34, 0x9d, 0xc2, 0x57, 0xd8, 0xf6, 0xf1, 0xd5, 0x2d, 0x58, 0xe1, 0x49, 0x48, 0x40, 0x35, - 0x19, 0xea, 0x93, 0x3c, 0xd4, 0x97, 0xd9, 0x0b, 0x61, 0x1d, 0x11, 0xf3, 0x9f, 0x87, 0x2b, 0x01, - 0x5e, 0xbf, 0xe4, 0x15, 0x60, 0x43, 0xf5, 0xb9, 0x6b, 0xb2, 0xf6, 0xfd, 0x93, 0x32, 0xb1, 0xd0, - 0x04, 0x98, 0xcd, 0xc3, 0x50, 0xca, 0x77, 0x84, 0xa1, 0xe2, 0xdf, 0x1a, 0x43, 0x05, 0x4b, 0xf8, - 0x44, 0xb8, 0x84, 0xff, 0x46, 0x99, 0xec, 0x89, 0x8f, 0x88, 0xda, 0x56, 0x87, 0xc8, 0xa2, 0x9a, - 0x3f, 0xb3, 0x34, 0xdf, 0xb7, 0xba, 0xb2, 0x74, 0x66, 0x8f, 0x8c, 0xcb, 0x0f, 0xe6, 0x39, 0x19, - 0xab, 0xfd, 0x7a, 0x5c, 0x24, 0x53, 0x59, 0x8f, 0xab, 0x90, 0x78, 0x48, 0x44, 0xcf, 0xaa, 0xa0, - 0xb3, 0x47, 0xc6, 0xc7, 0x9d, 0x4f, 0x26, 0x45, 0x31, 0x40, 0xaf, 0x40, 0x8e, 0xf7, 0x13, 0x0d, - 0xcb, 0x76, 0x65, 0x9f, 0x2a, 0x54, 0x2e, 0x88, 0xa6, 0xe2, 0xe6, 0x21, 0xe3, 0x39, 0xb0, 0x5d, - 0x3d, 0x6b, 0xcb, 0xa7, 0x40, 0x16, 0xcf, 0x85, 0xb2, 0xf8, 0x75, 0xc8, 0xb1, 0xd5, 0xbb, 0x36, - 0x6e, 0x93, 0x0a, 0xf0, 0x85, 0x4e, 0x08, 0xda, 0xdf, 0xe2, 0x50, 0x9e, 0x0a, 0xde, 0x73, 0xbf, - 0xdd, 0x73, 0xc9, 0x78, 0x00, 0x21, 0x2e, 0x67, 0x8f, 0x35, 0x80, 0x2e, 0x76, 0x8d, 0x0f, 0xf1, - 0x90, 0x92, 0x8e, 0x34, 0x4a, 0x80, 0x82, 0xaa, 0x90, 0x65, 0xa3, 0x91, 0x4b, 0x3a, 0x12, 0xac, - 0xfa, 0x63, 0xd4, 0x84, 0x34, 0x39, 0x23, 0x43, 0xea, 0x56, 0x32, 0x7c, 0xdb, 0xaf, 0xce, 0xa2, - 0x07, 0xf6, 0x7a, 0xa7, 0xc2, 0x36, 0xfb, 0x5f, 0x5f, 0xae, 0xab, 0x82, 0xfb, 0x39, 0x6b, 0x60, - 0x52, 0x32, 0xb0, 0xe9, 0xb9, 0x2e, 0xe5, 0xc3, 0x56, 0xc8, 0x4e, 0x59, 0x21, 0x80, 0x8b, 0x72, - 0x41, 0x5c, 0xc4, 0xd6, 0x66, 0x3b, 0xa6, 0xe5, 0x98, 0xf4, 0x9c, 0x9b, 0x2e, 0xa1, 0xfb, 0x63, - 0xf4, 0x04, 0x14, 0x07, 0x64, 0x60, 0x5b, 0x56, 0xdf, 0x10, 0xe1, 0x20, 0xcf, 0x45, 0x0b, 0x92, - 0xd8, 0xe0, 0x51, 0xa1, 0xc6, 0x20, 0x70, 0x30, 0xbb, 0x31, 0x31, 0x87, 0x50, 0x6c, 0x0e, 0x8d, - 0x10, 0x92, 0x2e, 0x08, 0xa2, 0x38, 0x85, 0xbb, 0xc9, 0xac, 0xa2, 0xc6, 0x77, 0x93, 0xd9, 0xb8, - 0x9a, 0xd0, 0x0e, 0xe1, 0x91, 0xb9, 0xd9, 0x0d, 0xbd, 0x0c, 0xb9, 0x49, 0x62, 0x54, 0xb8, 0x7d, - 0x2e, 0x80, 0xb2, 0x13, 0x5e, 0xed, 0x8f, 0xca, 0x44, 0x65, 0x18, 0x1c, 0x37, 0x20, 0xed, 0x10, - 0x77, 0xd4, 0x17, 0x70, 0xb5, 0xb4, 0xfd, 0xfc, 0x72, 0x79, 0x91, 0x51, 0x47, 0x7d, 0xaa, 0x4b, - 0x61, 0xed, 0x5d, 0x48, 0x0b, 0x0a, 0xca, 0x43, 0xe6, 0x78, 0xff, 0xfe, 0xfe, 0xc1, 0xdb, 0xfb, - 0x6a, 0x0c, 0x01, 0xa4, 0x6b, 0xf5, 0x7a, 0xe3, 0xb0, 0xa5, 0x2a, 0x28, 0x07, 0xa9, 0xda, 0xce, - 0x81, 0xde, 0x52, 0xe3, 0x8c, 0xac, 0x37, 0x76, 0x1b, 0xf5, 0x96, 0x9a, 0x40, 0x2b, 0x50, 0x14, - 0xcf, 0xc6, 0xdd, 0x03, 0xfd, 0xcd, 0x5a, 0x4b, 0x4d, 0x06, 0x48, 0x47, 0x8d, 0xfd, 0x3b, 0x0d, - 0x5d, 0x4d, 0x69, 0x3f, 0x60, 0x78, 0x38, 0x22, 0x93, 0x4e, 0x90, 0xaf, 0x12, 0x40, 0xbe, 0xda, - 0xa7, 0x71, 0xa8, 0x46, 0xa7, 0x47, 0xb4, 0x3b, 0xf5, 0xe1, 0xdb, 0x97, 0xc8, 0xad, 0x53, 0x5f, - 0x8f, 0x9e, 0x84, 0x92, 0x43, 0x4e, 0x09, 0x6d, 0xf7, 0x44, 0xba, 0x16, 0x31, 0xab, 0xa8, 0x17, - 0x25, 0x95, 0x0b, 0xb9, 0x82, 0xed, 0x7d, 0xd2, 0xa6, 0x86, 0x70, 0x36, 0x97, 0x97, 0xf4, 0x39, - 0xc6, 0xc6, 0xa8, 0x47, 0x82, 0xa8, 0xbd, 0x77, 0x29, 0x5b, 0xe6, 0x20, 0xa5, 0x37, 0x5a, 0xfa, - 0x3b, 0x6a, 0x02, 0x21, 0x28, 0xf1, 0x47, 0xe3, 0x68, 0xbf, 0x76, 0x78, 0xd4, 0x3c, 0x60, 0xb6, - 0xbc, 0x02, 0x65, 0xcf, 0x96, 0x1e, 0x31, 0xa5, 0x3d, 0x0b, 0x8f, 0x46, 0xe4, 0xf6, 0x59, 0x60, - 0xa3, 0xfd, 0x4e, 0x09, 0x72, 0x4f, 0xe7, 0xe7, 0xb4, 0x4b, 0x31, 0x1d, 0xb9, 0xd2, 0x88, 0x2f, - 0x2f, 0x9b, 0xec, 0x37, 0xbd, 0x87, 0x23, 0x2e, 0xae, 0x4b, 0x35, 0xda, 0x6d, 0x28, 0x85, 0xdf, - 0x44, 0xdb, 0x60, 0xe2, 0x44, 0x71, 0xed, 0x9b, 0xf8, 0xc4, 0xbf, 0xc3, 0x40, 0x6d, 0x12, 0x4f, - 0x94, 0xff, 0x32, 0x9e, 0xbc, 0x0e, 0x40, 0xc7, 0x86, 0xd8, 0x71, 0x2f, 0x29, 0xdd, 0x98, 0xd3, - 0xdb, 0x20, 0xed, 0xd6, 0x58, 0xfa, 0x47, 0x8e, 0xca, 0x27, 0x86, 0xd3, 0x57, 0xfc, 0xc4, 0x64, - 0x8c, 0x78, 0xc2, 0x72, 0x25, 0xa2, 0x5b, 0x36, 0xb3, 0xa9, 0x67, 0x61, 0xb2, 0x8b, 0xde, 0x81, - 0x47, 0xa7, 0xb2, 0xae, 0xaf, 0x3a, 0xb9, 0x6c, 0xf2, 0x7d, 0x24, 0x9c, 0x7c, 0x3d, 0xd5, 0x4f, - 0x41, 0x19, 0x77, 0x1d, 0x42, 0x3a, 0xbc, 0x22, 0xe0, 0x89, 0xc0, 0x6b, 0x3f, 0x72, 0x72, 0xcd, - 0xb6, 0xef, 0x60, 0x8a, 0xb5, 0x77, 0x00, 0x26, 0x50, 0x96, 0x9d, 0x44, 0xc7, 0x1a, 0x0d, 0x3b, - 0xdc, 0x1d, 0x52, 0xba, 0x18, 0xa0, 0xdb, 0x90, 0x3a, 0xb3, 0xd8, 0xa2, 0xe2, 0x11, 0x21, 0xeb, - 0x81, 0x45, 0x49, 0x00, 0x0a, 0x0b, 0x6e, 0xcd, 0x04, 0x34, 0xdb, 0x29, 0x8a, 0x98, 0xe2, 0x8d, - 0xf0, 0x14, 0x8f, 0x47, 0xf6, 0x9c, 0xe6, 0x4f, 0xf5, 0x11, 0xa4, 0xb8, 0x1b, 0xb0, 0x74, 0xc6, - 0xdb, 0x93, 0xb2, 0x0e, 0x63, 0xcf, 0xe8, 0xe7, 0x00, 0x98, 0x52, 0xc7, 0x3c, 0x19, 0x4d, 0x26, - 0x58, 0x9f, 0xef, 0x46, 0x35, 0x8f, 0x6f, 0xe7, 0xba, 0xf4, 0xa7, 0xd5, 0x89, 0x68, 0xc0, 0xa7, - 0x02, 0x0a, 0xb5, 0x7d, 0x28, 0x85, 0x65, 0xbd, 0xca, 0x41, 0xac, 0x21, 0x5c, 0x39, 0x88, 0x42, - 0x50, 0x56, 0x0e, 0x7e, 0xdd, 0x91, 0x10, 0x3d, 0x58, 0x3e, 0xd0, 0xfe, 0xad, 0x40, 0x21, 0xe8, - 0x85, 0xdf, 0x71, 0x72, 0xbf, 0x31, 0x27, 0xb9, 0xe7, 0xba, 0xd8, 0x7d, 0x5b, 0xe4, 0xf6, 0x6b, - 0x33, 0xb9, 0x3d, 0xd3, 0xc5, 0xee, 0xf1, 0xf7, 0x98, 0xda, 0xb5, 0x5f, 0x2b, 0x90, 0xf5, 0x3f, - 0x3e, 0xdc, 0x8e, 0x0d, 0xf5, 0xaf, 0x85, 0xed, 0xe2, 0xc1, 0x1e, 0xaa, 0xe8, 0x56, 0x27, 0xfc, - 0x6e, 0xf5, 0x6b, 0x7e, 0x92, 0x88, 0x02, 0xef, 0x41, 0x4b, 0x4b, 0x9f, 0xf2, 0x72, 0xe2, 0x6b, - 0x90, 0xf3, 0x0f, 0x32, 0x2b, 0xe6, 0xbd, 0x26, 0x87, 0x22, 0x2b, 0x51, 0x31, 0xe4, 0x9d, 0x74, - 0xeb, 0x43, 0xd9, 0xa0, 0x4d, 0xe8, 0x62, 0xa0, 0x75, 0xa0, 0x3c, 0x15, 0x05, 0xd0, 0x6b, 0x90, - 0xb1, 0x47, 0x27, 0x86, 0xe7, 0x1a, 0x53, 0xad, 0x20, 0xaf, 0x4c, 0x1c, 0x9d, 0xf4, 0xcd, 0xf6, - 0x7d, 0x72, 0xee, 0x2d, 0xc6, 0x1e, 0x9d, 0xdc, 0x17, 0x1e, 0x24, 0x66, 0x89, 0x07, 0x67, 0x39, - 0x83, 0xac, 0x77, 0x20, 0xd0, 0x8f, 0x20, 0xe7, 0x07, 0x18, 0xff, 0x07, 0x4b, 0x64, 0x64, 0x92, - 0xea, 0x27, 0x22, 0x0c, 0x73, 0xb8, 0x66, 0x77, 0xe8, 0xf5, 0xbf, 0x04, 0x5c, 0x8b, 0x73, 0xcf, - 0x2c, 0x8b, 0x17, 0x7b, 0x1e, 0x96, 0xd0, 0x7e, 0xaf, 0x80, 0x3a, 0x7d, 0x22, 0xbf, 0xcf, 0x05, - 0xb0, 0x54, 0xcc, 0x4e, 0xbe, 0x41, 0xd8, 0x22, 0x7c, 0x10, 0x55, 0xd0, 0x8b, 0x8c, 0xda, 0xf0, - 0x88, 0xda, 0x2f, 0xe3, 0x90, 0x0f, 0xb4, 0xd7, 0xd0, 0x0f, 0x03, 0xe1, 0xa1, 0x34, 0x27, 0x70, - 0x07, 0x78, 0x27, 0x7f, 0x30, 0xc2, 0x1f, 0x16, 0xbf, 0xfc, 0x87, 0x45, 0xfd, 0x83, 0xf1, 0xba, - 0x75, 0xc9, 0x4b, 0x77, 0xeb, 0x9e, 0x03, 0x44, 0x2d, 0x8a, 0xfb, 0xc6, 0x99, 0x45, 0xcd, 0x61, - 0xd7, 0x10, 0xae, 0x21, 0x0e, 0xb3, 0xca, 0xdf, 0x3c, 0xe0, 0x2f, 0x0e, 0xb9, 0x97, 0xfc, 0x42, - 0x81, 0xac, 0x5f, 0x30, 0x5e, 0xf6, 0xff, 0xc6, 0x55, 0x48, 0xcb, 0x9a, 0x48, 0xfc, 0xe0, 0x90, - 0xa3, 0xb9, 0x6d, 0xc9, 0x2a, 0x64, 0x07, 0x84, 0xe2, 0x40, 0xb6, 0xf1, 0xc7, 0xb7, 0x5e, 0x85, - 0x7c, 0xe0, 0xdf, 0x10, 0x0b, 0x56, 0xfb, 0x8d, 0xb7, 0xd5, 0x58, 0x35, 0xf3, 0xf1, 0x67, 0x1b, - 0x89, 0x7d, 0xf2, 0x21, 0x3b, 0x61, 0x7a, 0xa3, 0xde, 0x6c, 0xd4, 0xef, 0xab, 0x4a, 0x35, 0xff, - 0xf1, 0x67, 0x1b, 0x19, 0x9d, 0xf0, 0x8e, 0xd4, 0xad, 0xfb, 0x50, 0x9e, 0xda, 0x98, 0x70, 0x55, - 0x81, 0xa0, 0x74, 0xe7, 0xf8, 0x70, 0xef, 0x5e, 0xbd, 0xd6, 0x6a, 0x18, 0x0f, 0x0e, 0x5a, 0x0d, - 0x55, 0x41, 0x8f, 0xc2, 0x95, 0xbd, 0x7b, 0x3f, 0x6e, 0xb6, 0x8c, 0xfa, 0xde, 0xbd, 0xc6, 0x7e, - 0xcb, 0xa8, 0xb5, 0x5a, 0xb5, 0xfa, 0x7d, 0x35, 0xbe, 0xfd, 0x97, 0x1c, 0x24, 0x6b, 0x3b, 0xf5, - 0x7b, 0xa8, 0x0e, 0x49, 0x0e, 0xfb, 0x2f, 0xbc, 0x72, 0x51, 0xbd, 0xb8, 0xb7, 0x88, 0xee, 0x42, - 0x8a, 0x77, 0x04, 0xd0, 0xc5, 0x77, 0x30, 0xaa, 0x0b, 0x9a, 0x8d, 0x6c, 0x31, 0xfc, 0x0c, 0x5d, - 0x78, 0x29, 0xa3, 0x7a, 0x71, 0xef, 0x11, 0xed, 0x41, 0xc6, 0x03, 0x84, 0x8b, 0x6e, 0x4a, 0x54, - 0x17, 0x36, 0x04, 0xd9, 0xa7, 0x09, 0x60, 0x7d, 0xf1, 0x7d, 0x8d, 0xea, 0x82, 0xae, 0x24, 0xba, - 0x07, 0x69, 0x09, 0xa4, 0x16, 0x5c, 0xc1, 0xa8, 0x2e, 0xea, 0x33, 0x22, 0x1d, 0x72, 0x93, 0x96, - 0xc5, 0xe2, 0x5b, 0x28, 0xd5, 0x25, 0x1a, 0xae, 0xe8, 0x5d, 0x28, 0x86, 0x41, 0xda, 0x72, 0xd7, - 0x3c, 0xaa, 0x4b, 0x76, 0x34, 0x99, 0xfe, 0x30, 0x62, 0x5b, 0xee, 0xda, 0x47, 0x75, 0xc9, 0x06, - 0x27, 0x7a, 0x1f, 0x56, 0x66, 0x11, 0xd5, 0xf2, 0xb7, 0x40, 0xaa, 0x97, 0x68, 0x79, 0xa2, 0x01, - 0xa0, 0x39, 0x48, 0xec, 0x12, 0x97, 0x42, 0xaa, 0x97, 0xe9, 0x80, 0xa2, 0x0e, 0x94, 0xa7, 0xe1, - 0xcd, 0xb2, 0x97, 0x44, 0xaa, 0x4b, 0x77, 0x43, 0xc5, 0x2c, 0x61, 0x58, 0xb4, 0xec, 0xa5, 0x91, - 0xea, 0xd2, 0xcd, 0x51, 0xe6, 0x06, 0x61, 0x60, 0xb3, 0xdc, 0x25, 0x92, 0xea, 0x92, 0x9d, 0xd2, - 0x9d, 0xc6, 0xe7, 0x5f, 0xad, 0x29, 0x5f, 0x7c, 0xb5, 0xa6, 0xfc, 0xe3, 0xab, 0x35, 0xe5, 0x93, - 0xaf, 0xd7, 0x62, 0x5f, 0x7c, 0xbd, 0x16, 0xfb, 0xeb, 0xd7, 0x6b, 0xb1, 0x9f, 0x3c, 0xdb, 0x35, - 0x69, 0x6f, 0x74, 0xb2, 0xd9, 0xb6, 0x06, 0x5b, 0xc1, 0xdb, 0x6c, 0xf3, 0xee, 0xd0, 0x9d, 0xa4, - 0x79, 0xf2, 0x79, 0xf1, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x45, 0x53, 0xe3, 0x57, 0x63, 0x27, - 0x00, 0x00, + // 3137 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0x4b, 0x73, 0xe3, 0xc6, + 0xf1, 0x27, 0xf8, 0x66, 0xf3, 0x05, 0x8d, 0xb4, 0x6b, 0x2e, 0xbd, 0x2b, 0xc9, 0x70, 0xd9, 0x5e, + 0xef, 0xda, 0x92, 0xff, 0xda, 0xff, 0xfa, 0x51, 0x6b, 0x27, 0x45, 0x71, 0xb9, 0xa1, 0xb4, 0xb2, + 0x24, 0x43, 0xd4, 0xba, 0x9c, 0x87, 0x61, 0x88, 0x1c, 0x91, 0xf0, 0x92, 0x04, 0x0c, 0x0c, 0x65, + 0xca, 0xc7, 0x3c, 0xaa, 0x52, 0x3e, 0xf9, 0x90, 0x83, 0x0f, 0xf1, 0x21, 0x87, 0x7c, 0x87, 0x9c, + 0x92, 0x4b, 0x0e, 0x3e, 0xe4, 0xe0, 0x63, 0x0e, 0x29, 0x27, 0x65, 0xdf, 0xf2, 0x05, 0x7c, 0x4b, + 0xa5, 0xe6, 0x01, 0x10, 0x20, 0x01, 0x91, 0x72, 0x5c, 0xae, 0x4a, 0x55, 0x6e, 0x98, 0x46, 0x77, + 0xcf, 0x4c, 0xa3, 0xa7, 0xbb, 0x7f, 0x8d, 0x81, 0x27, 0x09, 0x1e, 0x76, 0xb0, 0x3d, 0x30, 0x86, + 0x64, 0x53, 0x3f, 0x69, 0x1b, 0x9b, 0xe4, 0xdc, 0xc2, 0xce, 0x86, 0x65, 0x9b, 0xc4, 0x44, 0xe5, + 0xc9, 0xcb, 0x0d, 0xfa, 0xb2, 0x7a, 0xc3, 0xc7, 0xdd, 0xb6, 0xcf, 0x2d, 0x62, 0x6e, 0x5a, 0xb6, + 0x69, 0x9e, 0x72, 0xfe, 0xea, 0xf5, 0xd9, 0xd7, 0x8f, 0xf1, 0xb9, 0xd0, 0x16, 0x10, 0x66, 0xb3, + 0x6c, 0x5a, 0xba, 0xad, 0x0f, 0xdc, 0xd7, 0x6b, 0x5d, 0xd3, 0xec, 0xf6, 0xf1, 0x26, 0x1b, 0x9d, + 0x8c, 0x4e, 0x37, 0x89, 0x31, 0xc0, 0x0e, 0xd1, 0x07, 0x96, 0x60, 0x58, 0xe9, 0x9a, 0x5d, 0x93, + 0x3d, 0x6e, 0xd2, 0x27, 0x4e, 0x55, 0xfe, 0x94, 0x83, 0x8c, 0x8a, 0x3f, 0x18, 0x61, 0x87, 0xa0, + 0x2d, 0x48, 0xe2, 0x76, 0xcf, 0xac, 0x48, 0xeb, 0xd2, 0xcd, 0xfc, 0xd6, 0xf5, 0x8d, 0xa9, 0xe5, + 0x6f, 0x08, 0xbe, 0x46, 0xbb, 0x67, 0x36, 0x63, 0x2a, 0xe3, 0x45, 0x77, 0x21, 0x75, 0xda, 0x1f, + 0x39, 0xbd, 0x4a, 0x9c, 0x09, 0xdd, 0x88, 0x12, 0x7a, 0x40, 0x99, 0x9a, 0x31, 0x95, 0x73, 0xd3, + 0xa9, 0x8c, 0xe1, 0xa9, 0x59, 0x49, 0x5c, 0x3c, 0xd5, 0xce, 0xf0, 0x94, 0x4d, 0x45, 0x79, 0xd1, + 0x36, 0x80, 0x31, 0x34, 0x88, 0xd6, 0xee, 0xe9, 0xc6, 0xb0, 0x92, 0x62, 0x92, 0x4f, 0x45, 0x4b, + 0x1a, 0xa4, 0x4e, 0x19, 0x9b, 0x31, 0x35, 0x67, 0xb8, 0x03, 0xba, 0xdc, 0x0f, 0x46, 0xd8, 0x3e, + 0xaf, 0xa4, 0x2f, 0x5e, 0xee, 0x5b, 0x94, 0x89, 0x2e, 0x97, 0x71, 0xa3, 0xd7, 0x21, 0xdb, 0xee, + 0xe1, 0xf6, 0x63, 0x8d, 0x8c, 0x2b, 0x59, 0x26, 0xb9, 0x16, 0x25, 0x59, 0xa7, 0x7c, 0xad, 0x71, + 0x33, 0xa6, 0x66, 0xda, 0xfc, 0x11, 0xbd, 0x0a, 0xe9, 0xb6, 0x39, 0x18, 0x18, 0xa4, 0x92, 0x67, + 0xb2, 0xab, 0x91, 0xb2, 0x8c, 0xab, 0x19, 0x53, 0x05, 0x3f, 0xda, 0x87, 0x52, 0xdf, 0x70, 0x88, + 0xe6, 0x0c, 0x75, 0xcb, 0xe9, 0x99, 0xc4, 0xa9, 0x14, 0x98, 0x86, 0x67, 0xa2, 0x34, 0xec, 0x19, + 0x0e, 0x39, 0x72, 0x99, 0x9b, 0x31, 0xb5, 0xd8, 0xf7, 0x13, 0xa8, 0x3e, 0xf3, 0xf4, 0x14, 0xdb, + 0x9e, 0xc2, 0x4a, 0xf1, 0x62, 0x7d, 0x07, 0x94, 0xdb, 0x95, 0xa7, 0xfa, 0x4c, 0x3f, 0x01, 0xfd, + 0x04, 0x96, 0xfb, 0xa6, 0xde, 0xf1, 0xd4, 0x69, 0xed, 0xde, 0x68, 0xf8, 0xb8, 0x52, 0x62, 0x4a, + 0x9f, 0x8f, 0x5c, 0xa4, 0xa9, 0x77, 0x5c, 0x15, 0x75, 0x2a, 0xd0, 0x8c, 0xa9, 0x4b, 0xfd, 0x69, + 0x22, 0x7a, 0x17, 0x56, 0x74, 0xcb, 0xea, 0x9f, 0x4f, 0x6b, 0x2f, 0x33, 0xed, 0xb7, 0xa2, 0xb4, + 0xd7, 0xa8, 0xcc, 0xb4, 0x7a, 0xa4, 0xcf, 0x50, 0x51, 0x0b, 0x64, 0xcb, 0xc6, 0x96, 0x6e, 0x63, + 0xcd, 0xb2, 0x4d, 0xcb, 0x74, 0xf4, 0x7e, 0x45, 0x66, 0xba, 0x9f, 0x8b, 0xd2, 0x7d, 0xc8, 0xf9, + 0x0f, 0x05, 0x7b, 0x33, 0xa6, 0x96, 0xad, 0x20, 0x89, 0x6b, 0x35, 0xdb, 0xd8, 0x71, 0x26, 0x5a, + 0x97, 0xe6, 0x69, 0x65, 0xfc, 0x41, 0xad, 0x01, 0x12, 0x6a, 0x40, 0x1e, 0x8f, 0xa9, 0xb8, 0x76, + 0x66, 0x12, 0x5c, 0x41, 0x4c, 0xa1, 0x12, 0x79, 0x42, 0x19, 0xeb, 0x23, 0x93, 0xe0, 0x66, 0x4c, + 0x05, 0xec, 0x8d, 0x90, 0x0e, 0x57, 0xce, 0xb0, 0x6d, 0x9c, 0x9e, 0x33, 0x35, 0x1a, 0x7b, 0xe3, + 0x18, 0xe6, 0xb0, 0xb2, 0xcc, 0x14, 0xde, 0x8e, 0x52, 0xf8, 0x88, 0x09, 0x51, 0x15, 0x0d, 0x57, + 0xa4, 0x19, 0x53, 0x97, 0xcf, 0x66, 0xc9, 0xd4, 0xc5, 0x4e, 0x8d, 0xa1, 0xde, 0x37, 0x3e, 0xc2, + 0xda, 0x49, 0xdf, 0x6c, 0x3f, 0xae, 0xac, 0x5c, 0xec, 0x62, 0x0f, 0x04, 0xf7, 0x36, 0x65, 0xa6, + 0x2e, 0x76, 0xea, 0x27, 0x6c, 0x67, 0x20, 0x75, 0xa6, 0xf7, 0x47, 0x78, 0x37, 0x99, 0x4d, 0xca, + 0xa9, 0xdd, 0x64, 0x36, 0x23, 0x67, 0x77, 0x93, 0xd9, 0x9c, 0x0c, 0xbb, 0xc9, 0x2c, 0xc8, 0x79, + 0xe5, 0x39, 0xc8, 0xfb, 0x02, 0x13, 0xaa, 0x40, 0x66, 0x80, 0x1d, 0x47, 0xef, 0x62, 0x16, 0xc7, + 0x72, 0xaa, 0x3b, 0x54, 0x4a, 0x50, 0xf0, 0x07, 0x23, 0xe5, 0x13, 0xc9, 0x93, 0xa4, 0x71, 0x86, + 0x4a, 0x9e, 0x61, 0x9b, 0x99, 0x43, 0x48, 0x8a, 0x21, 0x7a, 0x1a, 0x8a, 0x6c, 0x2b, 0x9a, 0xfb, + 0x9e, 0x06, 0xbb, 0xa4, 0x5a, 0x60, 0xc4, 0x47, 0x82, 0x69, 0x0d, 0xf2, 0xd6, 0x96, 0xe5, 0xb1, + 0x24, 0x18, 0x0b, 0x58, 0x5b, 0x96, 0xcb, 0xf0, 0x14, 0x14, 0xe8, 0xbe, 0x3d, 0x8e, 0x24, 0x9b, + 0x24, 0x4f, 0x69, 0x82, 0x45, 0xf9, 0x4b, 0x1c, 0xe4, 0xe9, 0x00, 0x86, 0x5e, 0x85, 0x24, 0x8d, + 0xe5, 0x22, 0x2c, 0x57, 0x37, 0x78, 0xa0, 0xdf, 0x70, 0x03, 0xfd, 0x46, 0xcb, 0x0d, 0xf4, 0xdb, + 0xd9, 0xcf, 0xbf, 0x5c, 0x8b, 0x7d, 0xf2, 0xf7, 0x35, 0x49, 0x65, 0x12, 0xe8, 0x1a, 0x0d, 0x5b, + 0xba, 0x31, 0xd4, 0x8c, 0x0e, 0x5b, 0x72, 0x8e, 0xc6, 0x24, 0xdd, 0x18, 0xee, 0x74, 0xd0, 0x1e, + 0xc8, 0x6d, 0x73, 0xe8, 0xe0, 0xa1, 0x33, 0x72, 0x34, 0x9e, 0x48, 0x44, 0x30, 0x0e, 0x84, 0x54, + 0x9e, 0xce, 0xea, 0x2e, 0xe7, 0x21, 0x63, 0x54, 0xcb, 0xed, 0x20, 0x01, 0x3d, 0x00, 0x38, 0xd3, + 0xfb, 0x46, 0x47, 0x27, 0xa6, 0xed, 0x54, 0x92, 0xeb, 0x89, 0x9b, 0xf9, 0xad, 0xf5, 0x99, 0x0f, + 0xfe, 0xc8, 0x65, 0x39, 0xb6, 0x3a, 0x3a, 0xc1, 0xdb, 0x49, 0xba, 0x5c, 0xd5, 0x27, 0x89, 0x9e, + 0x85, 0xb2, 0x6e, 0x59, 0x9a, 0x43, 0x74, 0x82, 0xb5, 0x93, 0x73, 0x82, 0x1d, 0x16, 0xe7, 0x0b, + 0x6a, 0x51, 0xb7, 0xac, 0x23, 0x4a, 0xdd, 0xa6, 0x44, 0xf4, 0x0c, 0x94, 0x68, 0x4c, 0x37, 0xf4, + 0xbe, 0xd6, 0xc3, 0x46, 0xb7, 0x47, 0x58, 0x3c, 0x4f, 0xa8, 0x45, 0x41, 0x6d, 0x32, 0xa2, 0xd2, + 0xf1, 0xbe, 0x38, 0x8b, 0xe7, 0x08, 0x41, 0xb2, 0xa3, 0x13, 0x9d, 0x59, 0xb2, 0xa0, 0xb2, 0x67, + 0x4a, 0xb3, 0x74, 0xd2, 0x13, 0xf6, 0x61, 0xcf, 0xe8, 0x2a, 0xa4, 0x85, 0xda, 0x04, 0x53, 0x2b, + 0x46, 0x68, 0x05, 0x52, 0x96, 0x6d, 0x9e, 0x61, 0xf6, 0xe9, 0xb2, 0x2a, 0x1f, 0x28, 0x2a, 0x94, + 0x82, 0xb1, 0x1f, 0x95, 0x20, 0x4e, 0xc6, 0x62, 0x96, 0x38, 0x19, 0xa3, 0x97, 0x20, 0x49, 0x0d, + 0xc9, 0xe6, 0x28, 0x85, 0x64, 0x3b, 0x21, 0xd7, 0x3a, 0xb7, 0xb0, 0xca, 0x38, 0x95, 0x32, 0x14, + 0x03, 0x39, 0x41, 0xb9, 0x0a, 0x2b, 0x61, 0x21, 0x5e, 0xe9, 0x79, 0xf4, 0x40, 0xa8, 0x46, 0x77, + 0x21, 0xeb, 0xc5, 0x78, 0xee, 0x38, 0xd7, 0x66, 0xa6, 0x75, 0x99, 0x55, 0x8f, 0x95, 0x7a, 0x0c, + 0xfd, 0x00, 0x3d, 0x5d, 0x64, 0xf4, 0x82, 0x9a, 0xd1, 0x2d, 0xab, 0xa9, 0x3b, 0x3d, 0xe5, 0x3d, + 0xa8, 0x44, 0xc5, 0x6f, 0x9f, 0xc1, 0x24, 0xe6, 0xf6, 0xae, 0xc1, 0xae, 0x42, 0xfa, 0xd4, 0xb4, + 0x07, 0x3a, 0x61, 0xca, 0x8a, 0xaa, 0x18, 0x51, 0x43, 0xf2, 0x58, 0x9e, 0x60, 0x64, 0x3e, 0x50, + 0x34, 0xb8, 0x16, 0x19, 0xc3, 0xa9, 0x88, 0x31, 0xec, 0x60, 0x6e, 0xd6, 0xa2, 0xca, 0x07, 0x13, + 0x45, 0x7c, 0xb1, 0x7c, 0x40, 0xa7, 0x75, 0xd8, 0x5e, 0x99, 0xfe, 0x9c, 0x2a, 0x46, 0xca, 0xa7, + 0x09, 0xb8, 0x1a, 0x1e, 0xc9, 0xd1, 0x3a, 0x14, 0x06, 0xfa, 0x58, 0x23, 0x63, 0xe1, 0x76, 0x12, + 0xfb, 0xf0, 0x30, 0xd0, 0xc7, 0xad, 0x31, 0xf7, 0x39, 0x19, 0x12, 0x64, 0xec, 0x54, 0xe2, 0xeb, + 0x89, 0x9b, 0x05, 0x95, 0x3e, 0xa2, 0x63, 0x58, 0xea, 0x9b, 0x6d, 0xbd, 0xaf, 0xf5, 0x75, 0x87, + 0x68, 0x22, 0xc5, 0xf3, 0x43, 0xf4, 0xf4, 0x8c, 0xb1, 0x79, 0x4c, 0xc6, 0x1d, 0xfe, 0x3d, 0x69, + 0xc0, 0x11, 0xfe, 0x5f, 0x66, 0x3a, 0xf6, 0x74, 0xf7, 0x53, 0xa3, 0xfb, 0x90, 0x1f, 0x18, 0xce, + 0x09, 0xee, 0xe9, 0x67, 0x86, 0x69, 0x8b, 0xd3, 0x34, 0xeb, 0x34, 0x6f, 0x4e, 0x78, 0x84, 0x26, + 0xbf, 0x98, 0xef, 0x93, 0xa4, 0x02, 0x3e, 0xec, 0x46, 0x93, 0xf4, 0xa5, 0xa3, 0xc9, 0x4b, 0xb0, + 0x32, 0xc4, 0x63, 0xa2, 0x4d, 0xce, 0x2b, 0xf7, 0x93, 0x0c, 0x33, 0x3d, 0xa2, 0xef, 0xbc, 0x13, + 0xee, 0x50, 0x97, 0x41, 0xcf, 0xb3, 0x5c, 0x68, 0x99, 0x0e, 0xb6, 0x35, 0xbd, 0xd3, 0xb1, 0xb1, + 0xe3, 0xb0, 0xf2, 0xa9, 0xc0, 0x12, 0x1c, 0xa3, 0xd7, 0x38, 0x59, 0xf9, 0xb5, 0xff, 0xd3, 0x04, + 0x73, 0x9f, 0x30, 0xbc, 0x34, 0x31, 0xfc, 0x11, 0xac, 0x08, 0xf9, 0x4e, 0xc0, 0xf6, 0xbc, 0x06, + 0x7d, 0x72, 0xf6, 0x7c, 0x4d, 0xdb, 0x1c, 0xb9, 0xe2, 0xd1, 0x66, 0x4f, 0x7c, 0x3b, 0xb3, 0x23, + 0x48, 0x32, 0xa3, 0x24, 0x79, 0x88, 0xa1, 0xcf, 0xff, 0x6d, 0x9f, 0xe2, 0x87, 0xb0, 0x34, 0x53, + 0x47, 0x78, 0xfb, 0x92, 0x42, 0xf7, 0x15, 0xf7, 0xef, 0x4b, 0xf9, 0xad, 0x04, 0xd5, 0xe8, 0xc2, + 0x21, 0x54, 0xd5, 0x6d, 0x58, 0xf2, 0xf6, 0xe2, 0xad, 0x8f, 0x9f, 0x69, 0xd9, 0x7b, 0x21, 0x16, + 0x18, 0x19, 0x9e, 0x9f, 0x81, 0xd2, 0x54, 0x59, 0xc3, 0xbf, 0x42, 0xf1, 0xcc, 0x3f, 0xbf, 0xf2, + 0xcb, 0x84, 0x17, 0x33, 0x03, 0xb5, 0x47, 0x88, 0xa3, 0xbd, 0x05, 0xcb, 0x1d, 0xdc, 0x36, 0x3a, + 0xdf, 0xd6, 0xcf, 0x96, 0x84, 0xf4, 0xff, 0xdc, 0x6c, 0xd6, 0xcd, 0x7e, 0x03, 0x90, 0x55, 0xb1, + 0x63, 0xd1, 0x52, 0x02, 0x6d, 0x43, 0x0e, 0x8f, 0xdb, 0xd8, 0x22, 0x6e, 0xf5, 0x15, 0x5e, 0xdd, + 0x72, 0xee, 0x86, 0xcb, 0x49, 0xb1, 0x9d, 0x27, 0x86, 0xee, 0x08, 0xf8, 0x1a, 0x8d, 0x44, 0x85, + 0xb8, 0x1f, 0xbf, 0xbe, 0xec, 0xe2, 0xd7, 0x44, 0x24, 0x34, 0xe3, 0x52, 0x53, 0x00, 0xf6, 0x8e, + 0x00, 0xb0, 0xc9, 0x39, 0x93, 0x05, 0x10, 0x6c, 0x3d, 0x80, 0x60, 0xd3, 0x73, 0xb6, 0x19, 0x01, + 0x61, 0x5f, 0x76, 0x21, 0x6c, 0x66, 0xce, 0x8a, 0xa7, 0x30, 0xec, 0x1b, 0x3e, 0x0c, 0x9b, 0x63, + 0xa2, 0xeb, 0x91, 0xa2, 0x21, 0x20, 0xf6, 0x35, 0x0f, 0xc4, 0x16, 0x22, 0x01, 0xb0, 0x10, 0x9e, + 0x46, 0xb1, 0x07, 0x33, 0x28, 0x96, 0xa3, 0xce, 0x67, 0x23, 0x55, 0xcc, 0x81, 0xb1, 0x07, 0x33, + 0x30, 0xb6, 0x34, 0x47, 0xe1, 0x1c, 0x1c, 0xfb, 0xd3, 0x70, 0x1c, 0x1b, 0x8d, 0x34, 0xc5, 0x32, + 0x17, 0x03, 0xb2, 0x5a, 0x04, 0x90, 0x95, 0x23, 0x41, 0x17, 0x57, 0xbf, 0x30, 0x92, 0x3d, 0x0e, + 0x41, 0xb2, 0x1c, 0x73, 0xde, 0x8c, 0x54, 0xbe, 0x00, 0x94, 0x3d, 0x0e, 0x81, 0xb2, 0x68, 0xae, + 0xda, 0xb9, 0x58, 0xf6, 0x41, 0x10, 0xcb, 0x2e, 0x47, 0x14, 0x4c, 0x93, 0xd3, 0x1e, 0x01, 0x66, + 0x4f, 0xa2, 0xc0, 0x2c, 0x07, 0x9c, 0x2f, 0x44, 0x6a, 0xbc, 0x04, 0x9a, 0x3d, 0x98, 0x41, 0xb3, + 0x57, 0xe6, 0x78, 0xda, 0xe2, 0x70, 0x36, 0x25, 0xa7, 0x77, 0x93, 0xd9, 0xac, 0x9c, 0xe3, 0x40, + 0x76, 0x37, 0x99, 0xcd, 0xcb, 0x05, 0xe5, 0x79, 0x9a, 0x7d, 0xa7, 0xe2, 0x1c, 0x2d, 0x73, 0xb1, + 0x6d, 0x9b, 0xb6, 0x00, 0xa6, 0x7c, 0xa0, 0xdc, 0xa4, 0xf0, 0x66, 0x12, 0xd3, 0x2e, 0x80, 0xbe, + 0x0c, 0x4e, 0xf8, 0xe2, 0x98, 0xf2, 0x07, 0x69, 0x22, 0xcb, 0xc0, 0xaf, 0x1f, 0x1a, 0xe5, 0x04, + 0x34, 0xf2, 0x01, 0xe2, 0x78, 0x10, 0x10, 0xaf, 0x41, 0x9e, 0xc2, 0x84, 0x29, 0xac, 0xab, 0x5b, + 0x1e, 0xd6, 0xbd, 0x05, 0x4b, 0x2c, 0x61, 0x72, 0xd8, 0x2c, 0xd2, 0x52, 0x92, 0xa5, 0xa5, 0x32, + 0x7d, 0xc1, 0xad, 0xc3, 0xf3, 0xd3, 0x8b, 0xb0, 0xec, 0xe3, 0xf5, 0xe0, 0x07, 0x07, 0x7e, 0xb2, + 0xc7, 0x5d, 0x13, 0x38, 0xe4, 0xcf, 0xd2, 0xc4, 0x42, 0x13, 0x90, 0x1c, 0x86, 0x67, 0xa5, 0xef, + 0x08, 0xcf, 0xc6, 0xbf, 0x35, 0x9e, 0xf5, 0xc3, 0xa9, 0x44, 0x10, 0x4e, 0x7d, 0x23, 0x4d, 0xbe, + 0x89, 0x87, 0x4e, 0xdb, 0x66, 0x07, 0x0b, 0x80, 0xc3, 0x9e, 0x69, 0x49, 0xd2, 0x37, 0xbb, 0x02, + 0xc6, 0xd0, 0x47, 0xca, 0xe5, 0x25, 0x9e, 0x9c, 0xc8, 0x2b, 0x1e, 0x36, 0xe2, 0x89, 0x5f, 0x60, + 0x23, 0x19, 0x12, 0x8f, 0x31, 0xef, 0x74, 0x16, 0x54, 0xfa, 0x48, 0xf9, 0x98, 0xf3, 0x89, 0x04, + 0xce, 0x07, 0xe8, 0x55, 0xc8, 0xb1, 0x2e, 0xb4, 0x66, 0x5a, 0x8e, 0xe8, 0x6e, 0x06, 0x4a, 0x1b, + 0xde, 0x8a, 0xde, 0x38, 0xa4, 0x3c, 0x07, 0x96, 0xa3, 0x66, 0x2d, 0xf1, 0xe4, 0xab, 0x38, 0x72, + 0x81, 0x8a, 0xe3, 0x3a, 0xe4, 0xe8, 0xea, 0x1d, 0x4b, 0x6f, 0xe3, 0x0a, 0xb0, 0x85, 0x4e, 0x08, + 0xca, 0xdf, 0xe2, 0x50, 0x9e, 0x4a, 0x34, 0xa1, 0x7b, 0x77, 0x5d, 0x32, 0xee, 0x43, 0xeb, 0x8b, + 0xd9, 0x63, 0x15, 0xa0, 0xab, 0x3b, 0xda, 0x87, 0xfa, 0x90, 0xe0, 0x8e, 0x30, 0x8a, 0x8f, 0x82, + 0xaa, 0x90, 0xa5, 0xa3, 0x91, 0x83, 0x3b, 0xa2, 0x71, 0xe0, 0x8d, 0x51, 0x13, 0xd2, 0xf8, 0x0c, + 0x0f, 0x89, 0x53, 0xc9, 0xb0, 0xcf, 0x7e, 0x75, 0x16, 0xc9, 0xd1, 0xd7, 0xdb, 0x15, 0xfa, 0xb1, + 0xff, 0xf9, 0xe5, 0x9a, 0xcc, 0xb9, 0x5f, 0x30, 0x07, 0x06, 0xc1, 0x03, 0x8b, 0x9c, 0xab, 0x42, + 0x3e, 0x68, 0x85, 0xec, 0x94, 0x15, 0x7c, 0x18, 0x35, 0xe7, 0xc7, 0xa8, 0x74, 0x6d, 0x96, 0x6d, + 0x98, 0xb6, 0x41, 0xce, 0x99, 0xe9, 0x12, 0xaa, 0x37, 0x46, 0x4f, 0x43, 0x71, 0x80, 0x07, 0x96, + 0x69, 0xf6, 0x35, 0x1e, 0x0e, 0xf2, 0x4c, 0xb4, 0x20, 0x88, 0x0d, 0x16, 0x15, 0x6a, 0x50, 0x0a, + 0x66, 0x62, 0x2a, 0x66, 0x63, 0xa2, 0x1b, 0x43, 0x2d, 0x50, 0x36, 0x17, 0x38, 0x91, 0x9f, 0xc2, + 0xdd, 0x64, 0x56, 0x92, 0xe3, 0xbb, 0xc9, 0x6c, 0x5c, 0x4e, 0x28, 0x87, 0x70, 0x25, 0x34, 0x13, + 0xa3, 0x57, 0x20, 0x37, 0x49, 0xe2, 0x12, 0xb3, 0xcf, 0x05, 0x6d, 0x85, 0x09, 0xaf, 0xf2, 0x47, + 0x69, 0xa2, 0x32, 0xd8, 0xa8, 0x68, 0x40, 0xda, 0xc6, 0xce, 0xa8, 0xcf, 0x5b, 0x07, 0xa5, 0xad, + 0x17, 0x17, 0xcb, 0xe1, 0x94, 0x3a, 0xea, 0x13, 0x55, 0x08, 0x2b, 0xef, 0x42, 0x9a, 0x53, 0x50, + 0x1e, 0x32, 0xc7, 0xfb, 0x0f, 0xf7, 0x0f, 0xde, 0xde, 0x97, 0x63, 0x08, 0x20, 0x5d, 0xab, 0xd7, + 0x1b, 0x87, 0x2d, 0x59, 0x42, 0x39, 0x48, 0xd5, 0xb6, 0x0f, 0xd4, 0x96, 0x1c, 0xa7, 0x64, 0xb5, + 0xb1, 0xdb, 0xa8, 0xb7, 0xe4, 0x04, 0x5a, 0x82, 0x22, 0x7f, 0xd6, 0x1e, 0x1c, 0xa8, 0x6f, 0xd6, + 0x5a, 0x72, 0xd2, 0x47, 0x3a, 0x6a, 0xec, 0xdf, 0x6f, 0xa8, 0x72, 0x4a, 0xf9, 0x3f, 0xb8, 0x16, + 0x99, 0xf5, 0x27, 0x5d, 0x08, 0xc9, 0xd7, 0x85, 0x50, 0x3e, 0x8d, 0x53, 0x18, 0x14, 0x95, 0xca, + 0xd1, 0xee, 0xd4, 0xc6, 0xb7, 0x2e, 0x51, 0x07, 0x4c, 0xed, 0x9e, 0x22, 0x1f, 0x1b, 0x9f, 0x62, + 0xd2, 0xee, 0xf1, 0xd2, 0x82, 0xc7, 0xac, 0xa2, 0x5a, 0x14, 0x54, 0x26, 0xe4, 0x70, 0xb6, 0xf7, + 0x71, 0x9b, 0x68, 0xdc, 0xd9, 0x1c, 0x06, 0x3f, 0x72, 0x94, 0x8d, 0x52, 0x8f, 0x38, 0x51, 0x79, + 0xef, 0x52, 0xb6, 0xcc, 0x41, 0x4a, 0x6d, 0xb4, 0xd4, 0x77, 0xe4, 0x04, 0x42, 0x50, 0x62, 0x8f, + 0xda, 0xd1, 0x7e, 0xed, 0xf0, 0xa8, 0x79, 0x40, 0x6d, 0xb9, 0x0c, 0x65, 0xd7, 0x96, 0x2e, 0x31, + 0xa5, 0xdc, 0x86, 0x27, 0x22, 0xea, 0x90, 0x59, 0x10, 0xa6, 0xfc, 0x4e, 0xf2, 0x73, 0x07, 0x6b, + 0x89, 0x03, 0x48, 0x3b, 0x44, 0x27, 0x23, 0x47, 0x18, 0xf1, 0x95, 0x45, 0x0b, 0x93, 0x0d, 0xf7, + 0xe1, 0x88, 0x89, 0xab, 0x42, 0x8d, 0x72, 0x17, 0x4a, 0xc1, 0x37, 0xd1, 0x36, 0x98, 0x38, 0x51, + 0x5c, 0xb9, 0x07, 0x68, 0xb6, 0x5e, 0x09, 0x01, 0xa4, 0x52, 0x18, 0x20, 0xfd, 0xbd, 0x04, 0x4f, + 0x5e, 0x50, 0x9b, 0xa0, 0xb7, 0xa6, 0x36, 0xf9, 0xda, 0x65, 0x2a, 0x9b, 0x0d, 0x4e, 0x9b, 0xda, + 0xe6, 0x1d, 0x28, 0xf8, 0xe9, 0x8b, 0x6d, 0xf2, 0x9b, 0xf8, 0xe4, 0x10, 0x07, 0x91, 0xf3, 0x24, + 0x68, 0x4a, 0xff, 0x61, 0xd0, 0x7c, 0x1d, 0x80, 0x8c, 0x35, 0xee, 0xd6, 0x6e, 0xe6, 0xbd, 0x11, + 0xd2, 0x4c, 0xc3, 0xed, 0xd6, 0x58, 0x1c, 0x82, 0x1c, 0x11, 0x4f, 0x0e, 0x3a, 0xf2, 0xb7, 0x11, + 0x46, 0x2c, 0x2b, 0x3b, 0x02, 0x62, 0x2f, 0x9a, 0xbe, 0x27, 0xed, 0x06, 0x4e, 0x76, 0xd0, 0x3b, + 0xf0, 0xc4, 0x54, 0x69, 0xe1, 0xa9, 0x4e, 0x2e, 0x5a, 0x61, 0x5c, 0x09, 0x56, 0x18, 0xae, 0xea, + 0x67, 0xa1, 0xac, 0x77, 0x6d, 0x8c, 0x3b, 0xac, 0xec, 0x61, 0xd9, 0xce, 0xed, 0x77, 0x33, 0x72, + 0xcd, 0xb2, 0xee, 0xeb, 0x44, 0x57, 0xde, 0x01, 0x98, 0xf4, 0x16, 0x68, 0xb8, 0xb1, 0xcd, 0xd1, + 0xb0, 0xc3, 0xdc, 0x21, 0xa5, 0xf2, 0x01, 0xba, 0x0b, 0x29, 0xea, 0x56, 0xae, 0xd1, 0x66, 0xe3, + 0x32, 0x75, 0x0b, 0x5f, 0x6f, 0x82, 0x73, 0x2b, 0x06, 0xa0, 0xd9, 0xd6, 0x64, 0xc4, 0x14, 0x6f, + 0x04, 0xa7, 0x78, 0x2a, 0xb2, 0xc9, 0x19, 0x3e, 0xd5, 0x47, 0x90, 0x62, 0x6e, 0x40, 0x73, 0x36, + 0xeb, 0x87, 0x8b, 0x62, 0x93, 0x3e, 0xa3, 0x9f, 0x01, 0xe8, 0x84, 0xd8, 0xc6, 0xc9, 0x68, 0x32, + 0xc1, 0x5a, 0xb8, 0x1b, 0xd5, 0x5c, 0xbe, 0xed, 0xeb, 0xc2, 0x9f, 0x56, 0x26, 0xa2, 0x3e, 0x9f, + 0xf2, 0x29, 0x54, 0xf6, 0xa1, 0x14, 0x94, 0x75, 0xcb, 0x23, 0xbe, 0x86, 0x60, 0x79, 0xc4, 0xab, + 0x5d, 0x51, 0x1e, 0x79, 0xc5, 0x55, 0x82, 0x37, 0xfd, 0xd9, 0x40, 0xf9, 0x97, 0x04, 0x05, 0xbf, + 0x17, 0x7e, 0xc7, 0x15, 0xcc, 0x8d, 0x90, 0x0a, 0x26, 0xd7, 0xd5, 0x9d, 0xb7, 0x79, 0x01, 0x73, + 0x6d, 0xa6, 0x80, 0xc9, 0x74, 0x75, 0xe7, 0xf8, 0x7b, 0xac, 0x5f, 0x94, 0x5f, 0x49, 0x90, 0xf5, + 0x36, 0x1f, 0xec, 0xff, 0x07, 0x7e, 0x98, 0x70, 0xdb, 0xc5, 0xfd, 0x4d, 0x7b, 0xfe, 0x7b, 0x24, + 0xe1, 0xfd, 0x1e, 0xb9, 0xe7, 0x65, 0xc2, 0xa8, 0x6e, 0x8a, 0xdf, 0xd2, 0xc2, 0xa7, 0xdc, 0xc4, + 0x7f, 0x0f, 0x72, 0xde, 0x41, 0xa6, 0x88, 0xc5, 0xed, 0x3a, 0x49, 0xa2, 0xdc, 0x16, 0x3d, 0xc3, + 0x15, 0x48, 0x59, 0xe6, 0x87, 0xe2, 0x8f, 0x40, 0x42, 0xe5, 0x03, 0xa5, 0x03, 0xe5, 0xa9, 0x28, + 0x80, 0xee, 0x41, 0xc6, 0x1a, 0x9d, 0x68, 0xae, 0x6b, 0x4c, 0xf5, 0xe6, 0xdc, 0x5a, 0x78, 0x74, + 0xd2, 0x37, 0xda, 0x0f, 0xf1, 0xb9, 0xbb, 0x18, 0x6b, 0x74, 0xf2, 0x90, 0x7b, 0x10, 0x9f, 0x25, + 0xee, 0x9f, 0xe5, 0x0c, 0xb2, 0xee, 0x81, 0x40, 0x3f, 0x80, 0x9c, 0x17, 0x60, 0xbc, 0x3f, 0x7a, + 0x91, 0x91, 0x49, 0xa8, 0x9f, 0x88, 0x50, 0x60, 0xe5, 0x18, 0xdd, 0xa1, 0xdb, 0x90, 0xe4, 0x98, + 0x34, 0xce, 0x3c, 0xb3, 0xcc, 0x5f, 0xec, 0xb9, 0x80, 0x89, 0xe6, 0x15, 0x79, 0xfa, 0x44, 0x7e, + 0x9f, 0x0b, 0x08, 0xc9, 0x7f, 0x89, 0xb0, 0xfc, 0xf7, 0x8b, 0x38, 0xe4, 0x7d, 0xfd, 0x4e, 0xf4, + 0xff, 0xbe, 0xf0, 0x50, 0x0a, 0x09, 0xdc, 0x3e, 0xde, 0xc9, 0x2f, 0xb3, 0xe0, 0xc6, 0xe2, 0x97, + 0xdf, 0x58, 0x54, 0x57, 0xd9, 0x6d, 0x9f, 0x26, 0x2f, 0xdd, 0x3e, 0x7d, 0x01, 0x10, 0x31, 0x89, + 0xde, 0xd7, 0xce, 0x4c, 0x62, 0x0c, 0xbb, 0x1a, 0x77, 0x0d, 0x7e, 0x98, 0x65, 0xf6, 0xe6, 0x11, + 0x7b, 0x71, 0xc8, 0xbc, 0xe4, 0xe7, 0x12, 0x64, 0xbd, 0xaa, 0xf8, 0xb2, 0x3f, 0xd4, 0xae, 0x42, + 0x5a, 0x14, 0x7e, 0xfc, 0x8f, 0x9a, 0x18, 0x85, 0xf6, 0x89, 0xab, 0x90, 0x1d, 0x60, 0xa2, 0xfb, + 0xb2, 0x8d, 0x37, 0xbe, 0xf5, 0x1a, 0xe4, 0x7d, 0x3f, 0x23, 0x69, 0xb0, 0xda, 0x6f, 0xbc, 0x2d, + 0xc7, 0xaa, 0x99, 0x8f, 0x3f, 0x5b, 0x4f, 0xec, 0xe3, 0x0f, 0xe9, 0x09, 0x53, 0x1b, 0xf5, 0x66, + 0xa3, 0xfe, 0x50, 0x96, 0xaa, 0xf9, 0x8f, 0x3f, 0x5b, 0xcf, 0xa8, 0x98, 0xb5, 0x08, 0x6f, 0x3d, + 0x84, 0xf2, 0xd4, 0x87, 0x09, 0x56, 0x15, 0x08, 0x4a, 0xf7, 0x8f, 0x0f, 0xf7, 0x76, 0xea, 0xb5, + 0x56, 0x43, 0x7b, 0x74, 0xd0, 0x6a, 0xc8, 0x12, 0x7a, 0x02, 0x96, 0xf7, 0x76, 0x7e, 0xd4, 0x6c, + 0x69, 0xf5, 0xbd, 0x9d, 0xc6, 0x7e, 0x4b, 0xab, 0xb5, 0x5a, 0xb5, 0xfa, 0x43, 0x39, 0xbe, 0xf5, + 0x59, 0x1e, 0x92, 0xb5, 0xed, 0xfa, 0x0e, 0xaa, 0x43, 0x92, 0xf5, 0x36, 0x2e, 0xbc, 0x8d, 0x54, + 0xbd, 0xb8, 0xd9, 0x8b, 0x1e, 0x40, 0x8a, 0xb5, 0x3d, 0xd0, 0xc5, 0xd7, 0x93, 0xaa, 0x73, 0xba, + 0xbf, 0x74, 0x31, 0xec, 0x0c, 0x5d, 0x78, 0x5f, 0xa9, 0x7a, 0x71, 0x33, 0x18, 0xed, 0x41, 0xc6, + 0x45, 0xbd, 0xf3, 0x2e, 0x11, 0x55, 0xe7, 0x76, 0x68, 0xe9, 0xd6, 0x78, 0xf7, 0xe0, 0xe2, 0xab, + 0x4c, 0xd5, 0x39, 0x6d, 0x62, 0xb4, 0x03, 0x69, 0x81, 0x16, 0xe7, 0xdc, 0x4e, 0xaa, 0xce, 0x6b, + 0xfc, 0x22, 0x15, 0x72, 0x93, 0xbe, 0xcc, 0xfc, 0x0b, 0x5a, 0xd5, 0x05, 0x3a, 0xe0, 0xe8, 0x5d, + 0x28, 0x06, 0x91, 0xe8, 0x62, 0x37, 0xa0, 0xaa, 0x0b, 0xb6, 0x98, 0xa9, 0xfe, 0x20, 0x2c, 0x5d, + 0xec, 0x46, 0x54, 0x75, 0xc1, 0x8e, 0x33, 0x7a, 0x1f, 0x96, 0x66, 0x61, 0xe3, 0xe2, 0x17, 0xa4, + 0xaa, 0x97, 0xe8, 0x41, 0xa3, 0x01, 0xa0, 0x10, 0xb8, 0x79, 0x89, 0xfb, 0x52, 0xd5, 0xcb, 0xb4, + 0xa4, 0x51, 0x07, 0xca, 0xd3, 0x18, 0x6e, 0xd1, 0xfb, 0x53, 0xd5, 0x85, 0xdb, 0xd3, 0x7c, 0x96, + 0x20, 0xf6, 0x5b, 0xf4, 0x3e, 0x55, 0x75, 0xe1, 0x6e, 0x35, 0x3a, 0x06, 0xf0, 0xc1, 0xb7, 0x05, + 0xee, 0x57, 0x55, 0x17, 0xe9, 0x5b, 0x23, 0x0b, 0x96, 0xc3, 0x70, 0xdd, 0x65, 0xae, 0x5b, 0x55, + 0x2f, 0xd5, 0xce, 0xa6, 0xfe, 0x1c, 0x44, 0x68, 0x8b, 0x5d, 0xbf, 0xaa, 0x2e, 0xd8, 0xd7, 0xde, + 0x6e, 0x7c, 0xfe, 0xd5, 0xaa, 0xf4, 0xc5, 0x57, 0xab, 0xd2, 0x3f, 0xbe, 0x5a, 0x95, 0x3e, 0xf9, + 0x7a, 0x35, 0xf6, 0xc5, 0xd7, 0xab, 0xb1, 0xbf, 0x7e, 0xbd, 0x1a, 0xfb, 0xf1, 0xed, 0xae, 0x41, + 0x7a, 0xa3, 0x93, 0x8d, 0xb6, 0x39, 0xd8, 0xf4, 0xdf, 0x58, 0x0d, 0xbb, 0x27, 0x7b, 0x92, 0x66, + 0x59, 0xf4, 0xce, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xaf, 0xf6, 0x05, 0xd5, 0x47, 0x2b, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -3538,6 +3847,8 @@ type ABCIClient interface { ApplySnapshotChunk(ctx context.Context, in *RequestApplySnapshotChunk, opts ...grpc.CallOption) (*ResponseApplySnapshotChunk, error) PrepareProposal(ctx context.Context, in *RequestPrepareProposal, opts ...grpc.CallOption) (*ResponsePrepareProposal, error) ProcessProposal(ctx context.Context, in *RequestProcessProposal, opts ...grpc.CallOption) (*ResponseProcessProposal, error) + ExtendVote(ctx context.Context, in *RequestExtendVote, opts ...grpc.CallOption) (*ResponseExtendVote, error) + VerifyVoteExtension(ctx context.Context, in *RequestVerifyVoteExtension, opts ...grpc.CallOption) (*ResponseVerifyVoteExtension, error) FinalizeBlock(ctx context.Context, in *RequestFinalizeBlock, opts ...grpc.CallOption) (*ResponseFinalizeBlock, error) } @@ -3666,6 +3977,24 @@ func (c *aBCIClient) ProcessProposal(ctx context.Context, in *RequestProcessProp return out, nil } +func (c *aBCIClient) ExtendVote(ctx context.Context, in *RequestExtendVote, opts ...grpc.CallOption) (*ResponseExtendVote, error) { + out := new(ResponseExtendVote) + err := c.cc.Invoke(ctx, "/tendermint.abci.ABCI/ExtendVote", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *aBCIClient) VerifyVoteExtension(ctx context.Context, in *RequestVerifyVoteExtension, opts ...grpc.CallOption) (*ResponseVerifyVoteExtension, error) { + out := new(ResponseVerifyVoteExtension) + err := c.cc.Invoke(ctx, "/tendermint.abci.ABCI/VerifyVoteExtension", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *aBCIClient) FinalizeBlock(ctx context.Context, in *RequestFinalizeBlock, opts ...grpc.CallOption) (*ResponseFinalizeBlock, error) { out := new(ResponseFinalizeBlock) err := c.cc.Invoke(ctx, "/tendermint.abci.ABCI/FinalizeBlock", in, out, opts...) @@ -3690,6 +4019,8 @@ type ABCIServer interface { ApplySnapshotChunk(context.Context, *RequestApplySnapshotChunk) (*ResponseApplySnapshotChunk, error) PrepareProposal(context.Context, *RequestPrepareProposal) (*ResponsePrepareProposal, error) ProcessProposal(context.Context, *RequestProcessProposal) (*ResponseProcessProposal, error) + ExtendVote(context.Context, *RequestExtendVote) (*ResponseExtendVote, error) + VerifyVoteExtension(context.Context, *RequestVerifyVoteExtension) (*ResponseVerifyVoteExtension, error) FinalizeBlock(context.Context, *RequestFinalizeBlock) (*ResponseFinalizeBlock, error) } @@ -3736,6 +4067,12 @@ func (*UnimplementedABCIServer) PrepareProposal(ctx context.Context, req *Reques func (*UnimplementedABCIServer) ProcessProposal(ctx context.Context, req *RequestProcessProposal) (*ResponseProcessProposal, error) { return nil, status.Errorf(codes.Unimplemented, "method ProcessProposal not implemented") } +func (*UnimplementedABCIServer) ExtendVote(ctx context.Context, req *RequestExtendVote) (*ResponseExtendVote, error) { + return nil, status.Errorf(codes.Unimplemented, "method ExtendVote not implemented") +} +func (*UnimplementedABCIServer) VerifyVoteExtension(ctx context.Context, req *RequestVerifyVoteExtension) (*ResponseVerifyVoteExtension, error) { + return nil, status.Errorf(codes.Unimplemented, "method VerifyVoteExtension not implemented") +} func (*UnimplementedABCIServer) FinalizeBlock(ctx context.Context, req *RequestFinalizeBlock) (*ResponseFinalizeBlock, error) { return nil, status.Errorf(codes.Unimplemented, "method FinalizeBlock not implemented") } @@ -3978,6 +4315,42 @@ func _ABCI_ProcessProposal_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _ABCI_ExtendVote_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestExtendVote) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIServer).ExtendVote(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tendermint.abci.ABCI/ExtendVote", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIServer).ExtendVote(ctx, req.(*RequestExtendVote)) + } + return interceptor(ctx, in, info, handler) +} + +func _ABCI_VerifyVoteExtension_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestVerifyVoteExtension) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIServer).VerifyVoteExtension(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tendermint.abci.ABCI/VerifyVoteExtension", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIServer).VerifyVoteExtension(ctx, req.(*RequestVerifyVoteExtension)) + } + return interceptor(ctx, in, info, handler) +} + func _ABCI_FinalizeBlock_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RequestFinalizeBlock) if err := dec(in); err != nil { @@ -4052,6 +4425,14 @@ var _ABCI_serviceDesc = grpc.ServiceDesc{ MethodName: "ProcessProposal", Handler: _ABCI_ProcessProposal_Handler, }, + { + MethodName: "ExtendVote", + Handler: _ABCI_ExtendVote_Handler, + }, + { + MethodName: "VerifyVoteExtension", + Handler: _ABCI_VerifyVoteExtension_Handler, + }, { MethodName: "FinalizeBlock", Handler: _ABCI_FinalizeBlock_Handler, @@ -4370,6 +4751,52 @@ func (m *Request_ProcessProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) } return len(dAtA) - i, nil } +func (m *Request_ExtendVote) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Request_ExtendVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ExtendVote != nil { + { + size, err := m.ExtendVote.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x92 + } + return len(dAtA) - i, nil +} +func (m *Request_VerifyVoteExtension) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Request_VerifyVoteExtension) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.VerifyVoteExtension != nil { + { + size, err := m.VerifyVoteExtension.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x9a + } + return len(dAtA) - i, nil +} func (m *Request_FinalizeBlock) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) @@ -4558,12 +4985,12 @@ func (m *RequestInitChain) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x12 } - n16, err16 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err16 != nil { - return 0, err16 + n18, err18 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) + if err18 != nil { + return 0, err18 } - i -= n16 - i = encodeVarintTypes(dAtA, i, uint64(n16)) + i -= n18 + i = encodeVarintTypes(dAtA, i, uint64(n18)) i-- dAtA[i] = 0xa return len(dAtA) - i, nil @@ -4858,12 +5285,12 @@ func (m *RequestPrepareProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) i-- dAtA[i] = 0x3a } - n18, err18 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err18 != nil { - return 0, err18 + n20, err20 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) + if err20 != nil { + return 0, err20 } - i -= n18 - i = encodeVarintTypes(dAtA, i, uint64(n18)) + i -= n20 + i = encodeVarintTypes(dAtA, i, uint64(n20)) i-- dAtA[i] = 0x32 if m.Height != 0 { @@ -4946,12 +5373,12 @@ func (m *RequestProcessProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) i-- dAtA[i] = 0x3a } - n20, err20 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err20 != nil { - return 0, err20 + n22, err22 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) + if err22 != nil { + return 0, err22 } - i -= n20 - i = encodeVarintTypes(dAtA, i, uint64(n20)) + i -= n22 + i = encodeVarintTypes(dAtA, i, uint64(n22)) i-- dAtA[i] = 0x32 if m.Height != 0 { @@ -5002,6 +5429,90 @@ func (m *RequestProcessProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *RequestExtendVote) 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 *RequestExtendVote) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RequestExtendVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x10 + } + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RequestVerifyVoteExtension) 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 *RequestVerifyVoteExtension) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RequestVerifyVoteExtension) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.VoteExtension) > 0 { + i -= len(m.VoteExtension) + copy(dAtA[i:], m.VoteExtension) + i = encodeVarintTypes(dAtA, i, uint64(len(m.VoteExtension))) + i-- + dAtA[i] = 0x22 + } + if m.Height != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x18 + } + if len(m.ValidatorAddress) > 0 { + i -= len(m.ValidatorAddress) + copy(dAtA[i:], m.ValidatorAddress) + i = encodeVarintTypes(dAtA, i, uint64(len(m.ValidatorAddress))) + i-- + dAtA[i] = 0x12 + } + if len(m.Hash) > 0 { + i -= len(m.Hash) + copy(dAtA[i:], m.Hash) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Hash))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *RequestFinalizeBlock) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5036,12 +5547,12 @@ func (m *RequestFinalizeBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x3a } - n22, err22 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err22 != nil { - return 0, err22 + n24, err24 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) + if err24 != nil { + return 0, err24 } - i -= n22 - i = encodeVarintTypes(dAtA, i, uint64(n22)) + i -= n24 + i = encodeVarintTypes(dAtA, i, uint64(n24)) i-- dAtA[i] = 0x32 if m.Height != 0 { @@ -5424,6 +5935,52 @@ func (m *Response_ProcessProposal) MarshalToSizedBuffer(dAtA []byte) (int, error } return len(dAtA) - i, nil } +func (m *Response_ExtendVote) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Response_ExtendVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ExtendVote != nil { + { + size, err := m.ExtendVote.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x9a + } + return len(dAtA) - i, nil +} +func (m *Response_VerifyVoteExtension) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Response_VerifyVoteExtension) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.VerifyVoteExtension != nil { + { + size, err := m.VerifyVoteExtension.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa2 + } + return len(dAtA) - i, nil +} func (m *Response_FinalizeBlock) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) @@ -5977,20 +6534,20 @@ func (m *ResponseApplySnapshotChunk) MarshalToSizedBuffer(dAtA []byte) (int, err } } if len(m.RefetchChunks) > 0 { - dAtA42 := make([]byte, len(m.RefetchChunks)*10) - var j41 int + dAtA46 := make([]byte, len(m.RefetchChunks)*10) + var j45 int for _, num := range m.RefetchChunks { for num >= 1<<7 { - dAtA42[j41] = uint8(uint64(num)&0x7f | 0x80) + dAtA46[j45] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j41++ + j45++ } - dAtA42[j41] = uint8(num) - j41++ + dAtA46[j45] = uint8(num) + j45++ } - i -= j41 - copy(dAtA[i:], dAtA42[:j41]) - i = encodeVarintTypes(dAtA, i, uint64(j41)) + i -= j45 + copy(dAtA[i:], dAtA46[:j45]) + i = encodeVarintTypes(dAtA, i, uint64(j45)) i-- dAtA[i] = 0x12 } @@ -6062,6 +6619,64 @@ func (m *ResponseProcessProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *ResponseExtendVote) 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 *ResponseExtendVote) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResponseExtendVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.VoteExtension) > 0 { + i -= len(m.VoteExtension) + copy(dAtA[i:], m.VoteExtension) + i = encodeVarintTypes(dAtA, i, uint64(len(m.VoteExtension))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResponseVerifyVoteExtension) 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 *ResponseVerifyVoteExtension) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResponseVerifyVoteExtension) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Status != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *ResponseFinalizeBlock) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -6642,12 +7257,12 @@ func (m *Misbehavior) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x28 } - n48, err48 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err48 != nil { - return 0, err48 + n52, err52 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) + if err52 != nil { + return 0, err52 } - i -= n48 - i = encodeVarintTypes(dAtA, i, uint64(n48)) + i -= n52 + i = encodeVarintTypes(dAtA, i, uint64(n52)) i-- dAtA[i] = 0x22 if m.Height != 0 { @@ -6904,6 +7519,30 @@ func (m *Request_ProcessProposal) Size() (n int) { } return n } +func (m *Request_ExtendVote) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ExtendVote != nil { + l = m.ExtendVote.Size() + n += 2 + l + sovTypes(uint64(l)) + } + return n +} +func (m *Request_VerifyVoteExtension) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.VerifyVoteExtension != nil { + l = m.VerifyVoteExtension.Size() + n += 2 + l + sovTypes(uint64(l)) + } + return n +} func (m *Request_FinalizeBlock) Size() (n int) { if m == nil { return 0 @@ -7184,6 +7823,46 @@ func (m *RequestProcessProposal) Size() (n int) { return n } +func (m *RequestExtendVote) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.Height != 0 { + n += 1 + sovTypes(uint64(m.Height)) + } + return n +} + +func (m *RequestVerifyVoteExtension) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Hash) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.ValidatorAddress) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.Height != 0 { + n += 1 + sovTypes(uint64(m.Height)) + } + l = len(m.VoteExtension) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func (m *RequestFinalizeBlock) Size() (n int) { if m == nil { return 0 @@ -7404,6 +8083,30 @@ func (m *Response_ProcessProposal) Size() (n int) { } return n } +func (m *Response_ExtendVote) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ExtendVote != nil { + l = m.ExtendVote.Size() + n += 2 + l + sovTypes(uint64(l)) + } + return n +} +func (m *Response_VerifyVoteExtension) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.VerifyVoteExtension != nil { + l = m.VerifyVoteExtension.Size() + n += 2 + l + sovTypes(uint64(l)) + } + return n +} func (m *Response_FinalizeBlock) Size() (n int) { if m == nil { return 0 @@ -7698,6 +8401,31 @@ func (m *ResponseProcessProposal) Size() (n int) { return n } +func (m *ResponseExtendVote) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.VoteExtension) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + +func (m *ResponseVerifyVoteExtension) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Status != 0 { + n += 1 + sovTypes(uint64(m.Status)) + } + return n +} + func (m *ResponseFinalizeBlock) Size() (n int) { if m == nil { return 0 @@ -8469,6 +9197,76 @@ func (m *Request) Unmarshal(dAtA []byte) error { } m.Value = &Request_ProcessProposal{v} iNdEx = postIndex + case 18: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtendVote", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &RequestExtendVote{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &Request_ExtendVote{v} + iNdEx = postIndex + case 19: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VerifyVoteExtension", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &RequestVerifyVoteExtension{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &Request_VerifyVoteExtension{v} + iNdEx = postIndex case 20: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FinalizeBlock", wireType) @@ -10358,6 +11156,280 @@ func (m *RequestProcessProposal) Unmarshal(dAtA []byte) error { } return nil } +func (m *RequestExtendVote) 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: RequestExtendVote: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RequestExtendVote: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", 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.Hash = append(m.Hash[:0], dAtA[iNdEx:postIndex]...) + if m.Hash == nil { + m.Hash = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + 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 *RequestVerifyVoteExtension) 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: RequestVerifyVoteExtension: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RequestVerifyVoteExtension: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", 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.Hash = append(m.Hash[:0], dAtA[iNdEx:postIndex]...) + if m.Hash == nil { + m.Hash = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ValidatorAddress", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ValidatorAddress = append(m.ValidatorAddress[:0], dAtA[iNdEx:postIndex]...) + if m.ValidatorAddress == nil { + m.ValidatorAddress = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtension", 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.VoteExtension = append(m.VoteExtension[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtension == nil { + m.VoteExtension = []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 *RequestFinalizeBlock) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -11180,6 +12252,76 @@ func (m *Response) Unmarshal(dAtA []byte) error { } m.Value = &Response_ProcessProposal{v} iNdEx = postIndex + case 19: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ExtendVote", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ResponseExtendVote{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &Response_ExtendVote{v} + iNdEx = postIndex + case 20: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VerifyVoteExtension", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ResponseVerifyVoteExtension{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &Response_VerifyVoteExtension{v} + iNdEx = postIndex case 21: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FinalizeBlock", wireType) @@ -13085,6 +14227,159 @@ func (m *ResponseProcessProposal) Unmarshal(dAtA []byte) error { } return nil } +func (m *ResponseExtendVote) 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: ResponseExtendVote: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResponseExtendVote: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtension", 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.VoteExtension = append(m.VoteExtension[:0], dAtA[iNdEx:postIndex]...) + if m.VoteExtension == nil { + m.VoteExtension = []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 *ResponseVerifyVoteExtension) 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: ResponseVerifyVoteExtension: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResponseVerifyVoteExtension: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= ResponseVerifyVoteExtension_VerifyStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ResponseFinalizeBlock) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index a1cb43949..ccfa98370 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -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) diff --git a/consensus/common_test.go b/consensus/common_test.go index 2144bd556..d5bc92c62 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -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) } diff --git a/consensus/msgs_test.go b/consensus/msgs_test.go index 122a2a411..dffbdbbe3 100644 --- a/consensus/msgs_test.go +++ b/consensus/msgs_test.go @@ -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"}, diff --git a/consensus/state.go b/consensus/state.go index b1b64d7ef..5d665716c 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -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 } diff --git a/consensus/state_test.go b/consensus/state_test.go index 648359845..2ee641805 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -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) +} diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index ccbdbed9d..6e07c4c27 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -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 } diff --git a/internal/test/commit.go b/internal/test/commit.go index fd3a8ac5a..d94bd1054 100644 --- a/internal/test/commit.go +++ b/internal/test/commit.go @@ -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 } diff --git a/internal/test/vote.go b/internal/test/vote.go index 180cb943d..b6c563633 100644 --- a/internal/test/vote.go +++ b/internal/test/vote.go @@ -38,5 +38,6 @@ func MakeVote( } v.Signature = vpb.Signature + v.ExtensionSignature = vpb.ExtensionSignature return v, nil } diff --git a/privval/file.go b/privval/file.go index ad5623915..31fa234a3 100644 --- a/privval/file.go +++ b/privval/file.go @@ -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 { diff --git a/privval/file_test.go b/privval/file_test.go index c847af2fd..5b034c6d9 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -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() +} diff --git a/privval/msgs_test.go b/privval/msgs_test.go index 9a2cebf1c..a187624d4 100644 --- a/privval/msgs_test.go +++ b/privval/msgs_test.go @@ -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"}, diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index 241decdf1..ea00a8654 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -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 { diff --git a/proto/tendermint/crypto/keys.pb.go b/proto/tendermint/crypto/keys.pb.go index cfd176c6e..bbd97d446 100644 --- a/proto/tendermint/crypto/keys.pb.go +++ b/proto/tendermint/crypto/keys.pb.go @@ -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"` diff --git a/proto/tendermint/mempool/types.pb.go b/proto/tendermint/mempool/types.pb.go index e63d67611..f7d16a64c 100644 --- a/proto/tendermint/mempool/types.pb.go +++ b/proto/tendermint/mempool/types.pb.go @@ -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"` } diff --git a/proto/tendermint/p2p/conn.pb.go b/proto/tendermint/p2p/conn.pb.go index b2ace8eb8..dccdbe4a0 100644 --- a/proto/tendermint/p2p/conn.pb.go +++ b/proto/tendermint/p2p/conn.pb.go @@ -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 diff --git a/proto/tendermint/statesync/types.pb.go b/proto/tendermint/statesync/types.pb.go index 9ec4a1f36..147e0f7d4 100644 --- a/proto/tendermint/statesync/types.pb.go +++ b/proto/tendermint/statesync/types.pb.go @@ -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 diff --git a/proto/tendermint/types/canonical.pb.go b/proto/tendermint/types/canonical.pb.go index 5d3d4ce3a..66ab49d20 100644 --- a/proto/tendermint/types/canonical.pb.go +++ b/proto/tendermint/types/canonical.pb.go @@ -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:]) diff --git a/proto/tendermint/types/canonical.proto b/proto/tendermint/types/canonical.proto index e88fd6ffe..705561490 100644 --- a/proto/tendermint/types/canonical.proto +++ b/proto/tendermint/types/canonical.proto @@ -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; } diff --git a/proto/tendermint/types/types.pb.go b/proto/tendermint/types/types.pb.go index 4f8346253..ff108c664 100644 --- a/proto/tendermint/types/types.pb.go +++ b/proto/tendermint/types/types.pb.go @@ -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:]) diff --git a/proto/tendermint/types/types.proto b/proto/tendermint/types/types.proto index bf3bc1600..0595049a3 100644 --- a/proto/tendermint/types/types.proto +++ b/proto/tendermint/types/types.proto @@ -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 { diff --git a/proxy/app_conn.go b/proxy/app_conn.go index 7a0efef61..a9f84e7e6 100644 --- a/proxy/app_conn.go +++ b/proxy/app_conn.go @@ -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) diff --git a/proxy/mocks/app_conn_consensus.go b/proxy/mocks/app_conn_consensus.go index 6c1abcef6..3b30c3cdd 100644 --- a/proxy/mocks/app_conn_consensus.go +++ b/proxy/mocks/app_conn_consensus.go @@ -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()) diff --git a/proxy/mocks/app_conn_mempool.go b/proxy/mocks/app_conn_mempool.go index 0ce564199..3720fc451 100644 --- a/proxy/mocks/app_conn_mempool.go +++ b/proxy/mocks/app_conn_mempool.go @@ -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()) diff --git a/state/execution.go b/state/execution.go index 8ef696975..d563c45c5 100644 --- a/state/execution.go +++ b/state/execution.go @@ -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, diff --git a/state/execution_test.go b/state/execution_test.go index 1c575d437..9d516be2e 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -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") diff --git a/state/helpers_test.go b/state/helpers_test.go index f1d5c971e..59d4344d7 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -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) { diff --git a/test/e2e/app/app.go b/test/e2e/app/app.go index a2f393977..28f0834db 100644 --- a/test/e2e/app/app.go +++ b/test/e2e/app/app.go @@ -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 +} diff --git a/test/e2e/app/sync_app.go b/test/e2e/app/sync_app.go index 0b6d413c1..7b1a0c044 100644 --- a/test/e2e/app/sync_app.go +++ b/test/e2e/app/sync_app.go @@ -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() diff --git a/test/e2e/tests/app_test.go b/test/e2e/tests/app_test.go index 801b5a5c7..7b700da5e 100644 --- a/test/e2e/tests/app_test.go +++ b/test/e2e/tests/app_test.go @@ -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) + }) +} diff --git a/tools/tm-signer-harness/internal/test_harness.go b/tools/tm-signer-harness/internal/test_harness.go index 33ae537fe..62004e157 100644 --- a/tools/tm-signer-harness/internal/test_harness.go +++ b/tools/tm-signer-harness/internal/test_harness.go @@ -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 { diff --git a/types/block_test.go b/types/block_test.go index cbc4cf65a..041398dc4 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -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) diff --git a/types/canonical.go b/types/canonical.go index 49d98405d..a9e0aede7 100644 --- a/types/canonical.go +++ b/types/canonical.go @@ -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 diff --git a/types/evidence_test.go b/types/evidence_test.go index feb0b0195..e4d253531 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -251,6 +251,7 @@ func makeVote( require.NoError(t, err) v.Signature = vpb.Signature + v.ExtensionSignature = vpb.ExtensionSignature return v } diff --git a/types/priv_validator.go b/types/priv_validator.go index 49211773a..e947dd27c 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -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 } diff --git a/types/test_util.go b/types/test_util.go index 0481c1388..ecb4d75d6 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -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 +} diff --git a/types/validation_test.go b/types/validation_test.go index d194d680e..518a1512d 100644 --- a/types/validation_test.go +++ b/types/validation_test.go @@ -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}) diff --git a/types/vote.go b/types/vote.go index 02b3cad3d..37eccbed0 100644 --- a/types/vote.go +++ b/types/vote.go @@ -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() } diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 4899b04b2..5d70ed3bd 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -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) { diff --git a/types/vote_test.go b/types/vote_test.go index 927b9abc5..2cdd61de9 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -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) }