diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c871571be..ce6958eab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ jobs: with: go-version: "1.17" - uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.0.1 + - uses: technote-space/get-diff-action@v6 with: PATTERNS: | **/**.go @@ -45,7 +45,7 @@ jobs: with: go-version: "1.17" - uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.0.1 + - uses: technote-space/get-diff-action@v6 with: PATTERNS: | **/**.go @@ -67,7 +67,7 @@ jobs: with: go-version: "1.17" - uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.0.1 + - uses: technote-space/get-diff-action@v6 with: PATTERNS: | **/**.go diff --git a/.github/workflows/docs-toc.yml b/.github/workflows/docs-toc.yml new file mode 100644 index 000000000..fbe759877 --- /dev/null +++ b/.github/workflows/docs-toc.yml @@ -0,0 +1,20 @@ +# Verify that important design docs have ToC entries. +name: Check documentation ToC +on: + pull_request: + push: + branches: + - master + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: technote-space/get-diff-action@v6 + with: + PATTERNS: | + docs/architecture/** + docs/rfc/** + - run: ./docs/presubmit.sh + if: env.GIT_DIFF diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 124d06f5a..6666999fc 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -18,7 +18,7 @@ jobs: with: go-version: '1.17' - uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.0.1 + - uses: technote-space/get-diff-action@v6 with: PATTERNS: | **/**.go diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 332f55705..359514426 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/setup-go@v3 with: go-version: '^1.17' - - uses: technote-space/get-diff-action@v6.0.1 + - uses: technote-space/get-diff-action@v6 with: PATTERNS: | **/**.go diff --git a/.github/workflows/markdown-links.yml b/.github/workflows/markdown-links.yml index 356eb3f31..7af7e3ce9 100644 --- a/.github/workflows/markdown-links.yml +++ b/.github/workflows/markdown-links.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.0.1 + - uses: technote-space/get-diff-action@v6 with: PATTERNS: | **/**.md diff --git a/.github/workflows/proto-lint.yml b/.github/workflows/proto-lint.yml index 8d0da6798..b1fbeab9d 100644 --- a/.github/workflows/proto-lint.yml +++ b/.github/workflows/proto-lint.yml @@ -15,7 +15,7 @@ jobs: timeout-minutes: 5 steps: - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.3.1 + - uses: bufbuild/buf-setup-action@v1.4.0 - uses: bufbuild/buf-lint-action@v1 with: input: 'proto' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1ef5759e5..6ce8a4d34 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,7 +20,7 @@ jobs: with: go-version: "1.17" - uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.0.1 + - uses: technote-space/get-diff-action@v6 with: PATTERNS: | **/**.go @@ -42,7 +42,7 @@ jobs: needs: tests steps: - uses: actions/checkout@v3 - - uses: technote-space/get-diff-action@v6.0.1 + - uses: technote-space/get-diff-action@v6 with: PATTERNS: | **/**.go @@ -69,7 +69,7 @@ jobs: - run: | cat ./*profile.out | grep -v "mode: set" >> coverage.txt if: env.GIT_DIFF - - uses: codecov/codecov-action@v3.0.0 + - uses: codecov/codecov-action@v3.1.0 with: file: ./coverage.txt if: env.GIT_DIFF diff --git a/CHANGELOG.md b/CHANGELOG.md index 73fee3289..cfafd8c7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ Friendly reminder: We have a [bug bounty program](https://hackerone.com/cosmos). +## v0.35.4 + +April 18, 2022 + +Special thanks to external contributors on this release: @firelizzard18 + +### FEATURES + +- [cli] [\#8300](https://github.com/tendermint/tendermint/pull/8300) Add a tool to update old config files to the latest version [backport [\#8281](https://github.com/tendermint/tendermint/pull/8281)]. (@creachadair) + +### IMPROVEMENTS + +### BUG FIXES + +- [cli] [\#8294](https://github.com/tendermint/tendermint/pull/8294) keymigrate: ensure block hash keys are correctly translated. (@creachadair) +- [cli] [\#8352](https://github.com/tendermint/tendermint/pull/8352) keymigrate: ensure transaction hash keys are correctly translated. (@creachadair) + ## v0.35.3 April 8, 2022 @@ -110,7 +127,7 @@ Special thanks to external contributors on this release: @JayT106, - [\#7106](https://github.com/tendermint/tendermint/pull/7106) Revert mutex change to ABCI Clients (@tychoish). - [\#7142](https://github.com/tendermint/tendermint/pull/7142) mempool: remove panic when recheck-tx was not sent to ABCI application (@williambanfield). -- [consensus]: [\#7060](https://github.com/tendermint/tendermint/pull/7060) +- [consensus]: [\#7060](https://github.com/tendermint/tendermint/pull/7060) wait until peerUpdates channel is closed to close remaining peers (@williambanfield) - [privval] [\#5638](https://github.com/tendermint/tendermint/pull/5638) Increase read/write timeout to 5s and calculate ping interval based on it (@JoeKash) - [evidence] [\#6375](https://github.com/tendermint/tendermint/pull/6375) Fix bug with inconsistent LightClientAttackEvidence hashing (cmwaters) @@ -1416,7 +1433,7 @@ Special thanks to external contributors on this release: @jon-certik, @gracenoah *August 28, 2019* @climber73 wrote the [Writing a Tendermint Core application in Java -(gRPC)](https://github.com/tendermint/tendermint/blob/master/docs/guides/java.md) +(gRPC)](https://github.com/tendermint/tendermint/blob/master/docs/guides/java.md) guide. Special thanks to external contributors on this release: @@ -3688,7 +3705,7 @@ containing substructs: `BaseConfig`, `P2PConfig`, `MempoolConfig`, `ConsensusCon - Change some [function and method signatures](https://gist.github.com/ebuchman/640d5fc6c2605f73497992fe107ebe0b) accomodate new config - Logger - - Replace static `log15` logger with a simple interface, and provide a new implementation using `go-kit`. + - Replace static `log15` logger with a simple interface, and provide a new implementation using `go-kit`. See our new [logging library](https://github.com/tendermint/tmlibs/log) and [blog post](https://tendermint.com/blog/abstracting-the-logger-interface-in-go) for more details - Levels `warn` and `notice` are removed (you may need to change them in your `config.toml`!) - Change some [function and method signatures](https://gist.github.com/ebuchman/640d5fc6c2605f73497992fe107ebe0b) to accept a logger diff --git a/Makefile b/Makefile index ba8850164..703220953 100644 --- a/Makefile +++ b/Makefile @@ -117,12 +117,6 @@ proto-check-breaking: check-proto-deps @buf breaking --against ".git" .PHONY: proto-check-breaking -# TODO: Should be removed when work on ABCI++ is complete. -# For more information, see https://github.com/tendermint/tendermint/issues/8066 -abci-proto-gen: - ./scripts/abci-gen.sh -.PHONY: abci-proto-gen - ############################################################################### ### Build ABCI ### ############################################################################### diff --git a/abci/client/client.go b/abci/client/client.go index de72a0606..4508dcbc8 100644 --- a/abci/client/client.go +++ b/abci/client/client.go @@ -17,35 +17,18 @@ const ( //go:generate ../../scripts/mockery_generate.sh Client -// Client defines an interface for an ABCI client. -// -// All methods return the appropriate protobuf ResponseXxx struct and -// an error. +// Client defines the interface for an ABCI client. // // NOTE these are client errors, eg. ABCI socket connectivity issues. // Application-related errors are reflected in response via ABCI error codes -// and logs. +// and (potentially) error response. type Client interface { service.Service + types.Application Error() error - Flush(context.Context) error - Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) - Info(context.Context, types.RequestInfo) (*types.ResponseInfo, error) - CheckTx(context.Context, types.RequestCheckTx) (*types.ResponseCheckTx, error) - Query(context.Context, types.RequestQuery) (*types.ResponseQuery, error) - Commit(context.Context) (*types.ResponseCommit, error) - 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) - ListSnapshots(context.Context, types.RequestListSnapshots) (*types.ResponseListSnapshots, error) - OfferSnapshot(context.Context, types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) - LoadSnapshotChunk(context.Context, types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) - ApplySnapshotChunk(context.Context, types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) + Echo(context.Context, string) (*types.ResponseEcho, error) } //---------------------------------------- diff --git a/abci/client/grpc_client.go b/abci/client/grpc_client.go index 251939253..1e163056d 100644 --- a/abci/client/grpc_client.go +++ b/abci/client/grpc_client.go @@ -52,6 +52,9 @@ func dialerFunc(ctx context.Context, addr string) (net.Conn, error) { } func (cli *grpcClient) OnStart(ctx context.Context) error { + timer := time.NewTimer(0) + defer timer.Stop() + RETRY_LOOP: for { conn, err := grpc.Dial(cli.addr, @@ -63,8 +66,13 @@ RETRY_LOOP: return err } cli.logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v. Retrying...\n", cli.addr), "err", err) - time.Sleep(time.Second * dialRetryIntervalSeconds) - continue RETRY_LOOP + timer.Reset(time.Second * dialRetryIntervalSeconds) + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + continue RETRY_LOOP + } } cli.logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr) @@ -82,7 +90,13 @@ RETRY_LOOP: } cli.logger.Error("Echo failed", "err", err) - time.Sleep(time.Second * echoRetryIntervalSeconds) + timer.Reset(time.Second * echoRetryIntervalSeconds) + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + continue ENSURE_CONNECTED + } } cli.client = client @@ -114,15 +128,15 @@ func (cli *grpcClient) Echo(ctx context.Context, msg string) (*types.ResponseEch return cli.client.Echo(ctx, types.ToRequestEcho(msg).GetEcho(), grpc.WaitForReady(true)) } -func (cli *grpcClient) Info(ctx context.Context, params types.RequestInfo) (*types.ResponseInfo, error) { +func (cli *grpcClient) Info(ctx context.Context, params *types.RequestInfo) (*types.ResponseInfo, error) { return cli.client.Info(ctx, types.ToRequestInfo(params).GetInfo(), grpc.WaitForReady(true)) } -func (cli *grpcClient) CheckTx(ctx context.Context, params types.RequestCheckTx) (*types.ResponseCheckTx, error) { +func (cli *grpcClient) CheckTx(ctx context.Context, params *types.RequestCheckTx) (*types.ResponseCheckTx, error) { return cli.client.CheckTx(ctx, types.ToRequestCheckTx(params).GetCheckTx(), grpc.WaitForReady(true)) } -func (cli *grpcClient) Query(ctx context.Context, params types.RequestQuery) (*types.ResponseQuery, error) { +func (cli *grpcClient) Query(ctx context.Context, params *types.RequestQuery) (*types.ResponseQuery, error) { return cli.client.Query(ctx, types.ToRequestQuery(params).GetQuery(), grpc.WaitForReady(true)) } @@ -130,42 +144,42 @@ func (cli *grpcClient) Commit(ctx context.Context) (*types.ResponseCommit, error return cli.client.Commit(ctx, types.ToRequestCommit().GetCommit(), grpc.WaitForReady(true)) } -func (cli *grpcClient) InitChain(ctx context.Context, params types.RequestInitChain) (*types.ResponseInitChain, error) { +func (cli *grpcClient) InitChain(ctx context.Context, params *types.RequestInitChain) (*types.ResponseInitChain, error) { return cli.client.InitChain(ctx, types.ToRequestInitChain(params).GetInitChain(), grpc.WaitForReady(true)) } -func (cli *grpcClient) ListSnapshots(ctx context.Context, params types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { +func (cli *grpcClient) ListSnapshots(ctx context.Context, params *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { return cli.client.ListSnapshots(ctx, types.ToRequestListSnapshots(params).GetListSnapshots(), grpc.WaitForReady(true)) } -func (cli *grpcClient) OfferSnapshot(ctx context.Context, params types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { +func (cli *grpcClient) OfferSnapshot(ctx context.Context, params *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { return cli.client.OfferSnapshot(ctx, types.ToRequestOfferSnapshot(params).GetOfferSnapshot(), grpc.WaitForReady(true)) } -func (cli *grpcClient) LoadSnapshotChunk(ctx context.Context, params types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { +func (cli *grpcClient) LoadSnapshotChunk(ctx context.Context, params *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { return cli.client.LoadSnapshotChunk(ctx, types.ToRequestLoadSnapshotChunk(params).GetLoadSnapshotChunk(), grpc.WaitForReady(true)) } -func (cli *grpcClient) ApplySnapshotChunk(ctx context.Context, params types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { +func (cli *grpcClient) ApplySnapshotChunk(ctx context.Context, params *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { return cli.client.ApplySnapshotChunk(ctx, types.ToRequestApplySnapshotChunk(params).GetApplySnapshotChunk(), grpc.WaitForReady(true)) } -func (cli *grpcClient) PrepareProposal(ctx context.Context, params types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { +func (cli *grpcClient) PrepareProposal(ctx context.Context, params *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { return cli.client.PrepareProposal(ctx, types.ToRequestPrepareProposal(params).GetPrepareProposal(), grpc.WaitForReady(true)) } -func (cli *grpcClient) ProcessProposal(ctx context.Context, params types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { +func (cli *grpcClient) ProcessProposal(ctx context.Context, params *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { return cli.client.ProcessProposal(ctx, types.ToRequestProcessProposal(params).GetProcessProposal(), grpc.WaitForReady(true)) } -func (cli *grpcClient) ExtendVote(ctx context.Context, params types.RequestExtendVote) (*types.ResponseExtendVote, error) { +func (cli *grpcClient) ExtendVote(ctx context.Context, params *types.RequestExtendVote) (*types.ResponseExtendVote, error) { return cli.client.ExtendVote(ctx, types.ToRequestExtendVote(params).GetExtendVote(), grpc.WaitForReady(true)) } -func (cli *grpcClient) VerifyVoteExtension(ctx context.Context, params types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) { +func (cli *grpcClient) VerifyVoteExtension(ctx context.Context, params *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) { return cli.client.VerifyVoteExtension(ctx, types.ToRequestVerifyVoteExtension(params).GetVerifyVoteExtension(), grpc.WaitForReady(true)) } -func (cli *grpcClient) FinalizeBlock(ctx context.Context, params types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { +func (cli *grpcClient) FinalizeBlock(ctx context.Context, params *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { return cli.client.FinalizeBlock(ctx, types.ToRequestFinalizeBlock(params).GetFinalizeBlock(), grpc.WaitForReady(true)) } diff --git a/abci/client/local_client.go b/abci/client/local_client.go index 938ed2ab6..1002c64e8 100644 --- a/abci/client/local_client.go +++ b/abci/client/local_client.go @@ -34,81 +34,7 @@ func NewLocalClient(logger log.Logger, app types.Application) Client { func (*localClient) OnStart(context.Context) error { return nil } func (*localClient) OnStop() {} func (*localClient) Error() error { return nil } - -//------------------------------------------------------- - -func (*localClient) Flush(context.Context) error { return nil } - -func (app *localClient) Echo(_ context.Context, msg string) (*types.ResponseEcho, error) { +func (*localClient) Flush(context.Context) error { return nil } +func (*localClient) Echo(_ context.Context, msg string) (*types.ResponseEcho, error) { return &types.ResponseEcho{Message: msg}, nil } - -func (app *localClient) Info(ctx context.Context, req types.RequestInfo) (*types.ResponseInfo, error) { - res := app.Application.Info(req) - return &res, nil -} - -func (app *localClient) CheckTx(_ context.Context, req types.RequestCheckTx) (*types.ResponseCheckTx, error) { - res := app.Application.CheckTx(req) - return &res, nil -} - -func (app *localClient) Query(_ context.Context, req types.RequestQuery) (*types.ResponseQuery, error) { - res := app.Application.Query(req) - return &res, nil -} - -func (app *localClient) Commit(ctx context.Context) (*types.ResponseCommit, error) { - res := app.Application.Commit() - return &res, nil -} - -func (app *localClient) InitChain(_ context.Context, req types.RequestInitChain) (*types.ResponseInitChain, error) { - res := app.Application.InitChain(req) - return &res, nil -} - -func (app *localClient) ListSnapshots(_ context.Context, req types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { - res := app.Application.ListSnapshots(req) - return &res, nil -} - -func (app *localClient) OfferSnapshot(_ context.Context, req types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { - res := app.Application.OfferSnapshot(req) - return &res, nil -} - -func (app *localClient) LoadSnapshotChunk(_ context.Context, req types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { - res := app.Application.LoadSnapshotChunk(req) - return &res, nil -} - -func (app *localClient) ApplySnapshotChunk(_ context.Context, req types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { - res := app.Application.ApplySnapshotChunk(req) - return &res, nil -} - -func (app *localClient) PrepareProposal(_ context.Context, req types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { - res := app.Application.PrepareProposal(req) - return &res, nil -} - -func (app *localClient) ProcessProposal(_ context.Context, req types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { - res := app.Application.ProcessProposal(req) - return &res, nil -} - -func (app *localClient) ExtendVote(_ context.Context, req types.RequestExtendVote) (*types.ResponseExtendVote, error) { - res := app.Application.ExtendVote(req) - return &res, nil -} - -func (app *localClient) VerifyVoteExtension(_ context.Context, req types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) { - res := app.Application.VerifyVoteExtension(req) - return &res, nil -} - -func (app *localClient) FinalizeBlock(_ context.Context, req types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { - res := app.Application.FinalizeBlock(req) - return &res, nil -} diff --git a/abci/client/mocks/client.go b/abci/client/mocks/client.go index 37df53979..e5f2898f3 100644 --- a/abci/client/mocks/client.go +++ b/abci/client/mocks/client.go @@ -4,8 +4,10 @@ package mocks import ( context "context" + testing "testing" mock "github.com/stretchr/testify/mock" + types "github.com/tendermint/tendermint/abci/types" ) @@ -15,11 +17,11 @@ type Client struct { } // ApplySnapshotChunk provides a mock function with given fields: _a0, _a1 -func (_m *Client) ApplySnapshotChunk(_a0 context.Context, _a1 types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { +func (_m *Client) ApplySnapshotChunk(_a0 context.Context, _a1 *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponseApplySnapshotChunk - if rf, ok := ret.Get(0).(func(context.Context, types.RequestApplySnapshotChunk) *types.ResponseApplySnapshotChunk); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestApplySnapshotChunk) *types.ResponseApplySnapshotChunk); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -28,7 +30,7 @@ func (_m *Client) ApplySnapshotChunk(_a0 context.Context, _a1 types.RequestApply } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestApplySnapshotChunk) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestApplySnapshotChunk) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -38,11 +40,11 @@ func (_m *Client) ApplySnapshotChunk(_a0 context.Context, _a1 types.RequestApply } // CheckTx provides a mock function with given fields: _a0, _a1 -func (_m *Client) CheckTx(_a0 context.Context, _a1 types.RequestCheckTx) (*types.ResponseCheckTx, error) { +func (_m *Client) CheckTx(_a0 context.Context, _a1 *types.RequestCheckTx) (*types.ResponseCheckTx, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponseCheckTx - if rf, ok := ret.Get(0).(func(context.Context, types.RequestCheckTx) *types.ResponseCheckTx); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestCheckTx) *types.ResponseCheckTx); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -51,7 +53,7 @@ func (_m *Client) CheckTx(_a0 context.Context, _a1 types.RequestCheckTx) (*types } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestCheckTx) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestCheckTx) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -83,13 +85,13 @@ func (_m *Client) Commit(_a0 context.Context) (*types.ResponseCommit, error) { return r0, r1 } -// Echo provides a mock function with given fields: ctx, msg -func (_m *Client) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) { - ret := _m.Called(ctx, msg) +// Echo provides a mock function with given fields: _a0, _a1 +func (_m *Client) Echo(_a0 context.Context, _a1 string) (*types.ResponseEcho, error) { + ret := _m.Called(_a0, _a1) var r0 *types.ResponseEcho if rf, ok := ret.Get(0).(func(context.Context, string) *types.ResponseEcho); ok { - r0 = rf(ctx, msg) + r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*types.ResponseEcho) @@ -98,7 +100,7 @@ func (_m *Client) Echo(ctx context.Context, msg string) (*types.ResponseEcho, er var r1 error if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { - r1 = rf(ctx, msg) + r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) } @@ -121,11 +123,11 @@ func (_m *Client) Error() error { } // ExtendVote provides a mock function with given fields: _a0, _a1 -func (_m *Client) ExtendVote(_a0 context.Context, _a1 types.RequestExtendVote) (*types.ResponseExtendVote, error) { +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 { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestExtendVote) *types.ResponseExtendVote); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -134,7 +136,7 @@ func (_m *Client) ExtendVote(_a0 context.Context, _a1 types.RequestExtendVote) ( } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestExtendVote) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestExtendVote) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -144,11 +146,11 @@ func (_m *Client) ExtendVote(_a0 context.Context, _a1 types.RequestExtendVote) ( } // FinalizeBlock provides a mock function with given fields: _a0, _a1 -func (_m *Client) FinalizeBlock(_a0 context.Context, _a1 types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { +func (_m *Client) FinalizeBlock(_a0 context.Context, _a1 *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponseFinalizeBlock - if rf, ok := ret.Get(0).(func(context.Context, types.RequestFinalizeBlock) *types.ResponseFinalizeBlock); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestFinalizeBlock) *types.ResponseFinalizeBlock); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -157,7 +159,7 @@ func (_m *Client) FinalizeBlock(_a0 context.Context, _a1 types.RequestFinalizeBl } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestFinalizeBlock) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestFinalizeBlock) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -181,11 +183,11 @@ func (_m *Client) Flush(_a0 context.Context) error { } // Info provides a mock function with given fields: _a0, _a1 -func (_m *Client) Info(_a0 context.Context, _a1 types.RequestInfo) (*types.ResponseInfo, error) { +func (_m *Client) Info(_a0 context.Context, _a1 *types.RequestInfo) (*types.ResponseInfo, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponseInfo - if rf, ok := ret.Get(0).(func(context.Context, types.RequestInfo) *types.ResponseInfo); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestInfo) *types.ResponseInfo); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -194,7 +196,7 @@ func (_m *Client) Info(_a0 context.Context, _a1 types.RequestInfo) (*types.Respo } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestInfo) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestInfo) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -204,11 +206,11 @@ func (_m *Client) Info(_a0 context.Context, _a1 types.RequestInfo) (*types.Respo } // InitChain provides a mock function with given fields: _a0, _a1 -func (_m *Client) InitChain(_a0 context.Context, _a1 types.RequestInitChain) (*types.ResponseInitChain, error) { +func (_m *Client) InitChain(_a0 context.Context, _a1 *types.RequestInitChain) (*types.ResponseInitChain, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponseInitChain - if rf, ok := ret.Get(0).(func(context.Context, types.RequestInitChain) *types.ResponseInitChain); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestInitChain) *types.ResponseInitChain); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -217,7 +219,7 @@ func (_m *Client) InitChain(_a0 context.Context, _a1 types.RequestInitChain) (*t } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestInitChain) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestInitChain) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -241,11 +243,11 @@ func (_m *Client) IsRunning() bool { } // ListSnapshots provides a mock function with given fields: _a0, _a1 -func (_m *Client) ListSnapshots(_a0 context.Context, _a1 types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { +func (_m *Client) ListSnapshots(_a0 context.Context, _a1 *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponseListSnapshots - if rf, ok := ret.Get(0).(func(context.Context, types.RequestListSnapshots) *types.ResponseListSnapshots); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestListSnapshots) *types.ResponseListSnapshots); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -254,7 +256,7 @@ func (_m *Client) ListSnapshots(_a0 context.Context, _a1 types.RequestListSnapsh } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestListSnapshots) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestListSnapshots) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -264,11 +266,11 @@ func (_m *Client) ListSnapshots(_a0 context.Context, _a1 types.RequestListSnapsh } // LoadSnapshotChunk provides a mock function with given fields: _a0, _a1 -func (_m *Client) LoadSnapshotChunk(_a0 context.Context, _a1 types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { +func (_m *Client) LoadSnapshotChunk(_a0 context.Context, _a1 *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponseLoadSnapshotChunk - if rf, ok := ret.Get(0).(func(context.Context, types.RequestLoadSnapshotChunk) *types.ResponseLoadSnapshotChunk); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestLoadSnapshotChunk) *types.ResponseLoadSnapshotChunk); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -277,7 +279,7 @@ func (_m *Client) LoadSnapshotChunk(_a0 context.Context, _a1 types.RequestLoadSn } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestLoadSnapshotChunk) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestLoadSnapshotChunk) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -287,11 +289,11 @@ func (_m *Client) LoadSnapshotChunk(_a0 context.Context, _a1 types.RequestLoadSn } // OfferSnapshot provides a mock function with given fields: _a0, _a1 -func (_m *Client) OfferSnapshot(_a0 context.Context, _a1 types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { +func (_m *Client) OfferSnapshot(_a0 context.Context, _a1 *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponseOfferSnapshot - if rf, ok := ret.Get(0).(func(context.Context, types.RequestOfferSnapshot) *types.ResponseOfferSnapshot); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestOfferSnapshot) *types.ResponseOfferSnapshot); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -300,7 +302,7 @@ func (_m *Client) OfferSnapshot(_a0 context.Context, _a1 types.RequestOfferSnaps } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestOfferSnapshot) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestOfferSnapshot) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -310,11 +312,11 @@ func (_m *Client) OfferSnapshot(_a0 context.Context, _a1 types.RequestOfferSnaps } // PrepareProposal provides a mock function with given fields: _a0, _a1 -func (_m *Client) PrepareProposal(_a0 context.Context, _a1 types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { +func (_m *Client) PrepareProposal(_a0 context.Context, _a1 *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponsePrepareProposal - if rf, ok := ret.Get(0).(func(context.Context, types.RequestPrepareProposal) *types.ResponsePrepareProposal); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestPrepareProposal) *types.ResponsePrepareProposal); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -323,7 +325,7 @@ func (_m *Client) PrepareProposal(_a0 context.Context, _a1 types.RequestPrepareP } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestPrepareProposal) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestPrepareProposal) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -333,11 +335,11 @@ func (_m *Client) PrepareProposal(_a0 context.Context, _a1 types.RequestPrepareP } // ProcessProposal provides a mock function with given fields: _a0, _a1 -func (_m *Client) ProcessProposal(_a0 context.Context, _a1 types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { +func (_m *Client) ProcessProposal(_a0 context.Context, _a1 *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponseProcessProposal - if rf, ok := ret.Get(0).(func(context.Context, types.RequestProcessProposal) *types.ResponseProcessProposal); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestProcessProposal) *types.ResponseProcessProposal); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -346,7 +348,7 @@ func (_m *Client) ProcessProposal(_a0 context.Context, _a1 types.RequestProcessP } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestProcessProposal) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestProcessProposal) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -356,11 +358,11 @@ func (_m *Client) ProcessProposal(_a0 context.Context, _a1 types.RequestProcessP } // Query provides a mock function with given fields: _a0, _a1 -func (_m *Client) Query(_a0 context.Context, _a1 types.RequestQuery) (*types.ResponseQuery, error) { +func (_m *Client) Query(_a0 context.Context, _a1 *types.RequestQuery) (*types.ResponseQuery, error) { ret := _m.Called(_a0, _a1) var r0 *types.ResponseQuery - if rf, ok := ret.Get(0).(func(context.Context, types.RequestQuery) *types.ResponseQuery); ok { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestQuery) *types.ResponseQuery); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -369,7 +371,7 @@ func (_m *Client) Query(_a0 context.Context, _a1 types.RequestQuery) (*types.Res } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestQuery) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestQuery) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -393,11 +395,11 @@ func (_m *Client) Start(_a0 context.Context) error { } // VerifyVoteExtension provides a mock function with given fields: _a0, _a1 -func (_m *Client) VerifyVoteExtension(_a0 context.Context, _a1 types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) { +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 { + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestVerifyVoteExtension) *types.ResponseVerifyVoteExtension); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { @@ -406,7 +408,7 @@ func (_m *Client) VerifyVoteExtension(_a0 context.Context, _a1 types.RequestVeri } var r1 error - if rf, ok := ret.Get(1).(func(context.Context, types.RequestVerifyVoteExtension) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestVerifyVoteExtension) error); ok { r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) @@ -419,3 +421,13 @@ func (_m *Client) VerifyVoteExtension(_a0 context.Context, _a1 types.RequestVeri func (_m *Client) Wait() { _m.Called() } + +// NewClient creates a new instance of Client. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewClient(t testing.TB) *Client { + mock := &Client{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/abci/client/socket_client.go b/abci/client/socket_client.go index dcf5fa519..aa4fdcbe9 100644 --- a/abci/client/socket_client.go +++ b/abci/client/socket_client.go @@ -64,6 +64,8 @@ func (cli *socketClient) OnStart(ctx context.Context) error { err error conn net.Conn ) + timer := time.NewTimer(0) + defer timer.Stop() for { conn, err = tmnet.Connect(cli.addr) @@ -73,8 +75,15 @@ func (cli *socketClient) OnStart(ctx context.Context) error { } cli.logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v. Retrying after %vs...", cli.addr, dialRetryIntervalSeconds), "err", err) - time.Sleep(time.Second * dialRetryIntervalSeconds) - continue + + timer.Reset(time.Second * dialRetryIntervalSeconds) + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + continue + } + } cli.conn = conn @@ -90,11 +99,7 @@ func (cli *socketClient) OnStop() { if cli.conn != nil { cli.conn.Close() } - - // this timeout is arbitrary. - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - cli.drainQueue(ctx) + cli.drainQueue() } // Error returns an error if the client was stopped abruptly. @@ -113,12 +118,6 @@ func (cli *socketClient) sendRequestsRoutine(ctx context.Context, conn io.Writer case <-ctx.Done(): return case reqres := <-cli.reqQueue: - if ctx.Err() != nil { - return - } - - cli.willSendReq(reqres) - if err := types.WriteMessage(reqres.Request, bw); err != nil { cli.stopForError(fmt.Errorf("write to buffer: %w", err)) return @@ -162,6 +161,11 @@ func (cli *socketClient) recvResponseRoutine(ctx context.Context, conn io.Reader func (cli *socketClient) willSendReq(reqres *requestAndResponse) { cli.mtx.Lock() defer cli.mtx.Unlock() + + if !cli.IsRunning() { + return + } + cli.reqSent.PushBack(reqres) } @@ -189,138 +193,13 @@ func (cli *socketClient) didRecvResponse(res *types.Response) error { //---------------------------------------- -func (cli *socketClient) Flush(ctx context.Context) error { - _, err := cli.doRequest(ctx, types.ToRequestFlush()) - if err != nil { - return err - } - return nil -} - -func (cli *socketClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) { - res, err := cli.doRequest(ctx, types.ToRequestEcho(msg)) - if err != nil { - return nil, err - } - return res.GetEcho(), nil -} - -func (cli *socketClient) Info(ctx context.Context, req types.RequestInfo) (*types.ResponseInfo, error) { - res, err := cli.doRequest(ctx, types.ToRequestInfo(req)) - if err != nil { - return nil, err - } - return res.GetInfo(), nil -} - -func (cli *socketClient) CheckTx(ctx context.Context, req types.RequestCheckTx) (*types.ResponseCheckTx, error) { - res, err := cli.doRequest(ctx, types.ToRequestCheckTx(req)) - if err != nil { - return nil, err - } - return res.GetCheckTx(), nil -} - -func (cli *socketClient) Query(ctx context.Context, req types.RequestQuery) (*types.ResponseQuery, error) { - res, err := cli.doRequest(ctx, types.ToRequestQuery(req)) - if err != nil { - return nil, err - } - return res.GetQuery(), nil -} - -func (cli *socketClient) Commit(ctx context.Context) (*types.ResponseCommit, error) { - res, err := cli.doRequest(ctx, types.ToRequestCommit()) - if err != nil { - return nil, err - } - return res.GetCommit(), nil -} - -func (cli *socketClient) InitChain(ctx context.Context, req types.RequestInitChain) (*types.ResponseInitChain, error) { - res, err := cli.doRequest(ctx, types.ToRequestInitChain(req)) - if err != nil { - return nil, err - } - return res.GetInitChain(), nil -} - -func (cli *socketClient) ListSnapshots(ctx context.Context, req types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { - res, err := cli.doRequest(ctx, types.ToRequestListSnapshots(req)) - if err != nil { - return nil, err - } - return res.GetListSnapshots(), nil -} - -func (cli *socketClient) OfferSnapshot(ctx context.Context, req types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { - res, err := cli.doRequest(ctx, types.ToRequestOfferSnapshot(req)) - if err != nil { - return nil, err - } - return res.GetOfferSnapshot(), nil -} - -func (cli *socketClient) LoadSnapshotChunk(ctx context.Context, req types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { - res, err := cli.doRequest(ctx, types.ToRequestLoadSnapshotChunk(req)) - if err != nil { - return nil, err - } - return res.GetLoadSnapshotChunk(), nil -} - -func (cli *socketClient) ApplySnapshotChunk(ctx context.Context, req types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { - res, err := cli.doRequest(ctx, types.ToRequestApplySnapshotChunk(req)) - if err != nil { - return nil, err - } - return res.GetApplySnapshotChunk(), nil -} - -func (cli *socketClient) PrepareProposal(ctx context.Context, req types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { - res, err := cli.doRequest(ctx, types.ToRequestPrepareProposal(req)) - if err != nil { - return nil, err - } - return res.GetPrepareProposal(), nil -} - -func (cli *socketClient) ProcessProposal(ctx context.Context, req types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { - res, err := cli.doRequest(ctx, types.ToRequestProcessProposal(req)) - if err != nil { - return nil, err - } - return res.GetProcessProposal(), nil -} - -func (cli *socketClient) ExtendVote(ctx context.Context, req types.RequestExtendVote) (*types.ResponseExtendVote, error) { - res, err := cli.doRequest(ctx, types.ToRequestExtendVote(req)) - if err != nil { - return nil, err - } - return res.GetExtendVote(), nil -} - -func (cli *socketClient) VerifyVoteExtension(ctx context.Context, req types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) { - res, err := cli.doRequest(ctx, types.ToRequestVerifyVoteExtension(req)) - if err != nil { - return nil, err - } - return res.GetVerifyVoteExtension(), nil -} - -func (cli *socketClient) FinalizeBlock(ctx context.Context, req types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { - res, err := cli.doRequest(ctx, types.ToRequestFinalizeBlock(req)) - if err != nil { - return nil, err - } - return res.GetFinalizeBlock(), nil -} - -//---------------------------------------- - func (cli *socketClient) doRequest(ctx context.Context, req *types.Request) (*types.Response, error) { + if !cli.IsRunning() { + return nil, errors.New("client has stopped") + } + reqres := makeReqRes(req) + cli.willSendReq(reqres) select { case cli.reqQueue <- reqres: @@ -342,7 +221,7 @@ func (cli *socketClient) doRequest(ctx context.Context, req *types.Request) (*ty // drainQueue marks as complete and discards all remaining pending requests // from the queue. -func (cli *socketClient) drainQueue(ctx context.Context) { +func (cli *socketClient) drainQueue() { cli.mtx.Lock() defer cli.mtx.Unlock() @@ -351,22 +230,136 @@ func (cli *socketClient) drainQueue(ctx context.Context) { reqres := req.Value.(*requestAndResponse) reqres.markDone() } +} - // Mark all queued messages as resolved. - // - // TODO(creachadair): We can't simply range the channel, because it is never - // closed, and the writer continues to add work. - // See https://github.com/tendermint/tendermint/issues/6996. - for { - select { - case <-ctx.Done(): - return - case reqres := <-cli.reqQueue: - reqres.markDone() - default: - return - } +//---------------------------------------- + +func (cli *socketClient) Flush(ctx context.Context) error { + _, err := cli.doRequest(ctx, types.ToRequestFlush()) + if err != nil { + return err } + return nil +} + +func (cli *socketClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) { + res, err := cli.doRequest(ctx, types.ToRequestEcho(msg)) + if err != nil { + return nil, err + } + return res.GetEcho(), nil +} + +func (cli *socketClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) { + res, err := cli.doRequest(ctx, types.ToRequestInfo(req)) + if err != nil { + return nil, err + } + return res.GetInfo(), nil +} + +func (cli *socketClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) { + res, err := cli.doRequest(ctx, types.ToRequestCheckTx(req)) + if err != nil { + return nil, err + } + return res.GetCheckTx(), nil +} + +func (cli *socketClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) { + res, err := cli.doRequest(ctx, types.ToRequestQuery(req)) + if err != nil { + return nil, err + } + return res.GetQuery(), nil +} + +func (cli *socketClient) Commit(ctx context.Context) (*types.ResponseCommit, error) { + res, err := cli.doRequest(ctx, types.ToRequestCommit()) + if err != nil { + return nil, err + } + return res.GetCommit(), nil +} + +func (cli *socketClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) { + res, err := cli.doRequest(ctx, types.ToRequestInitChain(req)) + if err != nil { + return nil, err + } + return res.GetInitChain(), nil +} + +func (cli *socketClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { + res, err := cli.doRequest(ctx, types.ToRequestListSnapshots(req)) + if err != nil { + return nil, err + } + return res.GetListSnapshots(), nil +} + +func (cli *socketClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { + res, err := cli.doRequest(ctx, types.ToRequestOfferSnapshot(req)) + if err != nil { + return nil, err + } + return res.GetOfferSnapshot(), nil +} + +func (cli *socketClient) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { + res, err := cli.doRequest(ctx, types.ToRequestLoadSnapshotChunk(req)) + if err != nil { + return nil, err + } + return res.GetLoadSnapshotChunk(), nil +} + +func (cli *socketClient) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { + res, err := cli.doRequest(ctx, types.ToRequestApplySnapshotChunk(req)) + if err != nil { + return nil, err + } + return res.GetApplySnapshotChunk(), nil +} + +func (cli *socketClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { + res, err := cli.doRequest(ctx, types.ToRequestPrepareProposal(req)) + if err != nil { + return nil, err + } + return res.GetPrepareProposal(), nil +} + +func (cli *socketClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { + res, err := cli.doRequest(ctx, types.ToRequestProcessProposal(req)) + if err != nil { + return nil, err + } + return res.GetProcessProposal(), nil +} + +func (cli *socketClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) { + res, err := cli.doRequest(ctx, types.ToRequestExtendVote(req)) + if err != nil { + return nil, err + } + return res.GetExtendVote(), nil +} + +func (cli *socketClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) { + res, err := cli.doRequest(ctx, types.ToRequestVerifyVoteExtension(req)) + if err != nil { + return nil, err + } + return res.GetVerifyVoteExtension(), nil +} + +func (cli *socketClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { + res, err := cli.doRequest(ctx, types.ToRequestFinalizeBlock(req)) + if err != nil { + return nil, err + } + return res.GetFinalizeBlock(), nil } //---------------------------------------- diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index 7bfea4a4c..cb1603be3 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -482,7 +482,7 @@ func cmdInfo(cmd *cobra.Command, args []string) error { if len(args) == 1 { version = args[0] } - res, err := client.Info(cmd.Context(), types.RequestInfo{Version: version}) + res, err := client.Info(cmd.Context(), &types.RequestInfo{Version: version}) if err != nil { return err } @@ -511,7 +511,7 @@ func cmdFinalizeBlock(cmd *cobra.Command, args []string) error { } txs[i] = txBytes } - res, err := client.FinalizeBlock(cmd.Context(), types.RequestFinalizeBlock{Txs: txs}) + res, err := client.FinalizeBlock(cmd.Context(), &types.RequestFinalizeBlock{Txs: txs}) if err != nil { return err } @@ -539,7 +539,7 @@ func cmdCheckTx(cmd *cobra.Command, args []string) error { if err != nil { return err } - res, err := client.CheckTx(cmd.Context(), types.RequestCheckTx{Tx: txBytes}) + res, err := client.CheckTx(cmd.Context(), &types.RequestCheckTx{Tx: txBytes}) if err != nil { return err } @@ -579,7 +579,7 @@ func cmdQuery(cmd *cobra.Command, args []string) error { return err } - resQuery, err := client.Query(cmd.Context(), types.RequestQuery{ + resQuery, err := client.Query(cmd.Context(), &types.RequestQuery{ Data: queryBytes, Path: flagPath, Height: int64(flagHeight), diff --git a/abci/example/example_test.go b/abci/example/example_test.go index 9d9d1548f..066d4071d 100644 --- a/abci/example/example_test.go +++ b/abci/example/example_test.go @@ -53,7 +53,7 @@ func TestGRPC(t *testing.T) { logger := log.NewNopLogger() t.Log("### Testing GRPC") - testGRPCSync(ctx, t, logger, types.NewGRPCApplication(types.NewBaseApplication())) + testGRPCSync(ctx, t, logger, types.NewBaseApplication()) } func testBulk(ctx context.Context, t *testing.T, logger log.Logger, app types.Application) { @@ -77,7 +77,7 @@ func testBulk(ctx context.Context, t *testing.T, logger log.Logger, app types.Ap require.NoError(t, err) // Construct request - rfb := types.RequestFinalizeBlock{Txs: make([][]byte, numDeliverTxs)} + rfb := &types.RequestFinalizeBlock{Txs: make([][]byte, numDeliverTxs)} for counter := 0; counter < numDeliverTxs; counter++ { rfb.Txs[counter] = []byte("test") } @@ -101,7 +101,7 @@ func dialerFunc(ctx context.Context, addr string) (net.Conn, error) { return tmnet.Connect(addr) } -func testGRPCSync(ctx context.Context, t *testing.T, logger log.Logger, app types.ABCIApplicationServer) { +func testGRPCSync(ctx context.Context, t *testing.T, logger log.Logger, app types.Application) { t.Helper() numDeliverTxs := 680000 socketFile := fmt.Sprintf("/tmp/test-%08x.sock", rand.Int31n(1<<30)) diff --git a/abci/example/kvstore/helpers.go b/abci/example/kvstore/helpers.go index 38bb42ea8..8a2a8a35b 100644 --- a/abci/example/kvstore/helpers.go +++ b/abci/example/kvstore/helpers.go @@ -1,6 +1,7 @@ package kvstore import ( + "context" mrand "math/rand" "github.com/tendermint/tendermint/abci/types" @@ -32,8 +33,9 @@ func RandVals(cnt int) []types.ValidatorUpdate { // InitKVStore initializes the kvstore app with some data, // which allows tests to pass and is fine as long as you // don't make any tx that modify the validator state -func InitKVStore(app *PersistentKVStoreApplication) { - app.InitChain(types.RequestInitChain{ +func InitKVStore(ctx context.Context, app *PersistentKVStoreApplication) error { + _, err := app.InitChain(ctx, &types.RequestInitChain{ Validators: RandVals(1), }) + return err } diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index 045e872d4..61d129239 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -2,6 +2,7 @@ package kvstore import ( "bytes" + "context" "encoding/base64" "encoding/binary" "encoding/json" @@ -90,7 +91,7 @@ func NewApplication() *Application { } } -func (app *Application) InitChain(req types.RequestInitChain) types.ResponseInitChain { +func (app *Application) InitChain(_ context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) { app.mu.Lock() defer app.mu.Unlock() @@ -101,19 +102,19 @@ func (app *Application) InitChain(req types.RequestInitChain) types.ResponseInit panic("problem updating validators") } } - return types.ResponseInitChain{} + return &types.ResponseInitChain{}, nil } -func (app *Application) Info(req types.RequestInfo) types.ResponseInfo { +func (app *Application) Info(_ context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) { app.mu.Lock() defer app.mu.Unlock() - return types.ResponseInfo{ + return &types.ResponseInfo{ Data: fmt.Sprintf("{\"size\":%v}", app.state.Size), Version: version.ABCIVersion, AppVersion: ProtocolVersion, LastBlockHeight: app.state.Height, LastBlockAppHash: app.state.AppHash, - } + }, nil } // tx is either "val:pubkey!power" or "key=value" or just arbitrary bytes @@ -166,7 +167,7 @@ func (app *Application) Close() error { return app.state.db.Close() } -func (app *Application) FinalizeBlock(req types.RequestFinalizeBlock) types.ResponseFinalizeBlock { +func (app *Application) FinalizeBlock(_ context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { app.mu.Lock() defer app.mu.Unlock() @@ -195,14 +196,14 @@ func (app *Application) FinalizeBlock(req types.RequestFinalizeBlock) types.Resp respTxs[i] = app.handleTx(tx) } - return types.ResponseFinalizeBlock{TxResults: respTxs, ValidatorUpdates: app.ValUpdates} + return &types.ResponseFinalizeBlock{TxResults: respTxs, ValidatorUpdates: app.ValUpdates}, nil } -func (*Application) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx { - return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1} +func (*Application) CheckTx(_ context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) { + return &types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}, nil } -func (app *Application) Commit() types.ResponseCommit { +func (app *Application) Commit(_ context.Context) (*types.ResponseCommit, error) { app.mu.Lock() defer app.mu.Unlock() @@ -213,15 +214,15 @@ func (app *Application) Commit() types.ResponseCommit { app.state.Height++ saveState(app.state) - resp := types.ResponseCommit{Data: appHash} + resp := &types.ResponseCommit{Data: appHash} if app.RetainBlocks > 0 && app.state.Height >= app.RetainBlocks { resp.RetainHeight = app.state.Height - app.RetainBlocks + 1 } - return resp + return resp, nil } // Returns an associated value or nil if missing. -func (app *Application) Query(reqQuery types.RequestQuery) types.ResponseQuery { +func (app *Application) Query(_ context.Context, reqQuery *types.RequestQuery) (*types.ResponseQuery, error) { app.mu.Lock() defer app.mu.Unlock() @@ -232,10 +233,10 @@ func (app *Application) Query(reqQuery types.RequestQuery) types.ResponseQuery { panic(err) } - return types.ResponseQuery{ + return &types.ResponseQuery{ Key: reqQuery.Data, Value: value, - } + }, nil } if reqQuery.Prove { @@ -257,7 +258,7 @@ func (app *Application) Query(reqQuery types.RequestQuery) types.ResponseQuery { resQuery.Log = "exists" } - return resQuery + return &resQuery, nil } value, err := app.state.db.Get(prefixKey(reqQuery.Data)) @@ -277,25 +278,25 @@ func (app *Application) Query(reqQuery types.RequestQuery) types.ResponseQuery { resQuery.Log = "exists" } - return resQuery + return &resQuery, nil } -func (app *Application) PrepareProposal(req types.RequestPrepareProposal) types.ResponsePrepareProposal { +func (app *Application) PrepareProposal(_ context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { app.mu.Lock() defer app.mu.Unlock() - return types.ResponsePrepareProposal{ + return &types.ResponsePrepareProposal{ TxRecords: app.substPrepareTx(req.Txs, req.MaxTxBytes), - } + }, nil } -func (*Application) ProcessProposal(req types.RequestProcessProposal) types.ResponseProcessProposal { +func (*Application) ProcessProposal(_ context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { for _, tx := range req.Txs { if len(tx) == 0 { - return types.ResponseProcessProposal{Status: types.ResponseProcessProposal_REJECT} + return &types.ResponseProcessProposal{Status: types.ResponseProcessProposal_REJECT}, nil } } - return types.ResponseProcessProposal{Status: types.ResponseProcessProposal_ACCEPT} + return &types.ResponseProcessProposal{Status: types.ResponseProcessProposal_ACCEPT}, nil } //--------------------------------------------- diff --git a/abci/example/kvstore/kvstore_test.go b/abci/example/kvstore/kvstore_test.go index 0b62bbc61..9631bc1ab 100644 --- a/abci/example/kvstore/kvstore_test.go +++ b/abci/example/kvstore/kvstore_test.go @@ -23,37 +23,43 @@ const ( testValue = "def" ) -func testKVStore(t *testing.T, app types.Application, tx []byte, key, value string) { - req := types.RequestFinalizeBlock{Txs: [][]byte{tx}} - ar := app.FinalizeBlock(req) +func testKVStore(ctx context.Context, t *testing.T, app types.Application, tx []byte, key, value string) { + req := &types.RequestFinalizeBlock{Txs: [][]byte{tx}} + ar, err := app.FinalizeBlock(ctx, req) + require.NoError(t, err) require.Equal(t, 1, len(ar.TxResults)) require.False(t, ar.TxResults[0].IsErr()) // repeating tx doesn't raise error - ar = app.FinalizeBlock(req) + ar, err = app.FinalizeBlock(ctx, req) + require.NoError(t, err) require.Equal(t, 1, len(ar.TxResults)) require.False(t, ar.TxResults[0].IsErr()) // commit - app.Commit() + _, err = app.Commit(ctx) + require.NoError(t, err) - info := app.Info(types.RequestInfo{}) + info, err := app.Info(ctx, &types.RequestInfo{}) + require.NoError(t, err) require.NotZero(t, info.LastBlockHeight) // make sure query is fine - resQuery := app.Query(types.RequestQuery{ + resQuery, err := app.Query(ctx, &types.RequestQuery{ Path: "/store", Data: []byte(key), }) + require.NoError(t, err) require.Equal(t, code.CodeTypeOK, resQuery.Code) require.Equal(t, key, string(resQuery.Key)) require.Equal(t, value, string(resQuery.Value)) require.EqualValues(t, info.LastBlockHeight, resQuery.Height) // make sure proof is fine - resQuery = app.Query(types.RequestQuery{ + resQuery, err = app.Query(ctx, &types.RequestQuery{ Path: "/store", Data: []byte(key), Prove: true, }) + require.NoError(t, err) require.EqualValues(t, code.CodeTypeOK, resQuery.Code) require.Equal(t, key, string(resQuery.Key)) require.Equal(t, value, string(resQuery.Value)) @@ -61,18 +67,24 @@ func testKVStore(t *testing.T, app types.Application, tx []byte, key, value stri } func TestKVStoreKV(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + kvstore := NewApplication() key := testKey value := key tx := []byte(key) - testKVStore(t, kvstore, tx, key, value) + testKVStore(ctx, t, kvstore, tx, key, value) value = testValue tx = []byte(key + "=" + value) - testKVStore(t, kvstore, tx, key, value) + testKVStore(ctx, t, kvstore, tx, key, value) } func TestPersistentKVStoreKV(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + dir := t.TempDir() logger := log.NewNopLogger() @@ -80,22 +92,30 @@ func TestPersistentKVStoreKV(t *testing.T) { key := testKey value := key tx := []byte(key) - testKVStore(t, kvstore, tx, key, value) + testKVStore(ctx, t, kvstore, tx, key, value) value = testValue tx = []byte(key + "=" + value) - testKVStore(t, kvstore, tx, key, value) + testKVStore(ctx, t, kvstore, tx, key, value) } func TestPersistentKVStoreInfo(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() dir := t.TempDir() logger := log.NewNopLogger() kvstore := NewPersistentKVStoreApplication(logger, dir) - InitKVStore(kvstore) + if err := InitKVStore(ctx, kvstore); err != nil { + t.Fatal(err) + } height := int64(0) - resInfo := kvstore.Info(types.RequestInfo{}) + resInfo, err := kvstore.Info(ctx, &types.RequestInfo{}) + if err != nil { + t.Fatal(err) + } + if resInfo.LastBlockHeight != height { t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight) } @@ -103,10 +123,19 @@ func TestPersistentKVStoreInfo(t *testing.T) { // make and apply block height = int64(1) hash := []byte("foo") - kvstore.FinalizeBlock(types.RequestFinalizeBlock{Hash: hash, Height: height}) - kvstore.Commit() + if _, err := kvstore.FinalizeBlock(ctx, &types.RequestFinalizeBlock{Hash: hash, Height: height}); err != nil { + t.Fatal(err) + } - resInfo = kvstore.Info(types.RequestInfo{}) + if _, err := kvstore.Commit(ctx); err != nil { + t.Fatal(err) + + } + + resInfo, err = kvstore.Info(ctx, &types.RequestInfo{}) + if err != nil { + t.Fatal(err) + } if resInfo.LastBlockHeight != height { t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight) } @@ -115,6 +144,9 @@ func TestPersistentKVStoreInfo(t *testing.T) { // add a validator, remove a validator, update a validator func TestValUpdates(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + kvstore := NewApplication() // init with some validators @@ -122,9 +154,12 @@ func TestValUpdates(t *testing.T) { nInit := 5 vals := RandVals(total) // initialize with the first nInit - kvstore.InitChain(types.RequestInitChain{ + _, err := kvstore.InitChain(ctx, &types.RequestInitChain{ Validators: vals[:nInit], }) + if err != nil { + t.Fatal(err) + } vals1, vals2 := vals[:nInit], kvstore.Validators() valsEqual(t, vals1, vals2) @@ -137,7 +172,7 @@ func TestValUpdates(t *testing.T) { tx1 := MakeValSetChangeTx(v1.PubKey, v1.Power) tx2 := MakeValSetChangeTx(v2.PubKey, v2.Power) - makeApplyBlock(t, kvstore, 1, diff, tx1, tx2) + makeApplyBlock(ctx, t, kvstore, 1, diff, tx1, tx2) vals1, vals2 = vals[:nInit+2], kvstore.Validators() valsEqual(t, vals1, vals2) @@ -152,7 +187,7 @@ func TestValUpdates(t *testing.T) { tx2 = MakeValSetChangeTx(v2.PubKey, v2.Power) tx3 := MakeValSetChangeTx(v3.PubKey, v3.Power) - makeApplyBlock(t, kvstore, 2, diff, tx1, tx2, tx3) + makeApplyBlock(ctx, t, kvstore, 2, diff, tx1, tx2, tx3) vals1 = append(vals[:nInit-2], vals[nInit+1]) // nolint: gocritic vals2 = kvstore.Validators() @@ -168,7 +203,7 @@ func TestValUpdates(t *testing.T) { diff = []types.ValidatorUpdate{v1} tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power) - makeApplyBlock(t, kvstore, 3, diff, tx1) + makeApplyBlock(ctx, t, kvstore, 3, diff, tx1) vals1 = append([]types.ValidatorUpdate{v1}, vals1[1:]...) vals2 = kvstore.Validators() @@ -176,22 +211,23 @@ func TestValUpdates(t *testing.T) { } -func makeApplyBlock( - t *testing.T, - kvstore types.Application, - heightInt int, - diff []types.ValidatorUpdate, - txs ...[]byte) { +func makeApplyBlock(ctx context.Context, t *testing.T, kvstore types.Application, heightInt int, diff []types.ValidatorUpdate, txs ...[]byte) { // make and apply block height := int64(heightInt) hash := []byte("foo") - resFinalizeBlock := kvstore.FinalizeBlock(types.RequestFinalizeBlock{ + resFinalizeBlock, err := kvstore.FinalizeBlock(ctx, &types.RequestFinalizeBlock{ Hash: hash, Height: height, Txs: txs, }) + if err != nil { + t.Fatal(err) + } - kvstore.Commit() + _, err = kvstore.Commit(ctx) + if err != nil { + t.Fatal(err) + } valsEqual(t, diff, resFinalizeBlock.ValidatorUpdates) @@ -260,8 +296,7 @@ func makeGRPCClientServer( // Start the listener socket := fmt.Sprintf("unix://%s.sock", name) - gapp := types.NewGRPCApplication(app) - server := abciserver.NewGRPCServer(logger.With("module", "abci-server"), socket, gapp) + server := abciserver.NewGRPCServer(logger.With("module", "abci-server"), socket, app) if err := server.Start(ctx); err != nil { cancel() @@ -315,12 +350,12 @@ func runClientTests(ctx context.Context, t *testing.T, client abciclient.Client) } func testClient(ctx context.Context, t *testing.T, app abciclient.Client, tx []byte, key, value string) { - ar, err := app.FinalizeBlock(ctx, types.RequestFinalizeBlock{Txs: [][]byte{tx}}) + ar, err := app.FinalizeBlock(ctx, &types.RequestFinalizeBlock{Txs: [][]byte{tx}}) require.NoError(t, err) require.Equal(t, 1, len(ar.TxResults)) require.False(t, ar.TxResults[0].IsErr()) // repeating FinalizeBlock doesn't raise error - ar, err = app.FinalizeBlock(ctx, types.RequestFinalizeBlock{Txs: [][]byte{tx}}) + ar, err = app.FinalizeBlock(ctx, &types.RequestFinalizeBlock{Txs: [][]byte{tx}}) require.NoError(t, err) require.Equal(t, 1, len(ar.TxResults)) require.False(t, ar.TxResults[0].IsErr()) @@ -328,12 +363,12 @@ func testClient(ctx context.Context, t *testing.T, app abciclient.Client, tx []b _, err = app.Commit(ctx) require.NoError(t, err) - info, err := app.Info(ctx, types.RequestInfo{}) + info, err := app.Info(ctx, &types.RequestInfo{}) require.NoError(t, err) require.NotZero(t, info.LastBlockHeight) // make sure query is fine - resQuery, err := app.Query(ctx, types.RequestQuery{ + resQuery, err := app.Query(ctx, &types.RequestQuery{ Path: "/store", Data: []byte(key), }) @@ -344,7 +379,7 @@ func testClient(ctx context.Context, t *testing.T, app abciclient.Client, tx []b require.EqualValues(t, info.LastBlockHeight, resQuery.Height) // make sure proof is fine - resQuery, err = app.Query(ctx, types.RequestQuery{ + resQuery, err = app.Query(ctx, &types.RequestQuery{ Path: "/store", Data: []byte(key), Prove: true, diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index 2a6e8aa19..bede5d517 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -1,14 +1,13 @@ package kvstore import ( - "bytes" + "context" dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" cryptoproto "github.com/tendermint/tendermint/proto/tendermint/crypto" - ptypes "github.com/tendermint/tendermint/proto/tendermint/types" ) const ( @@ -38,41 +37,10 @@ func NewPersistentKVStoreApplication(logger log.Logger, dbDir string) *Persisten } } -func (app *PersistentKVStoreApplication) OfferSnapshot(req types.RequestOfferSnapshot) types.ResponseOfferSnapshot { - return types.ResponseOfferSnapshot{Result: types.ResponseOfferSnapshot_ABORT} +func (app *PersistentKVStoreApplication) OfferSnapshot(_ context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { + return &types.ResponseOfferSnapshot{Result: types.ResponseOfferSnapshot_ABORT}, nil } -func (app *PersistentKVStoreApplication) ApplySnapshotChunk(req types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk { - return types.ResponseApplySnapshotChunk{Result: types.ResponseApplySnapshotChunk_ABORT} -} - -func (app *PersistentKVStoreApplication) ExtendVote(req types.RequestExtendVote) types.ResponseExtendVote { - return types.ResponseExtendVote{VoteExtension: ConstructVoteExtension(req.Vote.ValidatorAddress)} -} - -func (app *PersistentKVStoreApplication) VerifyVoteExtension(req types.RequestVerifyVoteExtension) types.ResponseVerifyVoteExtension { - return types.RespondVerifyVoteExtension(app.verifyExtension(req.Vote.ValidatorAddress, req.Vote.VoteExtension)) -} - -// ----------------------------- - -func ConstructVoteExtension(valAddr []byte) *ptypes.VoteExtension { - return &ptypes.VoteExtension{ - AppDataToSign: valAddr, - AppDataSelfAuthenticating: valAddr, - } -} - -func (app *PersistentKVStoreApplication) verifyExtension(valAddr []byte, ext *ptypes.VoteExtension) bool { - if ext == nil { - return false - } - canonical := ConstructVoteExtension(valAddr) - if !bytes.Equal(canonical.AppDataToSign, ext.AppDataToSign) { - return false - } - if !bytes.Equal(canonical.AppDataSelfAuthenticating, ext.AppDataSelfAuthenticating) { - return false - } - return true +func (app *PersistentKVStoreApplication) ApplySnapshotChunk(_ context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { + return &types.ResponseApplySnapshotChunk{Result: types.ResponseApplySnapshotChunk_ABORT}, nil } diff --git a/abci/server/grpc_server.go b/abci/server/grpc_server.go index c1446d17a..0dfee8169 100644 --- a/abci/server/grpc_server.go +++ b/abci/server/grpc_server.go @@ -20,11 +20,11 @@ type GRPCServer struct { addr string server *grpc.Server - app types.ABCIApplicationServer + app types.Application } // NewGRPCServer returns a new gRPC ABCI server -func NewGRPCServer(logger log.Logger, protoAddr string, app types.ABCIApplicationServer) service.Service { +func NewGRPCServer(logger log.Logger, protoAddr string, app types.Application) service.Service { proto, addr := tmnet.ProtocolAndAddress(protoAddr) s := &GRPCServer{ logger: logger, @@ -44,7 +44,7 @@ func (s *GRPCServer) OnStart(ctx context.Context) error { } s.server = grpc.NewServer() - types.RegisterABCIApplicationServer(s.server, s.app) + types.RegisterABCIApplicationServer(s.server, &gRPCApplication{Application: s.app}) s.logger.Info("Listening", "proto", s.proto, "addr", s.addr) go func() { @@ -62,3 +62,22 @@ func (s *GRPCServer) OnStart(ctx context.Context) error { // OnStop stops the gRPC server. func (s *GRPCServer) OnStop() { s.server.Stop() } + +//------------------------------------------------------- + +// gRPCApplication is a gRPC shim for Application +type gRPCApplication struct { + types.Application +} + +func (app *gRPCApplication) Echo(_ context.Context, req *types.RequestEcho) (*types.ResponseEcho, error) { + return &types.ResponseEcho{Message: req.Message}, nil +} + +func (app *gRPCApplication) Flush(_ context.Context, req *types.RequestFlush) (*types.ResponseFlush, error) { + return &types.ResponseFlush{}, nil +} + +func (app *gRPCApplication) Commit(ctx context.Context, req *types.RequestCommit) (*types.ResponseCommit, error) { + return app.Application.Commit(ctx) +} diff --git a/abci/server/server.go b/abci/server/server.go index 2a6d50fd2..0e0173ca6 100644 --- a/abci/server/server.go +++ b/abci/server/server.go @@ -23,7 +23,7 @@ func NewServer(logger log.Logger, protoAddr, transport string, app types.Applica case "socket": s = NewSocketServer(logger, protoAddr, app) case "grpc": - s = NewGRPCServer(logger, protoAddr, types.NewGRPCApplication(app)) + s = NewGRPCServer(logger, protoAddr, app) default: err = fmt.Errorf("unknown server type %s", transport) } diff --git a/abci/server/socket_server.go b/abci/server/socket_server.go index 72dd1221c..570ecfb4e 100644 --- a/abci/server/socket_server.go +++ b/abci/server/socket_server.go @@ -159,12 +159,7 @@ func (s *SocketServer) acceptConnectionsRoutine(ctx context.Context) { } // Read requests from conn and deal with them -func (s *SocketServer) handleRequests( - ctx context.Context, - closer func(error), - conn io.Reader, - responses chan<- *types.Response, -) { +func (s *SocketServer) handleRequests(ctx context.Context, closer func(error), conn io.Reader, responses chan<- *types.Response) { var bufReader = bufio.NewReader(conn) defer func() { @@ -184,7 +179,12 @@ func (s *SocketServer) handleRequests( return } - resp := s.processRequest(req) + resp, err := s.processRequest(ctx, req) + if err != nil { + closer(err) + return + } + select { case <-ctx.Done(): closer(ctx.Err()) @@ -194,42 +194,99 @@ func (s *SocketServer) handleRequests( } } -func (s *SocketServer) processRequest(req *types.Request) *types.Response { +func (s *SocketServer) processRequest(ctx context.Context, req *types.Request) (*types.Response, error) { switch r := req.Value.(type) { case *types.Request_Echo: - return types.ToResponseEcho(r.Echo.Message) + return types.ToResponseEcho(r.Echo.Message), nil case *types.Request_Flush: - return types.ToResponseFlush() + return types.ToResponseFlush(), nil case *types.Request_Info: - return types.ToResponseInfo(s.app.Info(*r.Info)) + res, err := s.app.Info(ctx, r.Info) + if err != nil { + return nil, err + } + + return types.ToResponseInfo(res), nil case *types.Request_CheckTx: - return types.ToResponseCheckTx(s.app.CheckTx(*r.CheckTx)) + res, err := s.app.CheckTx(ctx, r.CheckTx) + if err != nil { + return nil, err + } + return types.ToResponseCheckTx(res), nil case *types.Request_Commit: - return types.ToResponseCommit(s.app.Commit()) + res, err := s.app.Commit(ctx) + if err != nil { + return nil, err + } + return types.ToResponseCommit(res), nil case *types.Request_Query: - return types.ToResponseQuery(s.app.Query(*r.Query)) + res, err := s.app.Query(ctx, r.Query) + if err != nil { + return nil, err + } + return types.ToResponseQuery(res), nil case *types.Request_InitChain: - return types.ToResponseInitChain(s.app.InitChain(*r.InitChain)) + res, err := s.app.InitChain(ctx, r.InitChain) + if err != nil { + return nil, err + } + return types.ToResponseInitChain(res), nil case *types.Request_ListSnapshots: - return types.ToResponseListSnapshots(s.app.ListSnapshots(*r.ListSnapshots)) + res, err := s.app.ListSnapshots(ctx, r.ListSnapshots) + if err != nil { + return nil, err + } + return types.ToResponseListSnapshots(res), nil case *types.Request_OfferSnapshot: - return types.ToResponseOfferSnapshot(s.app.OfferSnapshot(*r.OfferSnapshot)) + res, err := s.app.OfferSnapshot(ctx, r.OfferSnapshot) + if err != nil { + return nil, err + } + return types.ToResponseOfferSnapshot(res), nil case *types.Request_PrepareProposal: - return types.ToResponsePrepareProposal(s.app.PrepareProposal(*r.PrepareProposal)) + res, err := s.app.PrepareProposal(ctx, r.PrepareProposal) + if err != nil { + return nil, err + } + return types.ToResponsePrepareProposal(res), nil case *types.Request_ProcessProposal: - return types.ToResponseProcessProposal(s.app.ProcessProposal(*r.ProcessProposal)) + res, err := s.app.ProcessProposal(ctx, r.ProcessProposal) + if err != nil { + return nil, err + } + return types.ToResponseProcessProposal(res), nil case *types.Request_LoadSnapshotChunk: - return types.ToResponseLoadSnapshotChunk(s.app.LoadSnapshotChunk(*r.LoadSnapshotChunk)) + res, err := s.app.LoadSnapshotChunk(ctx, r.LoadSnapshotChunk) + if err != nil { + return nil, err + } + return types.ToResponseLoadSnapshotChunk(res), nil case *types.Request_ApplySnapshotChunk: - return types.ToResponseApplySnapshotChunk(s.app.ApplySnapshotChunk(*r.ApplySnapshotChunk)) + res, err := s.app.ApplySnapshotChunk(ctx, r.ApplySnapshotChunk) + if err != nil { + return nil, err + } + return types.ToResponseApplySnapshotChunk(res), nil case *types.Request_ExtendVote: - return types.ToResponseExtendVote(s.app.ExtendVote(*r.ExtendVote)) + res, err := s.app.ExtendVote(ctx, r.ExtendVote) + if err != nil { + return nil, err + } + return types.ToResponseExtendVote(res), nil case *types.Request_VerifyVoteExtension: - return types.ToResponseVerifyVoteExtension(s.app.VerifyVoteExtension(*r.VerifyVoteExtension)) + res, err := s.app.VerifyVoteExtension(ctx, r.VerifyVoteExtension) + if err != nil { + return nil, err + } + return types.ToResponseVerifyVoteExtension(res), nil case *types.Request_FinalizeBlock: - return types.ToResponseFinalizeBlock(s.app.FinalizeBlock(*r.FinalizeBlock)) + res, err := s.app.FinalizeBlock(ctx, r.FinalizeBlock) + if err != nil { + return nil, err + } + return types.ToResponseFinalizeBlock(res), nil default: - return types.ToResponseException("Unknown request") + return types.ToResponseException("Unknown request"), errors.New("unknown request type") } } diff --git a/abci/tests/server/client.go b/abci/tests/server/client.go index 9273e8046..b02fd0b14 100644 --- a/abci/tests/server/client.go +++ b/abci/tests/server/client.go @@ -21,7 +21,7 @@ func InitChain(ctx context.Context, client abciclient.Client) error { power := mrand.Int() vals[i] = types.UpdateValidator(pubkey, int64(power), "") } - _, err := client.InitChain(ctx, types.RequestInitChain{ + _, err := client.InitChain(ctx, &types.RequestInitChain{ Validators: vals, }) if err != nil { @@ -50,7 +50,7 @@ func Commit(ctx context.Context, client abciclient.Client, hashExp []byte) error } func FinalizeBlock(ctx context.Context, client abciclient.Client, txBytes [][]byte, codeExp []uint32, dataExp []byte) error { - res, _ := client.FinalizeBlock(ctx, types.RequestFinalizeBlock{Txs: txBytes}) + res, _ := client.FinalizeBlock(ctx, &types.RequestFinalizeBlock{Txs: txBytes}) for i, tx := range res.TxResults { code, data, log := tx.Code, tx.Data, tx.Log if code != codeExp[i] { @@ -71,7 +71,7 @@ func FinalizeBlock(ctx context.Context, client abciclient.Client, txBytes [][]by } func CheckTx(ctx context.Context, client abciclient.Client, txBytes []byte, codeExp uint32, dataExp []byte) error { - res, _ := client.CheckTx(ctx, types.RequestCheckTx{Tx: txBytes}) + res, _ := client.CheckTx(ctx, &types.RequestCheckTx{Tx: txBytes}) code, data, log := res.Code, res.Data, res.Log if code != codeExp { fmt.Println("Failed test: CheckTx") diff --git a/abci/types/application.go b/abci/types/application.go index 959311e3b..e74b87743 100644 --- a/abci/types/application.go +++ b/abci/types/application.go @@ -1,40 +1,36 @@ package types -import ( - "context" -) +import "context" //go:generate ../../scripts/mockery_generate.sh Application // Application is an interface that enables any finite, deterministic state machine // to be driven by a blockchain-based replication engine via the ABCI. -// All methods take a RequestXxx argument and return a ResponseXxx argument, -// except CheckTx/DeliverTx, which take `tx []byte`, and `Commit`, which takes nothing. type Application interface { // Info/Query Connection - Info(RequestInfo) ResponseInfo // Return application info - Query(RequestQuery) ResponseQuery // Query for state + Info(context.Context, *RequestInfo) (*ResponseInfo, error) // Return application info + Query(context.Context, *RequestQuery) (*ResponseQuery, error) // Query for state // Mempool Connection - CheckTx(RequestCheckTx) ResponseCheckTx // Validate a tx for the mempool + CheckTx(context.Context, *RequestCheckTx) (*ResponseCheckTx, error) // Validate a tx for the mempool // Consensus Connection - InitChain(RequestInitChain) ResponseInitChain // Initialize blockchain w validators/other info from TendermintCore - PrepareProposal(RequestPrepareProposal) ResponsePrepareProposal - ProcessProposal(RequestProcessProposal) ResponseProcessProposal + InitChain(context.Context, *RequestInitChain) (*ResponseInitChain, error) // Initialize blockchain w validators/other info from TendermintCore + PrepareProposal(context.Context, *RequestPrepareProposal) (*ResponsePrepareProposal, error) + ProcessProposal(context.Context, *RequestProcessProposal) (*ResponseProcessProposal, error) // Commit the state and return the application Merkle root hash - Commit() ResponseCommit + Commit(context.Context) (*ResponseCommit, error) // Create application specific vote extension - ExtendVote(RequestExtendVote) ResponseExtendVote + ExtendVote(context.Context, *RequestExtendVote) (*ResponseExtendVote, error) // Verify application's vote extension data - VerifyVoteExtension(RequestVerifyVoteExtension) ResponseVerifyVoteExtension + VerifyVoteExtension(context.Context, *RequestVerifyVoteExtension) (*ResponseVerifyVoteExtension, error) // Deliver the decided block with its txs to the Application - FinalizeBlock(RequestFinalizeBlock) ResponseFinalizeBlock + FinalizeBlock(context.Context, *RequestFinalizeBlock) (*ResponseFinalizeBlock, error) // State Sync Connection - ListSnapshots(RequestListSnapshots) ResponseListSnapshots // List available snapshots - OfferSnapshot(RequestOfferSnapshot) ResponseOfferSnapshot // Offer a snapshot to the application - LoadSnapshotChunk(RequestLoadSnapshotChunk) ResponseLoadSnapshotChunk // Load a snapshot chunk - ApplySnapshotChunk(RequestApplySnapshotChunk) ResponseApplySnapshotChunk // Apply a shapshot chunk + ListSnapshots(context.Context, *RequestListSnapshots) (*ResponseListSnapshots, error) // List available snapshots + OfferSnapshot(context.Context, *RequestOfferSnapshot) (*ResponseOfferSnapshot, error) // Offer a snapshot to the application + LoadSnapshotChunk(context.Context, *RequestLoadSnapshotChunk) (*ResponseLoadSnapshotChunk, error) // Load a snapshot chunk + ApplySnapshotChunk(context.Context, *RequestApplySnapshotChunk) (*ResponseApplySnapshotChunk, error) // Apply a shapshot chunk } //------------------------------------------------------- @@ -48,53 +44,53 @@ func NewBaseApplication() *BaseApplication { return &BaseApplication{} } -func (BaseApplication) Info(req RequestInfo) ResponseInfo { - return ResponseInfo{} +func (BaseApplication) Info(_ context.Context, req *RequestInfo) (*ResponseInfo, error) { + return &ResponseInfo{}, nil } -func (BaseApplication) CheckTx(req RequestCheckTx) ResponseCheckTx { - return ResponseCheckTx{Code: CodeTypeOK} +func (BaseApplication) CheckTx(_ context.Context, req *RequestCheckTx) (*ResponseCheckTx, error) { + return &ResponseCheckTx{Code: CodeTypeOK}, nil } -func (BaseApplication) Commit() ResponseCommit { - return ResponseCommit{} +func (BaseApplication) Commit(_ context.Context) (*ResponseCommit, error) { + return &ResponseCommit{}, nil } -func (BaseApplication) ExtendVote(req RequestExtendVote) ResponseExtendVote { - return ResponseExtendVote{} +func (BaseApplication) ExtendVote(_ context.Context, req *RequestExtendVote) (*ResponseExtendVote, error) { + return &ResponseExtendVote{}, nil } -func (BaseApplication) VerifyVoteExtension(req RequestVerifyVoteExtension) ResponseVerifyVoteExtension { - return ResponseVerifyVoteExtension{ +func (BaseApplication) VerifyVoteExtension(_ context.Context, req *RequestVerifyVoteExtension) (*ResponseVerifyVoteExtension, error) { + return &ResponseVerifyVoteExtension{ Status: ResponseVerifyVoteExtension_ACCEPT, - } + }, nil } -func (BaseApplication) Query(req RequestQuery) ResponseQuery { - return ResponseQuery{Code: CodeTypeOK} +func (BaseApplication) Query(_ context.Context, req *RequestQuery) (*ResponseQuery, error) { + return &ResponseQuery{Code: CodeTypeOK}, nil } -func (BaseApplication) InitChain(req RequestInitChain) ResponseInitChain { - return ResponseInitChain{} +func (BaseApplication) InitChain(_ context.Context, req *RequestInitChain) (*ResponseInitChain, error) { + return &ResponseInitChain{}, nil } -func (BaseApplication) ListSnapshots(req RequestListSnapshots) ResponseListSnapshots { - return ResponseListSnapshots{} +func (BaseApplication) ListSnapshots(_ context.Context, req *RequestListSnapshots) (*ResponseListSnapshots, error) { + return &ResponseListSnapshots{}, nil } -func (BaseApplication) OfferSnapshot(req RequestOfferSnapshot) ResponseOfferSnapshot { - return ResponseOfferSnapshot{} +func (BaseApplication) OfferSnapshot(_ context.Context, req *RequestOfferSnapshot) (*ResponseOfferSnapshot, error) { + return &ResponseOfferSnapshot{}, nil } -func (BaseApplication) LoadSnapshotChunk(req RequestLoadSnapshotChunk) ResponseLoadSnapshotChunk { - return ResponseLoadSnapshotChunk{} +func (BaseApplication) LoadSnapshotChunk(_ context.Context, _ *RequestLoadSnapshotChunk) (*ResponseLoadSnapshotChunk, error) { + return &ResponseLoadSnapshotChunk{}, nil } -func (BaseApplication) ApplySnapshotChunk(req RequestApplySnapshotChunk) ResponseApplySnapshotChunk { - return ResponseApplySnapshotChunk{} +func (BaseApplication) ApplySnapshotChunk(_ context.Context, req *RequestApplySnapshotChunk) (*ResponseApplySnapshotChunk, error) { + return &ResponseApplySnapshotChunk{}, nil } -func (BaseApplication) PrepareProposal(req RequestPrepareProposal) ResponsePrepareProposal { +func (BaseApplication) PrepareProposal(_ context.Context, req *RequestPrepareProposal) (*ResponsePrepareProposal, error) { trs := make([]*TxRecord, 0, len(req.Txs)) var totalBytes int64 for _, tx := range req.Txs { @@ -107,117 +103,19 @@ func (BaseApplication) PrepareProposal(req RequestPrepareProposal) ResponsePrepa Tx: tx, }) } - return ResponsePrepareProposal{TxRecords: trs} + return &ResponsePrepareProposal{TxRecords: trs}, nil } -func (BaseApplication) ProcessProposal(req RequestProcessProposal) ResponseProcessProposal { - return ResponseProcessProposal{Status: ResponseProcessProposal_ACCEPT} +func (BaseApplication) ProcessProposal(_ context.Context, req *RequestProcessProposal) (*ResponseProcessProposal, error) { + return &ResponseProcessProposal{Status: ResponseProcessProposal_ACCEPT}, nil } -func (BaseApplication) FinalizeBlock(req RequestFinalizeBlock) ResponseFinalizeBlock { +func (BaseApplication) FinalizeBlock(_ context.Context, req *RequestFinalizeBlock) (*ResponseFinalizeBlock, error) { txs := make([]*ExecTxResult, len(req.Txs)) for i := range req.Txs { txs[i] = &ExecTxResult{Code: CodeTypeOK} } - return ResponseFinalizeBlock{ + return &ResponseFinalizeBlock{ TxResults: txs, - } -} - -//------------------------------------------------------- - -// GRPCApplication is a GRPC wrapper for Application -type GRPCApplication struct { - app Application -} - -func NewGRPCApplication(app Application) *GRPCApplication { - return &GRPCApplication{app} -} - -func (app *GRPCApplication) Echo(ctx context.Context, req *RequestEcho) (*ResponseEcho, error) { - return &ResponseEcho{Message: req.Message}, nil -} - -func (app *GRPCApplication) Flush(ctx context.Context, req *RequestFlush) (*ResponseFlush, error) { - return &ResponseFlush{}, nil -} - -func (app *GRPCApplication) Info(ctx context.Context, req *RequestInfo) (*ResponseInfo, error) { - res := app.app.Info(*req) - return &res, nil -} - -func (app *GRPCApplication) CheckTx(ctx context.Context, req *RequestCheckTx) (*ResponseCheckTx, error) { - res := app.app.CheckTx(*req) - return &res, nil -} - -func (app *GRPCApplication) Query(ctx context.Context, req *RequestQuery) (*ResponseQuery, error) { - res := app.app.Query(*req) - return &res, nil -} - -func (app *GRPCApplication) Commit(ctx context.Context, req *RequestCommit) (*ResponseCommit, error) { - res := app.app.Commit() - return &res, nil -} - -func (app *GRPCApplication) InitChain(ctx context.Context, req *RequestInitChain) (*ResponseInitChain, error) { - res := app.app.InitChain(*req) - return &res, nil -} - -func (app *GRPCApplication) ListSnapshots( - ctx context.Context, req *RequestListSnapshots) (*ResponseListSnapshots, error) { - res := app.app.ListSnapshots(*req) - return &res, nil -} - -func (app *GRPCApplication) OfferSnapshot( - ctx context.Context, req *RequestOfferSnapshot) (*ResponseOfferSnapshot, error) { - res := app.app.OfferSnapshot(*req) - return &res, nil -} - -func (app *GRPCApplication) LoadSnapshotChunk( - ctx context.Context, req *RequestLoadSnapshotChunk) (*ResponseLoadSnapshotChunk, error) { - res := app.app.LoadSnapshotChunk(*req) - return &res, nil -} - -func (app *GRPCApplication) ApplySnapshotChunk( - ctx context.Context, req *RequestApplySnapshotChunk) (*ResponseApplySnapshotChunk, error) { - res := app.app.ApplySnapshotChunk(*req) - return &res, nil -} - -func (app *GRPCApplication) ExtendVote( - ctx context.Context, req *RequestExtendVote) (*ResponseExtendVote, error) { - res := app.app.ExtendVote(*req) - return &res, nil -} - -func (app *GRPCApplication) VerifyVoteExtension( - ctx context.Context, req *RequestVerifyVoteExtension) (*ResponseVerifyVoteExtension, error) { - res := app.app.VerifyVoteExtension(*req) - return &res, nil -} - -func (app *GRPCApplication) PrepareProposal( - ctx context.Context, req *RequestPrepareProposal) (*ResponsePrepareProposal, error) { - res := app.app.PrepareProposal(*req) - return &res, nil -} - -func (app *GRPCApplication) ProcessProposal( - ctx context.Context, req *RequestProcessProposal) (*ResponseProcessProposal, error) { - res := app.app.ProcessProposal(*req) - return &res, nil -} - -func (app *GRPCApplication) FinalizeBlock( - ctx context.Context, req *RequestFinalizeBlock) (*ResponseFinalizeBlock, error) { - res := app.app.FinalizeBlock(*req) - return &res, nil + }, nil } diff --git a/abci/types/messages.go b/abci/types/messages.go index 74d9b9d1a..80ab19525 100644 --- a/abci/types/messages.go +++ b/abci/types/messages.go @@ -39,15 +39,15 @@ func ToRequestFlush() *Request { } } -func ToRequestInfo(req RequestInfo) *Request { +func ToRequestInfo(req *RequestInfo) *Request { return &Request{ - Value: &Request_Info{&req}, + Value: &Request_Info{req}, } } -func ToRequestCheckTx(req RequestCheckTx) *Request { +func ToRequestCheckTx(req *RequestCheckTx) *Request { return &Request{ - Value: &Request_CheckTx{&req}, + Value: &Request_CheckTx{req}, } } @@ -57,69 +57,69 @@ func ToRequestCommit() *Request { } } -func ToRequestQuery(req RequestQuery) *Request { +func ToRequestQuery(req *RequestQuery) *Request { return &Request{ - Value: &Request_Query{&req}, + Value: &Request_Query{req}, } } -func ToRequestInitChain(req RequestInitChain) *Request { +func ToRequestInitChain(req *RequestInitChain) *Request { return &Request{ - Value: &Request_InitChain{&req}, + Value: &Request_InitChain{req}, } } -func ToRequestListSnapshots(req RequestListSnapshots) *Request { +func ToRequestListSnapshots(req *RequestListSnapshots) *Request { return &Request{ - Value: &Request_ListSnapshots{&req}, + Value: &Request_ListSnapshots{req}, } } -func ToRequestOfferSnapshot(req RequestOfferSnapshot) *Request { +func ToRequestOfferSnapshot(req *RequestOfferSnapshot) *Request { return &Request{ - Value: &Request_OfferSnapshot{&req}, + Value: &Request_OfferSnapshot{req}, } } -func ToRequestLoadSnapshotChunk(req RequestLoadSnapshotChunk) *Request { +func ToRequestLoadSnapshotChunk(req *RequestLoadSnapshotChunk) *Request { return &Request{ - Value: &Request_LoadSnapshotChunk{&req}, + Value: &Request_LoadSnapshotChunk{req}, } } -func ToRequestApplySnapshotChunk(req RequestApplySnapshotChunk) *Request { +func ToRequestApplySnapshotChunk(req *RequestApplySnapshotChunk) *Request { return &Request{ - Value: &Request_ApplySnapshotChunk{&req}, + Value: &Request_ApplySnapshotChunk{req}, } } -func ToRequestExtendVote(req RequestExtendVote) *Request { +func ToRequestExtendVote(req *RequestExtendVote) *Request { return &Request{ - Value: &Request_ExtendVote{&req}, + Value: &Request_ExtendVote{req}, } } -func ToRequestVerifyVoteExtension(req RequestVerifyVoteExtension) *Request { +func ToRequestVerifyVoteExtension(req *RequestVerifyVoteExtension) *Request { return &Request{ - Value: &Request_VerifyVoteExtension{&req}, + Value: &Request_VerifyVoteExtension{req}, } } -func ToRequestPrepareProposal(req RequestPrepareProposal) *Request { +func ToRequestPrepareProposal(req *RequestPrepareProposal) *Request { return &Request{ - Value: &Request_PrepareProposal{&req}, + Value: &Request_PrepareProposal{req}, } } -func ToRequestProcessProposal(req RequestProcessProposal) *Request { +func ToRequestProcessProposal(req *RequestProcessProposal) *Request { return &Request{ - Value: &Request_ProcessProposal{&req}, + Value: &Request_ProcessProposal{req}, } } -func ToRequestFinalizeBlock(req RequestFinalizeBlock) *Request { +func ToRequestFinalizeBlock(req *RequestFinalizeBlock) *Request { return &Request{ - Value: &Request_FinalizeBlock{&req}, + Value: &Request_FinalizeBlock{req}, } } @@ -143,86 +143,86 @@ func ToResponseFlush() *Response { } } -func ToResponseInfo(res ResponseInfo) *Response { +func ToResponseInfo(res *ResponseInfo) *Response { return &Response{ - Value: &Response_Info{&res}, + Value: &Response_Info{res}, } } -func ToResponseCheckTx(res ResponseCheckTx) *Response { +func ToResponseCheckTx(res *ResponseCheckTx) *Response { return &Response{ - Value: &Response_CheckTx{&res}, + Value: &Response_CheckTx{res}, } } -func ToResponseCommit(res ResponseCommit) *Response { +func ToResponseCommit(res *ResponseCommit) *Response { return &Response{ - Value: &Response_Commit{&res}, + Value: &Response_Commit{res}, } } -func ToResponseQuery(res ResponseQuery) *Response { +func ToResponseQuery(res *ResponseQuery) *Response { return &Response{ - Value: &Response_Query{&res}, + Value: &Response_Query{res}, } } -func ToResponseInitChain(res ResponseInitChain) *Response { +func ToResponseInitChain(res *ResponseInitChain) *Response { return &Response{ - Value: &Response_InitChain{&res}, + Value: &Response_InitChain{res}, } } -func ToResponseListSnapshots(res ResponseListSnapshots) *Response { +func ToResponseListSnapshots(res *ResponseListSnapshots) *Response { return &Response{ - Value: &Response_ListSnapshots{&res}, + Value: &Response_ListSnapshots{res}, } } -func ToResponseOfferSnapshot(res ResponseOfferSnapshot) *Response { +func ToResponseOfferSnapshot(res *ResponseOfferSnapshot) *Response { return &Response{ - Value: &Response_OfferSnapshot{&res}, + Value: &Response_OfferSnapshot{res}, } } -func ToResponseLoadSnapshotChunk(res ResponseLoadSnapshotChunk) *Response { +func ToResponseLoadSnapshotChunk(res *ResponseLoadSnapshotChunk) *Response { return &Response{ - Value: &Response_LoadSnapshotChunk{&res}, + Value: &Response_LoadSnapshotChunk{res}, } } -func ToResponseApplySnapshotChunk(res ResponseApplySnapshotChunk) *Response { +func ToResponseApplySnapshotChunk(res *ResponseApplySnapshotChunk) *Response { return &Response{ - Value: &Response_ApplySnapshotChunk{&res}, + Value: &Response_ApplySnapshotChunk{res}, } } -func ToResponseExtendVote(res ResponseExtendVote) *Response { +func ToResponseExtendVote(res *ResponseExtendVote) *Response { return &Response{ - Value: &Response_ExtendVote{&res}, + Value: &Response_ExtendVote{res}, } } -func ToResponseVerifyVoteExtension(res ResponseVerifyVoteExtension) *Response { +func ToResponseVerifyVoteExtension(res *ResponseVerifyVoteExtension) *Response { return &Response{ - Value: &Response_VerifyVoteExtension{&res}, + Value: &Response_VerifyVoteExtension{res}, } } -func ToResponsePrepareProposal(res ResponsePrepareProposal) *Response { +func ToResponsePrepareProposal(res *ResponsePrepareProposal) *Response { return &Response{ - Value: &Response_PrepareProposal{&res}, + Value: &Response_PrepareProposal{res}, } } -func ToResponseProcessProposal(res ResponseProcessProposal) *Response { +func ToResponseProcessProposal(res *ResponseProcessProposal) *Response { return &Response{ - Value: &Response_ProcessProposal{&res}, + Value: &Response_ProcessProposal{res}, } } -func ToResponseFinalizeBlock(res ResponseFinalizeBlock) *Response { +func ToResponseFinalizeBlock(res *ResponseFinalizeBlock) *Response { return &Response{ - Value: &Response_FinalizeBlock{&res}, + Value: &Response_FinalizeBlock{res}, } } diff --git a/abci/types/mocks/application.go b/abci/types/mocks/application.go index 30bf0f84c..2d35c481f 100644 --- a/abci/types/mocks/application.go +++ b/abci/types/mocks/application.go @@ -3,7 +3,11 @@ package mocks import ( + context "context" + testing "testing" + mock "github.com/stretchr/testify/mock" + types "github.com/tendermint/tendermint/abci/types" ) @@ -12,198 +16,334 @@ type Application struct { mock.Mock } -// ApplySnapshotChunk provides a mock function with given fields: _a0 -func (_m *Application) ApplySnapshotChunk(_a0 types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk { +// ApplySnapshotChunk provides a mock function with given fields: _a0, _a1 +func (_m *Application) ApplySnapshotChunk(_a0 context.Context, _a1 *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { + ret := _m.Called(_a0, _a1) + + var r0 *types.ResponseApplySnapshotChunk + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestApplySnapshotChunk) *types.ResponseApplySnapshotChunk); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseApplySnapshotChunk) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestApplySnapshotChunk) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CheckTx provides a mock function with given fields: _a0, _a1 +func (_m *Application) CheckTx(_a0 context.Context, _a1 *types.RequestCheckTx) (*types.ResponseCheckTx, error) { + ret := _m.Called(_a0, _a1) + + var r0 *types.ResponseCheckTx + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestCheckTx) *types.ResponseCheckTx); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseCheckTx) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestCheckTx) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Commit provides a mock function with given fields: _a0 +func (_m *Application) Commit(_a0 context.Context) (*types.ResponseCommit, error) { ret := _m.Called(_a0) - var r0 types.ResponseApplySnapshotChunk - if rf, ok := ret.Get(0).(func(types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk); ok { + var r0 *types.ResponseCommit + if rf, ok := ret.Get(0).(func(context.Context) *types.ResponseCommit); ok { r0 = rf(_a0) } else { - r0 = ret.Get(0).(types.ResponseApplySnapshotChunk) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseCommit) + } } - return r0 -} - -// CheckTx provides a mock function with given fields: _a0 -func (_m *Application) CheckTx(_a0 types.RequestCheckTx) types.ResponseCheckTx { - ret := _m.Called(_a0) - - var r0 types.ResponseCheckTx - if rf, ok := ret.Get(0).(func(types.RequestCheckTx) types.ResponseCheckTx); ok { - r0 = rf(_a0) + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) } else { - r0 = ret.Get(0).(types.ResponseCheckTx) + r1 = ret.Error(1) } - return r0 + return r0, r1 } -// Commit provides a mock function with given fields: -func (_m *Application) Commit() types.ResponseCommit { - ret := _m.Called() +// 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.ResponseCommit - if rf, ok := ret.Get(0).(func() types.ResponseCommit); ok { - r0 = rf() + var r0 *types.ResponseExtendVote + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestExtendVote) *types.ResponseExtendVote); ok { + r0 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseCommit) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseExtendVote) + } } - return r0 -} - -// ExtendVote provides a mock function with given fields: _a0 -func (_m *Application) ExtendVote(_a0 types.RequestExtendVote) types.ResponseExtendVote { - ret := _m.Called(_a0) - - var r0 types.ResponseExtendVote - if rf, ok := ret.Get(0).(func(types.RequestExtendVote) types.ResponseExtendVote); ok { - r0 = rf(_a0) + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestExtendVote) error); ok { + r1 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseExtendVote) + r1 = ret.Error(1) } - return r0 + return r0, r1 } -// FinalizeBlock provides a mock function with given fields: _a0 -func (_m *Application) FinalizeBlock(_a0 types.RequestFinalizeBlock) types.ResponseFinalizeBlock { - ret := _m.Called(_a0) +// 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) - var r0 types.ResponseFinalizeBlock - if rf, ok := ret.Get(0).(func(types.RequestFinalizeBlock) types.ResponseFinalizeBlock); ok { - r0 = rf(_a0) + var r0 *types.ResponseFinalizeBlock + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestFinalizeBlock) *types.ResponseFinalizeBlock); ok { + r0 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseFinalizeBlock) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseFinalizeBlock) + } } - return r0 -} - -// Info provides a mock function with given fields: _a0 -func (_m *Application) Info(_a0 types.RequestInfo) types.ResponseInfo { - ret := _m.Called(_a0) - - var r0 types.ResponseInfo - if rf, ok := ret.Get(0).(func(types.RequestInfo) types.ResponseInfo); ok { - r0 = rf(_a0) + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestFinalizeBlock) error); ok { + r1 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseInfo) + r1 = ret.Error(1) } - return r0 + return r0, r1 } -// InitChain provides a mock function with given fields: _a0 -func (_m *Application) InitChain(_a0 types.RequestInitChain) types.ResponseInitChain { - ret := _m.Called(_a0) +// Info provides a mock function with given fields: _a0, _a1 +func (_m *Application) Info(_a0 context.Context, _a1 *types.RequestInfo) (*types.ResponseInfo, error) { + ret := _m.Called(_a0, _a1) - var r0 types.ResponseInitChain - if rf, ok := ret.Get(0).(func(types.RequestInitChain) types.ResponseInitChain); ok { - r0 = rf(_a0) + var r0 *types.ResponseInfo + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestInfo) *types.ResponseInfo); ok { + r0 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseInitChain) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseInfo) + } } - return r0 -} - -// ListSnapshots provides a mock function with given fields: _a0 -func (_m *Application) ListSnapshots(_a0 types.RequestListSnapshots) types.ResponseListSnapshots { - ret := _m.Called(_a0) - - var r0 types.ResponseListSnapshots - if rf, ok := ret.Get(0).(func(types.RequestListSnapshots) types.ResponseListSnapshots); ok { - r0 = rf(_a0) + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestInfo) error); ok { + r1 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseListSnapshots) + r1 = ret.Error(1) } - return r0 + return r0, r1 } -// LoadSnapshotChunk provides a mock function with given fields: _a0 -func (_m *Application) LoadSnapshotChunk(_a0 types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk { - ret := _m.Called(_a0) +// InitChain provides a mock function with given fields: _a0, _a1 +func (_m *Application) InitChain(_a0 context.Context, _a1 *types.RequestInitChain) (*types.ResponseInitChain, error) { + ret := _m.Called(_a0, _a1) - var r0 types.ResponseLoadSnapshotChunk - if rf, ok := ret.Get(0).(func(types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk); ok { - r0 = rf(_a0) + var r0 *types.ResponseInitChain + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestInitChain) *types.ResponseInitChain); ok { + r0 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseLoadSnapshotChunk) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseInitChain) + } } - return r0 -} - -// OfferSnapshot provides a mock function with given fields: _a0 -func (_m *Application) OfferSnapshot(_a0 types.RequestOfferSnapshot) types.ResponseOfferSnapshot { - ret := _m.Called(_a0) - - var r0 types.ResponseOfferSnapshot - if rf, ok := ret.Get(0).(func(types.RequestOfferSnapshot) types.ResponseOfferSnapshot); ok { - r0 = rf(_a0) + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestInitChain) error); ok { + r1 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseOfferSnapshot) + r1 = ret.Error(1) } - return r0 + return r0, r1 } -// PrepareProposal provides a mock function with given fields: _a0 -func (_m *Application) PrepareProposal(_a0 types.RequestPrepareProposal) types.ResponsePrepareProposal { - ret := _m.Called(_a0) +// ListSnapshots provides a mock function with given fields: _a0, _a1 +func (_m *Application) ListSnapshots(_a0 context.Context, _a1 *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { + ret := _m.Called(_a0, _a1) - var r0 types.ResponsePrepareProposal - if rf, ok := ret.Get(0).(func(types.RequestPrepareProposal) types.ResponsePrepareProposal); ok { - r0 = rf(_a0) + var r0 *types.ResponseListSnapshots + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestListSnapshots) *types.ResponseListSnapshots); ok { + r0 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponsePrepareProposal) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseListSnapshots) + } } - return r0 -} - -// ProcessProposal provides a mock function with given fields: _a0 -func (_m *Application) ProcessProposal(_a0 types.RequestProcessProposal) types.ResponseProcessProposal { - ret := _m.Called(_a0) - - var r0 types.ResponseProcessProposal - if rf, ok := ret.Get(0).(func(types.RequestProcessProposal) types.ResponseProcessProposal); ok { - r0 = rf(_a0) + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestListSnapshots) error); ok { + r1 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseProcessProposal) + r1 = ret.Error(1) } - return r0 + return r0, r1 } -// Query provides a mock function with given fields: _a0 -func (_m *Application) Query(_a0 types.RequestQuery) types.ResponseQuery { - ret := _m.Called(_a0) +// LoadSnapshotChunk provides a mock function with given fields: _a0, _a1 +func (_m *Application) LoadSnapshotChunk(_a0 context.Context, _a1 *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { + ret := _m.Called(_a0, _a1) - var r0 types.ResponseQuery - if rf, ok := ret.Get(0).(func(types.RequestQuery) types.ResponseQuery); ok { - r0 = rf(_a0) + var r0 *types.ResponseLoadSnapshotChunk + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestLoadSnapshotChunk) *types.ResponseLoadSnapshotChunk); ok { + r0 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseQuery) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseLoadSnapshotChunk) + } } - return r0 -} - -// VerifyVoteExtension provides a mock function with given fields: _a0 -func (_m *Application) VerifyVoteExtension(_a0 types.RequestVerifyVoteExtension) types.ResponseVerifyVoteExtension { - ret := _m.Called(_a0) - - var r0 types.ResponseVerifyVoteExtension - if rf, ok := ret.Get(0).(func(types.RequestVerifyVoteExtension) types.ResponseVerifyVoteExtension); ok { - r0 = rf(_a0) + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestLoadSnapshotChunk) error); ok { + r1 = rf(_a0, _a1) } else { - r0 = ret.Get(0).(types.ResponseVerifyVoteExtension) + r1 = ret.Error(1) } - return r0 + return r0, r1 +} + +// OfferSnapshot provides a mock function with given fields: _a0, _a1 +func (_m *Application) OfferSnapshot(_a0 context.Context, _a1 *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { + ret := _m.Called(_a0, _a1) + + var r0 *types.ResponseOfferSnapshot + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestOfferSnapshot) *types.ResponseOfferSnapshot); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseOfferSnapshot) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestOfferSnapshot) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PrepareProposal provides a mock function with given fields: _a0, _a1 +func (_m *Application) PrepareProposal(_a0 context.Context, _a1 *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { + ret := _m.Called(_a0, _a1) + + var r0 *types.ResponsePrepareProposal + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestPrepareProposal) *types.ResponsePrepareProposal); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponsePrepareProposal) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestPrepareProposal) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ProcessProposal provides a mock function with given fields: _a0, _a1 +func (_m *Application) ProcessProposal(_a0 context.Context, _a1 *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { + ret := _m.Called(_a0, _a1) + + var r0 *types.ResponseProcessProposal + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestProcessProposal) *types.ResponseProcessProposal); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseProcessProposal) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestProcessProposal) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Query provides a mock function with given fields: _a0, _a1 +func (_m *Application) Query(_a0 context.Context, _a1 *types.RequestQuery) (*types.ResponseQuery, error) { + ret := _m.Called(_a0, _a1) + + var r0 *types.ResponseQuery + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestQuery) *types.ResponseQuery); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseQuery) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestQuery) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + 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 +} + +// NewApplication creates a new instance of Application. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewApplication(t testing.TB) *Application { + mock := &Application{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock } diff --git a/abci/types/mocks/base.go b/abci/types/mocks/base.go deleted file mode 100644 index fe0516e26..000000000 --- a/abci/types/mocks/base.go +++ /dev/null @@ -1,175 +0,0 @@ -package mocks - -import ( - types "github.com/tendermint/tendermint/abci/types" -) - -// BaseMock provides a wrapper around the generated Application mock and a BaseApplication. -// BaseMock first tries to use the mock's implementation of the method. -// If no functionality was provided for the mock by the user, BaseMock dispatches -// to the BaseApplication and uses its functionality. -// BaseMock allows users to provide mocked functionality for only the methods that matter -// for their test while avoiding a panic if the code calls Application methods that are -// not relevant to the test. -type BaseMock struct { - base *types.BaseApplication - *Application -} - -func NewBaseMock() BaseMock { - return BaseMock{ - base: types.NewBaseApplication(), - Application: new(Application), - } -} - -// Info/Query Connection -// Return application info -func (m BaseMock) Info(input types.RequestInfo) (ret types.ResponseInfo) { - defer func() { - if r := recover(); r != nil { - ret = m.base.Info(input) - } - }() - ret = m.Application.Info(input) - return ret -} - -func (m BaseMock) Query(input types.RequestQuery) (ret types.ResponseQuery) { - defer func() { - if r := recover(); r != nil { - ret = m.base.Query(input) - } - }() - ret = m.Application.Query(input) - return ret -} - -// Mempool Connection -// Validate a tx for the mempool -func (m BaseMock) CheckTx(input types.RequestCheckTx) (ret types.ResponseCheckTx) { - defer func() { - if r := recover(); r != nil { - ret = m.base.CheckTx(input) - } - }() - ret = m.Application.CheckTx(input) - return ret -} - -// Consensus Connection -// Initialize blockchain w validators/other info from TendermintCore -func (m BaseMock) InitChain(input types.RequestInitChain) (ret types.ResponseInitChain) { - defer func() { - if r := recover(); r != nil { - ret = m.base.InitChain(input) - } - }() - ret = m.Application.InitChain(input) - return ret -} - -func (m BaseMock) PrepareProposal(input types.RequestPrepareProposal) (ret types.ResponsePrepareProposal) { - defer func() { - if r := recover(); r != nil { - ret = m.base.PrepareProposal(input) - } - }() - ret = m.Application.PrepareProposal(input) - return ret -} - -func (m BaseMock) ProcessProposal(input types.RequestProcessProposal) (ret types.ResponseProcessProposal) { - defer func() { - if r := recover(); r != nil { - ret = m.base.ProcessProposal(input) - } - }() - ret = m.Application.ProcessProposal(input) - return ret -} - -// Commit the state and return the application Merkle root hash -func (m BaseMock) Commit() (ret types.ResponseCommit) { - defer func() { - if r := recover(); r != nil { - ret = m.base.Commit() - } - }() - ret = m.Application.Commit() - return ret -} - -// Create application specific vote extension -func (m BaseMock) ExtendVote(input types.RequestExtendVote) (ret types.ResponseExtendVote) { - defer func() { - if r := recover(); r != nil { - ret = m.base.ExtendVote(input) - } - }() - ret = m.Application.ExtendVote(input) - return ret -} - -// Verify application's vote extension data -func (m BaseMock) VerifyVoteExtension(input types.RequestVerifyVoteExtension) (ret types.ResponseVerifyVoteExtension) { - defer func() { - if r := recover(); r != nil { - ret = m.base.VerifyVoteExtension(input) - } - }() - ret = m.Application.VerifyVoteExtension(input) - return ret -} - -// State Sync Connection -// List available snapshots -func (m BaseMock) ListSnapshots(input types.RequestListSnapshots) (ret types.ResponseListSnapshots) { - defer func() { - if r := recover(); r != nil { - ret = m.base.ListSnapshots(input) - } - }() - ret = m.Application.ListSnapshots(input) - return ret -} - -func (m BaseMock) OfferSnapshot(input types.RequestOfferSnapshot) (ret types.ResponseOfferSnapshot) { - defer func() { - if r := recover(); r != nil { - ret = m.base.OfferSnapshot(input) - } - }() - ret = m.Application.OfferSnapshot(input) - return ret -} - -func (m BaseMock) LoadSnapshotChunk(input types.RequestLoadSnapshotChunk) (ret types.ResponseLoadSnapshotChunk) { - defer func() { - if r := recover(); r != nil { - ret = m.base.LoadSnapshotChunk(input) - } - }() - ret = m.Application.LoadSnapshotChunk(input) - return ret -} - -func (m BaseMock) ApplySnapshotChunk(input types.RequestApplySnapshotChunk) (ret types.ResponseApplySnapshotChunk) { - defer func() { - if r := recover(); r != nil { - ret = m.base.ApplySnapshotChunk(input) - } - }() - ret = m.Application.ApplySnapshotChunk(input) - return ret -} - -func (m BaseMock) FinalizeBlock(input types.RequestFinalizeBlock) (ret types.ResponseFinalizeBlock) { - defer func() { - if r := recover(); r != nil { - ret = m.base.FinalizeBlock(input) - } - }() - ret = m.Application.FinalizeBlock(input) - return ret -} diff --git a/abci/types/types.go b/abci/types/types.go index d74a2289a..d13947d1a 100644 --- a/abci/types/types.go +++ b/abci/types/types.go @@ -5,8 +5,6 @@ import ( "encoding/json" "github.com/gogo/protobuf/jsonpb" - - types "github.com/tendermint/tendermint/proto/tendermint/types" ) const ( @@ -157,15 +155,6 @@ var _ jsonRoundTripper = (*EventAttribute)(nil) // ----------------------------------------------- // construct Result data -func RespondExtendVote(appDataToSign, appDataSelfAuthenticating []byte) ResponseExtendVote { - return ResponseExtendVote{ - VoteExtension: &types.VoteExtension{ - AppDataToSign: appDataToSign, - AppDataSelfAuthenticating: appDataSelfAuthenticating, - }, - } -} - func RespondVerifyVoteExtension(ok bool) ResponseVerifyVoteExtension { status := ResponseVerifyVoteExtension_REJECT if ok { diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index 9e7955462..dd1308628 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -1521,7 +1521,8 @@ func (m *RequestProcessProposal) GetProposerAddress() []byte { // Extends a vote with application-side injection type RequestExtendVote struct { - Vote *types1.Vote `protobuf:"bytes,1,opt,name=vote,proto3" json:"vote,omitempty"` + 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{} } @@ -1557,16 +1558,26 @@ func (m *RequestExtendVote) XXX_DiscardUnknown() { var xxx_messageInfo_RequestExtendVote proto.InternalMessageInfo -func (m *RequestExtendVote) GetVote() *types1.Vote { +func (m *RequestExtendVote) GetHash() []byte { if m != nil { - return m.Vote + 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 { - Vote *types1.Vote `protobuf:"bytes,1,opt,name=vote,proto3" json:"vote,omitempty"` + 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{} } @@ -1602,9 +1613,30 @@ func (m *RequestVerifyVoteExtension) XXX_DiscardUnknown() { var xxx_messageInfo_RequestVerifyVoteExtension proto.InternalMessageInfo -func (m *RequestVerifyVoteExtension) GetVote() *types1.Vote { +func (m *RequestVerifyVoteExtension) GetHash() []byte { if m != nil { - return m.Vote + 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 } @@ -3131,7 +3163,7 @@ func (m *ResponseProcessProposal) GetConsensusParamUpdates() *types1.ConsensusPa } type ResponseExtendVote struct { - VoteExtension *types1.VoteExtension `protobuf:"bytes,1,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,omitempty"` + VoteExtension []byte `protobuf:"bytes,1,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,omitempty"` } func (m *ResponseExtendVote) Reset() { *m = ResponseExtendVote{} } @@ -3167,7 +3199,7 @@ func (m *ResponseExtendVote) XXX_DiscardUnknown() { var xxx_messageInfo_ResponseExtendVote proto.InternalMessageInfo -func (m *ResponseExtendVote) GetVoteExtension() *types1.VoteExtension { +func (m *ResponseExtendVote) GetVoteExtension() []byte { if m != nil { return m.VoteExtension } @@ -3354,8 +3386,14 @@ 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 { - Round int32 `protobuf:"varint,1,opt,name=round,proto3" json:"round,omitempty"` + // The round at which the block proposer decided in the previous height. + Round int32 `protobuf:"varint,1,opt,name=round,proto3" json:"round,omitempty"` + // List of validators' addresses in the last validator set with their voting + // information, including vote extensions. Votes []ExtendedVoteInfo `protobuf:"bytes,2,rep,name=votes,proto3" json:"votes"` } @@ -3908,10 +3946,14 @@ func (m *VoteInfo) GetSignedLastBlock() bool { return false } +// ExtendedVoteInfo 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{} } @@ -4194,222 +4236,222 @@ func init() { proto.RegisterFile("tendermint/abci/types.proto", fileDescriptor_2 var fileDescriptor_252557cfdd89a31a = []byte{ // 3451 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5b, 0x4b, 0x73, 0x23, 0xd5, - 0xf5, 0xd7, 0xfb, 0x71, 0x64, 0x3d, 0x7c, 0x6d, 0x06, 0x8d, 0x98, 0xb1, 0x87, 0x9e, 0x02, 0x66, - 0x06, 0xf0, 0xf0, 0x9f, 0xf9, 0x0f, 0x0c, 0x01, 0x42, 0xd9, 0xb2, 0x8c, 0x3c, 0xe3, 0xb1, 0x4d, - 0x5b, 0x36, 0x45, 0x42, 0xa6, 0x69, 0xa9, 0xaf, 0xad, 0x66, 0x24, 0x75, 0xd3, 0xdd, 0x32, 0x32, - 0xcb, 0x50, 0x6c, 0xa8, 0x54, 0x85, 0x4d, 0x2a, 0xc9, 0x82, 0x5d, 0x52, 0x95, 0x7c, 0x83, 0xac, - 0xb2, 0xca, 0x82, 0x45, 0x16, 0xac, 0x92, 0x54, 0x16, 0x24, 0x05, 0xbb, 0x7c, 0x81, 0xec, 0x92, - 0xd4, 0x7d, 0xf4, 0x53, 0x6a, 0xa9, 0xc5, 0x00, 0x55, 0xa9, 0xb0, 0xd3, 0x3d, 0x7d, 0xce, 0xe9, - 0xbe, 0xf7, 0x9e, 0x7b, 0x1e, 0xbf, 0x73, 0x05, 0x8f, 0x59, 0x78, 0xa0, 0x60, 0xa3, 0xaf, 0x0e, - 0xac, 0xeb, 0x72, 0xbb, 0xa3, 0x5e, 0xb7, 0xce, 0x74, 0x6c, 0xae, 0xe9, 0x86, 0x66, 0x69, 0xa8, - 0xec, 0x3e, 0x5c, 0x23, 0x0f, 0x6b, 0x17, 0x3d, 0xdc, 0x1d, 0xe3, 0x4c, 0xb7, 0xb4, 0xeb, 0xba, - 0xa1, 0x69, 0xc7, 0x8c, 0xbf, 0x76, 0xc1, 0xf3, 0x98, 0xea, 0xf1, 0x6a, 0xf3, 0x3d, 0xe5, 0xc2, - 0x0f, 0xf0, 0x99, 0xfd, 0xf4, 0xe2, 0x98, 0xac, 0x2e, 0x1b, 0x72, 0xdf, 0x7e, 0xbc, 0x7a, 0xa2, - 0x69, 0x27, 0x3d, 0x7c, 0x9d, 0x8e, 0xda, 0xc3, 0xe3, 0xeb, 0x96, 0xda, 0xc7, 0xa6, 0x25, 0xf7, - 0x75, 0xce, 0xb0, 0x7c, 0xa2, 0x9d, 0x68, 0xf4, 0xe7, 0x75, 0xf2, 0x8b, 0x51, 0x85, 0x7f, 0x03, - 0x64, 0x45, 0xfc, 0xee, 0x10, 0x9b, 0x16, 0xba, 0x01, 0x29, 0xdc, 0xe9, 0x6a, 0xd5, 0xf8, 0xa5, - 0xf8, 0x95, 0xc2, 0x8d, 0x0b, 0x6b, 0x81, 0xc9, 0xad, 0x71, 0xbe, 0x46, 0xa7, 0xab, 0x35, 0x63, - 0x22, 0xe5, 0x45, 0xb7, 0x20, 0x7d, 0xdc, 0x1b, 0x9a, 0xdd, 0x6a, 0x82, 0x0a, 0x5d, 0x0c, 0x13, - 0xda, 0x22, 0x4c, 0xcd, 0x98, 0xc8, 0xb8, 0xc9, 0xab, 0xd4, 0xc1, 0xb1, 0x56, 0x4d, 0x4e, 0x7f, - 0xd5, 0xf6, 0xe0, 0x98, 0xbe, 0x8a, 0xf0, 0xa2, 0x0d, 0x00, 0x75, 0xa0, 0x5a, 0x52, 0xa7, 0x2b, - 0xab, 0x83, 0x6a, 0x8a, 0x4a, 0x3e, 0x1e, 0x2e, 0xa9, 0x5a, 0x75, 0xc2, 0xd8, 0x8c, 0x89, 0x79, - 0xd5, 0x1e, 0x90, 0xcf, 0x7d, 0x77, 0x88, 0x8d, 0xb3, 0x6a, 0x7a, 0xfa, 0xe7, 0xbe, 0x4e, 0x98, - 0xc8, 0xe7, 0x52, 0x6e, 0xb4, 0x0d, 0x85, 0x36, 0x3e, 0x51, 0x07, 0x52, 0xbb, 0xa7, 0x75, 0x1e, - 0x54, 0x33, 0x54, 0x58, 0x08, 0x13, 0xde, 0x20, 0xac, 0x1b, 0x84, 0x73, 0x23, 0x51, 0x8d, 0x37, - 0x63, 0x22, 0xb4, 0x1d, 0x0a, 0x7a, 0x19, 0x72, 0x9d, 0x2e, 0xee, 0x3c, 0x90, 0xac, 0x51, 0x35, - 0x4b, 0xf5, 0xac, 0x86, 0xe9, 0xa9, 0x13, 0xbe, 0xd6, 0xa8, 0x19, 0x13, 0xb3, 0x1d, 0xf6, 0x13, - 0x6d, 0x01, 0x28, 0xb8, 0xa7, 0x9e, 0x62, 0x83, 0xc8, 0xe7, 0xa6, 0xaf, 0xc1, 0x26, 0xe3, 0x6c, - 0x8d, 0xf8, 0x67, 0xe4, 0x15, 0x9b, 0x80, 0xea, 0x90, 0xc7, 0x03, 0x85, 0x4f, 0x27, 0x4f, 0xd5, - 0x5c, 0x0a, 0xdd, 0xef, 0x81, 0xe2, 0x9d, 0x4c, 0x0e, 0xf3, 0x31, 0xba, 0x0d, 0x99, 0x8e, 0xd6, - 0xef, 0xab, 0x56, 0x15, 0xa8, 0x86, 0x95, 0xd0, 0x89, 0x50, 0xae, 0x66, 0x4c, 0xe4, 0xfc, 0x68, - 0x17, 0x4a, 0x3d, 0xd5, 0xb4, 0x24, 0x73, 0x20, 0xeb, 0x66, 0x57, 0xb3, 0xcc, 0x6a, 0x81, 0x6a, - 0x78, 0x22, 0x4c, 0xc3, 0x8e, 0x6a, 0x5a, 0x07, 0x36, 0x73, 0x33, 0x26, 0x16, 0x7b, 0x5e, 0x02, - 0xd1, 0xa7, 0x1d, 0x1f, 0x63, 0xc3, 0x51, 0x58, 0x5d, 0x98, 0xae, 0x6f, 0x8f, 0x70, 0xdb, 0xf2, - 0x44, 0x9f, 0xe6, 0x25, 0xa0, 0x1f, 0xc2, 0x52, 0x4f, 0x93, 0x15, 0x47, 0x9d, 0xd4, 0xe9, 0x0e, - 0x07, 0x0f, 0xaa, 0x45, 0xaa, 0xf4, 0x6a, 0xe8, 0x47, 0x6a, 0xb2, 0x62, 0xab, 0xa8, 0x13, 0x81, - 0x66, 0x4c, 0x5c, 0xec, 0x05, 0x89, 0xe8, 0x3e, 0x2c, 0xcb, 0xba, 0xde, 0x3b, 0x0b, 0x6a, 0x2f, - 0x51, 0xed, 0xd7, 0xc2, 0xb4, 0xaf, 0x13, 0x99, 0xa0, 0x7a, 0x24, 0x8f, 0x51, 0x51, 0x0b, 0x2a, - 0xba, 0x81, 0x75, 0xd9, 0xc0, 0x92, 0x6e, 0x68, 0xba, 0x66, 0xca, 0xbd, 0x6a, 0x99, 0xea, 0x7e, - 0x2a, 0x4c, 0xf7, 0x3e, 0xe3, 0xdf, 0xe7, 0xec, 0xcd, 0x98, 0x58, 0xd6, 0xfd, 0x24, 0xa6, 0x55, - 0xeb, 0x60, 0xd3, 0x74, 0xb5, 0x56, 0x66, 0x69, 0xa5, 0xfc, 0x7e, 0xad, 0x3e, 0x12, 0x6a, 0x40, - 0x01, 0x8f, 0x88, 0xb8, 0x74, 0xaa, 0x59, 0xb8, 0xba, 0x38, 0xfd, 0x60, 0x35, 0x28, 0xeb, 0x91, - 0x66, 0x61, 0x72, 0xa8, 0xb0, 0x33, 0x42, 0x32, 0x3c, 0x72, 0x8a, 0x0d, 0xf5, 0xf8, 0x8c, 0xaa, - 0x91, 0xe8, 0x13, 0x53, 0xd5, 0x06, 0x55, 0x44, 0x15, 0x3e, 0x1d, 0xa6, 0xf0, 0x88, 0x0a, 0x11, - 0x15, 0x0d, 0x5b, 0xa4, 0x19, 0x13, 0x97, 0x4e, 0xc7, 0xc9, 0xc4, 0xc4, 0x8e, 0xd5, 0x81, 0xdc, - 0x53, 0xdf, 0xc7, 0xfc, 0xd8, 0x2c, 0x4d, 0x37, 0xb1, 0x2d, 0xce, 0x4d, 0xcf, 0x0a, 0x31, 0xb1, - 0x63, 0x2f, 0x61, 0x23, 0x0b, 0xe9, 0x53, 0xb9, 0x37, 0xc4, 0xc2, 0x53, 0x50, 0xf0, 0x38, 0x56, - 0x54, 0x85, 0x6c, 0x1f, 0x9b, 0xa6, 0x7c, 0x82, 0xa9, 0x1f, 0xce, 0x8b, 0xf6, 0x50, 0x28, 0xc1, - 0x82, 0xd7, 0x99, 0x0a, 0x1f, 0xc7, 0x1d, 0x49, 0xe2, 0x27, 0x89, 0xe4, 0x29, 0x36, 0xe8, 0xb4, - 0xb9, 0x24, 0x1f, 0xa2, 0xcb, 0x50, 0xa4, 0x9f, 0x2c, 0xd9, 0xcf, 0x89, 0xb3, 0x4e, 0x89, 0x0b, - 0x94, 0x78, 0xc4, 0x99, 0x56, 0xa1, 0xa0, 0xdf, 0xd0, 0x1d, 0x96, 0x24, 0x65, 0x01, 0xfd, 0x86, - 0x6e, 0x33, 0x3c, 0x0e, 0x0b, 0x64, 0x7e, 0x0e, 0x47, 0x8a, 0xbe, 0xa4, 0x40, 0x68, 0x9c, 0x45, - 0xf8, 0x63, 0x02, 0x2a, 0x41, 0x07, 0x8c, 0x6e, 0x43, 0x8a, 0xc4, 0x22, 0x1e, 0x56, 0x6a, 0x6b, - 0x2c, 0x50, 0xad, 0xd9, 0x81, 0x6a, 0xad, 0x65, 0x07, 0xaa, 0x8d, 0xdc, 0xa7, 0x9f, 0xaf, 0xc6, - 0x3e, 0xfe, 0xdb, 0x6a, 0x5c, 0xa4, 0x12, 0xe8, 0x3c, 0xf1, 0x95, 0xb2, 0x3a, 0x90, 0x54, 0x85, - 0x7e, 0x72, 0x9e, 0x38, 0x42, 0x59, 0x1d, 0x6c, 0x2b, 0x68, 0x07, 0x2a, 0x1d, 0x6d, 0x60, 0xe2, - 0x81, 0x39, 0x34, 0x25, 0x16, 0x08, 0x79, 0x30, 0xf1, 0xb9, 0x43, 0x16, 0x5e, 0xeb, 0x36, 0xe7, - 0x3e, 0x65, 0x14, 0xcb, 0x1d, 0x3f, 0x81, 0xb8, 0xd5, 0x53, 0xb9, 0xa7, 0x2a, 0xb2, 0xa5, 0x19, - 0x66, 0x35, 0x75, 0x29, 0x39, 0xd1, 0x1f, 0x1e, 0xd9, 0x2c, 0x87, 0xba, 0x22, 0x5b, 0x78, 0x23, - 0x45, 0x3e, 0x57, 0xf4, 0x48, 0xa2, 0x27, 0xa1, 0x2c, 0xeb, 0xba, 0x64, 0x5a, 0xb2, 0x85, 0xa5, - 0xf6, 0x99, 0x85, 0x4d, 0x1a, 0x68, 0x16, 0xc4, 0xa2, 0xac, 0xeb, 0x07, 0x84, 0xba, 0x41, 0x88, - 0xe8, 0x09, 0x28, 0x91, 0x98, 0xa4, 0xca, 0x3d, 0xa9, 0x8b, 0xd5, 0x93, 0xae, 0x45, 0x43, 0x4a, - 0x52, 0x2c, 0x72, 0x6a, 0x93, 0x12, 0x05, 0xc5, 0xd9, 0x71, 0x1a, 0x8f, 0x10, 0x82, 0x94, 0x22, - 0x5b, 0x32, 0x5d, 0xc9, 0x05, 0x91, 0xfe, 0x26, 0x34, 0x5d, 0xb6, 0xba, 0x7c, 0x7d, 0xe8, 0x6f, - 0x74, 0x0e, 0x32, 0x5c, 0x6d, 0x92, 0xaa, 0xe5, 0x23, 0xb4, 0x0c, 0x69, 0xdd, 0xd0, 0x4e, 0x31, - 0xdd, 0xba, 0x9c, 0xc8, 0x06, 0xc2, 0x07, 0x09, 0x58, 0x1c, 0x8b, 0x5c, 0x44, 0x6f, 0x57, 0x36, - 0xbb, 0xf6, 0xbb, 0xc8, 0x6f, 0xf4, 0x3c, 0xd1, 0x2b, 0x2b, 0xd8, 0xe0, 0xd1, 0xbe, 0x3a, 0xbe, - 0xd4, 0x4d, 0xfa, 0x9c, 0x2f, 0x0d, 0xe7, 0x46, 0x77, 0xa1, 0xd2, 0x93, 0x4d, 0x4b, 0x62, 0xde, - 0x5f, 0xf2, 0x44, 0xfe, 0xc7, 0xc6, 0x16, 0x99, 0xc5, 0x0a, 0x62, 0xd0, 0x5c, 0x49, 0x89, 0x88, - 0xba, 0x54, 0x74, 0x08, 0xcb, 0xed, 0xb3, 0xf7, 0xe5, 0x81, 0xa5, 0x0e, 0xb0, 0x34, 0xb6, 0x6b, - 0xe3, 0xa9, 0xc4, 0x3d, 0xd5, 0x6c, 0xe3, 0xae, 0x7c, 0xaa, 0x6a, 0xf6, 0x67, 0x2d, 0x39, 0xf2, - 0xce, 0x8e, 0x9a, 0x82, 0x08, 0x25, 0x7f, 0xd8, 0x45, 0x25, 0x48, 0x58, 0x23, 0x3e, 0xff, 0x84, - 0x35, 0x42, 0xcf, 0x41, 0x8a, 0xcc, 0x91, 0xce, 0xbd, 0x34, 0xe1, 0x45, 0x5c, 0xae, 0x75, 0xa6, - 0x63, 0x91, 0x72, 0x0a, 0x82, 0x73, 0x1a, 0x9c, 0x50, 0x1c, 0xd4, 0x2a, 0x5c, 0x85, 0x72, 0x20, - 0xce, 0x7a, 0xb6, 0x2f, 0xee, 0xdd, 0x3e, 0xa1, 0x0c, 0x45, 0x5f, 0x40, 0x15, 0xce, 0xc1, 0xf2, - 0xa4, 0xf8, 0x28, 0x74, 0x1d, 0xba, 0x2f, 0xce, 0xa1, 0x5b, 0x90, 0x73, 0x02, 0x24, 0x3b, 0x8d, - 0xe7, 0xc7, 0x66, 0x61, 0x33, 0x8b, 0x0e, 0x2b, 0x39, 0x86, 0xc4, 0xaa, 0xa9, 0x39, 0x24, 0xe8, - 0x87, 0x67, 0x65, 0x5d, 0x6f, 0xca, 0x66, 0x57, 0x78, 0x1b, 0xaa, 0x61, 0xc1, 0x2f, 0x30, 0x8d, - 0x94, 0x63, 0x85, 0xe7, 0x20, 0x73, 0xac, 0x19, 0x7d, 0xd9, 0xa2, 0xca, 0x8a, 0x22, 0x1f, 0x11, - 0xeb, 0x64, 0x81, 0x30, 0x49, 0xc9, 0x6c, 0x20, 0x48, 0x70, 0x3e, 0x34, 0x00, 0x12, 0x11, 0x75, - 0xa0, 0x60, 0xb6, 0x9e, 0x45, 0x91, 0x0d, 0x5c, 0x45, 0xec, 0x63, 0xd9, 0x80, 0xbc, 0xd6, 0xa4, - 0x73, 0xa5, 0xfa, 0xf3, 0x22, 0x1f, 0x09, 0xbf, 0x4d, 0xc2, 0xb9, 0xc9, 0x61, 0x10, 0x5d, 0x82, - 0x85, 0xbe, 0x3c, 0x92, 0xac, 0x11, 0x3f, 0xcb, 0x6c, 0x3b, 0xa0, 0x2f, 0x8f, 0x5a, 0x23, 0x76, - 0x90, 0x2b, 0x90, 0xb4, 0x46, 0x66, 0x35, 0x71, 0x29, 0x79, 0x65, 0x41, 0x24, 0x3f, 0xd1, 0x21, - 0x2c, 0xf6, 0xb4, 0x8e, 0xdc, 0x93, 0x3c, 0x16, 0xcf, 0x8d, 0xfd, 0xf2, 0xd8, 0x62, 0xb3, 0x80, - 0x86, 0x95, 0x31, 0xa3, 0x2f, 0x53, 0x1d, 0x3b, 0x8e, 0xe5, 0x7f, 0x43, 0x56, 0xef, 0xd9, 0xa3, - 0xb4, 0xcf, 0x53, 0xd8, 0x3e, 0x3b, 0x33, 0xb7, 0xcf, 0x7e, 0x0e, 0x96, 0x07, 0x78, 0x64, 0x79, - 0xbe, 0x91, 0x19, 0x4e, 0x96, 0xee, 0x05, 0x22, 0xcf, 0xdc, 0xf7, 0x13, 0x1b, 0x42, 0x57, 0x69, - 0x66, 0xa1, 0x6b, 0x26, 0x36, 0x24, 0x59, 0x51, 0x0c, 0x6c, 0x9a, 0x34, 0xb3, 0x5d, 0xa0, 0xe9, - 0x02, 0xa5, 0xaf, 0x33, 0xb2, 0xf0, 0x4b, 0xef, 0x5e, 0xf9, 0x33, 0x09, 0xbe, 0x13, 0x71, 0x77, - 0x27, 0x0e, 0x60, 0x99, 0xcb, 0x2b, 0xbe, 0xcd, 0x48, 0x44, 0xf5, 0x3c, 0xc8, 0x16, 0x8f, 0xb0, - 0x0f, 0xc9, 0x87, 0xdb, 0x07, 0xdb, 0xdb, 0xa6, 0x3c, 0xde, 0xf6, 0xbf, 0x6c, 0x6f, 0x5e, 0x75, - 0xa2, 0x88, 0x9b, 0xa6, 0xa1, 0x6b, 0x90, 0xa2, 0x89, 0x1d, 0xf3, 0x36, 0xe7, 0xc6, 0xe3, 0x05, - 0xe1, 0x12, 0x29, 0x8f, 0xd0, 0x84, 0x5a, 0x78, 0x5a, 0x36, 0x97, 0xa6, 0x9f, 0x25, 0x1d, 0x07, - 0xe8, 0xcb, 0xc2, 0x26, 0x18, 0xc9, 0xeb, 0xb0, 0xa4, 0xe0, 0x8e, 0xaa, 0x7c, 0x55, 0x1b, 0x59, - 0xe4, 0xd2, 0xdf, 0x99, 0x48, 0x04, 0x13, 0xf9, 0x73, 0x01, 0x72, 0x22, 0x36, 0x75, 0x92, 0x7d, - 0xa1, 0x0d, 0xc8, 0xe3, 0x51, 0x07, 0xeb, 0x96, 0x9d, 0xb0, 0x4e, 0x4e, 0xfc, 0x19, 0x77, 0xc3, - 0xe6, 0x24, 0x65, 0xac, 0x23, 0x86, 0x6e, 0x72, 0xc4, 0x22, 0x1c, 0x7c, 0xe0, 0xe2, 0x5e, 0xc8, - 0xe2, 0x79, 0x1b, 0xb2, 0x48, 0x86, 0x56, 0xad, 0x4c, 0x2a, 0x80, 0x59, 0xdc, 0xe4, 0x98, 0x45, - 0x6a, 0xc6, 0xcb, 0x7c, 0xa0, 0x45, 0xdd, 0x07, 0x5a, 0xa4, 0x67, 0x4c, 0x33, 0x04, 0xb5, 0x78, - 0xde, 0x46, 0x2d, 0x32, 0x33, 0xbe, 0x38, 0x00, 0x5b, 0xdc, 0xf1, 0xc3, 0x16, 0xd9, 0x90, 0x28, - 0x64, 0x4b, 0x4f, 0xc5, 0x2d, 0x5e, 0xf1, 0xe0, 0x16, 0xb9, 0x50, 0xc0, 0x80, 0x29, 0x9a, 0x00, - 0x5c, 0xbc, 0xe6, 0x03, 0x2e, 0xf2, 0x33, 0xd6, 0x61, 0x0a, 0x72, 0xb1, 0xe9, 0x45, 0x2e, 0x20, - 0x14, 0x00, 0xe1, 0xfb, 0x1e, 0x06, 0x5d, 0xbc, 0xe8, 0x40, 0x17, 0x85, 0x50, 0x0c, 0x86, 0xcf, - 0x25, 0x88, 0x5d, 0xec, 0x8d, 0x61, 0x17, 0x0c, 0x6b, 0x78, 0x32, 0x54, 0xc5, 0x0c, 0xf0, 0x62, - 0x6f, 0x0c, 0xbc, 0x28, 0xce, 0x50, 0x38, 0x03, 0xbd, 0x78, 0x6b, 0x32, 0x7a, 0x11, 0x8e, 0x2f, - 0xf0, 0xcf, 0x8c, 0x06, 0x5f, 0x48, 0x21, 0xf0, 0x45, 0x39, 0xb4, 0xd4, 0x66, 0xea, 0x23, 0xe3, - 0x17, 0x87, 0x13, 0xf0, 0x0b, 0x86, 0x34, 0x5c, 0x09, 0x55, 0x1e, 0x01, 0xc0, 0x38, 0x9c, 0x00, - 0x60, 0x2c, 0xce, 0x54, 0x3b, 0x13, 0xc1, 0xd8, 0xf2, 0x23, 0x18, 0x68, 0xc6, 0x19, 0x0b, 0x85, - 0x30, 0xda, 0x61, 0x10, 0x06, 0x83, 0x19, 0x9e, 0x09, 0xd5, 0x38, 0x07, 0x86, 0xb1, 0x37, 0x86, - 0x61, 0x2c, 0xcf, 0xb0, 0xb4, 0xa8, 0x20, 0xc6, 0x55, 0x12, 0xfc, 0x03, 0xae, 0x9a, 0xe4, 0xe1, - 0xd8, 0x30, 0x34, 0x83, 0xc3, 0x11, 0x6c, 0x20, 0x5c, 0x21, 0x45, 0xad, 0xeb, 0x96, 0xa7, 0x00, - 0x1e, 0xb4, 0xde, 0xf1, 0xb8, 0x62, 0xe1, 0x77, 0x71, 0x57, 0x96, 0xd6, 0x82, 0xde, 0x82, 0x38, - 0xcf, 0x0b, 0x62, 0x0f, 0x0c, 0x92, 0xf0, 0xc3, 0x20, 0xab, 0x50, 0x20, 0x75, 0x4c, 0x00, 0xe1, - 0x90, 0x75, 0x07, 0xe1, 0xb8, 0x06, 0x8b, 0x34, 0x09, 0x60, 0x60, 0x09, 0x8f, 0xac, 0x29, 0x1a, - 0x59, 0xcb, 0xe4, 0x01, 0x5b, 0x05, 0x16, 0x62, 0x9f, 0x85, 0x25, 0x0f, 0xaf, 0x53, 0x1f, 0xb1, - 0x72, 0xbf, 0xe2, 0x70, 0xaf, 0xf3, 0x42, 0xe9, 0x0f, 0x71, 0x77, 0x85, 0x5c, 0x68, 0x64, 0x12, - 0x8a, 0x11, 0xff, 0x9a, 0x50, 0x8c, 0xc4, 0x57, 0x46, 0x31, 0xbc, 0xf5, 0x5e, 0xd2, 0x5f, 0xef, - 0xfd, 0x33, 0xee, 0xee, 0x89, 0x83, 0x49, 0x74, 0x34, 0x05, 0xf3, 0x0a, 0x8c, 0xfe, 0x26, 0x69, - 0x56, 0x4f, 0x3b, 0xe1, 0x75, 0x16, 0xf9, 0x49, 0xb8, 0x9c, 0xd8, 0x99, 0xe7, 0xa1, 0xd1, 0x29, - 0xde, 0x58, 0xee, 0xc2, 0x8b, 0xb7, 0x0a, 0x24, 0x1f, 0x60, 0x16, 0xe9, 0x16, 0x44, 0xf2, 0x93, - 0xf0, 0x51, 0x23, 0xe3, 0x39, 0x08, 0x1b, 0xa0, 0xdb, 0x90, 0xa7, 0x9d, 0x15, 0x49, 0xd3, 0x4d, - 0x1e, 0x90, 0x7c, 0xe9, 0x1a, 0x6b, 0xa0, 0xac, 0xed, 0x13, 0x9e, 0x3d, 0xdd, 0x14, 0x73, 0x3a, - 0xff, 0xe5, 0x49, 0x9a, 0xf2, 0xbe, 0xa4, 0xe9, 0x02, 0xe4, 0xc9, 0xd7, 0x9b, 0xba, 0xdc, 0xc1, - 0x34, 0xb2, 0xe4, 0x45, 0x97, 0x20, 0xdc, 0x07, 0x34, 0x1e, 0x27, 0x51, 0x13, 0x32, 0xf8, 0x14, - 0x0f, 0x2c, 0x96, 0x53, 0x06, 0xf2, 0x52, 0x56, 0xe2, 0x91, 0xc7, 0x1b, 0x55, 0xb2, 0xc8, 0xff, - 0xf8, 0x7c, 0xb5, 0xc2, 0xb8, 0x9f, 0xd1, 0xfa, 0xaa, 0x85, 0xfb, 0xba, 0x75, 0x26, 0x72, 0x79, - 0xe1, 0xaf, 0x09, 0x28, 0x07, 0xe2, 0xe7, 0xc4, 0xb5, 0xb5, 0x4d, 0x3e, 0xe1, 0xc1, 0x80, 0xa2, - 0xad, 0xf7, 0x45, 0x80, 0x13, 0xd9, 0x94, 0xde, 0x93, 0x07, 0x16, 0x56, 0xf8, 0xa2, 0xe7, 0x4f, - 0x64, 0xf3, 0x0d, 0x4a, 0x20, 0xbb, 0x4e, 0x1e, 0x0f, 0x4d, 0xac, 0x70, 0x34, 0x2a, 0x7b, 0x22, - 0x9b, 0x87, 0x26, 0x56, 0x3c, 0xb3, 0xcc, 0x3e, 0xdc, 0x2c, 0xfd, 0x6b, 0x9c, 0x0b, 0xac, 0xb1, - 0xa7, 0x44, 0xcf, 0x7b, 0x4b, 0x74, 0x54, 0x83, 0x9c, 0x6e, 0xa8, 0x9a, 0xa1, 0x5a, 0x67, 0x74, - 0x63, 0x92, 0xa2, 0x33, 0x46, 0x97, 0xa1, 0xd8, 0xc7, 0x7d, 0x5d, 0xd3, 0x7a, 0x12, 0x73, 0x36, - 0x05, 0x2a, 0xba, 0xc0, 0x89, 0x0d, 0xea, 0x73, 0x3e, 0x4c, 0xb8, 0xa7, 0xcf, 0x85, 0x62, 0xbe, - 0xde, 0xe5, 0x5d, 0x99, 0xb0, 0xbc, 0x1e, 0x0a, 0x99, 0x44, 0x60, 0x7d, 0x9d, 0xf1, 0xb7, 0xb5, - 0xc0, 0xc2, 0x4f, 0x28, 0x3e, 0xeb, 0xcf, 0x8d, 0xd0, 0x01, 0x2c, 0x3a, 0x87, 0x5f, 0x1a, 0x52, - 0xa7, 0x60, 0x9b, 0x73, 0x54, 0xef, 0x51, 0x39, 0xf5, 0x93, 0x4d, 0xf4, 0x26, 0x3c, 0x1a, 0xf0, - 0x6c, 0x8e, 0xea, 0x44, 0x54, 0x07, 0xf7, 0x88, 0xdf, 0xc1, 0xd9, 0xaa, 0xdd, 0xc5, 0x4a, 0x3e, - 0xe4, 0x99, 0xdb, 0x86, 0x92, 0x3f, 0xcd, 0x9b, 0xb8, 0xfd, 0x97, 0xa1, 0x68, 0x60, 0x4b, 0x56, - 0x07, 0x92, 0x0f, 0x54, 0x5d, 0x60, 0x44, 0x0e, 0xd5, 0xee, 0xc3, 0x23, 0x13, 0xd3, 0x3d, 0xf4, - 0x02, 0xe4, 0xdd, 0x4c, 0x91, 0xad, 0xea, 0x14, 0xd0, 0xcd, 0xe5, 0x15, 0x7e, 0x1f, 0x77, 0x55, - 0xfa, 0x61, 0xbc, 0x06, 0x64, 0x0c, 0x6c, 0x0e, 0x7b, 0x0c, 0x58, 0x2b, 0xdd, 0x78, 0x36, 0x5a, - 0xa2, 0x48, 0xa8, 0xc3, 0x9e, 0x25, 0x72, 0x61, 0xe1, 0x3e, 0x64, 0x18, 0x05, 0x15, 0x20, 0x7b, - 0xb8, 0x7b, 0x77, 0x77, 0xef, 0x8d, 0xdd, 0x4a, 0x0c, 0x01, 0x64, 0xd6, 0xeb, 0xf5, 0xc6, 0x7e, - 0xab, 0x12, 0x47, 0x79, 0x48, 0xaf, 0x6f, 0xec, 0x89, 0xad, 0x4a, 0x82, 0x90, 0xc5, 0xc6, 0x9d, - 0x46, 0xbd, 0x55, 0x49, 0xa2, 0x45, 0x28, 0xb2, 0xdf, 0xd2, 0xd6, 0x9e, 0x78, 0x6f, 0xbd, 0x55, - 0x49, 0x79, 0x48, 0x07, 0x8d, 0xdd, 0xcd, 0x86, 0x58, 0x49, 0x0b, 0xff, 0x07, 0xe7, 0x43, 0x53, - 0x4b, 0x17, 0xa3, 0x8b, 0x7b, 0x30, 0x3a, 0xe1, 0x17, 0x09, 0xa8, 0x85, 0xe7, 0x8b, 0xe8, 0x4e, - 0x60, 0xe2, 0x37, 0xe6, 0x48, 0x36, 0x03, 0xb3, 0x47, 0x4f, 0x40, 0xc9, 0xc0, 0xc7, 0xd8, 0xea, - 0x74, 0x59, 0xfe, 0xca, 0x02, 0x66, 0x51, 0x2c, 0x72, 0x2a, 0x15, 0x32, 0x19, 0xdb, 0x3b, 0xb8, - 0x63, 0x49, 0xcc, 0x17, 0x31, 0xa3, 0xcb, 0x13, 0x36, 0x42, 0x3d, 0x60, 0x44, 0xe1, 0xed, 0xb9, - 0xd6, 0x32, 0x0f, 0x69, 0xb1, 0xd1, 0x12, 0xdf, 0xac, 0x24, 0x11, 0x82, 0x12, 0xfd, 0x29, 0x1d, - 0xec, 0xae, 0xef, 0x1f, 0x34, 0xf7, 0xc8, 0x5a, 0x2e, 0x41, 0xd9, 0x5e, 0x4b, 0x9b, 0x98, 0x16, - 0xfe, 0x94, 0x80, 0x47, 0x43, 0xb2, 0x5d, 0x74, 0x1b, 0xc0, 0x1a, 0x49, 0x06, 0xee, 0x68, 0x86, - 0x12, 0x6e, 0x64, 0xad, 0x91, 0x48, 0x39, 0xc4, 0xbc, 0xc5, 0x7f, 0x99, 0x53, 0xa0, 0x5d, 0xf4, - 0x32, 0x57, 0x4a, 0x66, 0x65, 0x1f, 0xb5, 0x8b, 0x13, 0x10, 0x4c, 0xdc, 0x21, 0x8a, 0xe9, 0xda, - 0x52, 0xc5, 0x94, 0x1f, 0xdd, 0x9b, 0xe4, 0x54, 0x22, 0x36, 0x56, 0xe6, 0x73, 0x27, 0xe9, 0x87, - 0x73, 0x27, 0xc2, 0xaf, 0x92, 0xde, 0x85, 0xf5, 0x27, 0xf7, 0x7b, 0x90, 0x31, 0x2d, 0xd9, 0x1a, - 0x9a, 0xdc, 0xe0, 0x5e, 0x88, 0x5a, 0x29, 0xac, 0xd9, 0x3f, 0x0e, 0xa8, 0xb8, 0xc8, 0xd5, 0x7c, - 0xb7, 0xde, 0xa6, 0x70, 0x0b, 0x4a, 0xfe, 0xc5, 0x09, 0x3f, 0x32, 0xae, 0xcf, 0x49, 0x08, 0x6f, - 0xb9, 0xf9, 0x97, 0x07, 0x5f, 0xdc, 0x82, 0x52, 0xa0, 0x5c, 0x8a, 0x8f, 0xd7, 0xf3, 0x2e, 0x3e, - 0xe8, 0x94, 0x42, 0x62, 0xf1, 0xd4, 0x3b, 0x14, 0x7e, 0x1d, 0x87, 0xc7, 0xa6, 0x14, 0x54, 0xe8, - 0xf5, 0x80, 0x21, 0xbc, 0x38, 0x4f, 0x39, 0xb6, 0xc6, 0x68, 0x7e, 0x53, 0x10, 0x6e, 0xc2, 0x82, - 0x97, 0x1e, 0x6d, 0x15, 0x7e, 0x9a, 0x74, 0x83, 0x82, 0x1f, 0xda, 0xfc, 0xda, 0x32, 0xd1, 0x80, - 0x21, 0x26, 0xe6, 0x34, 0xc4, 0x89, 0xd9, 0x44, 0xf2, 0x9b, 0xcb, 0x26, 0x52, 0x0f, 0x99, 0x4d, - 0x78, 0x4f, 0x64, 0xda, 0x7f, 0x22, 0xc7, 0x02, 0x7f, 0x66, 0x42, 0xe0, 0x7f, 0x13, 0xc0, 0xd3, - 0x9c, 0x5c, 0x86, 0xb4, 0xa1, 0x0d, 0x07, 0x0a, 0x35, 0x93, 0xb4, 0xc8, 0x06, 0xe8, 0x16, 0xa4, - 0x89, 0xb9, 0xd9, 0x8b, 0x39, 0xee, 0x9a, 0x89, 0xb9, 0x78, 0x40, 0x65, 0xc6, 0x2d, 0xa8, 0x80, - 0xc6, 0x1b, 0x44, 0x21, 0xaf, 0x78, 0xc5, 0xff, 0x8a, 0xc7, 0x43, 0x5b, 0x4d, 0x93, 0x5f, 0xf5, - 0x3e, 0xa4, 0xa9, 0x79, 0x90, 0x04, 0x88, 0x36, 0x39, 0x79, 0x45, 0x4d, 0x7e, 0xa3, 0x1f, 0x01, - 0xc8, 0x96, 0x65, 0xa8, 0xed, 0xa1, 0xfb, 0x82, 0xd5, 0xc9, 0xe6, 0xb5, 0x6e, 0xf3, 0x6d, 0x5c, - 0xe0, 0x76, 0xb6, 0xec, 0x8a, 0x7a, 0x6c, 0xcd, 0xa3, 0x50, 0xd8, 0x85, 0x92, 0x5f, 0xd6, 0xae, - 0x01, 0xd9, 0x37, 0xf8, 0x6b, 0x40, 0x56, 0xd2, 0xf3, 0x1a, 0xd0, 0xa9, 0x20, 0x93, 0xac, 0x9f, - 0x4d, 0x07, 0xc2, 0xbf, 0xe2, 0xb0, 0xe0, 0xb5, 0xce, 0xff, 0xb5, 0x32, 0x4a, 0xf8, 0x30, 0x0e, - 0x39, 0x67, 0xf2, 0x21, 0xcd, 0x64, 0x77, 0xed, 0x12, 0xde, 0xd6, 0x29, 0xeb, 0x4e, 0x27, 0x9d, - 0x9e, 0xf7, 0x4b, 0x4e, 0xc6, 0x15, 0x86, 0x7a, 0x7b, 0x57, 0xda, 0x6e, 0xfb, 0xf3, 0x04, 0xf3, - 0xe7, 0xfc, 0x3b, 0x48, 0xaa, 0x81, 0xbe, 0x07, 0x19, 0xb9, 0xe3, 0x60, 0xfd, 0xa5, 0x09, 0xe0, - 0xaf, 0xcd, 0xba, 0xd6, 0x1a, 0xad, 0x53, 0x4e, 0x91, 0x4b, 0xf0, 0xaf, 0x4a, 0x38, 0x3d, 0xf3, - 0x57, 0x89, 0x5e, 0xc6, 0xe3, 0x77, 0x9b, 0x25, 0x80, 0xc3, 0xdd, 0x7b, 0x7b, 0x9b, 0xdb, 0x5b, - 0xdb, 0x8d, 0x4d, 0x9e, 0x73, 0x6d, 0x6e, 0x36, 0x36, 0x2b, 0x09, 0xc2, 0x27, 0x36, 0xee, 0xed, - 0x1d, 0x35, 0x36, 0x2b, 0x49, 0xe1, 0x25, 0xc8, 0x3b, 0xae, 0x07, 0x55, 0x21, 0x6b, 0xf7, 0x2d, - 0xe2, 0xdc, 0x01, 0xb0, 0x21, 0xbd, 0x2f, 0xa1, 0xbd, 0xc7, 0x3b, 0xc6, 0x49, 0x91, 0x0d, 0x04, - 0x05, 0xca, 0x01, 0xbf, 0x85, 0x5e, 0x82, 0xac, 0x3e, 0x6c, 0x4b, 0xb6, 0xd1, 0x06, 0xba, 0x3c, - 0x36, 0x14, 0x31, 0x6c, 0xf7, 0xd4, 0xce, 0x5d, 0x7c, 0x66, 0x2f, 0x93, 0x3e, 0x6c, 0xdf, 0x65, - 0xb6, 0xcd, 0xde, 0x92, 0xf0, 0xbe, 0xe5, 0xc7, 0x71, 0xc8, 0xd9, 0x67, 0x15, 0x7d, 0x1f, 0xf2, - 0x8e, 0x4f, 0x74, 0xee, 0xd1, 0x84, 0x3a, 0x53, 0xae, 0xdf, 0x15, 0x41, 0xd7, 0x60, 0xd1, 0x54, - 0x4f, 0x06, 0x76, 0x93, 0x8b, 0x61, 0x7f, 0x09, 0x7a, 0x68, 0xca, 0xec, 0xc1, 0x8e, 0x0d, 0x58, - 0xdd, 0x49, 0xe5, 0x92, 0x95, 0xd4, 0x9d, 0x54, 0x2e, 0x55, 0x49, 0x93, 0xb0, 0x58, 0x09, 0x3a, - 0x8e, 0x6f, 0xf3, 0x63, 0x48, 0xfa, 0x1d, 0x88, 0xef, 0xcc, 0x36, 0x03, 0xe1, 0xfb, 0x83, 0x04, - 0x14, 0x3c, 0x6d, 0x34, 0xf4, 0xff, 0x1e, 0x2f, 0x56, 0x9a, 0x10, 0x77, 0x3c, 0xbc, 0xee, 0x75, - 0x0d, 0xff, 0xc4, 0x12, 0xf3, 0x4f, 0x2c, 0xec, 0xda, 0x8d, 0xdd, 0x8d, 0x4b, 0xcd, 0xdd, 0x8d, - 0x7b, 0x06, 0x90, 0xa5, 0x59, 0x72, 0x4f, 0x3a, 0xd5, 0x2c, 0x75, 0x70, 0x22, 0x31, 0x3b, 0x61, - 0x3e, 0xa7, 0x42, 0x9f, 0x1c, 0xd1, 0x07, 0xfb, 0x8e, 0xc9, 0x38, 0x45, 0xe2, 0xbc, 0xb7, 0x2f, - 0xce, 0x41, 0x86, 0xd7, 0x41, 0xec, 0xfa, 0x05, 0x1f, 0x4d, 0x6c, 0x3b, 0xd6, 0x20, 0xd7, 0xc7, - 0x96, 0x4c, 0x1d, 0x28, 0x8b, 0x99, 0xce, 0xf8, 0xda, 0x8b, 0x50, 0xf0, 0x5c, 0x84, 0x21, 0x3e, - 0x75, 0xb7, 0xf1, 0x46, 0x25, 0x56, 0xcb, 0x7e, 0xf4, 0xc9, 0xa5, 0xe4, 0x2e, 0x7e, 0x8f, 0x1c, - 0x37, 0xb1, 0x51, 0x6f, 0x36, 0xea, 0x77, 0x2b, 0xf1, 0x5a, 0xe1, 0xa3, 0x4f, 0x2e, 0x65, 0x45, - 0x4c, 0xbb, 0x44, 0xd7, 0xee, 0x42, 0x39, 0xb0, 0x31, 0xfe, 0xd3, 0x8d, 0xa0, 0xb4, 0x79, 0xb8, - 0xbf, 0xb3, 0x5d, 0x5f, 0x6f, 0x35, 0xa4, 0xa3, 0xbd, 0x56, 0xa3, 0x12, 0x47, 0x8f, 0xc2, 0xd2, - 0xce, 0xf6, 0x6b, 0xcd, 0x96, 0x54, 0xdf, 0xd9, 0x6e, 0xec, 0xb6, 0xa4, 0xf5, 0x56, 0x6b, 0xbd, - 0x7e, 0xb7, 0x92, 0xb8, 0xf1, 0x9b, 0x02, 0x94, 0xd7, 0x37, 0xea, 0xdb, 0xa4, 0x12, 0x54, 0x3b, - 0x32, 0xf5, 0x15, 0x75, 0x48, 0x51, 0xc8, 0x79, 0xea, 0xd5, 0xe6, 0xda, 0xf4, 0x36, 0x22, 0xda, - 0x82, 0x34, 0x45, 0xa3, 0xd1, 0xf4, 0xbb, 0xce, 0xb5, 0x19, 0x7d, 0x45, 0xf2, 0x31, 0xf4, 0x38, - 0x4d, 0xbd, 0xfc, 0x5c, 0x9b, 0xde, 0x66, 0x44, 0x3b, 0x90, 0xb5, 0xc1, 0xc2, 0x59, 0xd7, 0x88, - 0x6b, 0x33, 0xfb, 0x75, 0x64, 0x6a, 0x0c, 0xd4, 0x9d, 0x7e, 0x2f, 0xba, 0x36, 0xa3, 0x01, 0x89, - 0xb6, 0x21, 0xc3, 0xf1, 0x94, 0x19, 0x57, 0x82, 0x6b, 0xb3, 0xfa, 0x6e, 0x48, 0x84, 0xbc, 0x0b, - 0x97, 0xcf, 0xbe, 0xed, 0x5d, 0x8b, 0xd0, 0x5b, 0x45, 0xf7, 0xa1, 0xe8, 0xc7, 0x68, 0xa2, 0x5d, - 0x3b, 0xae, 0x45, 0xec, 0xf0, 0x11, 0xfd, 0x7e, 0xc0, 0x26, 0xda, 0x35, 0xe4, 0x5a, 0xc4, 0x86, - 0x1f, 0x7a, 0x07, 0x16, 0xc7, 0x01, 0x95, 0xe8, 0xb7, 0x92, 0x6b, 0x73, 0xb4, 0x00, 0x51, 0x1f, - 0xd0, 0x04, 0x20, 0x66, 0x8e, 0x4b, 0xca, 0xb5, 0x79, 0x3a, 0x82, 0x48, 0x81, 0x72, 0x10, 0xdc, - 0x88, 0x7a, 0x69, 0xb9, 0x16, 0xb9, 0x3b, 0xc8, 0xde, 0xe2, 0xaf, 0xf4, 0xa3, 0x5e, 0x62, 0xae, - 0x45, 0x6e, 0x16, 0xa2, 0x43, 0x00, 0x4f, 0xa5, 0x1a, 0xe1, 0x52, 0x73, 0x2d, 0x4a, 0xdb, 0x10, - 0xe9, 0xb0, 0x34, 0xa9, 0x42, 0x9d, 0xe7, 0x8e, 0x73, 0x6d, 0xae, 0x6e, 0x22, 0xb1, 0x67, 0x7f, - 0xad, 0x19, 0xed, 0xce, 0x73, 0x2d, 0x62, 0x5b, 0x71, 0xa3, 0xf1, 0xe9, 0x17, 0x2b, 0xf1, 0xcf, - 0xbe, 0x58, 0x89, 0xff, 0xfd, 0x8b, 0x95, 0xf8, 0xc7, 0x5f, 0xae, 0xc4, 0x3e, 0xfb, 0x72, 0x25, - 0xf6, 0x97, 0x2f, 0x57, 0x62, 0x3f, 0x78, 0xfa, 0x44, 0xb5, 0xba, 0xc3, 0xf6, 0x5a, 0x47, 0xeb, - 0x5f, 0xf7, 0xfe, 0xfd, 0x65, 0xd2, 0x5f, 0x72, 0xda, 0x19, 0x1a, 0x50, 0x6f, 0xfe, 0x27, 0x00, - 0x00, 0xff, 0xff, 0xc7, 0x45, 0xe7, 0x5f, 0xb2, 0x33, 0x00, 0x00, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5b, 0xcb, 0x73, 0x23, 0xe5, + 0xb5, 0x97, 0x5a, 0xef, 0x23, 0xeb, 0xe1, 0xcf, 0x66, 0xd0, 0x88, 0x19, 0x7b, 0xe8, 0xa9, 0x81, + 0x99, 0x01, 0x3c, 0x5c, 0xcf, 0x1d, 0x18, 0xee, 0xc0, 0xa5, 0x6c, 0x59, 0x83, 0xcc, 0x78, 0x6c, + 0xd3, 0x96, 0x4d, 0x71, 0x6f, 0x32, 0x4d, 0x4b, 0xfd, 0xd9, 0x6a, 0x46, 0x52, 0x37, 0xdd, 0x2d, + 0x23, 0xb3, 0x0c, 0xc5, 0x86, 0x4a, 0x55, 0xd8, 0xa4, 0x92, 0x54, 0x85, 0x5d, 0x52, 0x95, 0xfc, + 0x07, 0x59, 0x65, 0x95, 0x05, 0x8b, 0x2c, 0x58, 0x25, 0xa9, 0x2c, 0x48, 0x0a, 0x76, 0xf9, 0x07, + 0xb2, 0x4b, 0x52, 0xdf, 0xa3, 0x5f, 0x52, 0xb7, 0x1e, 0x0c, 0x50, 0x95, 0x0a, 0x3b, 0xf5, 0xe9, + 0x73, 0x4e, 0x7f, 0x8f, 0xf3, 0x9d, 0xc7, 0xef, 0x7c, 0x82, 0x27, 0x6c, 0xdc, 0x57, 0xb1, 0xd9, + 0xd3, 0xfa, 0xf6, 0x0d, 0xa5, 0xd5, 0xd6, 0x6e, 0xd8, 0x67, 0x06, 0xb6, 0xd6, 0x0c, 0x53, 0xb7, + 0x75, 0x54, 0xf2, 0x5e, 0xae, 0x91, 0x97, 0xd5, 0x8b, 0x3e, 0xee, 0xb6, 0x79, 0x66, 0xd8, 0xfa, + 0x0d, 0xc3, 0xd4, 0xf5, 0x63, 0xc6, 0x5f, 0xbd, 0xe0, 0x7b, 0x4d, 0xf5, 0xf8, 0xb5, 0x05, 0xde, + 0x72, 0xe1, 0x87, 0xf8, 0xcc, 0x79, 0x7b, 0x71, 0x4c, 0xd6, 0x50, 0x4c, 0xa5, 0xe7, 0xbc, 0x5e, + 0x3d, 0xd1, 0xf5, 0x93, 0x2e, 0xbe, 0x41, 0x9f, 0x5a, 0x83, 0xe3, 0x1b, 0xb6, 0xd6, 0xc3, 0x96, + 0xad, 0xf4, 0x0c, 0xce, 0xb0, 0x7c, 0xa2, 0x9f, 0xe8, 0xf4, 0xe7, 0x0d, 0xf2, 0x8b, 0x51, 0xc5, + 0x7f, 0x02, 0x64, 0x24, 0xfc, 0xee, 0x00, 0x5b, 0x36, 0x5a, 0x87, 0x24, 0x6e, 0x77, 0xf4, 0x4a, + 0xfc, 0x52, 0xfc, 0x6a, 0x7e, 0xfd, 0xc2, 0xda, 0xc8, 0xe4, 0xd6, 0x38, 0x5f, 0xbd, 0xdd, 0xd1, + 0x1b, 0x31, 0x89, 0xf2, 0xa2, 0x5b, 0x90, 0x3a, 0xee, 0x0e, 0xac, 0x4e, 0x45, 0xa0, 0x42, 0x17, + 0xa3, 0x84, 0xee, 0x12, 0xa6, 0x46, 0x4c, 0x62, 0xdc, 0xe4, 0x53, 0x5a, 0xff, 0x58, 0xaf, 0x24, + 0x26, 0x7f, 0x6a, 0xbb, 0x7f, 0x4c, 0x3f, 0x45, 0x78, 0xd1, 0x26, 0x80, 0xd6, 0xd7, 0x6c, 0xb9, + 0xdd, 0x51, 0xb4, 0x7e, 0x25, 0x49, 0x25, 0x9f, 0x8c, 0x96, 0xd4, 0xec, 0x1a, 0x61, 0x6c, 0xc4, + 0xa4, 0x9c, 0xe6, 0x3c, 0x90, 0xe1, 0xbe, 0x3b, 0xc0, 0xe6, 0x59, 0x25, 0x35, 0x79, 0xb8, 0x6f, + 0x10, 0x26, 0x32, 0x5c, 0xca, 0x8d, 0xb6, 0x21, 0xdf, 0xc2, 0x27, 0x5a, 0x5f, 0x6e, 0x75, 0xf5, + 0xf6, 0xc3, 0x4a, 0x9a, 0x0a, 0x8b, 0x51, 0xc2, 0x9b, 0x84, 0x75, 0x93, 0x70, 0x6e, 0x0a, 0x95, + 0x78, 0x23, 0x26, 0x41, 0xcb, 0xa5, 0xa0, 0x97, 0x21, 0xdb, 0xee, 0xe0, 0xf6, 0x43, 0xd9, 0x1e, + 0x56, 0x32, 0x54, 0xcf, 0x6a, 0x94, 0x9e, 0x1a, 0xe1, 0x6b, 0x0e, 0x1b, 0x31, 0x29, 0xd3, 0x66, + 0x3f, 0xd1, 0x5d, 0x00, 0x15, 0x77, 0xb5, 0x53, 0x6c, 0x12, 0xf9, 0xec, 0xe4, 0x35, 0xd8, 0x62, + 0x9c, 0xcd, 0x21, 0x1f, 0x46, 0x4e, 0x75, 0x08, 0xa8, 0x06, 0x39, 0xdc, 0x57, 0xf9, 0x74, 0x72, + 0x54, 0xcd, 0xa5, 0xc8, 0xfd, 0xee, 0xab, 0xfe, 0xc9, 0x64, 0x31, 0x7f, 0x46, 0xb7, 0x21, 0xdd, + 0xd6, 0x7b, 0x3d, 0xcd, 0xae, 0x00, 0xd5, 0xb0, 0x12, 0x39, 0x11, 0xca, 0xd5, 0x88, 0x49, 0x9c, + 0x1f, 0xed, 0x42, 0xb1, 0xab, 0x59, 0xb6, 0x6c, 0xf5, 0x15, 0xc3, 0xea, 0xe8, 0xb6, 0x55, 0xc9, + 0x53, 0x0d, 0x57, 0xa2, 0x34, 0xec, 0x68, 0x96, 0x7d, 0xe0, 0x30, 0x37, 0x62, 0x52, 0xa1, 0xeb, + 0x27, 0x10, 0x7d, 0xfa, 0xf1, 0x31, 0x36, 0x5d, 0x85, 0x95, 0x85, 0xc9, 0xfa, 0xf6, 0x08, 0xb7, + 0x23, 0x4f, 0xf4, 0xe9, 0x7e, 0x02, 0xfa, 0x7f, 0x58, 0xea, 0xea, 0x8a, 0xea, 0xaa, 0x93, 0xdb, + 0x9d, 0x41, 0xff, 0x61, 0xa5, 0x40, 0x95, 0x5e, 0x8b, 0x1c, 0xa4, 0xae, 0xa8, 0x8e, 0x8a, 0x1a, + 0x11, 0x68, 0xc4, 0xa4, 0xc5, 0xee, 0x28, 0x11, 0x3d, 0x80, 0x65, 0xc5, 0x30, 0xba, 0x67, 0xa3, + 0xda, 0x8b, 0x54, 0xfb, 0xf5, 0x28, 0xed, 0x1b, 0x44, 0x66, 0x54, 0x3d, 0x52, 0xc6, 0xa8, 0xa8, + 0x09, 0x65, 0xc3, 0xc4, 0x86, 0x62, 0x62, 0xd9, 0x30, 0x75, 0x43, 0xb7, 0x94, 0x6e, 0xa5, 0x44, + 0x75, 0x3f, 0x1d, 0xa5, 0x7b, 0x9f, 0xf1, 0xef, 0x73, 0xf6, 0x46, 0x4c, 0x2a, 0x19, 0x41, 0x12, + 0xd3, 0xaa, 0xb7, 0xb1, 0x65, 0x79, 0x5a, 0xcb, 0xd3, 0xb4, 0x52, 0xfe, 0xa0, 0xd6, 0x00, 0x09, + 0xd5, 0x21, 0x8f, 0x87, 0x44, 0x5c, 0x3e, 0xd5, 0x6d, 0x5c, 0x59, 0x9c, 0x7c, 0xb0, 0xea, 0x94, + 0xf5, 0x48, 0xb7, 0x31, 0x39, 0x54, 0xd8, 0x7d, 0x42, 0x0a, 0x3c, 0x76, 0x8a, 0x4d, 0xed, 0xf8, + 0x8c, 0xaa, 0x91, 0xe9, 0x1b, 0x4b, 0xd3, 0xfb, 0x15, 0x44, 0x15, 0x3e, 0x13, 0xa5, 0xf0, 0x88, + 0x0a, 0x11, 0x15, 0x75, 0x47, 0xa4, 0x11, 0x93, 0x96, 0x4e, 0xc7, 0xc9, 0xc4, 0xc4, 0x8e, 0xb5, + 0xbe, 0xd2, 0xd5, 0xde, 0xc7, 0xfc, 0xd8, 0x2c, 0x4d, 0x36, 0xb1, 0xbb, 0x9c, 0x9b, 0x9e, 0x15, + 0x62, 0x62, 0xc7, 0x7e, 0xc2, 0x66, 0x06, 0x52, 0xa7, 0x4a, 0x77, 0x80, 0xc5, 0xa7, 0x21, 0xef, + 0x73, 0xac, 0xa8, 0x02, 0x99, 0x1e, 0xb6, 0x2c, 0xe5, 0x04, 0x53, 0x3f, 0x9c, 0x93, 0x9c, 0x47, + 0xb1, 0x08, 0x0b, 0x7e, 0x67, 0x2a, 0x7e, 0x1c, 0x77, 0x25, 0x89, 0x9f, 0x24, 0x92, 0xa7, 0xd8, + 0xa4, 0xd3, 0xe6, 0x92, 0xfc, 0x11, 0x5d, 0x86, 0x02, 0x1d, 0xb2, 0xec, 0xbc, 0x27, 0xce, 0x3a, + 0x29, 0x2d, 0x50, 0xe2, 0x11, 0x67, 0x5a, 0x85, 0xbc, 0xb1, 0x6e, 0xb8, 0x2c, 0x09, 0xca, 0x02, + 0xc6, 0xba, 0xe1, 0x30, 0x3c, 0x09, 0x0b, 0x64, 0x7e, 0x2e, 0x47, 0x92, 0x7e, 0x24, 0x4f, 0x68, + 0x9c, 0x45, 0xfc, 0xbd, 0x00, 0xe5, 0x51, 0x07, 0x8c, 0x6e, 0x43, 0x92, 0xc4, 0x22, 0x1e, 0x56, + 0xaa, 0x6b, 0x2c, 0x50, 0xad, 0x39, 0x81, 0x6a, 0xad, 0xe9, 0x04, 0xaa, 0xcd, 0xec, 0xa7, 0x9f, + 0xaf, 0xc6, 0x3e, 0xfe, 0xcb, 0x6a, 0x5c, 0xa2, 0x12, 0xe8, 0x3c, 0xf1, 0x95, 0x8a, 0xd6, 0x97, + 0x35, 0x95, 0x0e, 0x39, 0x47, 0x1c, 0xa1, 0xa2, 0xf5, 0xb7, 0x55, 0xb4, 0x03, 0xe5, 0xb6, 0xde, + 0xb7, 0x70, 0xdf, 0x1a, 0x58, 0x32, 0x0b, 0x84, 0x3c, 0x98, 0x04, 0xdc, 0x21, 0x0b, 0xaf, 0x35, + 0x87, 0x73, 0x9f, 0x32, 0x4a, 0xa5, 0x76, 0x90, 0x40, 0xdc, 0xea, 0xa9, 0xd2, 0xd5, 0x54, 0xc5, + 0xd6, 0x4d, 0xab, 0x92, 0xbc, 0x94, 0x08, 0xf5, 0x87, 0x47, 0x0e, 0xcb, 0xa1, 0xa1, 0x2a, 0x36, + 0xde, 0x4c, 0x92, 0xe1, 0x4a, 0x3e, 0x49, 0xf4, 0x14, 0x94, 0x14, 0xc3, 0x90, 0x2d, 0x5b, 0xb1, + 0xb1, 0xdc, 0x3a, 0xb3, 0xb1, 0x45, 0x03, 0xcd, 0x82, 0x54, 0x50, 0x0c, 0xe3, 0x80, 0x50, 0x37, + 0x09, 0x11, 0x5d, 0x81, 0x22, 0x89, 0x49, 0x9a, 0xd2, 0x95, 0x3b, 0x58, 0x3b, 0xe9, 0xd8, 0x34, + 0xa4, 0x24, 0xa4, 0x02, 0xa7, 0x36, 0x28, 0x51, 0x54, 0xdd, 0x1d, 0xa7, 0xf1, 0x08, 0x21, 0x48, + 0xaa, 0x8a, 0xad, 0xd0, 0x95, 0x5c, 0x90, 0xe8, 0x6f, 0x42, 0x33, 0x14, 0xbb, 0xc3, 0xd7, 0x87, + 0xfe, 0x46, 0xe7, 0x20, 0xcd, 0xd5, 0x26, 0xa8, 0x5a, 0xfe, 0x84, 0x96, 0x21, 0x65, 0x98, 0xfa, + 0x29, 0xa6, 0x5b, 0x97, 0x95, 0xd8, 0x83, 0xf8, 0x81, 0x00, 0x8b, 0x63, 0x91, 0x8b, 0xe8, 0xed, + 0x28, 0x56, 0xc7, 0xf9, 0x16, 0xf9, 0x8d, 0x5e, 0x20, 0x7a, 0x15, 0x15, 0x9b, 0x3c, 0xda, 0x57, + 0xc6, 0x97, 0xba, 0x41, 0xdf, 0xf3, 0xa5, 0xe1, 0xdc, 0xe8, 0x1e, 0x94, 0xbb, 0x8a, 0x65, 0xcb, + 0xcc, 0xfb, 0xcb, 0xbe, 0xc8, 0xff, 0xc4, 0xd8, 0x22, 0xb3, 0x58, 0x41, 0x0c, 0x9a, 0x2b, 0x29, + 0x12, 0x51, 0x8f, 0x8a, 0x0e, 0x61, 0xb9, 0x75, 0xf6, 0xbe, 0xd2, 0xb7, 0xb5, 0x3e, 0x96, 0xc7, + 0x76, 0x6d, 0x3c, 0x95, 0xb8, 0xaf, 0x59, 0x2d, 0xdc, 0x51, 0x4e, 0x35, 0xdd, 0x19, 0xd6, 0x92, + 0x2b, 0xef, 0xee, 0xa8, 0x25, 0x4a, 0x50, 0x0c, 0x86, 0x5d, 0x54, 0x04, 0xc1, 0x1e, 0xf2, 0xf9, + 0x0b, 0xf6, 0x10, 0x3d, 0x0f, 0x49, 0x32, 0x47, 0x3a, 0xf7, 0x62, 0xc8, 0x87, 0xb8, 0x5c, 0xf3, + 0xcc, 0xc0, 0x12, 0xe5, 0x14, 0x45, 0xf7, 0x34, 0xb8, 0xa1, 0x78, 0x54, 0xab, 0x78, 0x0d, 0x4a, + 0x23, 0x71, 0xd6, 0xb7, 0x7d, 0x71, 0xff, 0xf6, 0x89, 0x25, 0x28, 0x04, 0x02, 0xaa, 0x78, 0x0e, + 0x96, 0xc3, 0xe2, 0xa3, 0xd8, 0x71, 0xe9, 0x81, 0x38, 0x87, 0x6e, 0x41, 0xd6, 0x0d, 0x90, 0xec, + 0x34, 0x9e, 0x1f, 0x9b, 0x85, 0xc3, 0x2c, 0xb9, 0xac, 0xe4, 0x18, 0x12, 0xab, 0xa6, 0xe6, 0x20, + 0xd0, 0x81, 0x67, 0x14, 0xc3, 0x68, 0x28, 0x56, 0x47, 0x7c, 0x1b, 0x2a, 0x51, 0xc1, 0x6f, 0x64, + 0x1a, 0x49, 0xd7, 0x0a, 0xcf, 0x41, 0xfa, 0x58, 0x37, 0x7b, 0x8a, 0x4d, 0x95, 0x15, 0x24, 0xfe, + 0x44, 0xac, 0x93, 0x05, 0xc2, 0x04, 0x25, 0xb3, 0x07, 0x51, 0x86, 0xf3, 0x91, 0x01, 0x90, 0x88, + 0x68, 0x7d, 0x15, 0xb3, 0xf5, 0x2c, 0x48, 0xec, 0xc1, 0x53, 0xc4, 0x06, 0xcb, 0x1e, 0xc8, 0x67, + 0x2d, 0x3a, 0x57, 0xaa, 0x3f, 0x27, 0xf1, 0x27, 0xf1, 0xd7, 0x09, 0x38, 0x17, 0x1e, 0x06, 0xd1, + 0x25, 0x58, 0xe8, 0x29, 0x43, 0xd9, 0x1e, 0xf2, 0xb3, 0xcc, 0xb6, 0x03, 0x7a, 0xca, 0xb0, 0x39, + 0x64, 0x07, 0xb9, 0x0c, 0x09, 0x7b, 0x68, 0x55, 0x84, 0x4b, 0x89, 0xab, 0x0b, 0x12, 0xf9, 0x89, + 0x0e, 0x61, 0xb1, 0xab, 0xb7, 0x95, 0xae, 0xec, 0xb3, 0x78, 0x6e, 0xec, 0x97, 0xc7, 0x16, 0x9b, + 0x05, 0x34, 0xac, 0x8e, 0x19, 0x7d, 0x89, 0xea, 0xd8, 0x71, 0x2d, 0xff, 0x1b, 0xb2, 0x7a, 0xdf, + 0x1e, 0xa5, 0x02, 0x9e, 0xc2, 0xf1, 0xd9, 0xe9, 0xb9, 0x7d, 0xf6, 0xf3, 0xb0, 0xdc, 0xc7, 0x43, + 0xdb, 0x37, 0x46, 0x66, 0x38, 0x19, 0xba, 0x17, 0x88, 0xbc, 0xf3, 0xbe, 0x4f, 0x6c, 0x08, 0x5d, + 0xa3, 0x99, 0x85, 0xa1, 0x5b, 0xd8, 0x94, 0x15, 0x55, 0x35, 0xb1, 0x65, 0xd1, 0xcc, 0x76, 0x81, + 0xa6, 0x0b, 0x94, 0xbe, 0xc1, 0xc8, 0xe2, 0xcf, 0xfc, 0x7b, 0x15, 0xcc, 0x24, 0xf8, 0x4e, 0xc4, + 0xbd, 0x9d, 0x38, 0x80, 0x65, 0x2e, 0xaf, 0x06, 0x36, 0x43, 0x98, 0xd5, 0xf3, 0x20, 0x47, 0x7c, + 0x86, 0x7d, 0x48, 0x3c, 0xda, 0x3e, 0x38, 0xde, 0x36, 0xe9, 0xf3, 0xb6, 0xff, 0x66, 0x7b, 0xf3, + 0xaa, 0x1b, 0x45, 0xbc, 0x34, 0x2d, 0x34, 0x8a, 0x78, 0xf3, 0x12, 0x02, 0xee, 0xed, 0xe7, 0x71, + 0xa8, 0x46, 0xe7, 0x65, 0xa1, 0xaa, 0x9e, 0x81, 0x45, 0x77, 0x2e, 0xee, 0xf8, 0xd8, 0xa9, 0x2f, + 0xbb, 0x2f, 0xf8, 0x00, 0x23, 0xa3, 0xe2, 0x15, 0x28, 0x8e, 0x64, 0x8d, 0x6c, 0x17, 0x0a, 0xa7, + 0xfe, 0xef, 0x8b, 0x3f, 0x4e, 0xb8, 0x5e, 0x35, 0x90, 0xda, 0x85, 0x58, 0xde, 0x1b, 0xb0, 0xa4, + 0xe2, 0xb6, 0xa6, 0x7e, 0x55, 0xc3, 0x5b, 0xe4, 0xd2, 0xdf, 0xd9, 0xdd, 0x0c, 0x76, 0xf7, 0xc7, + 0x3c, 0x64, 0x25, 0x6c, 0x19, 0x24, 0xa5, 0x43, 0x9b, 0x90, 0xc3, 0xc3, 0x36, 0x36, 0x6c, 0x27, + 0x0b, 0x0e, 0xaf, 0x26, 0x18, 0x77, 0xdd, 0xe1, 0x24, 0xb5, 0xb1, 0x2b, 0x86, 0x6e, 0x72, 0x18, + 0x24, 0x1a, 0xd1, 0xe0, 0xe2, 0x7e, 0x1c, 0xe4, 0x05, 0x07, 0x07, 0x49, 0x44, 0x96, 0xc2, 0x4c, + 0x6a, 0x04, 0x08, 0xb9, 0xc9, 0x81, 0x90, 0xe4, 0x94, 0x8f, 0x05, 0x90, 0x90, 0x5a, 0x00, 0x09, + 0x49, 0x4d, 0x99, 0x66, 0x04, 0x14, 0xf2, 0x82, 0x03, 0x85, 0xa4, 0xa7, 0x8c, 0x78, 0x04, 0x0b, + 0x79, 0x3d, 0x88, 0x85, 0x64, 0x22, 0x42, 0x9b, 0x23, 0x3d, 0x11, 0x0c, 0x79, 0xc5, 0x07, 0x86, + 0x64, 0x23, 0x51, 0x08, 0xa6, 0x28, 0x04, 0x0d, 0x79, 0x2d, 0x80, 0x86, 0xe4, 0xa6, 0xac, 0xc3, + 0x04, 0x38, 0x64, 0xcb, 0x0f, 0x87, 0x40, 0x24, 0xaa, 0xc2, 0xf7, 0x3d, 0x0a, 0x0f, 0x79, 0xc9, + 0xc5, 0x43, 0xf2, 0x91, 0xc0, 0x0e, 0x9f, 0xcb, 0x28, 0x20, 0xb2, 0x37, 0x06, 0x88, 0x30, 0x00, + 0xe3, 0xa9, 0x48, 0x15, 0x53, 0x10, 0x91, 0xbd, 0x31, 0x44, 0xa4, 0x30, 0x45, 0xe1, 0x14, 0x48, + 0xe4, 0x7b, 0xe1, 0x90, 0x48, 0x34, 0x68, 0xc1, 0x87, 0x39, 0x1b, 0x26, 0x22, 0x47, 0x60, 0x22, + 0xa5, 0xc8, 0xfa, 0x9d, 0xa9, 0x9f, 0x19, 0x14, 0x39, 0x0c, 0x01, 0x45, 0x18, 0x7c, 0x71, 0x35, + 0x52, 0xf9, 0x0c, 0xa8, 0xc8, 0x61, 0x08, 0x2a, 0xb2, 0x38, 0x55, 0xed, 0x54, 0x58, 0xe4, 0x6e, + 0x10, 0x16, 0x41, 0x53, 0xce, 0x58, 0x24, 0x2e, 0xd2, 0x8a, 0xc2, 0x45, 0x18, 0x76, 0xf1, 0x6c, + 0xa4, 0xc6, 0x39, 0x80, 0x91, 0xbd, 0x31, 0x60, 0x64, 0x79, 0x8a, 0xa5, 0xcd, 0x8a, 0x8c, 0x5c, + 0x23, 0x19, 0xc5, 0x88, 0xab, 0x26, 0xc9, 0x3d, 0x36, 0x4d, 0xdd, 0xe4, 0x18, 0x07, 0x7b, 0x10, + 0xaf, 0x92, 0x4a, 0xd9, 0x73, 0xcb, 0x13, 0x50, 0x14, 0x5a, 0x44, 0xf9, 0x5c, 0xb1, 0xf8, 0x9b, + 0xb8, 0x27, 0x4b, 0x0b, 0x4c, 0x7f, 0x95, 0x9d, 0xe3, 0x55, 0xb6, 0x0f, 0x5b, 0x11, 0x82, 0xd8, + 0xca, 0x2a, 0xe4, 0x49, 0x71, 0x34, 0x02, 0x9b, 0x28, 0x86, 0x0b, 0x9b, 0x5c, 0x87, 0x45, 0x9a, + 0x04, 0x30, 0x04, 0x86, 0x47, 0xd6, 0x24, 0x8d, 0xac, 0x25, 0xf2, 0x82, 0xad, 0x02, 0x0b, 0xb1, + 0xcf, 0xc1, 0x92, 0x8f, 0xd7, 0x2d, 0xba, 0x18, 0x86, 0x50, 0x76, 0xb9, 0x37, 0x78, 0xf5, 0xf5, + 0xbb, 0xb8, 0xb7, 0x42, 0x1e, 0xde, 0x12, 0x06, 0x8d, 0xc4, 0xbf, 0x26, 0x68, 0x44, 0xf8, 0xca, + 0xd0, 0x88, 0xbf, 0x88, 0x4c, 0x04, 0x8b, 0xc8, 0xbf, 0xc7, 0xbd, 0x3d, 0x71, 0x81, 0x8e, 0xb6, + 0xae, 0x62, 0x5e, 0xd6, 0xd1, 0xdf, 0x24, 0xcd, 0xea, 0xea, 0x27, 0xbc, 0x78, 0x23, 0x3f, 0x09, + 0x97, 0x1b, 0x3b, 0x73, 0x3c, 0x34, 0xba, 0x15, 0x21, 0xcb, 0x5d, 0x78, 0x45, 0x58, 0x86, 0xc4, + 0x43, 0xcc, 0x22, 0xdd, 0x82, 0x44, 0x7e, 0x12, 0x3e, 0x6a, 0x64, 0x3c, 0x07, 0x61, 0x0f, 0xe8, + 0x36, 0xe4, 0x68, 0xbb, 0x46, 0xd6, 0x0d, 0x8b, 0x07, 0xa4, 0x40, 0xba, 0xc6, 0xba, 0x32, 0x6b, + 0xfb, 0x84, 0x67, 0xcf, 0xb0, 0xa4, 0xac, 0xc1, 0x7f, 0xf9, 0x92, 0xa6, 0x5c, 0x20, 0x69, 0xba, + 0x00, 0x39, 0x32, 0x7a, 0xcb, 0x50, 0xda, 0x98, 0x46, 0x96, 0x9c, 0xe4, 0x11, 0xc4, 0x07, 0x80, + 0xc6, 0xe3, 0x24, 0x6a, 0x40, 0x1a, 0x9f, 0xe2, 0xbe, 0xcd, 0x72, 0xca, 0xfc, 0xfa, 0xb9, 0xf1, + 0xba, 0x91, 0xbc, 0xde, 0xac, 0x90, 0x45, 0xfe, 0xdb, 0xe7, 0xab, 0x65, 0xc6, 0xfd, 0xac, 0xde, + 0xd3, 0x6c, 0xdc, 0x33, 0xec, 0x33, 0x89, 0xcb, 0x8b, 0x7f, 0x16, 0xa0, 0x34, 0x12, 0x3f, 0x43, + 0xd7, 0xd6, 0x31, 0x79, 0xc1, 0x07, 0x2c, 0xcd, 0xb6, 0xde, 0x17, 0x01, 0x4e, 0x14, 0x4b, 0x7e, + 0x4f, 0xe9, 0xdb, 0x58, 0xe5, 0x8b, 0x9e, 0x3b, 0x51, 0xac, 0x37, 0x29, 0x81, 0xec, 0x3a, 0x79, + 0x3d, 0xb0, 0xb0, 0xca, 0x21, 0xae, 0xcc, 0x89, 0x62, 0x1d, 0x5a, 0x58, 0xf5, 0xcd, 0x32, 0xf3, + 0x68, 0xb3, 0x0c, 0xae, 0x71, 0x76, 0x64, 0x8d, 0x7d, 0x75, 0x7f, 0xce, 0x5f, 0xf7, 0xa3, 0x2a, + 0x64, 0x0d, 0x53, 0xd3, 0x4d, 0xcd, 0x3e, 0xa3, 0x1b, 0x93, 0x90, 0xdc, 0x67, 0x74, 0x19, 0x0a, + 0x3d, 0xdc, 0x33, 0x74, 0xbd, 0x2b, 0x33, 0x67, 0x93, 0xa7, 0xa2, 0x0b, 0x9c, 0x58, 0xa7, 0x3e, + 0xe7, 0x43, 0xc1, 0x3b, 0x7d, 0x1e, 0xbe, 0xf3, 0xf5, 0x2e, 0xef, 0x4a, 0xc8, 0xf2, 0xfa, 0x28, + 0x64, 0x12, 0x23, 0xeb, 0xeb, 0x3e, 0x7f, 0x5b, 0x0b, 0x2c, 0xfe, 0x90, 0x82, 0xbe, 0xc1, 0xdc, + 0x08, 0x1d, 0xf8, 0x2b, 0xb3, 0x01, 0x75, 0x0a, 0x8e, 0x39, 0xcf, 0xea, 0x3d, 0xbc, 0x0a, 0x8e, + 0x91, 0x2d, 0xf4, 0x16, 0x3c, 0x3e, 0xe2, 0xd9, 0x5c, 0xd5, 0xc2, 0xac, 0x0e, 0xee, 0xb1, 0xa0, + 0x83, 0x73, 0x54, 0x7b, 0x8b, 0x95, 0x78, 0xc4, 0x33, 0xb7, 0x0d, 0xc5, 0x60, 0x9a, 0x17, 0xba, + 0xfd, 0x97, 0xa1, 0x60, 0x62, 0x5b, 0xd1, 0xfa, 0x72, 0xa0, 0x26, 0x5d, 0x60, 0x44, 0x8e, 0xff, + 0xee, 0xc3, 0x63, 0xa1, 0xe9, 0x1e, 0x7a, 0x11, 0x72, 0x5e, 0xa6, 0xc8, 0x56, 0x75, 0x02, 0x92, + 0xe7, 0xf1, 0x8a, 0xbf, 0x8d, 0x7b, 0x2a, 0x83, 0xd8, 0x60, 0x1d, 0xd2, 0x26, 0xb6, 0x06, 0x5d, + 0x86, 0xd6, 0x15, 0xd7, 0x9f, 0x9b, 0x2d, 0x51, 0x24, 0xd4, 0x41, 0xd7, 0x96, 0xb8, 0xb0, 0xf8, + 0x00, 0xd2, 0x8c, 0x82, 0xf2, 0x90, 0x39, 0xdc, 0xbd, 0xb7, 0xbb, 0xf7, 0xe6, 0x6e, 0x39, 0x86, + 0x00, 0xd2, 0x1b, 0xb5, 0x5a, 0x7d, 0xbf, 0x59, 0x8e, 0xa3, 0x1c, 0xa4, 0x36, 0x36, 0xf7, 0xa4, + 0x66, 0x59, 0x20, 0x64, 0xa9, 0xfe, 0x7a, 0xbd, 0xd6, 0x2c, 0x27, 0xd0, 0x22, 0x14, 0xd8, 0x6f, + 0xf9, 0xee, 0x9e, 0x74, 0x7f, 0xa3, 0x59, 0x4e, 0xfa, 0x48, 0x07, 0xf5, 0xdd, 0xad, 0xba, 0x54, + 0x4e, 0x89, 0xff, 0x05, 0xe7, 0x23, 0x53, 0x4b, 0x0f, 0xf8, 0x8b, 0xfb, 0x80, 0x3f, 0xf1, 0xa7, + 0x02, 0x54, 0xa3, 0xf3, 0x45, 0xf4, 0xfa, 0xc8, 0xc4, 0xd7, 0xe7, 0x48, 0x36, 0x47, 0x66, 0x8f, + 0xae, 0x40, 0xd1, 0xc4, 0xc7, 0xd8, 0x6e, 0x77, 0x58, 0xfe, 0xca, 0x02, 0x66, 0x41, 0x2a, 0x70, + 0x2a, 0x15, 0xb2, 0x18, 0xdb, 0x3b, 0xb8, 0x6d, 0xcb, 0xcc, 0x17, 0x31, 0xa3, 0xcb, 0x11, 0x36, + 0x42, 0x3d, 0x60, 0x44, 0xf1, 0xed, 0xb9, 0xd6, 0x32, 0x07, 0x29, 0xa9, 0xde, 0x94, 0xde, 0x2a, + 0x27, 0x10, 0x82, 0x22, 0xfd, 0x29, 0x1f, 0xec, 0x6e, 0xec, 0x1f, 0x34, 0xf6, 0xc8, 0x5a, 0x2e, + 0x41, 0xc9, 0x59, 0x4b, 0x87, 0x98, 0x12, 0xff, 0x20, 0xc0, 0xe3, 0x11, 0xd9, 0x2e, 0xba, 0x0d, + 0x60, 0x0f, 0x65, 0x13, 0xb7, 0x75, 0x53, 0x8d, 0x36, 0xb2, 0xe6, 0x50, 0xa2, 0x1c, 0x52, 0xce, + 0xe6, 0xbf, 0xac, 0x09, 0x78, 0x31, 0x7a, 0x99, 0x2b, 0x25, 0xb3, 0x72, 0x8e, 0xda, 0xc5, 0x10, + 0x58, 0x14, 0xb7, 0x89, 0x62, 0xba, 0xb6, 0x54, 0x31, 0xe5, 0x47, 0xf7, 0xc3, 0x9c, 0xca, 0x8c, + 0xdd, 0x9a, 0xf9, 0xdc, 0x49, 0xea, 0xd1, 0xdc, 0x89, 0xf8, 0x8b, 0x84, 0x7f, 0x61, 0x83, 0xc9, + 0xfd, 0x1e, 0xa4, 0x2d, 0x5b, 0xb1, 0x07, 0x16, 0x37, 0xb8, 0x17, 0x67, 0xad, 0x14, 0xd6, 0x9c, + 0x1f, 0x07, 0x54, 0x5c, 0xe2, 0x6a, 0xbe, 0x5b, 0x6f, 0x4b, 0xbc, 0x05, 0xc5, 0xe0, 0xe2, 0x44, + 0x1f, 0x19, 0xcf, 0xe7, 0x08, 0xe2, 0x1d, 0x2f, 0xff, 0xf2, 0x81, 0x96, 0xe3, 0x80, 0x60, 0x3c, + 0x0c, 0x10, 0xfc, 0x65, 0x1c, 0x9e, 0x98, 0x50, 0x2f, 0xa1, 0x37, 0x46, 0xf6, 0xf9, 0xa5, 0x79, + 0xaa, 0xad, 0x35, 0x46, 0x0b, 0xee, 0xb4, 0x78, 0x13, 0x16, 0xfc, 0xf4, 0xd9, 0x26, 0xf9, 0xa3, + 0x84, 0xe7, 0xf3, 0x83, 0xc8, 0xe5, 0xd7, 0x96, 0x68, 0x8e, 0xd8, 0x99, 0x30, 0xa7, 0x9d, 0x85, + 0x26, 0x0b, 0x89, 0x6f, 0x2e, 0x59, 0x48, 0x3e, 0x62, 0xb2, 0xe0, 0x3f, 0x70, 0xa9, 0xe0, 0x81, + 0x1b, 0x8b, 0xeb, 0xe9, 0x90, 0xb8, 0xfe, 0x16, 0x80, 0xaf, 0xa1, 0xb9, 0x0c, 0x29, 0x53, 0x1f, + 0xf4, 0x55, 0x6a, 0x26, 0x29, 0x89, 0x3d, 0xa0, 0x5b, 0x90, 0x22, 0xe6, 0xe6, 0x2c, 0xe6, 0xb8, + 0xe7, 0x25, 0xe6, 0xe2, 0xc3, 0x8c, 0x19, 0xb7, 0xa8, 0x01, 0x1a, 0x6f, 0x2a, 0x45, 0x7c, 0xe2, + 0x95, 0xe0, 0x27, 0x9e, 0x8c, 0x6c, 0x4f, 0x85, 0x7f, 0xea, 0x7d, 0x48, 0x51, 0xf3, 0x20, 0xf9, + 0x0d, 0x6d, 0x8c, 0xf2, 0x82, 0x99, 0xfc, 0x46, 0xdf, 0x07, 0x50, 0x6c, 0xdb, 0xd4, 0x5a, 0x03, + 0xef, 0x03, 0xab, 0xe1, 0xe6, 0xb5, 0xe1, 0xf0, 0x6d, 0x5e, 0xe0, 0x76, 0xb6, 0xec, 0x89, 0xfa, + 0x6c, 0xcd, 0xa7, 0x50, 0xdc, 0x85, 0x62, 0x50, 0xd6, 0x29, 0xf1, 0xd8, 0x18, 0x82, 0x25, 0x1e, + 0xab, 0xd8, 0x79, 0x89, 0xe7, 0x16, 0x88, 0x09, 0xd6, 0x03, 0xa7, 0x0f, 0xe2, 0x3f, 0xe2, 0xb0, + 0xe0, 0xb7, 0xce, 0xff, 0xb4, 0x2a, 0x49, 0xfc, 0x30, 0x0e, 0x59, 0x77, 0xf2, 0x11, 0x0d, 0x68, + 0x6f, 0xed, 0x04, 0x7f, 0xbb, 0x95, 0x75, 0xb4, 0x13, 0x6e, 0x9f, 0xfc, 0x8e, 0x9b, 0x50, 0x45, + 0x81, 0xda, 0xfe, 0x95, 0x76, 0xae, 0x0a, 0xf0, 0xfc, 0xf1, 0x27, 0x7c, 0x1c, 0x24, 0x93, 0x40, + 0xff, 0x03, 0x69, 0xa5, 0xed, 0x42, 0xf9, 0xc5, 0x10, 0x6c, 0xd7, 0x61, 0x5d, 0x6b, 0x0e, 0x37, + 0x28, 0xa7, 0xc4, 0x25, 0xf8, 0xa8, 0x04, 0xb7, 0xcf, 0xfe, 0x2a, 0xd1, 0xcb, 0x78, 0x82, 0x6e, + 0xb3, 0x08, 0x70, 0xb8, 0x7b, 0x7f, 0x6f, 0x6b, 0xfb, 0xee, 0x76, 0x7d, 0x8b, 0xa7, 0x54, 0x5b, + 0x5b, 0xf5, 0xad, 0xb2, 0x40, 0xf8, 0xa4, 0xfa, 0xfd, 0xbd, 0xa3, 0xfa, 0x56, 0x39, 0x21, 0xde, + 0x81, 0x9c, 0xeb, 0x7a, 0x50, 0x05, 0x32, 0x4e, 0x5b, 0x22, 0xce, 0x1d, 0x00, 0xef, 0x32, 0x2d, + 0x43, 0xca, 0xd0, 0xdf, 0xe3, 0x5d, 0xe6, 0x84, 0xc4, 0x1e, 0x44, 0x15, 0x4a, 0x23, 0x7e, 0x0b, + 0xdd, 0x81, 0x8c, 0x31, 0x68, 0xc9, 0x8e, 0xd1, 0x8e, 0x34, 0x71, 0x1c, 0xa4, 0x61, 0xd0, 0xea, + 0x6a, 0xed, 0x7b, 0xf8, 0xcc, 0x59, 0x26, 0x63, 0xd0, 0xba, 0xc7, 0x6c, 0x9b, 0x7d, 0x45, 0xf0, + 0x7f, 0xe5, 0x14, 0xb2, 0xce, 0x51, 0x45, 0xff, 0x0b, 0x39, 0xd7, 0x25, 0xba, 0x57, 0x6f, 0x22, + 0x7d, 0x29, 0x57, 0xef, 0x89, 0xa0, 0xeb, 0xb0, 0x68, 0x69, 0x27, 0x7d, 0xa7, 0x85, 0xc5, 0x90, + 0x3d, 0x81, 0x9e, 0x99, 0x12, 0x7b, 0xb1, 0xe3, 0xc0, 0x51, 0x24, 0x12, 0x96, 0x47, 0x7d, 0xc5, + 0xb7, 0x39, 0x80, 0x90, 0x88, 0x9d, 0x08, 0x8b, 0xd8, 0x1f, 0x08, 0x90, 0xf7, 0x35, 0xc6, 0xd0, + 0x7f, 0xfb, 0x1c, 0x57, 0x31, 0x24, 0xd4, 0xf8, 0x78, 0xbd, 0x5b, 0x1d, 0xc1, 0x89, 0x09, 0xf3, + 0x4f, 0x2c, 0xaa, 0x0f, 0xe9, 0xf4, 0xd7, 0x92, 0x73, 0xf7, 0xd7, 0x9e, 0x05, 0x64, 0xeb, 0xb6, + 0xd2, 0x95, 0x4f, 0x75, 0x5b, 0xeb, 0x9f, 0xc8, 0xcc, 0x34, 0x98, 0x9b, 0x29, 0xd3, 0x37, 0x47, + 0xf4, 0xc5, 0x3e, 0xb5, 0x92, 0x1f, 0xc4, 0x21, 0xeb, 0x96, 0x7d, 0xf3, 0x5e, 0xd2, 0x38, 0x07, + 0x69, 0x5e, 0xd9, 0xb0, 0x5b, 0x1a, 0xfc, 0x29, 0xb4, 0x91, 0x58, 0x85, 0x6c, 0x0f, 0xdb, 0x0a, + 0xf5, 0x99, 0x2c, 0x4c, 0xba, 0xcf, 0xd7, 0x5f, 0x82, 0xbc, 0xef, 0xbe, 0x0c, 0x71, 0xa3, 0xbb, + 0xf5, 0x37, 0xcb, 0xb1, 0x6a, 0xe6, 0xa3, 0x4f, 0x2e, 0x25, 0x76, 0xf1, 0x7b, 0xe4, 0x84, 0x49, + 0xf5, 0x5a, 0xa3, 0x5e, 0xbb, 0x57, 0x8e, 0x57, 0xf3, 0x1f, 0x7d, 0x72, 0x29, 0x23, 0x61, 0xda, + 0xf7, 0xb9, 0x7e, 0x0f, 0x4a, 0x23, 0x1b, 0x13, 0x3c, 0xd0, 0x08, 0x8a, 0x5b, 0x87, 0xfb, 0x3b, + 0xdb, 0xb5, 0x8d, 0x66, 0x5d, 0x3e, 0xda, 0x6b, 0xd6, 0xcb, 0x71, 0xf4, 0x38, 0x2c, 0xed, 0x6c, + 0xbf, 0xd6, 0x68, 0xca, 0xb5, 0x9d, 0xed, 0xfa, 0x6e, 0x53, 0xde, 0x68, 0x36, 0x37, 0x6a, 0xf7, + 0xca, 0xc2, 0xfa, 0xaf, 0xf2, 0x50, 0xda, 0xd8, 0xac, 0x6d, 0x93, 0xda, 0x4e, 0x6b, 0x2b, 0xd4, + 0x3d, 0xd4, 0x20, 0x49, 0x41, 0xe4, 0x89, 0x37, 0xa0, 0xab, 0x93, 0x1b, 0x83, 0xe8, 0x2e, 0xa4, + 0x28, 0xbe, 0x8c, 0x26, 0x5f, 0x89, 0xae, 0x4e, 0xe9, 0x14, 0x92, 0xc1, 0xd0, 0xe3, 0x34, 0xf1, + 0x8e, 0x74, 0x75, 0x72, 0xe3, 0x10, 0xed, 0x40, 0xc6, 0x81, 0xff, 0xa6, 0xdd, 0x36, 0xae, 0x4e, + 0xed, 0xc0, 0x91, 0xa9, 0x31, 0x98, 0x76, 0xf2, 0xf5, 0xe9, 0xea, 0x94, 0x96, 0x22, 0xda, 0x86, + 0x34, 0x47, 0x48, 0xa6, 0xdc, 0x1c, 0xae, 0x4e, 0xeb, 0xa4, 0x21, 0x09, 0x72, 0x1e, 0x00, 0x3e, + 0xfd, 0x52, 0x78, 0x75, 0x86, 0x6e, 0x29, 0x7a, 0x00, 0x85, 0x20, 0xea, 0x32, 0xdb, 0xed, 0xe4, + 0xea, 0x8c, 0x3d, 0x3b, 0xa2, 0x3f, 0x08, 0xc1, 0xcc, 0x76, 0x5b, 0xb9, 0x3a, 0x63, 0x0b, 0x0f, + 0xbd, 0x03, 0x8b, 0xe3, 0x10, 0xc9, 0xec, 0x97, 0x97, 0xab, 0x73, 0x34, 0xf5, 0x50, 0x0f, 0x50, + 0x08, 0xb4, 0x32, 0xc7, 0x5d, 0xe6, 0xea, 0x3c, 0x3d, 0x3e, 0xa4, 0x42, 0x69, 0x14, 0xae, 0x98, + 0xf5, 0x6e, 0x73, 0x75, 0xe6, 0x7e, 0x1f, 0xfb, 0x4a, 0xb0, 0x76, 0x9f, 0xf5, 0xae, 0x73, 0x75, + 0xe6, 0xf6, 0x1f, 0x3a, 0x04, 0xf0, 0xd5, 0x9e, 0x33, 0xdc, 0x7d, 0xae, 0xce, 0xd2, 0x08, 0x44, + 0x06, 0x2c, 0x85, 0x15, 0xa5, 0xf3, 0x5c, 0x85, 0xae, 0xce, 0xd5, 0x1f, 0x24, 0xf6, 0x1c, 0x2c, + 0x2f, 0x67, 0xbb, 0x1a, 0x5d, 0x9d, 0xb1, 0x51, 0xb8, 0x59, 0xff, 0xf4, 0x8b, 0x95, 0xf8, 0x67, + 0x5f, 0xac, 0xc4, 0xff, 0xfa, 0xc5, 0x4a, 0xfc, 0xe3, 0x2f, 0x57, 0x62, 0x9f, 0x7d, 0xb9, 0x12, + 0xfb, 0xd3, 0x97, 0x2b, 0xb1, 0xff, 0x7b, 0xe6, 0x44, 0xb3, 0x3b, 0x83, 0xd6, 0x5a, 0x5b, 0xef, + 0xdd, 0xf0, 0xff, 0x4b, 0x26, 0xec, 0x9f, 0x3b, 0xad, 0x34, 0x0d, 0xa8, 0x37, 0xff, 0x15, 0x00, + 0x00, 0xff, 0xff, 0xa5, 0x05, 0x49, 0xaf, 0xd9, 0x33, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -6222,15 +6264,15 @@ func (m *RequestExtendVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Vote != nil { - { - size, err := m.Vote.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTypes(dAtA, i, uint64(size)) - } + 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 } @@ -6257,15 +6299,29 @@ func (m *RequestVerifyVoteExtension) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l - if m.Vote != nil { - { - size, err := m.Vote.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTypes(dAtA, i, uint64(size)) - } + 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 } @@ -6306,12 +6362,12 @@ func (m *RequestFinalizeBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x3a } - n31, err31 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) - if err31 != nil { - return 0, err31 + n29, err29 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) + if err29 != nil { + return 0, err29 } - i -= n31 - i = encodeVarintTypes(dAtA, i, uint64(n31)) + i -= n29 + i = encodeVarintTypes(dAtA, i, uint64(n29)) i-- dAtA[i] = 0x32 if m.Height != 0 { @@ -7541,20 +7597,20 @@ func (m *ResponseApplySnapshotChunk) MarshalToSizedBuffer(dAtA []byte) (int, err } } if len(m.RefetchChunks) > 0 { - dAtA57 := make([]byte, len(m.RefetchChunks)*10) - var j56 int + dAtA55 := make([]byte, len(m.RefetchChunks)*10) + var j54 int for _, num := range m.RefetchChunks { for num >= 1<<7 { - dAtA57[j56] = uint8(uint64(num)&0x7f | 0x80) + dAtA55[j54] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 - j56++ + j54++ } - dAtA57[j56] = uint8(num) - j56++ + dAtA55[j54] = uint8(num) + j54++ } - i -= j56 - copy(dAtA[i:], dAtA57[:j56]) - i = encodeVarintTypes(dAtA, i, uint64(j56)) + i -= j54 + copy(dAtA[i:], dAtA55[:j54]) + i = encodeVarintTypes(dAtA, i, uint64(j54)) i-- dAtA[i] = 0x12 } @@ -7745,15 +7801,10 @@ func (m *ResponseExtendVote) 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)) - } + 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 } @@ -8408,12 +8459,12 @@ func (m *Misbehavior) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x28 } - n66, err66 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) - if err66 != nil { - return 0, err66 + n63, err63 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) + if err63 != nil { + return 0, err63 } - i -= n66 - i = encodeVarintTypes(dAtA, i, uint64(n66)) + i -= n63 + i = encodeVarintTypes(dAtA, i, uint64(n63)) i-- dAtA[i] = 0x22 if m.Height != 0 { @@ -9064,10 +9115,13 @@ func (m *RequestExtendVote) Size() (n int) { } var l int _ = l - if m.Vote != nil { - l = m.Vote.Size() + 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 } @@ -9077,8 +9131,19 @@ func (m *RequestVerifyVoteExtension) Size() (n int) { } var l int _ = l - if m.Vote != nil { - l = m.Vote.Size() + 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 @@ -9788,8 +9853,8 @@ func (m *ResponseExtendVote) Size() (n int) { } var l int _ = l - if m.VoteExtension != nil { - l = m.VoteExtension.Size() + l = len(m.VoteExtension) + if l > 0 { n += 1 + l + sovTypes(uint64(l)) } return n @@ -13029,9 +13094,9 @@ func (m *RequestExtendVote) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Vote", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTypes @@ -13041,28 +13106,45 @@ func (m *RequestExtendVote) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthTypes } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTypes } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Vote == nil { - m.Vote = &types1.Vote{} - } - if err := m.Vote.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + 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:]) @@ -13115,9 +13197,9 @@ func (m *RequestVerifyVoteExtension) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Vote", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTypes @@ -13127,26 +13209,111 @@ func (m *RequestVerifyVoteExtension) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthTypes } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthTypes } if postIndex > l { return io.ErrUnexpectedEOF } - if m.Vote == nil { - m.Vote = &types1.Vote{} + m.Hash = append(m.Hash[:0], dAtA[iNdEx:postIndex]...) + if m.Hash == nil { + m.Hash = []byte{} } - if err := m.Vote.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + 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: @@ -16926,7 +17093,7 @@ func (m *ResponseExtendVote) Unmarshal(dAtA []byte) error { if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VoteExtension", wireType) } - var msglen int + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowTypes @@ -16936,26 +17103,24 @@ func (m *ResponseExtendVote) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthTypes } - postIndex := iNdEx + msglen + 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 = &types1.VoteExtension{} - } - if err := m.VoteExtension.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + m.VoteExtension = []byte{} } iNdEx = postIndex default: diff --git a/config/toml.go b/config/toml.go index 7f49249e9..578718ca5 100644 --- a/config/toml.go +++ b/config/toml.go @@ -12,8 +12,8 @@ import ( tmrand "github.com/tendermint/tendermint/libs/rand" ) -// DefaultDirPerm is the default permissions used when creating directories. -const DefaultDirPerm = 0700 +// defaultDirPerm is the default permissions used when creating directories. +const defaultDirPerm = 0700 var configTemplate *template.Template @@ -32,13 +32,13 @@ func init() { // EnsureRoot creates the root, config, and data directories if they don't exist, // and panics if it fails. func EnsureRoot(rootDir string) { - if err := tmos.EnsureDir(rootDir, DefaultDirPerm); err != nil { + if err := tmos.EnsureDir(rootDir, defaultDirPerm); err != nil { panic(err.Error()) } - if err := tmos.EnsureDir(filepath.Join(rootDir, defaultConfigDir), DefaultDirPerm); err != nil { + if err := tmos.EnsureDir(filepath.Join(rootDir, defaultConfigDir), defaultDirPerm); err != nil { panic(err.Error()) } - if err := tmos.EnsureDir(filepath.Join(rootDir, defaultDataDir), DefaultDirPerm); err != nil { + if err := tmos.EnsureDir(filepath.Join(rootDir, defaultDataDir), defaultDirPerm); err != nil { panic(err.Error()) } } @@ -566,10 +566,10 @@ func ResetTestRootWithChainID(dir, testName string, chainID string) (*Config, er return nil, err } // ensure config and data subdirs are created - if err := tmos.EnsureDir(filepath.Join(rootDir, defaultConfigDir), DefaultDirPerm); err != nil { + if err := tmos.EnsureDir(filepath.Join(rootDir, defaultConfigDir), defaultDirPerm); err != nil { return nil, err } - if err := tmos.EnsureDir(filepath.Join(rootDir, defaultDataDir), DefaultDirPerm); err != nil { + if err := tmos.EnsureDir(filepath.Join(rootDir, defaultDataDir), defaultDirPerm); err != nil { return nil, err } @@ -612,7 +612,7 @@ func writeFile(filePath string, contents []byte, mode os.FileMode) error { return nil } -var testGenesisFmt = `{ +const testGenesisFmt = `{ "genesis_time": "2018-10-10T08:20:13.695936996Z", "chain_id": "%s", "initial_height": "1", @@ -659,7 +659,7 @@ var testGenesisFmt = `{ "app_hash": "" }` -var testPrivValidatorKey = `{ +const testPrivValidatorKey = `{ "address": "A3258DCBF45DCA0DF052981870F2D1441A36D145", "pub_key": { "type": "tendermint/PubKeyEd25519", @@ -671,7 +671,7 @@ var testPrivValidatorKey = `{ } }` -var testPrivValidatorState = `{ +const testPrivValidatorState = `{ "height": "0", "round": 0, "step": 0 diff --git a/crypto/ed25519/ed25519.go b/crypto/ed25519/ed25519.go index ffd4a3ed1..7282e5c70 100644 --- a/crypto/ed25519/ed25519.go +++ b/crypto/ed25519/ed25519.go @@ -2,6 +2,8 @@ package ed25519 import ( "bytes" + "crypto/rand" + "crypto/sha256" "crypto/subtle" "errors" "fmt" @@ -124,7 +126,7 @@ func (privKey PrivKey) Type() string { // It uses OS randomness in conjunction with the current global random seed // in tendermint/libs/common to generate the private key. func GenPrivKey() PrivKey { - return genPrivKey(crypto.CReader()) + return genPrivKey(rand.Reader) } // genPrivKey generates a new ed25519 private key using the provided reader. @@ -142,9 +144,8 @@ func genPrivKey(rand io.Reader) PrivKey { // NOTE: secret should be the output of a KDF like bcrypt, // if it's derived from user input. func GenPrivKeyFromSecret(secret []byte) PrivKey { - seed := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes. - - return PrivKey(ed25519.NewKeyFromSeed(seed)) + seed := sha256.Sum256(secret) + return PrivKey(ed25519.NewKeyFromSeed(seed[:])) } //------------------------------------- @@ -229,5 +230,5 @@ func (b *BatchVerifier) Add(key crypto.PubKey, msg, signature []byte) error { } func (b *BatchVerifier) Verify() (bool, []bool) { - return b.BatchVerifier.Verify(crypto.CReader()) + return b.BatchVerifier.Verify(rand.Reader) } diff --git a/crypto/example_test.go b/crypto/example_test.go deleted file mode 100644 index f1d0013d4..000000000 --- a/crypto/example_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 Tendermint. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crypto_test - -import ( - "fmt" - - "github.com/tendermint/tendermint/crypto" -) - -func ExampleSha256() { - sum := crypto.Sha256([]byte("This is Tendermint")) - fmt.Printf("%x\n", sum) - // Output: - // f91afb642f3d1c87c17eb01aae5cb65c242dfdbe7cf1066cc260f4ce5d33b94e -} diff --git a/crypto/hash.go b/crypto/hash.go deleted file mode 100644 index e1d22523f..000000000 --- a/crypto/hash.go +++ /dev/null @@ -1,11 +0,0 @@ -package crypto - -import ( - "crypto/sha256" -) - -func Sha256(bytes []byte) []byte { - hasher := sha256.New() - hasher.Write(bytes) - return hasher.Sum(nil) -} diff --git a/crypto/merkle/proof_value.go b/crypto/merkle/proof_value.go index 842dc8201..0f4f2eb3d 100644 --- a/crypto/merkle/proof_value.go +++ b/crypto/merkle/proof_value.go @@ -2,9 +2,9 @@ package merkle import ( "bytes" + "crypto/sha256" "fmt" - "github.com/tendermint/tendermint/crypto/tmhash" tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" ) @@ -79,14 +79,13 @@ func (op ValueOp) Run(args [][]byte) ([][]byte, error) { return nil, fmt.Errorf("expected 1 arg, got %v", len(args)) } value := args[0] - hasher := tmhash.New() - hasher.Write(value) - vhash := hasher.Sum(nil) + + vhash := sha256.Sum256(value) bz := new(bytes.Buffer) // Wrap to hash the KVPair. - encodeByteSlice(bz, op.key) //nolint: errcheck // does not error - encodeByteSlice(bz, vhash) //nolint: errcheck // does not error + encodeByteSlice(bz, op.key) //nolint: errcheck // does not error + encodeByteSlice(bz, vhash[:]) //nolint: errcheck // does not error kvhash := leafHash(bz.Bytes()) if !bytes.Equal(kvhash, op.Proof.LeafHash) { diff --git a/crypto/random.go b/crypto/random.go index 275fb1044..d3e66801c 100644 --- a/crypto/random.go +++ b/crypto/random.go @@ -1,35 +1,15 @@ package crypto import ( - crand "crypto/rand" - "encoding/hex" - "io" + "crypto/rand" ) // This only uses the OS's randomness -func randBytes(numBytes int) []byte { +func CRandBytes(numBytes int) []byte { b := make([]byte, numBytes) - _, err := crand.Read(b) + _, err := rand.Read(b) if err != nil { panic(err) } return b } - -// This only uses the OS's randomness -func CRandBytes(numBytes int) []byte { - return randBytes(numBytes) -} - -// CRandHex returns a hex encoded string that's floor(numDigits/2) * 2 long. -// -// Note: CRandHex(24) gives 96 bits of randomness that -// are usually strong enough for most purposes. -func CRandHex(numDigits int) string { - return hex.EncodeToString(CRandBytes(numDigits / 2)) -} - -// Returns a crand.Reader. -func CReader() io.Reader { - return crand.Reader -} diff --git a/crypto/secp256k1/secp256k1.go b/crypto/secp256k1/secp256k1.go index 7892cfbb1..d0626456c 100644 --- a/crypto/secp256k1/secp256k1.go +++ b/crypto/secp256k1/secp256k1.go @@ -2,6 +2,7 @@ package secp256k1 import ( "bytes" + "crypto/rand" "crypto/sha256" "crypto/subtle" "fmt" @@ -70,7 +71,7 @@ func (privKey PrivKey) Type() string { // GenPrivKey generates a new ECDSA private key on curve secp256k1 private key. // It uses OS randomness to generate the private key. func GenPrivKey() PrivKey { - return genPrivKey(crypto.CReader()) + return genPrivKey(rand.Reader) } // genPrivKey generates a new secp256k1 private key using the provided reader. @@ -190,8 +191,8 @@ var secp256k1halfN = new(big.Int).Rsh(secp256k1.S256().N, 1) // The returned signature will be of the form R || S (in lower-S form). func (privKey PrivKey) Sign(msg []byte) ([]byte, error) { priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey) - - sig, err := priv.Sign(crypto.Sha256(msg)) + seed := sha256.Sum256(msg) + sig, err := priv.Sign(seed[:]) if err != nil { return nil, err } @@ -220,7 +221,8 @@ func (pubKey PubKey) VerifySignature(msg []byte, sigStr []byte) bool { return false } - return signature.Verify(crypto.Sha256(msg), pub) + seed := sha256.Sum256(msg) + return signature.Verify(seed[:], pub) } // Read Signature struct from R || S. Caller needs to ensure diff --git a/crypto/sr25519/batch.go b/crypto/sr25519/batch.go index 462728598..3e959fbbf 100644 --- a/crypto/sr25519/batch.go +++ b/crypto/sr25519/batch.go @@ -1,6 +1,7 @@ package sr25519 import ( + "crypto/rand" "fmt" "github.com/oasisprotocol/curve25519-voi/primitives/sr25519" @@ -42,5 +43,5 @@ func (b *BatchVerifier) Add(key crypto.PubKey, msg, signature []byte) error { } func (b *BatchVerifier) Verify() (bool, []bool) { - return b.BatchVerifier.Verify(crypto.CReader()) + return b.BatchVerifier.Verify(rand.Reader) } diff --git a/crypto/sr25519/privkey.go b/crypto/sr25519/privkey.go index 4e9cc995f..c8da94d5d 100644 --- a/crypto/sr25519/privkey.go +++ b/crypto/sr25519/privkey.go @@ -1,6 +1,8 @@ package sr25519 import ( + "crypto/rand" + "crypto/sha256" "encoding/json" "fmt" "io" @@ -48,7 +50,7 @@ func (privKey PrivKey) Sign(msg []byte) ([]byte, error) { st := signingCtx.NewTranscriptBytes(msg) - sig, err := privKey.kp.Sign(crypto.CReader(), st) + sig, err := privKey.kp.Sign(rand.Reader, st) if err != nil { return nil, fmt.Errorf("sr25519: failed to sign message: %w", err) } @@ -132,7 +134,7 @@ func (privKey *PrivKey) UnmarshalJSON(data []byte) error { // It uses OS randomness in conjunction with the current global random seed // in tendermint/libs/common to generate the private key. func GenPrivKey() PrivKey { - return genPrivKey(crypto.CReader()) + return genPrivKey(rand.Reader) } func genPrivKey(rng io.Reader) PrivKey { @@ -154,10 +156,9 @@ func genPrivKey(rng io.Reader) PrivKey { // NOTE: secret should be the output of a KDF like bcrypt, // if it's derived from user input. func GenPrivKeyFromSecret(secret []byte) PrivKey { - seed := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes. - + seed := sha256.Sum256(secret) var privKey PrivKey - if err := privKey.msk.UnmarshalBinary(seed); err != nil { + if err := privKey.msk.UnmarshalBinary(seed[:]); err != nil { panic("sr25519: failed to deserialize MiniSecretKey: " + err.Error()) } diff --git a/crypto/tmhash/hash.go b/crypto/tmhash/hash.go index f9b958242..91e55bca4 100644 --- a/crypto/tmhash/hash.go +++ b/crypto/tmhash/hash.go @@ -2,19 +2,14 @@ package tmhash import ( "crypto/sha256" - "hash" ) const ( - Size = sha256.Size - BlockSize = sha256.BlockSize + Size = sha256.Size + BlockSize = sha256.BlockSize + TruncatedSize = 20 ) -// New returns a new hash.Hash. -func New() hash.Hash { - return sha256.New() -} - // Sum returns the SHA256 of the bz. func Sum(bz []byte) []byte { h := sha256.Sum256(bz) @@ -23,41 +18,6 @@ func Sum(bz []byte) []byte { //------------------------------------------------------------- -const ( - TruncatedSize = 20 -) - -type sha256trunc struct { - sha256 hash.Hash -} - -func (h sha256trunc) Write(p []byte) (n int, err error) { - return h.sha256.Write(p) -} -func (h sha256trunc) Sum(b []byte) []byte { - shasum := h.sha256.Sum(b) - return shasum[:TruncatedSize] -} - -func (h sha256trunc) Reset() { - h.sha256.Reset() -} - -func (h sha256trunc) Size() int { - return TruncatedSize -} - -func (h sha256trunc) BlockSize() int { - return h.sha256.BlockSize() -} - -// NewTruncated returns a new hash.Hash. -func NewTruncated() hash.Hash { - return sha256trunc{ - sha256: sha256.New(), - } -} - // SumTruncated returns the first 20 bytes of SHA256 of the bz. func SumTruncated(bz []byte) []byte { hash := sha256.Sum256(bz) diff --git a/crypto/tmhash/hash_test.go b/crypto/tmhash/hash_test.go deleted file mode 100644 index cf9991b3b..000000000 --- a/crypto/tmhash/hash_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package tmhash_test - -import ( - "crypto/sha256" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/tendermint/tendermint/crypto/tmhash" -) - -func TestHash(t *testing.T) { - testVector := []byte("abc") - hasher := tmhash.New() - _, err := hasher.Write(testVector) - require.NoError(t, err) - bz := hasher.Sum(nil) - - bz2 := tmhash.Sum(testVector) - - hasher = sha256.New() - _, err = hasher.Write(testVector) - require.NoError(t, err) - bz3 := hasher.Sum(nil) - - assert.Equal(t, bz, bz2) - assert.Equal(t, bz, bz3) -} - -func TestHashTruncated(t *testing.T) { - testVector := []byte("abc") - hasher := tmhash.NewTruncated() - _, err := hasher.Write(testVector) - require.NoError(t, err) - bz := hasher.Sum(nil) - - bz2 := tmhash.SumTruncated(testVector) - - hasher = sha256.New() - _, err = hasher.Write(testVector) - require.NoError(t, err) - bz3 := hasher.Sum(nil) - bz3 = bz3[:tmhash.TruncatedSize] - - assert.Equal(t, bz, bz2) - assert.Equal(t, bz, bz3) -} diff --git a/crypto/version.go b/crypto/version.go deleted file mode 100644 index 77c0bed8a..000000000 --- a/crypto/version.go +++ /dev/null @@ -1,3 +0,0 @@ -package crypto - -const Version = "0.9.0-dev" diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 00faaa8b8..e75896f38 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -104,12 +104,17 @@ None - [ADR-013: Symmetric-Crypto](./adr-013-symmetric-crypto.md) - [ADR-022: ABCI-Errors](./adr-022-abci-errors.md) - [ADR-030: Consensus-Refactor](./adr-030-consensus-refactor.md) +- [ADR-036: Empty Blocks via ABCI](./adr-036-empty-blocks-abci.md) - [ADR-037: Deliver-Block](./adr-037-deliver-block.md) - [ADR-038: Non-Zero-Start-Height](./adr-038-non-zero-start-height.md) +- [ADR-040: Blockchain Reactor Refactor](./adr-040-blockchain-reactor-refactor.md) - [ADR-041: Proposer-Selection-via-ABCI](./adr-041-proposer-selection-via-abci.md) +- [ADR-042: State Sync Design](./adr-042-state-sync.md) - [ADR-045: ABCI-Evidence](./adr-045-abci-evidence.md) +- [ADR-050: Improved Trusted Peering](./adr-050-improved-trusted-peering.md) - [ADR-057: RPC](./adr-057-RPC.md) +- [ADR-064: Batch Verification](./adr-064-batch-verification.md) - [ADR-069: Node Initialization](./adr-069-flexible-node-initialization.md) -- [ADR-071: Proposer-Based Timestamps](adr-071-proposer-based-timestamps.md) +- [ADR-071: Proposer-Based Timestamps](./adr-071-proposer-based-timestamps.md) +- [ADR-073: Adopt LibP2P](./adr-073-libp2p.md) - [ADR-074: Migrate Timeout Parameters to Consensus Parameters](./adr-074-timeout-params.md) - diff --git a/docs/package-lock.json b/docs/package-lock.json index 52f7b52a6..447c8c27d 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -3037,9 +3037,9 @@ } }, "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dependencies": { "lodash": "^4.17.14" } @@ -16588,9 +16588,9 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "requires": { "lodash": "^4.17.14" } diff --git a/docs/presubmit.sh b/docs/presubmit.sh new file mode 100755 index 000000000..19e931a4f --- /dev/null +++ b/docs/presubmit.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# +# This script verifies that each document in the docs and architecture +# directory has a corresponding table-of-contents entry in its README file. +# +# This can be run manually from the command line. +# It is also run in CI via the docs-toc.yml workflow. +# +set -euo pipefail + +readonly base="$(dirname $0)" +cd "$base" + +readonly workdir="$(mktemp -d)" +trap "rm -fr -- '$workdir'" EXIT + +checktoc() { + local dir="$1" + local tag="$2"'-*-*' + local out="$workdir/${dir}.out.txt" + ( + cd "$dir" >/dev/null + find . -type f -maxdepth 1 -name "$tag" -not -exec grep -q "({})" README.md ';' -print + ) > "$out" + if [[ -s "$out" ]] ; then + echo "-- The following files in $dir lack a ToC entry: +" + cat "$out" + return 1 + fi +} + +err=0 + +# Verify that each RFC and ADR has a ToC entry in its README file. +checktoc architecture adr || ((err++)) +checktoc rfc rfc || ((err++)) + +exit $err diff --git a/docs/rfc/README.md b/docs/rfc/README.md index b5c42ea85..f2ad6ad69 100644 --- a/docs/rfc/README.md +++ b/docs/rfc/README.md @@ -53,5 +53,6 @@ sections. - [RFC-013: ABCI++](./rfc-013-abci++.md) - [RFC-014: Semantic Versioning](./rfc-014-semantic-versioning.md) - [RFC-015: ABCI++ Tx Mutation](./rfc-015-abci++-tx-mutation.md) +- [RFC-019: Configuration File Versioning](./rfc-019-config-version.md) diff --git a/docs/rfc/rfc-019-config-version.md b/docs/rfc/rfc-019-config-version.md new file mode 100644 index 000000000..3dfd7840b --- /dev/null +++ b/docs/rfc/rfc-019-config-version.md @@ -0,0 +1,400 @@ +# RFC 019: Configuration File Versioning + +## Changelog + +- 19-Apr-2022: Initial draft (@creachadair) +- 20-Apr-2022: Updates from review feedback (@creachadair) + +## Abstract + +Updating configuration settings is an essential part of upgrading an existing +node to a new version of the Tendermint software. Unfortunately, it is also +currently a very manual process. This document discusses some of the history of +changes to the config format, actions we've taken to improve the tooling for +configuration upgrades, and additional steps we may want to consider. + +## Background + +A Tendermint node reads configuration settings at startup from a TOML formatted +text file, typically named `config.toml`. The contents of this file are defined +by the [`github.com/tendermint/tendermint/config`][config-pkg]. + +Although many settings in this file remain valid from one version of Tendermint +to the next, new versions of Tendermint often add, update, and remove settings. +These changes often require manual intervention by operators who are upgrading +their nodes. + +I propose we should provide better tools and documentation to help operators +make configuration changes correctly during version upgrades. Ideally, as much +as possible of any configuration file update should be automated, and where +that is not possible or practical, we should provide clear, explicit directions +for what steps need to be taken manually. Moreover, when the node discovers +incorrect or invalid configuration, we should improve the diagnostics it emits +so that the operator can quickly and easily find the relevant documentation, +without having to grep through source code. + +## Discussion + +By convention, we are supposed to document required changes to the config file +in the `UPGRADING.md` file for the release that introduces them. Although we +have mostly done this, the level of detail in the upgrading instructions is +often insufficient for an operator to correctly update their file. + +The updates vary widely in complexity: Operators may need to add new required +settings, update obsolete values for existing settings, move or rename existing +settings within the file, or remove obsolete settings (which are thus invalid). +Here are a few examples of each of these cases: + +- **New required settings:** Tendermint v0.35 added a new top-level `mode` + setting that determines whether a node runs as a validator, a full node, or a + seed node. The default value is `"full"`, which means the operator of a + validator must manually add `mode = "validator"` (or set the `--mode` flag on + the command line) for their node to come up in the correct mode. + +- **Updated obsolete values:** Tendermint v0.35 removed support for versions + `"v1"` and `"v2"` of the blocksync (formerly "fastsync") protocol, requiring + any node using either of those values to update to `"v0"`. + +- **Moved/renamed settings:** Version v0.34 moved the top-level `pprof_laddr` + setting under the `[rpc]` section. + + Version v0.35 renamed every setting in the file from `snake_case` to + `kebab-case`, moved the top-level `fast_sync` setting into the `[blocksync]` + section as (itself renamed from `[fastsync]`), and moved all the top-level + `priv-validator-*` settings under a new `[priv-validator]` section with their + prefix trimmed off. + +- **Removed obsolete settings:** Version v0.34 removed the `index_all_keys` and + `index_keys` settings from the `[tx_index]` section; version v0.35 removed + the `wal-dir` setting from the `[mempool]` section, and version v0.36 removed + the `[blocksync]` section entirely. + +While many of these changes are mentioned in the config section of the upgrade +instructions, some are not mentioned at all, or are hidden in other parts of +the doc. For instance, the v0.34 `pprof_laddr` change was documented only as an +RPC flag change. (A savvy reader might realize that the flag `--rpc.pprof_laddr` +implies a corresponding config section, but it omits the related detail that +there was a top-level setting that's been renamed). The lesson here is not +that the docs are bad, but to point out that prose is not the most efficient +format to convey detailed changes like this. The upgrading instructions are +still valuable for the human reader to understand what to expect. + +### Concrete Steps + +As part of the v0.36 development cycle, we spent some time reverse-engineering +the configuration changes since the v0.34 release and built an experimental +command-line tool called [`confix`][confix], whose job it is to automatically +update the settings in a `config.toml` file to the latest version. We also +backported a version of this tool into the v0.35.x branch at release v0.35.4. + +This tool should work fine for configuration files created by Tendermint v0.34 +and later, but does not (yet) know how to handle changes from prior versions of +Tendermint. Part of the difficulty for older versions is simply logistical: To +figure out which changes to apply, we need to understand something about the +version that made the file, as well as the version we're converting it to. + +> **Discussion point:** In the future we might want to consider incorporating +> this into the node CLI directly, but we're keeping it separate for now until +> we can get some feedback from operators. + +For the experiment, we handled this by carefully searching the history of +config format changes for shibboleths to bound the version: For example, the +`[fastsync]` section was added in Tendermint v0.32 and renamed `[blocksync]` in +Tendermint v0.35. So if we see a `[fastsync]` section, we have some confidence +that the file was created by v0.32, v0.33, or v0.34. + +But such signals are delicate: The `[blocksync]` section was removed in v0.36, +so if we do not find `[fastsync]`, we cannot conclude from that alone that the +file is from v0.31 or earlier -- we have to look for corroborating details. +While such "sniffing" tactics are fine for an experiment, they aren't as robust +as we might like. + +This is especially relevant for configuration files that may have already been +manually upgraded across several versions by the time we are asked to update +them again. Another related concern is that we'd like to make sure conversion +is idempotent, so that it would be safe to rerun the tool over an +already-converted file without breaking anything. + +### Config Versioning + +One obvious tactic we could use for future releases is add a version marker to +the config file. This would give tools like `confix` (and the node itself) a +way to calibrate their expectations. Rather than being a version for the file +itself, however, this version marker would indicate which version of Tendermint +is needed to read the file. + +Provisionally, this might look something like: + +```toml +# THe minimum version of Tendermint compatible with the contents of +# this configuration file. +config-version = 'v0.35' +``` + +When initializing a new node, Tendermint would populate this field with its own +version (e.g., `v0.36`). When conducting an upgrade, tools like `confix` can +then use this to decide which conversions are valid, and then update the value +accordingly. After converting a file marked `'v0.35'` to`'v0.37'`, the +conversion tool sets the file's `config-version` to reflect its compatibility. + +> **Discussion point:** This example presumes we would keep config files +> compatible within a given release cycle, e.g., all of v0.36.x. We could also +> use patch numbers here, if we think there's some reason to permit changes +> that would require config file edits at that granularity. I don't think we +> should, but that's a design question to consider. + +Upon seeing an up-to-date version marker, the conversion tool can simply exit +with a diagnostic like "this file is already up-to-date", rather than sniffing +the keyspace and potentially introducing errors. In addition, this would let a +tool detect config files that are _newer_ than the one it understands, and +issue a safe diagnostic rather than doing something wrong. Plus, besides +avoiding potentially unsafe conversions, this would also serve as +human-readable documentation that the file is up-to-date for a given version. + +Adding a config version would not address the problem of how to convert files +created by older versions of Tendermint, but it would at least help us build +more robust config tooling going forward. + +### Stability and Change + +In light of the discussion so far, it is natural to examine why we make so many +changes to the configuration file from one version to the next, and whether we +could reduce friction by being more conservative about what we make +configurable, what config changes we make over time, and how we roll them out. + +Some changes, like renaming everything from snake case to kebab case, are +entirely gratuitous. We could safely agree not to make those kinds of changes. +Apart from that obvious case, however, many other configuration settings +provide value to node operators in cases where there is no simple, universal +setting that matches every application. + +Taking a high-level view, there are several broad reasons why we might want to +make changes to configuration settings: + +- **Lessons learned:** Configuration settings are a good way to try things out + in production, before making more invasive changes to the consensus protocol. + + For example, up until Tendermint v0.35, consensus timeouts were specified as + per-node configuration settings (e.g., `timeout-precommit` et al.). This + allowed operators to tune these values for the needs of their network, but + had the downside that individually-misconfigured nodes could stall consensus. + + Based on that experience, these timeouts have been deprecated in Tendermint + v0.36 and converted to consensus parameters, to be consistent across all + nodes in the network. + +- **Migration & experimentation:** Introducing new features and updating old + features can complicate migration for existing users of the software. + Temporary or "experimental" configuration settings can be a valuable way to + mitigate that friction. + + For example, Tendermint v0.36 introduces a new RPC event subscription + endpoint (see [ADR 075][adr075]) that will eventually replace the existing + webwocket-based interface. To give users time to migrate, v0.36 adds an + `experimental-disable-websocket` setting, defaulted to `false`, that allows + operators to selectively disable the websocket API for testing purposes + during the conversion. This setting is designed to be removed in v0.37, when + the old interface is no longer supported. + +- **Ongoing maintenance:** Sometimes configuration settings become obsolete, + and the cost of removing them trades off against the potential risks of + leaving a non-functional or deprecated knob hooked up indefinitely. + + For example, Tendermint v0.35 deprecated two alternate implementations of the + blocksync protocol, one of which was deleted entirely (`v1`) and one of which + was scheduled for removal (`v2`). The `blocksync.version` setting, which had + been added as a migration aid, became obsolete and needed to be updated. + + Despite our best intentions, sometimes engineering designs do not work out. + It's just as important to leave room to back out of changes we have since + reconsidered, as it is to support migrations forward onto new and improved + code. + +- **Clarity and legibility:** Besides configuring the software, another + important purpose of a config file is to document intent for the humans who + operate and maintain the software. Operators need adjust settings to keep the + node running, and developers need to know what options were in use when + something goes wrong so they can diagnose and fix bugs. The legibility of a + config file as a _human_ artifact is also thus important. + + For example, Tendermint v0.35 moved settings related to validator private + keys from the top-level section of the configuration file to their own + designated `[priv-validator]` section. Although this change did not make any + difference to the meaning of those settings, it made the organization of the + file easier to understand, and allowed the names of the individual settings + to be simplified (e.g., `priv-validator-key-file` became simply `key-file` in + the new section). + + Although such changes are "gratuitous" with respect to the software, there is + often value in making things more legible for the humans. While there is no + simple rule to define the line, the Potter Stewart principle can be used with + due care. + +Keeping these examples in mind, we can and should take reasonable steps to +avoid churn in the configuration file across versions where we can. However, we +must also accept that part of the reason for _having_ a config file is to allow +us flexibility elsewhere in the design. On that basis, we should not attempt +to be too dogmatic about config changes either. Unlike changes in the block +protocol, for example, which affect every user of every network that adopts +them, config changes are relatively self-contained. + +There are few guiding principles I think we can use to strike a sensible +balance: + +1. **No gratuitous changes.** Aesthetic changes that do not enhance legibility, + avert confusion, or clarity documentation, should be entirely avoided. + +2. **Prefer mechanical changes.** Whenever it is practical, change settings in + a way that can be updated by a tool without operator judgement. This implies + finding safe, universal defaults for new settings, and not changing the + default values of existing settings. + + Even if that means we have to make multiple changes (e.g., add a new setting + in the current version, deprecate the old one, and remove the old one in the + next version) it's preferable if we can mechanize each step. + +3. **Clearly signal intent.** When adding temporary or experimental settings, + they should be clearly named and documented as such. Use long names and + suggestive prefixes (e.g., `experimental-*`) so that they stand out when + read in the config file or printed in logs. + + Relatedly, using temporary or experimental settings should cause the + software to emit diagnostic logs at runtime. These log messages should be + easy to grep for, and should contain pointers to more complete documentation + (say, issue numbers or URLs) that the operator can read, as well as a hint + about when the setting is expected to become invalid. For example: + + ``` + WARNING: Websocket RPC access is deprecated and will be removed in + Tendermint v0.37. See https://tinyurl.com/adr075 for more information. + ``` + +4. **Consider both directions.** When adding a configuration setting, take some + time during the implementation process to think about how the setting could + be removed, as well as how it will be rolled out. This applies even for + settings we imagine should be permanent. Experience may cause is to rethink + our original design intent more broadly than we expected. + + This does not mean we have to spend a long time picking nits over the design + of every setting; merely that we should convince ourselves we _could_ undo + it without making too big a mess later. Even a little extra effort up front + can sometimes save a lot. + +## References + +- [Tendermint `config` package][config-pkg] +- [`confix` command-line tool][confix] +- [`condiff` command-line tool][condiff] +- [Configuration update plan][plan] +- [ADR 075: RPC Event Subscription Interface][adr075] + +[config-pkg]: https://godoc.org/github.com/tendermint/tendermint/config +[confix]: https://github.com/tendermint/tendermint/blob/master/scripts/confix +[condiff]: https://github.com/tendermint/tendermint/blob/master/scripts/confix/condiff +[plan]: https://github.com/tendermint/tendermint/blob/master/scripts/confix/plan.go +[testdata]: https://github.com/tendermint/tendermint/blob/master/scripts/confix/testdata +[adr075]: https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-075-rpc-subscription.md + +## Appendix: Research Notes + +Discovering when various configuration settings were added, updated, and +removed turns out to be surprisingly tedious. To solve this puzzle, we had to +answer the following questions: + +1. What changes were made between v0.x and v0.y? This is further complicated by + cases where we have backported config changes into the middle of an earlier + release cycle (e.g., `psql-conn` from v0.35.x into v0.34.13). + +2. When during the development cycle were those changes made? This allows us to + recognize features that were backported into a previous release. + +3. What were the default values of the changed settings, and did they change at + all during or across the release boundary? + +Each step of the [configuration update plan][plan] is commented with a link to +one or more PRs where that change was made. The sections below discuss how we +found these references. + +### Tracking Changes Across Releases + +To figure out what changed between two releases, we built a tool called +[`condiff`][condiff], which performs a "keyspace" diff of two TOML documents. +This diff respects the structure of the TOML file, but ignores comments, blank +lines, and configuration values, so that we can see what was added and removed. + +To use it, run: + +```shell +go run ./scripts/confix/condiff old.toml new.toml +``` + +This tool works on any TOML documents, but for our purposes we needed +Tendermint `config.toml` files. The easiest way to get these is to build the +node binary for your version of interest, run `tendermint init` on a clean home +directory, and copy the generated config file out. The [`testdata`][testdata] +directory for the `confix` tool has configs generated from the heads of each +release branch from v0.31 through v0.35. + +If you want to reproduce this yourself, it looks something like this: + +```shell +# Example for Tendermint v0.32. +git checkout --track origin/v0.32.x +go get golang.org/x/sys/unix +go mod tidy +make build +rm -fr -- tmhome +./build/tendermint --home=tmhome init +cp tmhome/config/config.toml config-v32.toml +``` + +Be advised that the further back you go, the more idiosyncrasies you will +encounter. For example, Tendermint v0.31 and earlier predate Go modules (v0.31 +used dep), and lack backport branches. And you may need to do some editing of +Makefile rules once you get back into the 20s. + +Note that when diffing config files across the v0.34/v0.35 gap, the swap from +`snake_case` to `kebab-case` makes it look like everything changed. The +`condiff` tool has a `-desnake` flag that normalizes all the keys to kebab case +in both inputs before comparison. + +### Locating Additions and Deletions + +To figure out when a configuration setting was added or removed, your tool of +choice is `git bisect`. The only tricky part is finding the endpoints for the +search. If the transition happened within a release, you can use that +release's backport branch as the endpoint (if it has one, e.g., `v0.35.x`). + +However, the start point can be more problematic. The backport branches are not +ancestors of `master` or of each other, which means you need to find some point +in history _prior_ to the change but still attached to the mainline. For recent +releases there is a dev root (e.g., `v0.35.0-dev`, `v0.34.0-dev1`, etc.). These +are not named consistently, but you can usually grep the output of `git tag` to +find them. + +In the worst case you could try starting from the root commit of the repo, but +that turns out not to work in all cases. We've done some branching shenanigans +over the years that mean the root is not a direct ancestor of all our release +branches. When you find this you will probably swear a lot. I did. + +Once you have a start and end point (say, `v0.35.0-dev` and `master`), you can +bisect in the usual way. I use `git grep` on the `config` directory to check +whether the case I am looking for is present. For example, to find when the +`[fastsync]` section was removed: + +```shell +# Setup: +git checkout master +git bisect start +git bisect bad # it's not present on tip of master. +git bisect good v0.34.0-dev1 # it was present at the start of v0.34. +``` + +```shell +# Now repeat this until it gives you a specific commit: +if git grep -q '\[fastsync\]' config ; then git bisect good ; else git bisect bad ; fi +``` + +The above example finds where a config was removed: To find where a setting was +added, do the same thing except reverse the sense of the test (`if ! git grep -q +...`). diff --git a/go.mod b/go.mod index 9890018c4..33f5e5a6d 100644 --- a/go.mod +++ b/go.mod @@ -32,17 +32,17 @@ require ( golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 golang.org/x/net v0.0.0-20220412020605-290c469a71a5 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - google.golang.org/grpc v1.45.0 + google.golang.org/grpc v1.46.0 gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect pgregory.net/rapid v0.4.7 ) require ( - github.com/creachadair/atomicfile v0.2.4 + github.com/creachadair/atomicfile v0.2.5 github.com/creachadair/taskgroup v0.3.2 github.com/golangci/golangci-lint v1.45.2 github.com/google/go-cmp v0.5.7 - github.com/vektra/mockery/v2 v2.10.6 + github.com/vektra/mockery/v2 v2.12.0 gotest.tools v2.2.0+incompatible ) @@ -75,7 +75,7 @@ require ( github.com/charithe/durationcheck v0.0.9 // indirect github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af // indirect github.com/containerd/continuity v0.2.1 // indirect - github.com/creachadair/tomledit v0.0.16 + github.com/creachadair/tomledit v0.0.18 github.com/daixiang0/gci v0.3.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/denis-tingaikin/go-header v0.4.3 // indirect diff --git a/go.sum b/go.sum index dc0875b4e..1609c9a40 100644 --- a/go.sum +++ b/go.sum @@ -225,12 +225,12 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creachadair/atomicfile v0.2.4 h1:GRjpQLmz/78I4+nBQpGMFrRa9yrL157AUTrA6hnF0YU= -github.com/creachadair/atomicfile v0.2.4/go.mod h1:BRq8Une6ckFneYXZQ+kO7p1ZZP3I2fzVzf28JxrIkBc= +github.com/creachadair/atomicfile v0.2.5 h1:wkOlpsjyJOvJ3Hd8juHKdirJnCSIPacvtY21/3nYjAo= +github.com/creachadair/atomicfile v0.2.5/go.mod h1:BRq8Une6ckFneYXZQ+kO7p1ZZP3I2fzVzf28JxrIkBc= github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM= github.com/creachadair/taskgroup v0.3.2/go.mod h1:wieWwecHVzsidg2CsUnFinW1faVN4+kq+TDlRJQ0Wbk= -github.com/creachadair/tomledit v0.0.16 h1:PDNxgDjeeiNk1cyFfliIVQmagh1jPbDMabOw9yfSKLk= -github.com/creachadair/tomledit v0.0.16/go.mod h1:gvtfnSZLa+YNQD28vaPq0Nk12bRxEhmUdBzAWn+EGF4= +github.com/creachadair/tomledit v0.0.18 h1:gleeCnEgMPYcaBz5KN/1CN9yycF5n6Q2py7Ahu6yc7A= +github.com/creachadair/tomledit v0.0.18/go.mod h1:gvtfnSZLa+YNQD28vaPq0Nk12bRxEhmUdBzAWn+EGF4= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= @@ -275,6 +275,7 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= @@ -1055,8 +1056,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus= github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/vektra/mockery/v2 v2.10.6 h1:iLVqC9FozavYx27ZwfXipuizLBN8YzXlh9x5fufk48w= -github.com/vektra/mockery/v2 v2.10.6/go.mod h1:8vf4KDDUptfkyypzdHLuE7OE2xA7Gdt60WgIS8PgD+U= +github.com/vektra/mockery/v2 v2.12.0 h1:g3lq1ni5swlT9y6jYBx/CITVpwg3vsWrhKIuIweAxYI= +github.com/vektra/mockery/v2 v2.12.0/go.mod h1:8vf4KDDUptfkyypzdHLuE7OE2xA7Gdt60WgIS8PgD+U= github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= @@ -1698,8 +1699,9 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/internal/blocksync/reactor.go b/internal/blocksync/reactor.go index 63a65529f..779e6faf2 100644 --- a/internal/blocksync/reactor.go +++ b/internal/blocksync/reactor.go @@ -357,7 +357,7 @@ func (r *Reactor) SwitchToBlockSync(ctx context.Context, state sm.State) error { go r.requestRoutine(ctx, bsCh) go r.poolRoutine(ctx, true, bsCh) - if err := r.PublishStatus(ctx, types.EventDataBlockSyncStatus{ + if err := r.PublishStatus(types.EventDataBlockSyncStatus{ Complete: false, Height: state.LastBlockHeight, }); err != nil { @@ -623,11 +623,11 @@ func (r *Reactor) GetRemainingSyncTime() time.Duration { return time.Duration(int64(remain * float64(time.Second))) } -func (r *Reactor) PublishStatus(ctx context.Context, event types.EventDataBlockSyncStatus) error { +func (r *Reactor) PublishStatus(event types.EventDataBlockSyncStatus) error { if r.eventBus == nil { return errors.New("event bus is not configured") } - return r.eventBus.PublishEventBlockSyncStatus(ctx, event) + return r.eventBus.PublishEventBlockSyncStatus(event) } // atomicBool is an atomic Boolean, safe for concurrent use by multiple diff --git a/internal/blocksync/reactor_test.go b/internal/blocksync/reactor_test.go index 99c2ba8dc..fc7f480a1 100644 --- a/internal/blocksync/reactor_test.go +++ b/internal/blocksync/reactor_test.go @@ -50,7 +50,6 @@ func setup( genDoc *types.GenesisDoc, privVal types.PrivValidator, maxBlockHeights []int64, - chBuf uint, ) *reactorTestSuite { t.Helper() @@ -228,7 +227,7 @@ func TestReactor_AbruptDisconnect(t *testing.T) { genDoc := factory.GenesisDoc(cfg, time.Now(), valSet.Validators, factory.ConsensusParams()) maxBlockHeight := int64(64) - rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0}, 0) + rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0}) require.Equal(t, maxBlockHeight, rts.reactors[rts.nodes[0]].store.Height()) @@ -268,7 +267,7 @@ func TestReactor_SyncTime(t *testing.T) { genDoc := factory.GenesisDoc(cfg, time.Now(), valSet.Validators, factory.ConsensusParams()) maxBlockHeight := int64(101) - rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0}, 0) + rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0}) require.Equal(t, maxBlockHeight, rts.reactors[rts.nodes[0]].store.Height()) rts.start(ctx, t) @@ -297,7 +296,7 @@ func TestReactor_NoBlockResponse(t *testing.T) { genDoc := factory.GenesisDoc(cfg, time.Now(), valSet.Validators, factory.ConsensusParams()) maxBlockHeight := int64(65) - rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0}, 0) + rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0}) require.Equal(t, maxBlockHeight, rts.reactors[rts.nodes[0]].store.Height()) @@ -352,7 +351,7 @@ func TestReactor_BadBlockStopsPeer(t *testing.T) { valSet, privVals := factory.ValidatorSet(ctx, t, 1, 30) genDoc := factory.GenesisDoc(cfg, time.Now(), valSet.Validators, factory.ConsensusParams()) - rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0, 0, 0, 0}, 1000) + rts := setup(ctx, t, genDoc, privVals[0], []int64{maxBlockHeight, 0, 0, 0, 0}) require.Equal(t, maxBlockHeight, rts.reactors[rts.nodes[0]].store.Height()) diff --git a/internal/consensus/byzantine_test.go b/internal/consensus/byzantine_test.go index cfcf8b04f..804ebdb18 100644 --- a/internal/consensus/byzantine_test.go +++ b/internal/consensus/byzantine_test.go @@ -68,7 +68,8 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { ensureDir(t, path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal app := kvstore.NewApplication() vals := types.TM2PB.ValidatorUpdates(state.Validators) - app.InitChain(abci.RequestInitChain{Validators: vals}) + _, err = app.InitChain(ctx, &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) blockDB := dbm.NewMemDB() blockStore := store.NewBlockStore(blockDB) @@ -96,7 +97,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { // Make State blockExec := sm.NewBlockExecutor(stateStore, log.NewNopLogger(), proxyAppConnCon, mempool, evpool, blockStore, eventBus, sm.NopMetrics()) - cs, err := NewState(ctx, logger, thisConfig.Consensus, stateStore, blockExec, blockStore, mempool, evpool, eventBus) + cs, err := NewState(logger, thisConfig.Consensus, stateStore, blockExec, blockStore, mempool, evpool, eventBus) require.NoError(t, err) // set private validator pv := privVals[i] @@ -203,7 +204,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { proposerAddr := lazyNodeState.privValidatorPubKey.Address() block, err := lazyNodeState.blockExec.CreateProposalBlock( - ctx, lazyNodeState.Height, lazyNodeState.state, commit, proposerAddr, nil) + ctx, lazyNodeState.Height, lazyNodeState.state, commit, proposerAddr, lazyNodeState.LastCommit.GetVotes()) require.NoError(t, err) blockParts, err := block.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) diff --git a/internal/consensus/common_test.go b/internal/consensus/common_test.go index a69fc1240..515a74d1a 100644 --- a/internal/consensus/common_test.go +++ b/internal/consensus/common_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" dbm "github.com/tendermint/tm-db" @@ -112,7 +113,8 @@ func (vs *validatorStub) signVote( ctx context.Context, voteType tmproto.SignedMsgType, chainID string, - blockID types.BlockID) (*types.Vote, error) { + blockID types.BlockID, + voteExtension []byte) (*types.Vote, error) { pubKey, err := vs.PrivValidator.GetPubKey(ctx) if err != nil { @@ -120,28 +122,30 @@ func (vs *validatorStub) signVote( } vote := &types.Vote{ - ValidatorIndex: vs.Index, - ValidatorAddress: pubKey.Address(), + Type: voteType, Height: vs.Height, Round: vs.Round, - Timestamp: vs.clock.Now(), - Type: voteType, BlockID: blockID, - VoteExtension: types.VoteExtensionFromProto(kvstore.ConstructVoteExtension(pubKey.Address())), + Timestamp: vs.clock.Now(), + ValidatorAddress: pubKey.Address(), + ValidatorIndex: vs.Index, + Extension: voteExtension, } v := vote.ToProto() - if err := vs.PrivValidator.SignVote(ctx, chainID, v); err != nil { + if err = vs.PrivValidator.SignVote(ctx, chainID, 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 } @@ -155,13 +159,9 @@ func signVote( chainID string, blockID types.BlockID) *types.Vote { - v, err := vs.signVote(ctx, voteType, chainID, blockID) + v, err := vs.signVote(ctx, voteType, chainID, blockID, []byte("extension")) require.NoError(t, err, "failed to sign vote") - // TODO: remove hardcoded vote extension. - // currently set for abci/examples/kvstore/persistent_kvstore.go - v.VoteExtension = types.VoteExtensionFromProto(kvstore.ConstructVoteExtension(v.ValidatorAddress)) - vs.lastVote = v return v @@ -351,18 +351,19 @@ func validatePrecommit( require.True(t, bytes.Equal(vote.BlockID.Hash, votedBlockHash), "Expected precommit to be for proposal block") } + rs := cs.GetRoundState() if lockedBlockHash == nil { - require.False(t, cs.LockedRound != lockRound || cs.LockedBlock != nil, + require.False(t, rs.LockedRound != lockRound || rs.LockedBlock != nil, "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 { - require.False(t, cs.LockedRound != lockRound || !bytes.Equal(cs.LockedBlock.Hash(), lockedBlockHash), + require.False(t, rs.LockedRound != lockRound || !bytes.Equal(rs.LockedBlock.Hash(), lockedBlockHash), "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) } } @@ -491,8 +492,7 @@ func newStateWithConfigAndBlockStore( require.NoError(t, eventBus.Start(ctx)) blockExec := sm.NewBlockExecutor(stateStore, logger, proxyAppConnCon, mempool, evpool, blockStore, eventBus, sm.NopMetrics()) - cs, err := NewState(ctx, - logger.With("module", "consensus"), + cs, err := NewState(logger.With("module", "consensus"), thisConfig.Consensus, stateStore, blockExec, @@ -731,10 +731,9 @@ func ensureVoteMatch(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, msg.Data()) vote := voteEvent.Vote - require.Equal(t, height, vote.Height) - require.Equal(t, round, vote.Round) - - require.Equal(t, voteType, vote.Type) + assert.Equal(t, height, vote.Height, "expected height %d, but got %d", height, vote.Height) + assert.Equal(t, round, vote.Round, "expected round %d, but got %d", round, vote.Round) + assert.Equal(t, voteType, vote.Type, "expected type %s, but got %s", voteType, vote.Type) if hash == nil { require.Nil(t, vote.BlockID.Hash, "Expected prevote to be for nil, got %X", vote.BlockID.Hash) } else { @@ -742,6 +741,7 @@ func ensureVoteMatch(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, } } } + func ensureVote(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, round int32, voteType tmproto.SignedMsgType) { t.Helper() msg := ensureMessageBeforeTimeout(t, voteCh, ensureTimeout) @@ -750,10 +750,9 @@ func ensureVote(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, roun msg.Data()) vote := voteEvent.Vote - require.Equal(t, height, vote.Height) - require.Equal(t, round, vote.Round) - - require.Equal(t, voteType, vote.Type) + require.Equal(t, height, vote.Height, "expected height %d, but got %d", height, vote.Height) + require.Equal(t, round, vote.Round, "expected round %d, but got %d", round, vote.Round) + require.Equal(t, voteType, vote.Type, "expected type %s, but got %s", voteType, vote.Type) } func ensureNewEventOnChannel(t *testing.T, ch <-chan tmpubsub.Message) { @@ -822,7 +821,8 @@ func makeConsensusState( closeFuncs = append(closeFuncs, app.Close) vals := types.TM2PB.ValidatorUpdates(state.Validators) - app.InitChain(abci.RequestInitChain{Validators: vals}) + _, err = app.InitChain(ctx, &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) l := logger.With("validator", i, "module", "consensus") css[i] = newStateWithConfigAndBlockStore(ctx, t, l, thisConfig, state, privVals[i], app, blockStore) @@ -893,7 +893,8 @@ func randConsensusNetWithPeers( case *kvstore.Application: state.Version.Consensus.App = kvstore.ProtocolVersion } - app.InitChain(abci.RequestInitChain{Validators: vals}) + _, err = app.InitChain(ctx, &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) // sm.SaveState(stateDB,state) //height 1's validatorsInfo already saved in LoadStateFromDBOrGenesisDoc above css[i] = newStateWithConfig(ctx, t, logger.With("validator", i, "module", "consensus"), thisConfig, state, privVal, app) @@ -987,5 +988,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/internal/consensus/mempool_test.go b/internal/consensus/mempool_test.go index ab7a2baf3..ac04e4027 100644 --- a/internal/consensus/mempool_test.go +++ b/internal/consensus/mempool_test.go @@ -199,10 +199,12 @@ func TestMempoolRmBadTx(t *testing.T) { txBytes := make([]byte, 8) binary.BigEndian.PutUint64(txBytes, uint64(0)) - resFinalize := app.FinalizeBlock(abci.RequestFinalizeBlock{Txs: [][]byte{txBytes}}) + resFinalize, err := app.FinalizeBlock(ctx, &abci.RequestFinalizeBlock{Txs: [][]byte{txBytes}}) + require.NoError(t, err) assert.False(t, resFinalize.TxResults[0].IsErr(), fmt.Sprintf("expected no error. got %v", resFinalize)) - resCommit := app.Commit() + resCommit, err := app.Commit(ctx) + require.NoError(t, err) assert.True(t, len(resCommit.Data) > 0) emptyMempoolCh := make(chan struct{}) @@ -267,11 +269,11 @@ func NewCounterApplication() *CounterApplication { return &CounterApplication{} } -func (app *CounterApplication) Info(req abci.RequestInfo) abci.ResponseInfo { - return abci.ResponseInfo{Data: fmt.Sprintf("txs:%v", app.txCount)} +func (app *CounterApplication) Info(_ context.Context, req *abci.RequestInfo) (*abci.ResponseInfo, error) { + return &abci.ResponseInfo{Data: fmt.Sprintf("txs:%v", app.txCount)}, nil } -func (app *CounterApplication) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFinalizeBlock { +func (app *CounterApplication) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) { respTxs := make([]*abci.ExecTxResult, len(req.Txs)) for i, tx := range req.Txs { txValue := txAsUint64(tx) @@ -285,18 +287,19 @@ func (app *CounterApplication) FinalizeBlock(req abci.RequestFinalizeBlock) abci app.txCount++ respTxs[i] = &abci.ExecTxResult{Code: code.CodeTypeOK} } - return abci.ResponseFinalizeBlock{TxResults: respTxs} + return &abci.ResponseFinalizeBlock{TxResults: respTxs}, nil } -func (app *CounterApplication) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { +func (app *CounterApplication) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) { txValue := txAsUint64(req.Tx) if txValue != uint64(app.mempoolTxCount) { - return abci.ResponseCheckTx{ + return &abci.ResponseCheckTx{ Code: code.CodeTypeBadNonce, - Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.mempoolTxCount, txValue)} + Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.mempoolTxCount, txValue), + }, nil } app.mempoolTxCount++ - return abci.ResponseCheckTx{Code: code.CodeTypeOK} + return &abci.ResponseCheckTx{Code: code.CodeTypeOK}, nil } func txAsUint64(tx []byte) uint64 { @@ -305,19 +308,17 @@ func txAsUint64(tx []byte) uint64 { return binary.BigEndian.Uint64(tx8) } -func (app *CounterApplication) Commit() abci.ResponseCommit { +func (app *CounterApplication) Commit(context.Context) (*abci.ResponseCommit, error) { app.mempoolTxCount = app.txCount if app.txCount == 0 { - return abci.ResponseCommit{} + return &abci.ResponseCommit{}, nil } hash := make([]byte, 8) binary.BigEndian.PutUint64(hash, uint64(app.txCount)) - return abci.ResponseCommit{Data: hash} + return &abci.ResponseCommit{Data: hash}, nil } -func (app *CounterApplication) PrepareProposal( - req abci.RequestPrepareProposal) abci.ResponsePrepareProposal { - +func (app *CounterApplication) PrepareProposal(_ context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { trs := make([]*abci.TxRecord, 0, len(req.Txs)) var totalBytes int64 for _, tx := range req.Txs { @@ -330,10 +331,9 @@ func (app *CounterApplication) PrepareProposal( Tx: tx, }) } - return abci.ResponsePrepareProposal{TxRecords: trs} + return &abci.ResponsePrepareProposal{TxRecords: trs}, nil } -func (app *CounterApplication) ProcessProposal( - req abci.RequestProcessProposal) abci.ResponseProcessProposal { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT} +func (app *CounterApplication) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) { + return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil } diff --git a/internal/consensus/mocks/cons_sync_reactor.go b/internal/consensus/mocks/cons_sync_reactor.go index 5ac592f0d..f904e9129 100644 --- a/internal/consensus/mocks/cons_sync_reactor.go +++ b/internal/consensus/mocks/cons_sync_reactor.go @@ -3,6 +3,8 @@ package mocks import ( + testing "testing" + mock "github.com/stretchr/testify/mock" state "github.com/tendermint/tendermint/internal/state" ) @@ -26,3 +28,13 @@ func (_m *ConsSyncReactor) SetStateSyncingMetrics(_a0 float64) { func (_m *ConsSyncReactor) SwitchToConsensus(_a0 state.State, _a1 bool) { _m.Called(_a0, _a1) } + +// NewConsSyncReactor creates a new instance of ConsSyncReactor. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewConsSyncReactor(t testing.TB) *ConsSyncReactor { + mock := &ConsSyncReactor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/consensus/msgs_test.go b/internal/consensus/msgs_test.go index e85936820..cfbd53ff7 100644 --- a/internal/consensus/msgs_test.go +++ b/internal/consensus/msgs_test.go @@ -357,11 +357,6 @@ func TestConsMsgsVectors(t *testing.T) { } pbProposal := proposal.ToProto() - ext := types.VoteExtension{ - AppDataToSign: []byte("signed"), - AppDataSelfAuthenticating: []byte("auth"), - } - v := &types.Vote{ ValidatorAddress: []byte("add_more_exclamation"), ValidatorIndex: 1, @@ -370,7 +365,7 @@ func TestConsMsgsVectors(t *testing.T) { Timestamp: date, Type: tmproto.PrecommitType, BlockID: bi, - VoteExtension: ext, + Extension: []byte("extension"), } vpb := v.ToProto() @@ -407,7 +402,7 @@ func TestConsMsgsVectors(t *testing.T) { "2a36080110011a3008011204746573741a26080110011a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d"}, {"Vote", &tmcons.Message{Sum: &tmcons.Message_Vote{ Vote: &tmcons.Vote{Vote: vpb}}}, - "3280010a7e0802100122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d2a0608c0b89fdc0532146164645f6d6f72655f6578636c616d6174696f6e38014a0e0a067369676e6564120461757468"}, + "327b0a790802100122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d2a0608c0b89fdc0532146164645f6d6f72655f6578636c616d6174696f6e38014a09657874656e73696f6e"}, {"HasVote", &tmcons.Message{Sum: &tmcons.Message_HasVote{ HasVote: &tmcons.HasVote{Height: 1, Round: 1, Type: tmproto.PrevoteType, Index: 1}}}, "3a080801100118012001"}, diff --git a/internal/consensus/reactor.go b/internal/consensus/reactor.go index 18589c93a..53e0b540d 100644 --- a/internal/consensus/reactor.go +++ b/internal/consensus/reactor.go @@ -210,7 +210,7 @@ func (r *Reactor) OnStart(ctx context.Context) error { // leak the goroutine when stopping the reactor. go r.peerStatsRoutine(ctx, peerUpdates) - r.subscribeToBroadcastEvents(chBundle.state) + r.subscribeToBroadcastEvents(ctx, chBundle.state) if !r.WaitSync() { if err := r.state.Start(ctx); err != nil { @@ -260,7 +260,7 @@ func (r *Reactor) SwitchToConsensus(ctx context.Context, state sm.State, skipWAL // NOTE: The line below causes broadcastNewRoundStepRoutine() to broadcast a // NewRoundStepMessage. - r.state.updateToState(ctx, state) + r.state.updateToState(state) if err := r.state.Start(ctx); err != nil { panic(fmt.Sprintf(`failed to start consensus state: %v @@ -284,7 +284,7 @@ conR: } d := types.EventDataBlockSyncStatus{Complete: true, Height: state.LastBlockHeight} - if err := r.eventBus.PublishEventBlockSyncStatus(ctx, d); err != nil { + if err := r.eventBus.PublishEventBlockSyncStatus(d); err != nil { r.logger.Error("failed to emit the blocksync complete event", "err", err) } } @@ -344,13 +344,13 @@ func (r *Reactor) broadcastHasVoteMessage(ctx context.Context, vote *types.Vote, // subscribeToBroadcastEvents subscribes for new round steps and votes using the // internal pubsub defined in the consensus state to broadcast them to peers // upon receiving. -func (r *Reactor) subscribeToBroadcastEvents(stateCh *p2p.Channel) { +func (r *Reactor) subscribeToBroadcastEvents(ctx context.Context, stateCh *p2p.Channel) { onStopCh := r.state.getOnStopCh() err := r.state.evsw.AddListenerForEvent( listenerIDConsensus, types.EventNewRoundStepValue, - func(ctx context.Context, data tmevents.EventData) error { + func(data tmevents.EventData) error { if err := r.broadcastNewRoundStepMessage(ctx, data.(*cstypes.RoundState), stateCh); err != nil { return err } @@ -371,7 +371,7 @@ func (r *Reactor) subscribeToBroadcastEvents(stateCh *p2p.Channel) { err = r.state.evsw.AddListenerForEvent( listenerIDConsensus, types.EventValidBlockValue, - func(ctx context.Context, data tmevents.EventData) error { + func(data tmevents.EventData) error { return r.broadcastNewValidBlockMessage(ctx, data.(*cstypes.RoundState), stateCh) }, ) @@ -382,7 +382,7 @@ func (r *Reactor) subscribeToBroadcastEvents(stateCh *p2p.Channel) { err = r.state.evsw.AddListenerForEvent( listenerIDConsensus, types.EventVoteValue, - func(ctx context.Context, data tmevents.EventData) error { + func(data tmevents.EventData) error { return r.broadcastHasVoteMessage(ctx, data.(*types.Vote), stateCh) }, ) diff --git a/internal/consensus/reactor_test.go b/internal/consensus/reactor_test.go index 9c6af5c5b..8a9abdf43 100644 --- a/internal/consensus/reactor_test.go +++ b/internal/consensus/reactor_test.go @@ -466,7 +466,8 @@ func TestReactorWithEvidence(t *testing.T) { app := kvstore.NewApplication() vals := types.TM2PB.ValidatorUpdates(state.Validators) - app.InitChain(abci.RequestInitChain{Validators: vals}) + _, err = app.InitChain(ctx, &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) pv := privVals[i] blockDB := dbm.NewMemDB() @@ -505,7 +506,7 @@ func TestReactorWithEvidence(t *testing.T) { blockExec := sm.NewBlockExecutor(stateStore, log.NewNopLogger(), proxyAppConnCon, mempool, evpool, blockStore, eventBus, sm.NopMetrics()) - cs, err := NewState(ctx, logger.With("validator", i, "module", "consensus"), + cs, err := NewState(logger.With("validator", i, "module", "consensus"), thisConfig.Consensus, stateStore, blockExec, blockStore, mempool, evpool2, eventBus) require.NoError(t, err) cs.SetPrivValidator(ctx, pv) diff --git a/internal/consensus/replay.go b/internal/consensus/replay.go index 3b2dea930..fac7c91ac 100644 --- a/internal/consensus/replay.go +++ b/internal/consensus/replay.go @@ -239,7 +239,7 @@ func (h *Handshaker) NBlocks() int { func (h *Handshaker) Handshake(ctx context.Context, appClient abciclient.Client) error { // Handshake is done via ABCI Info on the query conn. - res, err := appClient.Info(ctx, proxy.RequestInfo) + res, err := appClient.Info(ctx, &proxy.RequestInfo) if err != nil { return fmt.Errorf("error calling Info: %w", err) } @@ -307,15 +307,14 @@ func (h *Handshaker) ReplayBlocks( validatorSet := types.NewValidatorSet(validators) nextVals := types.TM2PB.ValidatorUpdates(validatorSet) pbParams := h.genDoc.ConsensusParams.ToProto() - req := abci.RequestInitChain{ + res, err := appClient.InitChain(ctx, &abci.RequestInitChain{ Time: h.genDoc.GenesisTime, ChainId: h.genDoc.ChainID, InitialHeight: h.genDoc.InitialHeight, ConsensusParams: &pbParams, Validators: nextVals, AppStateBytes: h.genDoc.AppState, - } - res, err := appClient.InitChain(ctx, req) + }) if err != nil { return nil, err } @@ -429,7 +428,7 @@ func (h *Handshaker) ReplayBlocks( if err != nil { return nil, err } - mockApp, err := newMockProxyApp(ctx, h.logger, appHash, abciResponses) + mockApp, err := newMockProxyApp(h.logger, appHash, abciResponses) if err != nil { return nil, err } diff --git a/internal/consensus/replay_file.go b/internal/consensus/replay_file.go index e88a06454..cd6d4b831 100644 --- a/internal/consensus/replay_file.go +++ b/internal/consensus/replay_file.go @@ -145,7 +145,7 @@ func (pb *playback) replayReset(ctx context.Context, count int, newStepSub event pb.cs.Stop() pb.cs.Wait() - newCS, err := NewState(ctx, pb.cs.logger, pb.cs.config, pb.stateStore, pb.cs.blockExec, + newCS, err := NewState(pb.cs.logger, pb.cs.config, pb.stateStore, pb.cs.blockExec, pb.cs.blockStore, pb.cs.txNotifier, pb.cs.evpool, pb.cs.eventBus) if err != nil { return err @@ -350,7 +350,7 @@ func newConsensusStateForReplay( mempool, evpool := emptyMempool{}, sm.EmptyEvidencePool{} blockExec := sm.NewBlockExecutor(stateStore, logger, proxyApp, mempool, evpool, blockStore, eventBus, sm.NopMetrics()) - consensusState, err := NewState(ctx, logger, csConfig, stateStore, blockExec, + consensusState, err := NewState(logger, csConfig, stateStore, blockExec, blockStore, mempool, evpool, eventBus) if err != nil { return nil, err diff --git a/internal/consensus/replay_stubs.go b/internal/consensus/replay_stubs.go index e479d344e..3cd5bdac0 100644 --- a/internal/consensus/replay_stubs.go +++ b/internal/consensus/replay_stubs.go @@ -57,7 +57,6 @@ func (emptyMempool) CloseWAL() {} // the real app. func newMockProxyApp( - ctx context.Context, logger log.Logger, appHash []byte, abciResponses *tmstate.ABCIResponses, @@ -76,15 +75,15 @@ type mockProxyApp struct { abciResponses *tmstate.ABCIResponses } -func (mock *mockProxyApp) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFinalizeBlock { +func (mock *mockProxyApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) { r := mock.abciResponses.FinalizeBlock mock.txCount++ if r == nil { - return abci.ResponseFinalizeBlock{} + return &abci.ResponseFinalizeBlock{}, nil } - return *r + return r, nil } -func (mock *mockProxyApp) Commit() abci.ResponseCommit { - return abci.ResponseCommit{Data: mock.appHash} +func (mock *mockProxyApp) Commit(context.Context) (*abci.ResponseCommit, error) { + return &abci.ResponseCommit{Data: mock.appHash}, nil } diff --git a/internal/consensus/replay_test.go b/internal/consensus/replay_test.go index 6d9e82a05..f112f23e8 100644 --- a/internal/consensus/replay_test.go +++ b/internal/consensus/replay_test.go @@ -118,9 +118,6 @@ func sendTxs(ctx context.Context, t *testing.T, cs *State) { // TestWALCrash uses crashing WAL to test we can recover from any WAL failure. func TestWALCrash(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - testCases := []struct { name string initFn func(dbm.DB, *State, context.Context) @@ -139,6 +136,9 @@ func TestWALCrash(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + consensusReplayConfig, err := ResetConfig(t.TempDir(), tc.name) require.NoError(t, err) crashWALandCheckLiveness(ctx, t, consensusReplayConfig, tc.initFn, tc.heightToStop) @@ -788,7 +788,7 @@ func testHandshakeReplay( require.NoError(t, err, "Error on abci handshake") // get the latest app hash from the app - res, err := proxyApp.Info(ctx, abci.RequestInfo{Version: ""}) + res, err := proxyApp.Info(ctx, &abci.RequestInfo{Version: ""}) if err != nil { t.Fatal(err) } @@ -856,7 +856,7 @@ func buildAppStateFromChain( state.Version.Consensus.App = kvstore.ProtocolVersion // simulate handshake, receive app version validators := types.TM2PB.ValidatorUpdates(state.Validators) - _, err := appClient.InitChain(ctx, abci.RequestInitChain{ + _, err := appClient.InitChain(ctx, &abci.RequestInitChain{ Validators: validators, }) require.NoError(t, err) @@ -910,7 +910,7 @@ func buildTMStateFromChain( state.Version.Consensus.App = kvstore.ProtocolVersion // simulate handshake, receive app version validators := types.TM2PB.ValidatorUpdates(state.Validators) - _, err := proxyApp.InitChain(ctx, abci.RequestInitChain{ + _, err := proxyApp.InitChain(ctx, &abci.RequestInitChain{ Validators: validators, }) require.NoError(t, err) @@ -1017,15 +1017,15 @@ type badApp struct { onlyLastHashIsWrong bool } -func (app *badApp) Commit() abci.ResponseCommit { +func (app *badApp) Commit(context.Context) (*abci.ResponseCommit, error) { app.height++ if app.onlyLastHashIsWrong { if app.height == app.numBlocks { - return abci.ResponseCommit{Data: tmrand.Bytes(8)} + return &abci.ResponseCommit{Data: tmrand.Bytes(8)}, nil } - return abci.ResponseCommit{Data: []byte{app.height}} + return &abci.ResponseCommit{Data: []byte{app.height}}, nil } else if app.allHashesAreWrong { - return abci.ResponseCommit{Data: tmrand.Bytes(8)} + return &abci.ResponseCommit{Data: tmrand.Bytes(8)}, nil } panic("either allHashesAreWrong or onlyLastHashIsWrong must be set") @@ -1275,8 +1275,6 @@ type initChainApp struct { vals []abci.ValidatorUpdate } -func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { - return abci.ResponseInitChain{ - Validators: ica.vals, - } +func (ica *initChainApp) InitChain(_ context.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) { + return &abci.ResponseInitChain{Validators: ica.vals}, nil } diff --git a/internal/consensus/state.go b/internal/consensus/state.go index 2f27afc81..490801ad2 100644 --- a/internal/consensus/state.go +++ b/internal/consensus/state.go @@ -194,7 +194,6 @@ func SkipStateStoreBootstrap(sm *State) { // NewState returns a new State. func NewState( - ctx context.Context, logger log.Logger, cfg *config.ConsensusConfig, store sm.Store, @@ -240,7 +239,7 @@ func NewState( // node-fragments gracefully while letting the nodes // themselves avoid this. if !cs.skipBootstrapping { - if err := cs.updateStateFromStore(ctx); err != nil { + if err := cs.updateStateFromStore(); err != nil { return nil, err } } @@ -248,7 +247,7 @@ func NewState( return cs, nil } -func (cs *State) updateStateFromStore(ctx context.Context) error { +func (cs *State) updateStateFromStore() error { if cs.initialStatePopulated { return nil } @@ -265,7 +264,7 @@ func (cs *State) updateStateFromStore(ctx context.Context) error { cs.reconstructLastCommit(state) } - cs.updateToState(ctx, state) + cs.updateToState(state) cs.initialStatePopulated = true return nil @@ -393,7 +392,7 @@ func (cs *State) LoadCommit(height int64) *types.Commit { // OnStart loads the latest state via the WAL, and starts the timeout and // receive routines. func (cs *State) OnStart(ctx context.Context) error { - if err := cs.updateStateFromStore(ctx); err != nil { + if err := cs.updateStateFromStore(); err != nil { return err } @@ -718,7 +717,7 @@ func (cs *State) reconstructLastCommit(state sm.State) { // Updates State and increments height to match that of state. // The round becomes 0 and cs.Step becomes cstypes.RoundStepNewHeight. -func (cs *State) updateToState(ctx context.Context, state sm.State) { +func (cs *State) updateToState(state sm.State) { if cs.CommitRound > -1 && 0 < cs.Height && cs.Height != state.LastBlockHeight { panic(fmt.Sprintf( "updateToState() expected state height of %v but found %v", @@ -753,7 +752,7 @@ func (cs *State) updateToState(ctx context.Context, state sm.State) { "new_height", state.LastBlockHeight+1, "old_height", cs.state.LastBlockHeight+1, ) - cs.newStep(ctx) + cs.newStep() return } } @@ -823,10 +822,10 @@ func (cs *State) updateToState(ctx context.Context, state sm.State) { cs.state = state // Finally, broadcast RoundState - cs.newStep(ctx) + cs.newStep() } -func (cs *State) newStep(ctx context.Context) { +func (cs *State) newStep() { rs := cs.RoundStateEvent() if err := cs.wal.Write(rs); err != nil { cs.logger.Error("failed writing to WAL", "err", err) @@ -836,11 +835,11 @@ func (cs *State) newStep(ctx context.Context) { // newStep is called by updateToState in NewState before the eventBus is set! if cs.eventBus != nil { - if err := cs.eventBus.PublishEventNewRoundStep(ctx, rs); err != nil { + if err := cs.eventBus.PublishEventNewRoundStep(rs); err != nil { cs.logger.Error("failed publishing new round step", "err", err) } - cs.evsw.FireEvent(ctx, types.EventNewRoundStepValue, &cs.RoundState) + cs.evsw.FireEvent(types.EventNewRoundStepValue, &cs.RoundState) } } @@ -923,7 +922,6 @@ func (cs *State) receiveRoutine(ctx context.Context, 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(ctx, mi) @@ -977,7 +975,7 @@ func (cs *State) handleMsg(ctx context.Context, mi msgInfo) { case *BlockPartMessage: // if the proposal is complete, we'll enterPrevote or tryFinalizeCommit - added, err = cs.addProposalBlockPart(ctx, msg, peerID) + added, err = cs.addProposalBlockPart(msg, peerID) // We unlock here to yield to any routines that need to read the the RoundState. // Previously, this code held the lock from the point at which the final block @@ -1083,21 +1081,21 @@ func (cs *State) handleTimeout( cs.enterPropose(ctx, ti.Height, 0) case cstypes.RoundStepPropose: - if err := cs.eventBus.PublishEventTimeoutPropose(ctx, cs.RoundStateEvent()); err != nil { + if err := cs.eventBus.PublishEventTimeoutPropose(cs.RoundStateEvent()); err != nil { cs.logger.Error("failed publishing timeout propose", "err", err) } cs.enterPrevote(ctx, ti.Height, ti.Round) case cstypes.RoundStepPrevoteWait: - if err := cs.eventBus.PublishEventTimeoutWait(ctx, cs.RoundStateEvent()); err != nil { + if err := cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent()); err != nil { cs.logger.Error("failed publishing timeout wait", "err", err) } cs.enterPrecommit(ctx, ti.Height, ti.Round) case cstypes.RoundStepPrecommitWait: - if err := cs.eventBus.PublishEventTimeoutWait(ctx, cs.RoundStateEvent()); err != nil { + if err := cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent()); err != nil { cs.logger.Error("failed publishing timeout wait", "err", err) } @@ -1200,7 +1198,7 @@ func (cs *State) enterNewRound(ctx context.Context, height int64, round int32) { cs.Votes.SetRound(r) // also track next round (round+1) to allow round-skipping cs.TriggeredTimeoutPrecommit = false - if err := cs.eventBus.PublishEventNewRound(ctx, cs.NewRoundEvent()); err != nil { + if err := cs.eventBus.PublishEventNewRound(cs.NewRoundEvent()); err != nil { cs.logger.Error("failed publishing new round", "err", err) } // Wait for txs to be available in the mempool @@ -1263,7 +1261,7 @@ func (cs *State) enterPropose(ctx context.Context, height int64, round int32) { defer func() { // Done enterPropose: cs.updateRoundStep(round, cstypes.RoundStepPropose) - cs.newStep(ctx) + cs.newStep() // If we have the whole proposal + POL, then goto Prevote now. // else, we'll enterPrevote when the rest of the proposal is received (in AddProposalBlockPart), @@ -1455,7 +1453,7 @@ func (cs *State) enterPrevote(ctx context.Context, height int64, round int32) { defer func() { // Done enterPrevote: cs.updateRoundStep(round, cstypes.RoundStepPrevote) - cs.newStep(ctx) + cs.newStep() }() logger.Debug("entering prevote step", "current", fmt.Sprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) @@ -1606,7 +1604,7 @@ func (cs *State) defaultDoPrevote(ctx context.Context, height int64, round int32 } // Enter: any +2/3 prevotes at next round. -func (cs *State) enterPrevoteWait(ctx context.Context, height int64, round int32) { +func (cs *State) enterPrevoteWait(height int64, round int32) { logger := cs.logger.With("height", height, "round", round) if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevoteWait <= cs.Step) { @@ -1629,7 +1627,7 @@ func (cs *State) enterPrevoteWait(ctx context.Context, height int64, round int32 defer func() { // Done enterPrevoteWait: cs.updateRoundStep(round, cstypes.RoundStepPrevoteWait) - cs.newStep(ctx) + cs.newStep() }() // Wait for some more prevotes; enterPrecommit @@ -1657,7 +1655,7 @@ func (cs *State) enterPrecommit(ctx context.Context, height int64, round int32) defer func() { // Done enterPrecommit: cs.updateRoundStep(round, cstypes.RoundStepPrecommit) - cs.newStep(ctx) + cs.newStep() }() // check for a polka @@ -1676,7 +1674,7 @@ func (cs *State) enterPrecommit(ctx context.Context, height int64, round int32) } // At this point +2/3 prevoted for a particular block or nil. - if err := cs.eventBus.PublishEventPolka(ctx, cs.RoundStateEvent()); err != nil { + if err := cs.eventBus.PublishEventPolka(cs.RoundStateEvent()); err != nil { logger.Error("failed publishing polka", "err", err) } @@ -1713,7 +1711,7 @@ func (cs *State) enterPrecommit(ctx context.Context, height int64, round int32) logger.Debug("precommit step: +2/3 prevoted locked block; relocking") cs.LockedRound = round - if err := cs.eventBus.PublishEventRelock(ctx, cs.RoundStateEvent()); err != nil { + if err := cs.eventBus.PublishEventRelock(cs.RoundStateEvent()); err != nil { logger.Error("precommit step: failed publishing event relock", "err", err) } @@ -1736,7 +1734,7 @@ func (cs *State) enterPrecommit(ctx context.Context, height int64, round int32) cs.LockedBlock = cs.ProposalBlock cs.LockedBlockParts = cs.ProposalBlockParts - if err := cs.eventBus.PublishEventLock(ctx, cs.RoundStateEvent()); err != nil { + if err := cs.eventBus.PublishEventLock(cs.RoundStateEvent()); err != nil { logger.Error("precommit step: failed publishing event lock", "err", err) } @@ -1758,7 +1756,7 @@ func (cs *State) enterPrecommit(ctx context.Context, height int64, round int32) } // Enter: any +2/3 precommits for next round. -func (cs *State) enterPrecommitWait(ctx context.Context, height int64, round int32) { +func (cs *State) enterPrecommitWait(height int64, round int32) { logger := cs.logger.With("height", height, "round", round) if cs.Height != height || round < cs.Round || (cs.Round == round && cs.TriggeredTimeoutPrecommit) { @@ -1782,7 +1780,7 @@ func (cs *State) enterPrecommitWait(ctx context.Context, height int64, round int defer func() { // Done enterPrecommitWait: cs.TriggeredTimeoutPrecommit = true - cs.newStep(ctx) + cs.newStep() }() // wait for some more precommits; enterNewRound @@ -1809,7 +1807,7 @@ func (cs *State) enterCommit(ctx context.Context, height int64, commitRound int3 cs.updateRoundStep(cs.Round, cstypes.RoundStepCommit) cs.CommitRound = commitRound cs.CommitTime = tmtime.Now() - cs.newStep(ctx) + cs.newStep() // Maybe finalize immediately. cs.tryFinalizeCommit(ctx, height) @@ -1844,11 +1842,11 @@ func (cs *State) enterCommit(ctx context.Context, height int64, commitRound int3 cs.metrics.MarkBlockGossipStarted() cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader) - if err := cs.eventBus.PublishEventValidBlock(ctx, cs.RoundStateEvent()); err != nil { + if err := cs.eventBus.PublishEventValidBlock(cs.RoundStateEvent()); err != nil { logger.Error("failed publishing valid block", "err", err) } - cs.evsw.FireEvent(ctx, types.EventValidBlockValue, &cs.RoundState) + cs.evsw.FireEvent(types.EventValidBlockValue, &cs.RoundState) } } } @@ -1975,7 +1973,7 @@ func (cs *State) finalizeCommit(ctx context.Context, height int64) { cs.RecordMetrics(height, block) // NewHeightStep! - cs.updateToState(ctx, stateCopy) + cs.updateToState(stateCopy) // Private validator might have changed it's key pair => refetch pubkey. if err := cs.updatePrivValidatorPubKey(ctx); err != nil { @@ -2130,7 +2128,6 @@ func (cs *State) defaultSetProposal(proposal *types.Proposal, recvTime time.Time // Asynchronously triggers either enterPrevote (before we timeout of propose) or tryFinalizeCommit, // once we have the full block. func (cs *State) addProposalBlockPart( - ctx context.Context, msg *BlockPartMessage, peerID types.NodeID, ) (added bool, err error) { @@ -2196,7 +2193,7 @@ func (cs *State) addProposalBlockPart( // NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal cs.logger.Info("received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash()) - if err := cs.eventBus.PublishEventCompleteProposal(ctx, cs.CompleteProposalEvent()); err != nil { + if err := cs.eventBus.PublishEventCompleteProposal(cs.CompleteProposalEvent()); err != nil { cs.logger.Error("failed publishing event complete proposal", "err", err) } } @@ -2315,11 +2312,11 @@ func (cs *State) addVote( } cs.logger.Debug("added vote to last precommits", "last_commit", cs.LastCommit.StringShort()) - if err := cs.eventBus.PublishEventVote(ctx, types.EventDataVote{Vote: vote}); err != nil { + if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil { return added, err } - cs.evsw.FireEvent(ctx, types.EventVoteValue, vote) + cs.evsw.FireEvent(types.EventVoteValue, vote) // if we can skip timeoutCommit and have all the votes now, if cs.bypassCommitTimeout() && cs.LastCommit.HasAll() { @@ -2352,10 +2349,10 @@ func (cs *State) addVote( return } - if err := cs.eventBus.PublishEventVote(ctx, types.EventDataVote{Vote: vote}); err != nil { + if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil { return added, err } - cs.evsw.FireEvent(ctx, types.EventVoteValue, vote) + cs.evsw.FireEvent(types.EventVoteValue, vote) switch vote.Type { case tmproto.PrevoteType: @@ -2390,8 +2387,8 @@ func (cs *State) addVote( cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader) } - cs.evsw.FireEvent(ctx, types.EventValidBlockValue, &cs.RoundState) - if err := cs.eventBus.PublishEventValidBlock(ctx, cs.RoundStateEvent()); err != nil { + cs.evsw.FireEvent(types.EventValidBlockValue, &cs.RoundState) + if err := cs.eventBus.PublishEventValidBlock(cs.RoundStateEvent()); err != nil { return added, err } } @@ -2408,7 +2405,7 @@ func (cs *State) addVote( if ok && (cs.isProposalComplete() || blockID.IsNil()) { cs.enterPrecommit(ctx, height, vote.Round) } else if prevotes.HasTwoThirdsAny() { - cs.enterPrevoteWait(ctx, height, vote.Round) + cs.enterPrevoteWait(height, vote.Round) } case cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round: @@ -2439,11 +2436,11 @@ func (cs *State) addVote( cs.enterNewRound(ctx, cs.Height, 0) } } else { - cs.enterPrecommitWait(ctx, height, vote.Round) + cs.enterPrecommitWait(height, vote.Round) } } else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() { cs.enterNewRound(ctx, height, vote.Round) - cs.enterPrecommitWait(ctx, height, vote.Round) + cs.enterPrecommitWait(height, vote.Round) } default: @@ -2494,7 +2491,7 @@ func (cs *State) signVote( if err != nil { return nil, err } - vote.VoteExtension = ext + vote.Extension = ext default: timeout = time.Second } @@ -2506,6 +2503,7 @@ func (cs *State) signVote( err := cs.privValidator.SignVote(ctxto, cs.state.ChainID, v) vote.Signature = v.Signature + vote.ExtensionSignature = v.ExtensionSignature vote.Timestamp = v.Timestamp return vote, err diff --git a/internal/consensus/state_test.go b/internal/consensus/state_test.go index 03bb85cf2..d4dd83202 100644 --- a/internal/consensus/state_test.go +++ b/internal/consensus/state_test.go @@ -1912,12 +1912,13 @@ func TestProcessProposalAccept(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - m := abcimocks.NewBaseMock() + m := abcimocks.NewApplication(t) status := abci.ResponseProcessProposal_REJECT if testCase.accept { status = abci.ResponseProcessProposal_ACCEPT } - m.On("ProcessProposal", mock.Anything).Return(abci.ResponseProcessProposal{Status: status}) + m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: status}, nil) + m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil).Maybe() cs1, _ := makeState(ctx, t, makeStateArgs{config: config, application: m}) height, round := cs1.Height, cs1.Round @@ -1964,12 +1965,18 @@ func TestFinalizeBlockCalled(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - m := abcimocks.NewBaseMock() - m.On("ProcessProposal", mock.Anything).Return(abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}) - m.On("VerifyVoteExtension", mock.Anything).Return(abci.ResponseVerifyVoteExtension{ + m := abcimocks.NewApplication(t) + m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{ + Status: abci.ResponseProcessProposal_ACCEPT, + }, nil) + m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil) + m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{ Status: abci.ResponseVerifyVoteExtension_ACCEPT, - }) - m.On("FinalizeBlock", mock.Anything).Return(abci.ResponseFinalizeBlock{}).Maybe() + }, nil) + m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe() + m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{}, nil) + m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe() + cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m}) height, round := cs1.Height, cs1.Round @@ -2007,14 +2014,254 @@ func TestFinalizeBlockCalled(t *testing.T) { m.AssertExpectations(t) if !testCase.expectCalled { - m.AssertNotCalled(t, "FinalizeBlock", mock.Anything) + m.AssertNotCalled(t, "FinalizeBlock", ctx, mock.Anything) } else { - m.AssertCalled(t, "FinalizeBlock", mock.Anything) + m.AssertCalled(t, "FinalizeBlock", ctx, mock.Anything) } }) } } +// TestExtendVoteCalled tests that the vote extension methods are called at the +// correct point in the consensus algorithm. +func TestExtendVoteCalled(t *testing.T) { + config := configSetup(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + m := abcimocks.NewApplication(t) + m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil) + m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil) + m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{ + VoteExtension: []byte("extension"), + }, nil) + m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{ + Status: abci.ResponseVerifyVoteExtension_ACCEPT, + }, nil) + m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe() + m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe() + cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m}) + height, round := cs1.Height, cs1.Round + + proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal) + newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound) + pv1, err := cs1.privValidator.GetPubKey(ctx) + require.NoError(t, err) + addr := pv1.Address() + voteCh := subscribeToVoter(ctx, t, cs1, addr) + + startTestRound(ctx, cs1, cs1.Height, round) + ensureNewRound(t, newRoundCh, height, round) + ensureNewProposal(t, proposalCh, height, round) + + m.AssertNotCalled(t, "ExtendVote", mock.Anything, mock.Anything) + + rs := cs1.GetRoundState() + + blockID := types.BlockID{ + Hash: rs.ProposalBlock.Hash(), + PartSetHeader: rs.ProposalBlockParts.Header(), + } + signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[1:]...) + ensurePrevoteMatch(t, voteCh, height, round, blockID.Hash) + + ensurePrecommit(t, voteCh, height, round) + + m.AssertCalled(t, "ExtendVote", ctx, &abci.RequestExtendVote{ + Height: height, + Hash: blockID.Hash, + }) + + m.AssertCalled(t, "VerifyVoteExtension", ctx, &abci.RequestVerifyVoteExtension{ + Hash: blockID.Hash, + ValidatorAddress: addr, + Height: height, + VoteExtension: []byte("extension"), + }) + signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), blockID, vss[1:]...) + ensureNewRound(t, newRoundCh, height+1, 0) + m.AssertExpectations(t) + + // Only 3 of the vote extensions are seen, as consensus proceeds as soon as the +2/3 threshold + // is observed by the consensus engine. + for _, pv := range vss[:3] { + pv, err := pv.GetPubKey(ctx) + require.NoError(t, err) + addr := pv.Address() + m.AssertCalled(t, "VerifyVoteExtension", ctx, &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) { + config := configSetup(t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + m := abcimocks.NewApplication(t) + m.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil) + m.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil) + m.On("ExtendVote", mock.Anything, mock.Anything).Return(&abci.ResponseExtendVote{ + 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() + cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m}) + height, round := cs1.Height, cs1.Round + + proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal) + newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound) + pv1, err := cs1.privValidator.GetPubKey(ctx) + require.NoError(t, err) + addr := pv1.Address() + voteCh := subscribeToVoter(ctx, t, cs1, addr) + + startTestRound(ctx, cs1, cs1.Height, round) + ensureNewRound(t, newRoundCh, height, round) + ensureNewProposal(t, proposalCh, height, round) + rs := cs1.GetRoundState() + + blockID := types.BlockID{ + Hash: rs.ProposalBlock.Hash(), + PartSetHeader: rs.ProposalBlockParts.Header(), + } + signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[2:]...) + ensurePrevoteMatch(t, voteCh, height, round, blockID.Hash) + + ensurePrecommit(t, voteCh, height, round) + + m.AssertCalled(t, "ExtendVote", mock.Anything, &abci.RequestExtendVote{ + Height: height, + Hash: blockID.Hash, + }) + + m.AssertCalled(t, "VerifyVoteExtension", mock.Anything, &abci.RequestVerifyVoteExtension{ + Hash: blockID.Hash, + ValidatorAddress: addr, + Height: height, + VoteExtension: []byte("extension"), + }) + + m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe() + signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), blockID, vss[2:]...) + ensureNewRound(t, newRoundCh, height+1, 0) + 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(ctx) + require.NoError(t, err) + addr = pv.Address() + + m.AssertNotCalled(t, "VerifyVoteExtension", ctx, &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 +// be 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) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + config := configSetup(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 := &abcimocks.Application{} + 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("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil).Once() + m.On("VerifyVoteExtension", mock.Anything, mock.Anything).Return(&abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT}, nil) + m.On("Commit", mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe() + m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil) + + cs1, vss := makeState(ctx, t, makeStateArgs{config: config, application: m}) + height, round := cs1.Height, cs1.Round + + newRoundCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryNewRound) + proposalCh := subscribe(ctx, t, cs1.eventBus, types.EventQueryCompleteProposal) + pv1, err := cs1.privValidator.GetPubKey(ctx) + require.NoError(t, err) + addr := pv1.Address() + voteCh := subscribeToVoter(ctx, t, cs1, addr) + + startTestRound(ctx, cs1, height, round) + ensureNewRound(t, newRoundCh, height, round) + ensureNewProposal(t, proposalCh, height, round) + + rs := cs1.GetRoundState() + blockID := types.BlockID{ + Hash: rs.ProposalBlock.Hash(), + PartSetHeader: rs.ProposalBlockParts.Header(), + } + signAddVotes(ctx, t, cs1, tmproto.PrevoteType, config.ChainID(), blockID, vss[1:]...) + + // create a precommit for each validator with the associated vote extension. + for i, vs := range vss[1:] { + signAddPrecommitWithExtension(ctx, t, cs1, config.ChainID(), blockID, voteExtensions[i+1], vs) + } + + ensurePrevote(t, voteCh, height, round) + + // ensure that the height is committed. + ensurePrecommitMatch(t, voteCh, height, round, blockID.Hash) + incrementHeight(vss[1:]...) + + height++ + round = 0 + ensureNewRound(t, newRoundCh, height, round) + incrementRound(vss[1:]...) + incrementRound(vss[1:]...) + incrementRound(vss[1:]...) + round = 3 + + signAddVotes(ctx, t, cs1, tmproto.PrecommitType, config.ChainID(), types.BlockID{}, vss[1:]...) + ensureNewRound(t, newRoundCh, height, round) + ensureNewProposal(t, 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]) + } +} + // 4 vals, 3 Nil Precommits at P0 // What we want: // P0 waits for timeoutPrecommit before starting next round @@ -2719,3 +2966,15 @@ func subscribe( }() return ch } + +func signAddPrecommitWithExtension(ctx context.Context, + t *testing.T, + cs *State, + chainID string, + blockID types.BlockID, + extension []byte, + stub *validatorStub) { + v, err := stub.signVote(ctx, tmproto.PrecommitType, chainID, blockID, extension) + require.NoError(t, err, "failed to sign vote") + addVotes(cs, v) +} diff --git a/internal/consensus/types/height_vote_set_test.go b/internal/consensus/types/height_vote_set_test.go index 671c5f214..8bafc7a90 100644 --- a/internal/consensus/types/height_vote_set_test.go +++ b/internal/consensus/types/height_vote_set_test.go @@ -88,6 +88,7 @@ func makeVoteHR( require.NoError(t, err, "Error signing vote") vote.Signature = v.Signature + vote.ExtensionSignature = v.ExtensionSignature return vote } diff --git a/internal/consensus/wal_generator.go b/internal/consensus/wal_generator.go index 8c61c1203..0e678db3a 100644 --- a/internal/consensus/wal_generator.go +++ b/internal/consensus/wal_generator.go @@ -81,7 +81,7 @@ func WALGenerateNBlocks(ctx context.Context, t *testing.T, logger log.Logger, wr mempool := emptyMempool{} evpool := sm.EmptyEvidencePool{} blockExec := sm.NewBlockExecutor(stateStore, log.NewNopLogger(), proxyApp, mempool, evpool, blockStore, eventBus, sm.NopMetrics()) - consensusState, err := NewState(ctx, logger, cfg.Consensus, stateStore, blockExec, blockStore, mempool, evpool, eventBus) + consensusState, err := NewState(logger, cfg.Consensus, stateStore, blockExec, blockStore, mempool, evpool, eventBus) if err != nil { t.Fatal(err) } diff --git a/internal/eventbus/event_bus.go b/internal/eventbus/event_bus.go index 5a64c5f1c..c01c37de7 100644 --- a/internal/eventbus/event_bus.go +++ b/internal/eventbus/event_bus.go @@ -66,7 +66,7 @@ func (b *EventBus) Observe(ctx context.Context, observe func(tmpubsub.Message) e return b.pubsub.Observe(ctx, observe, queries...) } -func (b *EventBus) Publish(ctx context.Context, eventValue string, eventData types.EventData) error { +func (b *EventBus) Publish(eventValue string, eventData types.EventData) error { tokens := strings.Split(types.EventTypeKey, ".") event := abci.Event{ Type: tokens[0], @@ -78,19 +78,19 @@ func (b *EventBus) Publish(ctx context.Context, eventValue string, eventData typ }, } - return b.pubsub.PublishWithEvents(ctx, eventData, []abci.Event{event}) + return b.pubsub.PublishWithEvents(eventData, []abci.Event{event}) } -func (b *EventBus) PublishEventNewBlock(ctx context.Context, data types.EventDataNewBlock) error { +func (b *EventBus) PublishEventNewBlock(data types.EventDataNewBlock) error { events := data.ResultFinalizeBlock.Events // add Tendermint-reserved new block event events = append(events, types.EventNewBlock) - return b.pubsub.PublishWithEvents(ctx, data, events) + return b.pubsub.PublishWithEvents(data, events) } -func (b *EventBus) PublishEventNewBlockHeader(ctx context.Context, data types.EventDataNewBlockHeader) error { +func (b *EventBus) PublishEventNewBlockHeader(data types.EventDataNewBlockHeader) error { // no explicit deadline for publishing events events := data.ResultFinalizeBlock.Events @@ -98,33 +98,33 @@ func (b *EventBus) PublishEventNewBlockHeader(ctx context.Context, data types.Ev // add Tendermint-reserved new block header event events = append(events, types.EventNewBlockHeader) - return b.pubsub.PublishWithEvents(ctx, data, events) + return b.pubsub.PublishWithEvents(data, events) } -func (b *EventBus) PublishEventNewEvidence(ctx context.Context, evidence types.EventDataNewEvidence) error { - return b.Publish(ctx, types.EventNewEvidenceValue, evidence) +func (b *EventBus) PublishEventNewEvidence(evidence types.EventDataNewEvidence) error { + return b.Publish(types.EventNewEvidenceValue, evidence) } -func (b *EventBus) PublishEventVote(ctx context.Context, data types.EventDataVote) error { - return b.Publish(ctx, types.EventVoteValue, data) +func (b *EventBus) PublishEventVote(data types.EventDataVote) error { + return b.Publish(types.EventVoteValue, data) } -func (b *EventBus) PublishEventValidBlock(ctx context.Context, data types.EventDataRoundState) error { - return b.Publish(ctx, types.EventValidBlockValue, data) +func (b *EventBus) PublishEventValidBlock(data types.EventDataRoundState) error { + return b.Publish(types.EventValidBlockValue, data) } -func (b *EventBus) PublishEventBlockSyncStatus(ctx context.Context, data types.EventDataBlockSyncStatus) error { - return b.Publish(ctx, types.EventBlockSyncStatusValue, data) +func (b *EventBus) PublishEventBlockSyncStatus(data types.EventDataBlockSyncStatus) error { + return b.Publish(types.EventBlockSyncStatusValue, data) } -func (b *EventBus) PublishEventStateSyncStatus(ctx context.Context, data types.EventDataStateSyncStatus) error { - return b.Publish(ctx, types.EventStateSyncStatusValue, data) +func (b *EventBus) PublishEventStateSyncStatus(data types.EventDataStateSyncStatus) error { + return b.Publish(types.EventStateSyncStatusValue, data) } // PublishEventTx publishes tx event with events from Result. Note it will add // predefined keys (EventTypeKey, TxHashKey). Existing events with the same keys // will be overwritten. -func (b *EventBus) PublishEventTx(ctx context.Context, data types.EventDataTx) error { +func (b *EventBus) PublishEventTx(data types.EventDataTx) error { events := data.Result.Events // add Tendermint-reserved events @@ -152,45 +152,45 @@ func (b *EventBus) PublishEventTx(ctx context.Context, data types.EventDataTx) e }, }) - return b.pubsub.PublishWithEvents(ctx, data, events) + return b.pubsub.PublishWithEvents(data, events) } -func (b *EventBus) PublishEventNewRoundStep(ctx context.Context, data types.EventDataRoundState) error { - return b.Publish(ctx, types.EventNewRoundStepValue, data) +func (b *EventBus) PublishEventNewRoundStep(data types.EventDataRoundState) error { + return b.Publish(types.EventNewRoundStepValue, data) } -func (b *EventBus) PublishEventTimeoutPropose(ctx context.Context, data types.EventDataRoundState) error { - return b.Publish(ctx, types.EventTimeoutProposeValue, data) +func (b *EventBus) PublishEventTimeoutPropose(data types.EventDataRoundState) error { + return b.Publish(types.EventTimeoutProposeValue, data) } -func (b *EventBus) PublishEventTimeoutWait(ctx context.Context, data types.EventDataRoundState) error { - return b.Publish(ctx, types.EventTimeoutWaitValue, data) +func (b *EventBus) PublishEventTimeoutWait(data types.EventDataRoundState) error { + return b.Publish(types.EventTimeoutWaitValue, data) } -func (b *EventBus) PublishEventNewRound(ctx context.Context, data types.EventDataNewRound) error { - return b.Publish(ctx, types.EventNewRoundValue, data) +func (b *EventBus) PublishEventNewRound(data types.EventDataNewRound) error { + return b.Publish(types.EventNewRoundValue, data) } -func (b *EventBus) PublishEventCompleteProposal(ctx context.Context, data types.EventDataCompleteProposal) error { - return b.Publish(ctx, types.EventCompleteProposalValue, data) +func (b *EventBus) PublishEventCompleteProposal(data types.EventDataCompleteProposal) error { + return b.Publish(types.EventCompleteProposalValue, data) } -func (b *EventBus) PublishEventPolka(ctx context.Context, data types.EventDataRoundState) error { - return b.Publish(ctx, types.EventPolkaValue, data) +func (b *EventBus) PublishEventPolka(data types.EventDataRoundState) error { + return b.Publish(types.EventPolkaValue, data) } -func (b *EventBus) PublishEventRelock(ctx context.Context, data types.EventDataRoundState) error { - return b.Publish(ctx, types.EventRelockValue, data) +func (b *EventBus) PublishEventRelock(data types.EventDataRoundState) error { + return b.Publish(types.EventRelockValue, data) } -func (b *EventBus) PublishEventLock(ctx context.Context, data types.EventDataRoundState) error { - return b.Publish(ctx, types.EventLockValue, data) +func (b *EventBus) PublishEventLock(data types.EventDataRoundState) error { + return b.Publish(types.EventLockValue, data) } -func (b *EventBus) PublishEventValidatorSetUpdates(ctx context.Context, data types.EventDataValidatorSetUpdates) error { - return b.Publish(ctx, types.EventValidatorSetUpdatesValue, data) +func (b *EventBus) PublishEventValidatorSetUpdates(data types.EventDataValidatorSetUpdates) error { + return b.Publish(types.EventValidatorSetUpdatesValue, data) } -func (b *EventBus) PublishEventEvidenceValidated(ctx context.Context, evidence types.EventDataEvidenceValidated) error { - return b.Publish(ctx, types.EventEvidenceValidatedValue, evidence) +func (b *EventBus) PublishEventEvidenceValidated(evidence types.EventDataEvidenceValidated) error { + return b.Publish(types.EventEvidenceValidatedValue, evidence) } diff --git a/internal/eventbus/event_bus_test.go b/internal/eventbus/event_bus_test.go index 1bfea02e1..6fd8a2d72 100644 --- a/internal/eventbus/event_bus_test.go +++ b/internal/eventbus/event_bus_test.go @@ -55,7 +55,7 @@ func TestEventBusPublishEventTx(t *testing.T) { assert.Equal(t, result, edt.Result) }() - err = eventBus.PublishEventTx(ctx, types.EventDataTx{ + err = eventBus.PublishEventTx(types.EventDataTx{ TxResult: abci.TxResult{ Height: 1, Index: 0, @@ -112,7 +112,7 @@ func TestEventBusPublishEventNewBlock(t *testing.T) { assert.Equal(t, resultFinalizeBlock, edt.ResultFinalizeBlock) }() - err = eventBus.PublishEventNewBlock(ctx, types.EventDataNewBlock{ + err = eventBus.PublishEventNewBlock(types.EventDataNewBlock{ Block: block, BlockID: blockID, ResultFinalizeBlock: resultFinalizeBlock, @@ -223,7 +223,7 @@ func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) { } }() - assert.NoError(t, eventBus.PublishEventTx(ctx, types.EventDataTx{ + assert.NoError(t, eventBus.PublishEventTx(types.EventDataTx{ TxResult: abci.TxResult{ Height: 1, Index: 0, @@ -280,7 +280,7 @@ func TestEventBusPublishEventNewBlockHeader(t *testing.T) { assert.Equal(t, resultFinalizeBlock, edt.ResultFinalizeBlock) }() - err = eventBus.PublishEventNewBlockHeader(ctx, types.EventDataNewBlockHeader{ + err = eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{ Header: block.Header, ResultFinalizeBlock: resultFinalizeBlock, }) @@ -322,7 +322,7 @@ func TestEventBusPublishEventEvidenceValidated(t *testing.T) { assert.Equal(t, int64(1), edt.Height) }() - err = eventBus.PublishEventEvidenceValidated(ctx, types.EventDataEvidenceValidated{ + err = eventBus.PublishEventEvidenceValidated(types.EventDataEvidenceValidated{ Evidence: ev, Height: int64(1), }) @@ -364,7 +364,7 @@ func TestEventBusPublishEventNewEvidence(t *testing.T) { assert.Equal(t, int64(4), edt.Height) }() - err = eventBus.PublishEventNewEvidence(ctx, types.EventDataNewEvidence{ + err = eventBus.PublishEventNewEvidence(types.EventDataNewEvidence{ Evidence: ev, Height: 4, }) @@ -408,22 +408,22 @@ func TestEventBusPublish(t *testing.T) { } }() - require.NoError(t, eventBus.Publish(ctx, types.EventNewBlockHeaderValue, + require.NoError(t, eventBus.Publish(types.EventNewBlockHeaderValue, types.EventDataNewBlockHeader{})) - require.NoError(t, eventBus.PublishEventNewBlock(ctx, types.EventDataNewBlock{})) - require.NoError(t, eventBus.PublishEventNewBlockHeader(ctx, types.EventDataNewBlockHeader{})) - require.NoError(t, eventBus.PublishEventVote(ctx, types.EventDataVote{})) - require.NoError(t, eventBus.PublishEventNewRoundStep(ctx, types.EventDataRoundState{})) - require.NoError(t, eventBus.PublishEventTimeoutPropose(ctx, types.EventDataRoundState{})) - require.NoError(t, eventBus.PublishEventTimeoutWait(ctx, types.EventDataRoundState{})) - require.NoError(t, eventBus.PublishEventNewRound(ctx, types.EventDataNewRound{})) - require.NoError(t, eventBus.PublishEventCompleteProposal(ctx, types.EventDataCompleteProposal{})) - require.NoError(t, eventBus.PublishEventPolka(ctx, types.EventDataRoundState{})) - require.NoError(t, eventBus.PublishEventRelock(ctx, types.EventDataRoundState{})) - require.NoError(t, eventBus.PublishEventLock(ctx, types.EventDataRoundState{})) - require.NoError(t, eventBus.PublishEventValidatorSetUpdates(ctx, types.EventDataValidatorSetUpdates{})) - require.NoError(t, eventBus.PublishEventBlockSyncStatus(ctx, types.EventDataBlockSyncStatus{})) - require.NoError(t, eventBus.PublishEventStateSyncStatus(ctx, types.EventDataStateSyncStatus{})) + require.NoError(t, eventBus.PublishEventNewBlock(types.EventDataNewBlock{})) + require.NoError(t, eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{})) + require.NoError(t, eventBus.PublishEventVote(types.EventDataVote{})) + require.NoError(t, eventBus.PublishEventNewRoundStep(types.EventDataRoundState{})) + require.NoError(t, eventBus.PublishEventTimeoutPropose(types.EventDataRoundState{})) + require.NoError(t, eventBus.PublishEventTimeoutWait(types.EventDataRoundState{})) + require.NoError(t, eventBus.PublishEventNewRound(types.EventDataNewRound{})) + require.NoError(t, eventBus.PublishEventCompleteProposal(types.EventDataCompleteProposal{})) + require.NoError(t, eventBus.PublishEventPolka(types.EventDataRoundState{})) + require.NoError(t, eventBus.PublishEventRelock(types.EventDataRoundState{})) + require.NoError(t, eventBus.PublishEventLock(types.EventDataRoundState{})) + require.NoError(t, eventBus.PublishEventValidatorSetUpdates(types.EventDataValidatorSetUpdates{})) + require.NoError(t, eventBus.PublishEventBlockSyncStatus(types.EventDataBlockSyncStatus{})) + require.NoError(t, eventBus.PublishEventStateSyncStatus(types.EventDataStateSyncStatus{})) require.GreaterOrEqual(t, <-count, numEventsExpected) } @@ -505,7 +505,7 @@ func benchmarkEventBus(numClients int, randQueries bool, randEvents bool, b *tes eventValue = randEventValue() } - err := eventBus.Publish(ctx, eventValue, types.EventDataString("Gamora")) + err := eventBus.Publish(eventValue, types.EventDataString("Gamora")) if err != nil { b.Error(err) } diff --git a/internal/evidence/mocks/block_store.go b/internal/evidence/mocks/block_store.go index ef3346b2a..e45b281b9 100644 --- a/internal/evidence/mocks/block_store.go +++ b/internal/evidence/mocks/block_store.go @@ -3,7 +3,10 @@ package mocks import ( + testing "testing" + mock "github.com/stretchr/testify/mock" + types "github.com/tendermint/tendermint/types" ) @@ -57,3 +60,13 @@ func (_m *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta { return r0 } + +// NewBlockStore creates a new instance of BlockStore. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewBlockStore(t testing.TB) *BlockStore { + mock := &BlockStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/evidence/pool.go b/internal/evidence/pool.go index d2d998c91..132c61f23 100644 --- a/internal/evidence/pool.go +++ b/internal/evidence/pool.go @@ -338,7 +338,7 @@ func (evpool *Pool) addPendingEvidence(ctx context.Context, ev types.Evidence) e return nil } - return evpool.eventBus.PublishEventEvidenceValidated(ctx, types.EventDataEvidenceValidated{ + return evpool.eventBus.PublishEventEvidenceValidated(types.EventDataEvidenceValidated{ Evidence: ev, Height: ev.Height(), }) diff --git a/internal/evidence/reactor_test.go b/internal/evidence/reactor_test.go index 2fdfa8c60..5575d7f97 100644 --- a/internal/evidence/reactor_test.go +++ b/internal/evidence/reactor_test.go @@ -46,7 +46,7 @@ type reactorTestSuite struct { numStateStores int } -func setup(ctx context.Context, t *testing.T, stateStores []sm.Store, chBuf uint) *reactorTestSuite { +func setup(ctx context.Context, t *testing.T, stateStores []sm.Store) *reactorTestSuite { t.Helper() pID := make([]byte, 16) @@ -245,7 +245,7 @@ func TestReactorMultiDisconnect(t *testing.T) { stateDB1 := initializeValidatorState(ctx, t, val, height) stateDB2 := initializeValidatorState(ctx, t, val, height) - rts := setup(ctx, t, []sm.Store{stateDB1, stateDB2}, 20) + rts := setup(ctx, t, []sm.Store{stateDB1, stateDB2}) primary := rts.nodes[0] secondary := rts.nodes[1] @@ -290,7 +290,7 @@ func TestReactorBroadcastEvidence(t *testing.T) { stateDBs[i] = initializeValidatorState(ctx, t, val, height) } - rts := setup(ctx, t, stateDBs, 0) + rts := setup(ctx, t, stateDBs) rts.start(ctx, t) @@ -348,7 +348,7 @@ func TestReactorBroadcastEvidence_Lagging(t *testing.T) { stateDB1 := initializeValidatorState(ctx, t, val, height1) stateDB2 := initializeValidatorState(ctx, t, val, height2) - rts := setup(ctx, t, []sm.Store{stateDB1, stateDB2}, 100) + rts := setup(ctx, t, []sm.Store{stateDB1, stateDB2}) rts.start(ctx, t) primary := rts.nodes[0] @@ -382,7 +382,7 @@ func TestReactorBroadcastEvidence_Pending(t *testing.T) { stateDB1 := initializeValidatorState(ctx, t, val, height) stateDB2 := initializeValidatorState(ctx, t, val, height) - rts := setup(ctx, t, []sm.Store{stateDB1, stateDB2}, 100) + rts := setup(ctx, t, []sm.Store{stateDB1, stateDB2}) primary := rts.nodes[0] secondary := rts.nodes[1] @@ -423,7 +423,7 @@ func TestReactorBroadcastEvidence_Committed(t *testing.T) { stateDB1 := initializeValidatorState(ctx, t, val, height) stateDB2 := initializeValidatorState(ctx, t, val, height) - rts := setup(ctx, t, []sm.Store{stateDB1, stateDB2}, 0) + rts := setup(ctx, t, []sm.Store{stateDB1, stateDB2}) primary := rts.nodes[0] secondary := rts.nodes[1] @@ -482,7 +482,7 @@ func TestReactorBroadcastEvidence_FullyConnected(t *testing.T) { stateDBs[i] = initializeValidatorState(ctx, t, val, height) } - rts := setup(ctx, t, stateDBs, 0) + rts := setup(ctx, t, stateDBs) rts.start(ctx, t) evList := createEvidenceList(ctx, t, rts.pools[rts.network.RandomNode().NodeID], val, numEvidence) diff --git a/internal/mempool/mempool.go b/internal/mempool/mempool.go index 6fcfe86c1..629fa0bda 100644 --- a/internal/mempool/mempool.go +++ b/internal/mempool/mempool.go @@ -260,7 +260,7 @@ func (txmp *TxMempool) CheckTx( return types.ErrTxInCache } - res, err := txmp.proxyAppConn.CheckTx(ctx, abci.RequestCheckTx{Tx: tx}) + res, err := txmp.proxyAppConn.CheckTx(ctx, &abci.RequestCheckTx{Tx: tx}) if err != nil { txmp.cache.Remove(tx) return err @@ -700,7 +700,7 @@ func (txmp *TxMempool) updateReCheckTxs(ctx context.Context) { // Only execute CheckTx if the transaction is not marked as removed which // could happen if the transaction was evicted. if !txmp.txStore.IsTxRemoved(wtx.hash) { - res, err := txmp.proxyAppConn.CheckTx(ctx, abci.RequestCheckTx{ + res, err := txmp.proxyAppConn.CheckTx(ctx, &abci.RequestCheckTx{ Tx: wtx.tx, Type: abci.CheckTxType_Recheck, }) diff --git a/internal/mempool/mempool_bench_test.go b/internal/mempool/mempool_bench_test.go index cc3ff7368..14fb22197 100644 --- a/internal/mempool/mempool_bench_test.go +++ b/internal/mempool/mempool_bench_test.go @@ -25,7 +25,7 @@ func BenchmarkTxMempool_CheckTx(b *testing.B) { // setup the cache and the mempool number for hitting GetEvictableTxs during the // benchmark. 5000 is the current default mempool size in the TM config. - txmp := setup(ctx, b, client, 10000) + txmp := setup(b, client, 10000) txmp.config.Size = 5000 rng := rand.New(rand.NewSource(time.Now().UnixNano())) diff --git a/internal/mempool/mempool_test.go b/internal/mempool/mempool_test.go index 0dd6ee00c..946377b1c 100644 --- a/internal/mempool/mempool_test.go +++ b/internal/mempool/mempool_test.go @@ -36,7 +36,7 @@ type testTx struct { priority int64 } -func (app *application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { +func (app *application) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) { var ( priority int64 sender string @@ -47,32 +47,32 @@ func (app *application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { if len(parts) == 3 { v, err := strconv.ParseInt(string(parts[2]), 10, 64) if err != nil { - return abci.ResponseCheckTx{ + return &abci.ResponseCheckTx{ Priority: priority, Code: 100, GasWanted: 1, - } + }, nil } priority = v sender = string(parts[0]) } else { - return abci.ResponseCheckTx{ + return &abci.ResponseCheckTx{ Priority: priority, Code: 101, GasWanted: 1, - } + }, nil } - return abci.ResponseCheckTx{ + return &abci.ResponseCheckTx{ Priority: priority, Sender: sender, Code: code.CodeTypeOK, GasWanted: 1, - } + }, nil } -func setup(ctx context.Context, t testing.TB, app abciclient.Client, cacheSize int, options ...TxMempoolOption) *TxMempool { +func setup(t testing.TB, app abciclient.Client, cacheSize int, options ...TxMempoolOption) *TxMempool { t.Helper() logger := log.NewNopLogger() @@ -131,7 +131,7 @@ func TestTxMempool_TxsAvailable(t *testing.T) { } t.Cleanup(client.Wait) - txmp := setup(ctx, t, client, 0) + txmp := setup(t, client, 0) txmp.EnableTxsAvailable() ensureNoTxFire := func() { @@ -194,7 +194,7 @@ func TestTxMempool_Size(t *testing.T) { } t.Cleanup(client.Wait) - txmp := setup(ctx, t, client, 0) + txmp := setup(t, client, 0) txs := checkTxs(ctx, t, txmp, 100, 0) require.Equal(t, len(txs), txmp.Size()) require.Equal(t, int64(5690), txmp.SizeBytes()) @@ -227,7 +227,7 @@ func TestTxMempool_Flush(t *testing.T) { } t.Cleanup(client.Wait) - txmp := setup(ctx, t, client, 0) + txmp := setup(t, client, 0) txs := checkTxs(ctx, t, txmp, 100, 0) require.Equal(t, len(txs), txmp.Size()) require.Equal(t, int64(5690), txmp.SizeBytes()) @@ -261,7 +261,7 @@ func TestTxMempool_ReapMaxBytesMaxGas(t *testing.T) { } t.Cleanup(client.Wait) - txmp := setup(ctx, t, client, 0) + txmp := setup(t, client, 0) tTxs := checkTxs(ctx, t, txmp, 100, 0) // all txs request 1 gas unit require.Equal(t, len(tTxs), txmp.Size()) require.Equal(t, int64(5690), txmp.SizeBytes()) @@ -320,7 +320,7 @@ func TestTxMempool_ReapMaxTxs(t *testing.T) { } t.Cleanup(client.Wait) - txmp := setup(ctx, t, client, 0) + txmp := setup(t, client, 0) tTxs := checkTxs(ctx, t, txmp, 100, 0) require.Equal(t, len(tTxs), txmp.Size()) require.Equal(t, int64(5690), txmp.SizeBytes()) @@ -377,7 +377,7 @@ func TestTxMempool_CheckTxExceedsMaxSize(t *testing.T) { t.Fatal(err) } t.Cleanup(client.Wait) - txmp := setup(ctx, t, client, 0) + txmp := setup(t, client, 0) rng := rand.New(rand.NewSource(time.Now().UnixNano())) tx := make([]byte, txmp.config.MaxTxBytes+1) @@ -403,7 +403,7 @@ func TestTxMempool_CheckTxSamePeer(t *testing.T) { } t.Cleanup(client.Wait) - txmp := setup(ctx, t, client, 100) + txmp := setup(t, client, 100) peerID := uint16(1) rng := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -427,7 +427,7 @@ func TestTxMempool_CheckTxSameSender(t *testing.T) { } t.Cleanup(client.Wait) - txmp := setup(ctx, t, client, 100) + txmp := setup(t, client, 100) peerID := uint16(1) rng := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -458,7 +458,7 @@ func TestTxMempool_ConcurrentTxs(t *testing.T) { } t.Cleanup(client.Wait) - txmp := setup(ctx, t, client, 100) + txmp := setup(t, client, 100) rng := rand.New(rand.NewSource(time.Now().UnixNano())) checkTxDone := make(chan struct{}) @@ -531,7 +531,7 @@ func TestTxMempool_ExpiredTxs_NumBlocks(t *testing.T) { } t.Cleanup(client.Wait) - txmp := setup(ctx, t, client, 500) + txmp := setup(t, client, 500) txmp.height = 100 txmp.config.TTLNumBlocks = 10 @@ -612,7 +612,7 @@ func TestTxMempool_CheckTxPostCheckError(t *testing.T) { postCheckFn := func(_ types.Tx, _ *abci.ResponseCheckTx) error { return testCase.err } - txmp := setup(ctx, t, client, 0, WithPostCheck(postCheckFn)) + txmp := setup(t, client, 0, WithPostCheck(postCheckFn)) rng := rand.New(rand.NewSource(time.Now().UnixNano())) tx := make([]byte, txmp.config.MaxTxBytes-1) _, err := rng.Read(tx) diff --git a/internal/mempool/mocks/mempool.go b/internal/mempool/mocks/mempool.go index d4cdfabd7..b82d7d63e 100644 --- a/internal/mempool/mocks/mempool.go +++ b/internal/mempool/mocks/mempool.go @@ -11,6 +11,8 @@ import ( mock "github.com/stretchr/testify/mock" + testing "testing" + types "github.com/tendermint/tendermint/types" ) @@ -170,3 +172,13 @@ func (_m *Mempool) Update(ctx context.Context, blockHeight int64, blockTxs types return r0 } + +// NewMempool creates a new instance of Mempool. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewMempool(t testing.TB) *Mempool { + mock := &Mempool{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/mempool/reactor_test.go b/internal/mempool/reactor_test.go index 82a97aeec..8ceae2013 100644 --- a/internal/mempool/reactor_test.go +++ b/internal/mempool/reactor_test.go @@ -68,7 +68,7 @@ func setupReactors(ctx context.Context, t *testing.T, logger log.Logger, numNode require.NoError(t, client.Start(ctx)) t.Cleanup(client.Wait) - mempool := setup(ctx, t, client, 0) + mempool := setup(t, client, 0) rts.mempools[nodeID] = mempool rts.peerChans[nodeID] = make(chan p2p.PeerUpdate, chBuf) diff --git a/internal/p2p/conn/conn_go110.go b/internal/p2p/conn/conn_go110.go deleted file mode 100644 index 459c3169b..000000000 --- a/internal/p2p/conn/conn_go110.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build go1.10 -// +build go1.10 - -package conn - -// Go1.10 has a proper net.Conn implementation that -// has the SetDeadline method implemented as per -// https://github.com/golang/go/commit/e2dd8ca946be884bb877e074a21727f1a685a706 -// lest we run into problems like -// https://github.com/tendermint/tendermint/issues/851 - -import "net" - -func NetPipe() (net.Conn, net.Conn) { - return net.Pipe() -} diff --git a/internal/p2p/conn/conn_notgo110.go b/internal/p2p/conn/conn_notgo110.go deleted file mode 100644 index 21dffad2c..000000000 --- a/internal/p2p/conn/conn_notgo110.go +++ /dev/null @@ -1,33 +0,0 @@ -//go:build !go1.10 -// +build !go1.10 - -package conn - -import ( - "net" - "time" -) - -// Only Go1.10 has a proper net.Conn implementation that -// has the SetDeadline method implemented as per -// https://github.com/golang/go/commit/e2dd8ca946be884bb877e074a21727f1a685a706 -// lest we run into problems like -// https://github.com/tendermint/tendermint/issues/851 -// so for go versions < Go1.10 use our custom net.Conn creator -// that doesn't return an `Unimplemented error` for net.Conn. -// Before https://github.com/tendermint/tendermint/commit/49faa79bdce5663894b3febbf4955fb1d172df04 -// we hadn't cared about errors from SetDeadline so swallow them up anyways. -type pipe struct { - net.Conn -} - -func (p *pipe) SetDeadline(t time.Time) error { - return nil -} - -func NetPipe() (net.Conn, net.Conn) { - p1, p2 := net.Pipe() - return &pipe{p1}, &pipe{p2} -} - -var _ net.Conn = (*pipe)(nil) diff --git a/internal/p2p/conn/connection_test.go b/internal/p2p/conn/connection_test.go index 49c53f6fb..5a604cd23 100644 --- a/internal/p2p/conn/connection_test.go +++ b/internal/p2p/conn/connection_test.go @@ -48,7 +48,7 @@ func createMConnectionWithCallbacks( } func TestMConnectionSendFlushStop(t *testing.T) { - server, client := NetPipe() + server, client := net.Pipe() t.Cleanup(closeAll(t, client, server)) ctx, cancel := context.WithCancel(context.Background()) @@ -85,7 +85,7 @@ func TestMConnectionSendFlushStop(t *testing.T) { } func TestMConnectionSend(t *testing.T) { - server, client := NetPipe() + server, client := net.Pipe() t.Cleanup(closeAll(t, client, server)) ctx, cancel := context.WithCancel(context.Background()) @@ -116,7 +116,7 @@ func TestMConnectionSend(t *testing.T) { } func TestMConnectionReceive(t *testing.T) { - server, client := NetPipe() + server, client := net.Pipe() t.Cleanup(closeAll(t, client, server)) receivedCh := make(chan []byte) @@ -378,7 +378,7 @@ func TestMConnectionPingPongs(t *testing.T) { } func TestMConnectionStopsAndReturnsError(t *testing.T) { - server, client := NetPipe() + server, client := net.Pipe() t.Cleanup(closeAll(t, client, server)) receivedCh := make(chan []byte) @@ -423,7 +423,7 @@ func newClientAndServerConnsForReadErrors( t *testing.T, chOnErr chan struct{}, ) (*MConnection, *MConnection) { - server, client := NetPipe() + server, client := net.Pipe() onReceive := func(context.Context, ChannelID, []byte) {} onError := func(context.Context, interface{}) {} @@ -558,7 +558,7 @@ func TestMConnectionReadErrorUnknownMsgType(t *testing.T) { } func TestMConnectionTrySend(t *testing.T) { - server, client := NetPipe() + server, client := net.Pipe() t.Cleanup(closeAll(t, client, server)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/internal/p2p/conn/secret_connection_test.go b/internal/p2p/conn/secret_connection_test.go index 6752d9d21..2e37f8388 100644 --- a/internal/p2p/conn/secret_connection_test.go +++ b/internal/p2p/conn/secret_connection_test.go @@ -228,7 +228,7 @@ func TestDeriveSecretsAndChallengeGolden(t *testing.T) { goldenFilepath := filepath.Join("testdata", t.Name()+".golden") if *update { t.Logf("Updating golden test vector file %s", goldenFilepath) - data := createGoldenTestVectors(t) + data := createGoldenTestVectors() require.NoError(t, os.WriteFile(goldenFilepath, []byte(data), 0644)) } f, err := os.Open(goldenFilepath) @@ -306,7 +306,7 @@ func readLots(t *testing.T, wg *sync.WaitGroup, conn io.Reader, n int) { // Creates the data for a test vector file. // The file format is: // Hex(diffie_hellman_secret), loc_is_least, Hex(recvSecret), Hex(sendSecret), Hex(challenge) -func createGoldenTestVectors(t *testing.T) string { +func createGoldenTestVectors() string { data := "" for i := 0; i < 32; i++ { randSecretVector := tmrand.Bytes(32) diff --git a/internal/p2p/conn_tracker.go b/internal/p2p/conn_tracker.go index 09673c093..54f9c8980 100644 --- a/internal/p2p/conn_tracker.go +++ b/internal/p2p/conn_tracker.go @@ -26,6 +26,7 @@ func newConnTracker(max uint, window time.Duration) connectionTracker { cache: make(map[string]uint), lastConnect: make(map[string]time.Time), max: max, + window: window, } } @@ -43,7 +44,7 @@ func (rat *connTrackerImpl) AddConn(addr net.IP) error { if num := rat.cache[address]; num >= rat.max { return fmt.Errorf("%q has %d connections [max=%d]", address, num, rat.max) } else if num == 0 { - // if there is already at least connection, check to + // if there is already at least one connection, check to // see if it was established before within the window, // and error if so. if last := rat.lastConnect[address]; time.Since(last) < rat.window { diff --git a/internal/p2p/conn_tracker_test.go b/internal/p2p/conn_tracker_test.go index 66656e114..daa3351f2 100644 --- a/internal/p2p/conn_tracker_test.go +++ b/internal/p2p/conn_tracker_test.go @@ -70,4 +70,15 @@ func TestConnTracker(t *testing.T) { } require.Equal(t, 10, ct.Len()) }) + t.Run("Window", func(t *testing.T) { + const window = 100 * time.Millisecond + ct := newConnTracker(10, window) + ip := randLocalIPv4() + require.NoError(t, ct.AddConn(ip)) + ct.RemoveConn(ip) + require.Error(t, ct.AddConn(ip)) + time.Sleep(window) + require.NoError(t, ct.AddConn(ip)) + }) + } diff --git a/internal/p2p/mocks/connection.go b/internal/p2p/mocks/connection.go index 576fb2386..73b6cfc3b 100644 --- a/internal/p2p/mocks/connection.go +++ b/internal/p2p/mocks/connection.go @@ -13,6 +13,8 @@ import ( p2p "github.com/tendermint/tendermint/internal/p2p" + testing "testing" + types "github.com/tendermint/tendermint/types" ) @@ -150,3 +152,13 @@ func (_m *Connection) String() string { return r0 } + +// NewConnection creates a new instance of Connection. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewConnection(t testing.TB) *Connection { + mock := &Connection{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/p2p/mocks/transport.go b/internal/p2p/mocks/transport.go index b17290118..d08533186 100644 --- a/internal/p2p/mocks/transport.go +++ b/internal/p2p/mocks/transport.go @@ -10,6 +10,8 @@ import ( mock "github.com/stretchr/testify/mock" p2p "github.com/tendermint/tendermint/internal/p2p" + + testing "testing" ) // Transport is an autogenerated mock type for the Transport type @@ -141,3 +143,13 @@ func (_m *Transport) String() string { return r0 } + +// NewTransport creates a new instance of Transport. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewTransport(t testing.TB) *Transport { + mock := &Transport{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/p2p/pex/reactor_test.go b/internal/p2p/pex/reactor_test.go index 325ea72ab..f2132fbba 100644 --- a/internal/p2p/pex/reactor_test.go +++ b/internal/p2p/pex/reactor_test.go @@ -151,14 +151,14 @@ func TestReactorErrorsOnReceivingTooManyPeers(t *testing.T) { defer cancel() r := setupSingle(ctx, t) - peer := p2p.NodeAddress{Protocol: p2p.MemoryProtocol, NodeID: randomNodeID(t)} + peer := p2p.NodeAddress{Protocol: p2p.MemoryProtocol, NodeID: randomNodeID()} added, err := r.manager.Add(peer) require.NoError(t, err) require.True(t, added) addresses := make([]p2pproto.PexAddress, 101) for i := 0; i < len(addresses); i++ { - nodeAddress := p2p.NodeAddress{Protocol: p2p.MemoryProtocol, NodeID: randomNodeID(t)} + nodeAddress := p2p.NodeAddress{Protocol: p2p.MemoryProtocol, NodeID: randomNodeID()} addresses[i] = p2pproto.PexAddress{ URL: nodeAddress.String(), } @@ -730,6 +730,6 @@ func newNodeID(t *testing.T, id string) types.NodeID { return nodeID } -func randomNodeID(t *testing.T) types.NodeID { +func randomNodeID() types.NodeID { return types.NodeIDFromPubKey(ed25519.GenPrivKey().PubKey()) } diff --git a/internal/proxy/client.go b/internal/proxy/client.go index 2af1b1021..735c99656 100644 --- a/internal/proxy/client.go +++ b/internal/proxy/client.go @@ -124,32 +124,32 @@ func kill() error { return p.Signal(syscall.SIGABRT) } -func (app *proxyClient) InitChain(ctx context.Context, req types.RequestInitChain) (*types.ResponseInitChain, error) { +func (app *proxyClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "init_chain", "type", "sync"))() return app.client.InitChain(ctx, req) } -func (app *proxyClient) PrepareProposal(ctx context.Context, req types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { +func (app *proxyClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "prepare_proposal", "type", "sync"))() return app.client.PrepareProposal(ctx, req) } -func (app *proxyClient) ProcessProposal(ctx context.Context, req types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { +func (app *proxyClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "process_proposal", "type", "sync"))() return app.client.ProcessProposal(ctx, req) } -func (app *proxyClient) ExtendVote(ctx context.Context, req types.RequestExtendVote) (*types.ResponseExtendVote, error) { +func (app *proxyClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "extend_vote", "type", "sync"))() return app.client.ExtendVote(ctx, req) } -func (app *proxyClient) VerifyVoteExtension(ctx context.Context, req types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) { +func (app *proxyClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "verify_vote_extension", "type", "sync"))() return app.client.VerifyVoteExtension(ctx, req) } -func (app *proxyClient) FinalizeBlock(ctx context.Context, req types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { +func (app *proxyClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "finalize_block", "type", "sync"))() return app.client.FinalizeBlock(ctx, req) } @@ -164,7 +164,7 @@ func (app *proxyClient) Flush(ctx context.Context) error { return app.client.Flush(ctx) } -func (app *proxyClient) CheckTx(ctx context.Context, req types.RequestCheckTx) (*types.ResponseCheckTx, error) { +func (app *proxyClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "check_tx", "type", "sync"))() return app.client.CheckTx(ctx, req) } @@ -174,32 +174,32 @@ func (app *proxyClient) Echo(ctx context.Context, msg string) (*types.ResponseEc return app.client.Echo(ctx, msg) } -func (app *proxyClient) Info(ctx context.Context, req types.RequestInfo) (*types.ResponseInfo, error) { +func (app *proxyClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "info", "type", "sync"))() return app.client.Info(ctx, req) } -func (app *proxyClient) Query(ctx context.Context, reqQuery types.RequestQuery) (*types.ResponseQuery, error) { +func (app *proxyClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "query", "type", "sync"))() - return app.client.Query(ctx, reqQuery) + return app.client.Query(ctx, req) } -func (app *proxyClient) ListSnapshots(ctx context.Context, req types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { +func (app *proxyClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "list_snapshots", "type", "sync"))() return app.client.ListSnapshots(ctx, req) } -func (app *proxyClient) OfferSnapshot(ctx context.Context, req types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { +func (app *proxyClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "offer_snapshot", "type", "sync"))() return app.client.OfferSnapshot(ctx, req) } -func (app *proxyClient) LoadSnapshotChunk(ctx context.Context, req types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { +func (app *proxyClient) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "load_snapshot_chunk", "type", "sync"))() return app.client.LoadSnapshotChunk(ctx, req) } -func (app *proxyClient) ApplySnapshotChunk(ctx context.Context, req types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { +func (app *proxyClient) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) { defer addTimeSample(app.metrics.MethodTiming.With("method", "apply_snapshot_chunk", "type", "sync"))() return app.client.ApplySnapshotChunk(ctx, req) } diff --git a/internal/proxy/client_test.go b/internal/proxy/client_test.go index 057177a60..09ac3f2c8 100644 --- a/internal/proxy/client_test.go +++ b/internal/proxy/client_test.go @@ -30,7 +30,7 @@ import ( type appConnTestI interface { Echo(context.Context, string) (*types.ResponseEcho, error) Flush(context.Context) error - Info(context.Context, types.RequestInfo) (*types.ResponseInfo, error) + Info(context.Context, *types.RequestInfo) (*types.ResponseInfo, error) } type appConnTest struct { @@ -49,7 +49,7 @@ func (app *appConnTest) Flush(ctx context.Context) error { return app.appConn.Flush(ctx) } -func (app *appConnTest) Info(ctx context.Context, req types.RequestInfo) (*types.ResponseInfo, error) { +func (app *appConnTest) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) { return app.appConn.Info(ctx, req) } @@ -164,7 +164,7 @@ func TestInfo(t *testing.T) { proxy := newAppConnTest(client) t.Log("Connected") - resInfo, err := proxy.Info(ctx, RequestInfo) + resInfo, err := proxy.Info(ctx, &RequestInfo) require.NoError(t, err) if resInfo.Data != "{\"size\":0}" { diff --git a/internal/pubsub/example_test.go b/internal/pubsub/example_test.go index 22e735d6a..c4b5dc5c9 100644 --- a/internal/pubsub/example_test.go +++ b/internal/pubsub/example_test.go @@ -29,6 +29,6 @@ func TestExample(t *testing.T) { Attributes: []abci.EventAttribute{{Key: "name", Value: "John"}}, }, } - require.NoError(t, s.PublishWithEvents(ctx, pubstring("Tombstone"), events)) + require.NoError(t, s.PublishWithEvents(pubstring("Tombstone"), events)) sub.mustReceive(ctx, pubstring("Tombstone")) } diff --git a/internal/pubsub/pubsub.go b/internal/pubsub/pubsub.go index 3e887226b..c0ad4ae3c 100644 --- a/internal/pubsub/pubsub.go +++ b/internal/pubsub/pubsub.go @@ -287,16 +287,16 @@ func (s *Server) NumClientSubscriptions(clientID string) int { } // Publish publishes the given message. An error will be returned to the caller -// if the context is canceled. -func (s *Server) Publish(ctx context.Context, msg types.EventData) error { - return s.publish(ctx, msg, []abci.Event{}) +// if the pubsub server has shut down. +func (s *Server) Publish(msg types.EventData) error { + return s.publish(msg, []abci.Event{}) } // PublishWithEvents publishes the given message with the set of events. The set // is matched with clients queries. If there is a match, the message is sent to // the client. -func (s *Server) PublishWithEvents(ctx context.Context, msg types.EventData, events []abci.Event) error { - return s.publish(ctx, msg, events) +func (s *Server) PublishWithEvents(msg types.EventData, events []abci.Event) error { + return s.publish(msg, events) } // OnStop implements part of the Service interface. It is a no-op. @@ -309,15 +309,13 @@ func (s *Server) Wait() { <-s.exited; s.BaseService.Wait() } // OnStart implements Service.OnStart by starting the server. func (s *Server) OnStart(ctx context.Context) error { s.run(ctx); return nil } -func (s *Server) publish(ctx context.Context, data types.EventData, events []abci.Event) error { +func (s *Server) publish(data types.EventData, events []abci.Event) error { s.pubs.RLock() defer s.pubs.RUnlock() select { case <-s.done: return ErrServerStopped - case <-ctx.Done(): - return ctx.Err() case s.queue <- item{ Data: data, Events: events, diff --git a/internal/pubsub/pubsub_test.go b/internal/pubsub/pubsub_test.go index a6938ff75..e366977b5 100644 --- a/internal/pubsub/pubsub_test.go +++ b/internal/pubsub/pubsub_test.go @@ -42,7 +42,7 @@ func TestSubscribeWithArgs(t *testing.T) { require.Equal(t, 1, s.NumClients()) require.Equal(t, 1, s.NumClientSubscriptions(clientID)) - require.NoError(t, s.Publish(ctx, pubstring("Ka-Zar"))) + require.NoError(t, s.Publish(pubstring("Ka-Zar"))) sub.mustReceive(ctx, pubstring("Ka-Zar")) }) t.Run("PositiveLimit", func(t *testing.T) { @@ -51,7 +51,7 @@ func TestSubscribeWithArgs(t *testing.T) { Query: query.All, Limit: 10, })) - require.NoError(t, s.Publish(ctx, pubstring("Aggamon"))) + require.NoError(t, s.Publish(pubstring("Aggamon"))) sub.mustReceive(ctx, pubstring("Aggamon")) }) } @@ -72,7 +72,7 @@ func TestObserver(t *testing.T) { })) const input = pubstring("Lions and tigers and bears, oh my!") - require.NoError(t, s.Publish(ctx, input)) + require.NoError(t, s.Publish(input)) <-done require.Equal(t, got, input) } @@ -106,9 +106,9 @@ func TestPublishDoesNotBlock(t *testing.T) { go func() { defer close(published) - require.NoError(t, s.Publish(ctx, pubstring("Quicksilver"))) - require.NoError(t, s.Publish(ctx, pubstring("Asylum"))) - require.NoError(t, s.Publish(ctx, pubstring("Ivan"))) + require.NoError(t, s.Publish(pubstring("Quicksilver"))) + require.NoError(t, s.Publish(pubstring("Asylum"))) + require.NoError(t, s.Publish(pubstring("Ivan"))) }() select { @@ -149,9 +149,9 @@ func TestSlowSubscriber(t *testing.T) { Query: query.All, })) - require.NoError(t, s.Publish(ctx, pubstring("Fat Cobra"))) - require.NoError(t, s.Publish(ctx, pubstring("Viper"))) - require.NoError(t, s.Publish(ctx, pubstring("Black Panther"))) + require.NoError(t, s.Publish(pubstring("Fat Cobra"))) + require.NoError(t, s.Publish(pubstring("Viper"))) + require.NoError(t, s.Publish(pubstring("Black Panther"))) // We had capacity for one item, so we should get that item, but after that // the subscription should have been terminated by the publisher. @@ -176,7 +176,7 @@ func TestDifferentClients(t *testing.T) { Attributes: []abci.EventAttribute{{Key: "type", Value: "NewBlock"}}, }} - require.NoError(t, s.PublishWithEvents(ctx, pubstring("Iceman"), events)) + require.NoError(t, s.PublishWithEvents(pubstring("Iceman"), events)) sub1.mustReceive(ctx, pubstring("Iceman")) sub2 := newTestSub(t).must(s.SubscribeWithArgs(ctx, pubsub.SubscribeArgs{ @@ -195,7 +195,7 @@ func TestDifferentClients(t *testing.T) { }, } - require.NoError(t, s.PublishWithEvents(ctx, pubstring("Ultimo"), events)) + require.NoError(t, s.PublishWithEvents(pubstring("Ultimo"), events)) sub1.mustReceive(ctx, pubstring("Ultimo")) sub2.mustReceive(ctx, pubstring("Ultimo")) @@ -210,7 +210,7 @@ func TestDifferentClients(t *testing.T) { Attributes: []abci.EventAttribute{{Key: "type", Value: "NewRoundStep"}}, }} - require.NoError(t, s.PublishWithEvents(ctx, pubstring("Valeria Richards"), events)) + require.NoError(t, s.PublishWithEvents(pubstring("Valeria Richards"), events)) sub3.mustTimeOut(ctx, 100*time.Millisecond) } @@ -259,7 +259,7 @@ func TestSubscribeDuplicateKeys(t *testing.T) { }, } - require.NoError(t, s.PublishWithEvents(ctx, pubstring("Iceman"), events)) + require.NoError(t, s.PublishWithEvents(pubstring("Iceman"), events)) if tc.expected != nil { sub.mustReceive(ctx, tc.expected) @@ -288,7 +288,7 @@ func TestClientSubscribesTwice(t *testing.T) { Query: q, })) - require.NoError(t, s.PublishWithEvents(ctx, pubstring("Goblin Queen"), events)) + require.NoError(t, s.PublishWithEvents(pubstring("Goblin Queen"), events)) sub1.mustReceive(ctx, pubstring("Goblin Queen")) // Subscribing a second time with the same client ID and query fails. @@ -302,7 +302,7 @@ func TestClientSubscribesTwice(t *testing.T) { } // The attempt to re-subscribe does not disrupt the existing sub. - require.NoError(t, s.PublishWithEvents(ctx, pubstring("Spider-Man"), events)) + require.NoError(t, s.PublishWithEvents(pubstring("Spider-Man"), events)) sub1.mustReceive(ctx, pubstring("Spider-Man")) } @@ -325,7 +325,7 @@ func TestUnsubscribe(t *testing.T) { })) // Publishing should still work. - require.NoError(t, s.Publish(ctx, pubstring("Nick Fury"))) + require.NoError(t, s.Publish(pubstring("Nick Fury"))) // The unsubscribed subscriber should report as such. sub.mustFail(ctx, pubsub.ErrUnsubscribed) @@ -373,7 +373,7 @@ func TestResubscribe(t *testing.T) { sub := newTestSub(t).must(s.SubscribeWithArgs(ctx, args)) - require.NoError(t, s.Publish(ctx, pubstring("Cable"))) + require.NoError(t, s.Publish(pubstring("Cable"))) sub.mustReceive(ctx, pubstring("Cable")) } @@ -394,7 +394,7 @@ func TestUnsubscribeAll(t *testing.T) { })) require.NoError(t, s.UnsubscribeAll(ctx, clientID)) - require.NoError(t, s.Publish(ctx, pubstring("Nick Fury"))) + require.NoError(t, s.Publish(pubstring("Nick Fury"))) sub1.mustFail(ctx, pubsub.ErrUnsubscribed) sub2.mustFail(ctx, pubsub.ErrUnsubscribed) @@ -410,13 +410,24 @@ func TestBufferCapacity(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - require.NoError(t, s.Publish(ctx, pubstring("Nighthawk"))) - require.NoError(t, s.Publish(ctx, pubstring("Sage"))) + require.NoError(t, s.Publish(pubstring("Nighthawk"))) + require.NoError(t, s.Publish(pubstring("Sage"))) ctx, cancel = context.WithTimeout(ctx, 100*time.Millisecond) defer cancel() - require.ErrorIs(t, s.Publish(ctx, pubstring("Ironclad")), context.DeadlineExceeded) + sig := make(chan struct{}) + + go func() { defer close(sig); _ = s.Publish(pubstring("Ironclad")) }() + + select { + case <-sig: + t.Fatal("should not fire") + + case <-ctx.Done(): + return + } + } func newTestServer(ctx context.Context, t testing.TB, logger log.Logger) *pubsub.Server { diff --git a/internal/rpc/core/abci.go b/internal/rpc/core/abci.go index 8f5e61d55..dc7a5bdfe 100644 --- a/internal/rpc/core/abci.go +++ b/internal/rpc/core/abci.go @@ -18,7 +18,7 @@ func (env *Environment) ABCIQuery( height int64, prove bool, ) (*coretypes.ResultABCIQuery, error) { - resQuery, err := env.ProxyApp.Query(ctx, abci.RequestQuery{ + resQuery, err := env.ProxyApp.Query(ctx, &abci.RequestQuery{ Path: path, Data: data, Height: height, @@ -34,7 +34,7 @@ func (env *Environment) ABCIQuery( // ABCIInfo gets some info about the application. // More: https://docs.tendermint.com/master/rpc/#/ABCI/abci_info func (env *Environment) ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo, error) { - resInfo, err := env.ProxyApp.Info(ctx, proxy.RequestInfo) + resInfo, err := env.ProxyApp.Info(ctx, &proxy.RequestInfo) if err != nil { return nil, err } diff --git a/internal/rpc/core/mempool.go b/internal/rpc/core/mempool.go index a852aeb1d..450adf4d5 100644 --- a/internal/rpc/core/mempool.go +++ b/internal/rpc/core/mempool.go @@ -176,7 +176,7 @@ func (env *Environment) NumUnconfirmedTxs(ctx context.Context) (*coretypes.Resul // be added to the mempool either. // More: https://docs.tendermint.com/master/rpc/#/Tx/check_tx func (env *Environment) CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultCheckTx, error) { - res, err := env.ProxyApp.CheckTx(ctx, abci.RequestCheckTx{Tx: tx}) + res, err := env.ProxyApp.CheckTx(ctx, &abci.RequestCheckTx{Tx: tx}) if err != nil { return nil, err } diff --git a/internal/state/execution.go b/internal/state/execution.go index 145251428..06dfc0b5c 100644 --- a/internal/state/execution.go +++ b/internal/state/execution.go @@ -7,6 +7,7 @@ import ( abciclient "github.com/tendermint/tendermint/abci/client" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/encoding" "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/internal/eventbus" @@ -105,7 +106,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock( localLastCommit := buildLastCommitInfo(block, blockExec.store, state.InitialHeight) rpp, err := blockExec.appClient.PrepareProposal( ctx, - abci.RequestPrepareProposal{ + &abci.RequestPrepareProposal{ MaxTxBytes: maxDataBytes, Txs: block.Txs.ToSliceOfBytes(), LocalLastCommit: extendedCommitInfo(localLastCommit, votes), @@ -147,7 +148,7 @@ func (blockExec *BlockExecutor) ProcessProposal( block *types.Block, state State, ) (bool, error) { - req := abci.RequestProcessProposal{ + resp, err := blockExec.appClient.ProcessProposal(ctx, &abci.RequestProcessProposal{ Hash: block.Header.Hash(), Height: block.Header.Height, Time: block.Header.Time, @@ -156,9 +157,7 @@ func (blockExec *BlockExecutor) ProcessProposal( ByzantineValidators: block.Evidence.ToABCI(), ProposerAddress: block.ProposerAddress, NextValidatorsHash: block.NextValidatorsHash, - } - - resp, err := blockExec.appClient.ProcessProposal(ctx, req) + }) if err != nil { return false, ErrInvalidBlock(err) } @@ -210,7 +209,7 @@ func (blockExec *BlockExecutor) ApplyBlock( startTime := time.Now().UnixNano() finalizeBlockResponse, err := blockExec.appClient.FinalizeBlock( ctx, - abci.RequestFinalizeBlock{ + &abci.RequestFinalizeBlock{ Hash: block.Hash(), Height: block.Header.Height, Time: block.Header.Time, @@ -291,34 +290,34 @@ func (blockExec *BlockExecutor) ApplyBlock( // Events are fired after everything else. // NOTE: if we crash between Commit and Save, events wont be fired during replay - fireEvents(ctx, blockExec.logger, blockExec.eventBus, block, blockID, finalizeBlockResponse, validatorUpdates) + fireEvents(blockExec.logger, blockExec.eventBus, block, blockID, finalizeBlockResponse, validatorUpdates) return state, nil } -func (blockExec *BlockExecutor) ExtendVote(ctx context.Context, vote *types.Vote) (types.VoteExtension, error) { - req := abci.RequestExtendVote{ - Vote: vote.ToProto(), - } - - resp, err := blockExec.appClient.ExtendVote(ctx, req) +func (blockExec *BlockExecutor) ExtendVote(ctx context.Context, vote *types.Vote) ([]byte, error) { + resp, err := blockExec.appClient.ExtendVote(ctx, &abci.RequestExtendVote{ + Hash: vote.BlockID.Hash, + Height: vote.Height, + }) if err != nil { - return types.VoteExtension{}, err + panic(fmt.Errorf("ExtendVote call failed: %w", err)) } - return types.VoteExtensionFromProto(resp.VoteExtension), nil + return resp.VoteExtension, nil } func (blockExec *BlockExecutor) VerifyVoteExtension(ctx context.Context, vote *types.Vote) error { - req := abci.RequestVerifyVoteExtension{ - Vote: vote.ToProto(), - } - - resp, err := blockExec.appClient.VerifyVoteExtension(ctx, req) + resp, err := blockExec.appClient.VerifyVoteExtension(ctx, &abci.RequestVerifyVoteExtension{ + Hash: vote.BlockID.Hash, + ValidatorAddress: vote.ValidatorAddress, + Height: vote.Height, + VoteExtension: vote.Extension, + }) if err != nil { - return err + panic(fmt.Errorf("VerifyVoteExtension call failed: %w", err)) } - if resp.IsErr() { + if !resp.IsOK() { return types.ErrVoteInvalidExtension } @@ -417,16 +416,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{ @@ -530,7 +552,6 @@ func (state State) Update( // Fire TxEvent for every tx. // NOTE: if Tendermint crashes before commit, some or all of these events may be published again. func fireEvents( - ctx context.Context, logger log.Logger, eventBus types.BlockEventPublisher, block *types.Block, @@ -538,7 +559,7 @@ func fireEvents( finalizeBlockResponse *abci.ResponseFinalizeBlock, validatorUpdates []*types.Validator, ) { - if err := eventBus.PublishEventNewBlock(ctx, types.EventDataNewBlock{ + if err := eventBus.PublishEventNewBlock(types.EventDataNewBlock{ Block: block, BlockID: blockID, ResultFinalizeBlock: *finalizeBlockResponse, @@ -546,7 +567,7 @@ func fireEvents( logger.Error("failed publishing new block", "err", err) } - if err := eventBus.PublishEventNewBlockHeader(ctx, types.EventDataNewBlockHeader{ + if err := eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{ Header: block.Header, NumTxs: int64(len(block.Txs)), ResultFinalizeBlock: *finalizeBlockResponse, @@ -556,7 +577,7 @@ func fireEvents( if len(block.Evidence) != 0 { for _, ev := range block.Evidence { - if err := eventBus.PublishEventNewEvidence(ctx, types.EventDataNewEvidence{ + if err := eventBus.PublishEventNewEvidence(types.EventDataNewEvidence{ Evidence: ev, Height: block.Height, }); err != nil { @@ -572,7 +593,7 @@ func fireEvents( } for i, tx := range block.Data.Txs { - if err := eventBus.PublishEventTx(ctx, types.EventDataTx{ + if err := eventBus.PublishEventTx(types.EventDataTx{ TxResult: abci.TxResult{ Height: block.Height, Index: uint32(i), @@ -585,7 +606,7 @@ func fireEvents( } if len(finalizeBlockResponse.ValidatorUpdates) > 0 { - if err := eventBus.PublishEventValidatorSetUpdates(ctx, + if err := eventBus.PublishEventValidatorSetUpdates( types.EventDataValidatorSetUpdates{ValidatorUpdates: validatorUpdates}); err != nil { logger.Error("failed publishing event", "err", err) } @@ -609,7 +630,7 @@ func ExecCommitBlock( ) ([]byte, error) { finalizeBlockResponse, err := appConn.FinalizeBlock( ctx, - abci.RequestFinalizeBlock{ + &abci.RequestFinalizeBlock{ Hash: block.Hash(), Height: block.Height, Time: block.Time, @@ -644,7 +665,7 @@ func ExecCommitBlock( } blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} - fireEvents(ctx, be.logger, be.eventBus, block, blockID, finalizeBlockResponse, validatorUpdates) + fireEvents(be.logger, be.eventBus, block, blockID, finalizeBlockResponse, validatorUpdates) } // Commit block, get hash back diff --git a/internal/state/execution_test.go b/internal/state/execution_test.go index c70286e28..317f09706 100644 --- a/internal/state/execution_test.go +++ b/internal/state/execution_test.go @@ -277,7 +277,7 @@ func TestProcessProposal(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - app := abcimocks.NewBaseMock() + app := abcimocks.NewApplication(t) logger := log.NewNopLogger() cc := abciclient.NewLocalClient(logger, app) proxyApp := proxy.New(cc, logger, proxy.NopMetrics()) @@ -329,7 +329,7 @@ func TestProcessProposal(t *testing.T) { block1 := sf.MakeBlock(state, height, lastCommit) block1.Txs = txs - expectedRpp := abci.RequestProcessProposal{ + expectedRpp := &abci.RequestProcessProposal{ Txs: block1.Txs.ToSliceOfBytes(), Hash: block1.Hash(), Height: block1.Header.Height, @@ -343,12 +343,12 @@ func TestProcessProposal(t *testing.T) { ProposerAddress: block1.ProposerAddress, } - app.On("ProcessProposal", mock.Anything).Return(abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}) + app.On("ProcessProposal", mock.Anything, mock.Anything).Return(&abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil) acceptBlock, err := blockExec.ProcessProposal(ctx, block1, state) require.NoError(t, err) require.True(t, acceptBlock) app.AssertExpectations(t) - app.AssertCalled(t, "ProcessProposal", expectedRpp) + app.AssertCalled(t, "ProcessProposal", ctx, expectedRpp) } func TestValidateValidatorUpdates(t *testing.T) { @@ -621,8 +621,8 @@ func TestEmptyPrepareProposal(t *testing.T) { eventBus := eventbus.NewDefault(logger) require.NoError(t, eventBus.Start(ctx)) - app := abcimocks.NewBaseMock() - app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{}) + app := abcimocks.NewApplication(t) + app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{}, nil) cc := abciclient.NewLocalClient(logger, app) proxyApp := proxy.New(cc, logger, proxy.NopMetrics()) err := proxyApp.Start(ctx) @@ -654,8 +654,8 @@ func TestEmptyPrepareProposal(t *testing.T) { sm.NopMetrics(), ) pa, _ := state.Validators.GetByIndex(0) - commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - _, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil) + commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) + _, err = blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes) require.NoError(t, err) } @@ -680,10 +680,10 @@ func TestPrepareProposalErrorOnNonExistingRemoved(t *testing.T) { mp := &mpmocks.Mempool{} mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{}) - app := abcimocks.NewBaseMock() + app := abcimocks.NewApplication(t) // create an invalid ResponsePrepareProposal - rpp := abci.ResponsePrepareProposal{ + rpp := &abci.ResponsePrepareProposal{ TxRecords: []*abci.TxRecord{ { Action: abci.TxRecord_REMOVED, @@ -691,7 +691,7 @@ func TestPrepareProposalErrorOnNonExistingRemoved(t *testing.T) { }, }, } - app.On("PrepareProposal", mock.Anything).Return(rpp) + app.On("PrepareProposal", mock.Anything, mock.Anything).Return(rpp, nil) cc := abciclient.NewLocalClient(logger, app) proxyApp := proxy.New(cc, logger, proxy.NopMetrics()) @@ -709,10 +709,10 @@ func TestPrepareProposalErrorOnNonExistingRemoved(t *testing.T) { sm.NopMetrics(), ) pa, _ := state.Validators.GetByIndex(0) - commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil) - require.Nil(t, block) + commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) + block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes) require.ErrorContains(t, err, "new transaction incorrectly marked as removed") + require.Nil(t, block) mp.AssertExpectations(t) } @@ -744,10 +744,10 @@ func TestPrepareProposalRemoveTxs(t *testing.T) { trs[1].Action = abci.TxRecord_REMOVED mp.On("RemoveTxByKey", mock.Anything).Return(nil).Twice() - app := abcimocks.NewBaseMock() - app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{ + app := abcimocks.NewApplication(t) + app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{ TxRecords: trs, - }) + }, nil) cc := abciclient.NewLocalClient(logger, app) proxyApp := proxy.New(cc, logger, proxy.NopMetrics()) @@ -765,8 +765,8 @@ func TestPrepareProposalRemoveTxs(t *testing.T) { sm.NopMetrics(), ) pa, _ := state.Validators.GetByIndex(0) - commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil) + commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) + block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes) require.NoError(t, err) require.Len(t, block.Data.Txs.ToSliceOfBytes(), len(trs)-2) @@ -803,10 +803,10 @@ func TestPrepareProposalAddedTxsIncluded(t *testing.T) { trs[0].Action = abci.TxRecord_ADDED trs[1].Action = abci.TxRecord_ADDED - app := abcimocks.NewBaseMock() - app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{ + app := abcimocks.NewApplication(t) + app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{ TxRecords: trs, - }) + }, nil) cc := abciclient.NewLocalClient(logger, app) proxyApp := proxy.New(cc, logger, proxy.NopMetrics()) @@ -824,8 +824,8 @@ func TestPrepareProposalAddedTxsIncluded(t *testing.T) { sm.NopMetrics(), ) pa, _ := state.Validators.GetByIndex(0) - commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil) + commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) + block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes) require.NoError(t, err) require.Equal(t, txs[0], block.Data.Txs[0]) @@ -859,10 +859,10 @@ func TestPrepareProposalReorderTxs(t *testing.T) { trs = trs[2:] trs = append(trs[len(trs)/2:], trs[:len(trs)/2]...) - app := abcimocks.NewBaseMock() - app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{ + app := abcimocks.NewApplication(t) + app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{ TxRecords: trs, - }) + }, nil) cc := abciclient.NewLocalClient(logger, app) proxyApp := proxy.New(cc, logger, proxy.NopMetrics()) @@ -880,8 +880,8 @@ func TestPrepareProposalReorderTxs(t *testing.T) { sm.NopMetrics(), ) pa, _ := state.Validators.GetByIndex(0) - commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil) + commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) + block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes) require.NoError(t, err) for i, tx := range block.Data.Txs { require.Equal(t, types.Tx(trs[i].Tx), tx) @@ -919,10 +919,10 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) { trs := txsToTxRecords(types.Txs(txs)) - app := abcimocks.NewBaseMock() - app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{ + app := abcimocks.NewApplication(t) + app.On("PrepareProposal", mock.Anything, mock.Anything).Return(&abci.ResponsePrepareProposal{ TxRecords: trs, - }) + }, nil) cc := abciclient.NewLocalClient(logger, app) proxyApp := proxy.New(cc, logger, proxy.NopMetrics()) @@ -940,11 +940,11 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) { sm.NopMetrics(), ) pa, _ := state.Validators.GetByIndex(0) - commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) + commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil) - require.Nil(t, block) + block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes) require.ErrorContains(t, err, "transaction data size exceeds maximum") + require.Nil(t, block, "") mp.AssertExpectations(t) } @@ -992,9 +992,9 @@ func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) { sm.NopMetrics(), ) pa, _ := state.Validators.GetByIndex(0) - commit := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) + commit, votes := makeValidCommit(ctx, t, height, types.BlockID{}, state.Validators, privVals) - block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, nil) + block, err := blockExec.CreateProposalBlock(ctx, height, state, commit, pa, votes) require.Nil(t, block) require.ErrorContains(t, err, "an injected error") diff --git a/internal/state/helpers_test.go b/internal/state/helpers_test.go index 1a926a91f..07dd0d865 100644 --- a/internal/state/helpers_test.go +++ b/internal/state/helpers_test.go @@ -46,7 +46,7 @@ func makeAndCommitGoodBlock( state, blockID := makeAndApplyGoodBlock(ctx, t, state, height, lastCommit, proposerAddr, blockExec, evidence) // Simulate a lastCommit for this block from all validators for the next height - commit := makeValidCommit(ctx, t, height, blockID, state.Validators, privVals) + commit, _ := makeValidCommit(ctx, t, height, blockID, state.Validators, privVals) return state, blockID, commit } @@ -82,17 +82,19 @@ func makeValidCommit( blockID types.BlockID, vals *types.ValidatorSet, privVals map[string]types.PrivValidator, -) *types.Commit { +) (*types.Commit, []*types.Vote) { t.Helper() - sigs := make([]types.CommitSig, 0) + 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 := factory.MakeVote(ctx, privVals[val.Address.String()], chainID, int32(i), height, 0, 2, blockID, time.Now()) require.NoError(t, err) - sigs = append(sigs, vote.CommitSig()) + sigs[i] = vote.CommitSig() + votes[i] = vote } - return types.NewCommit(height, 0, blockID, sigs) + return types.NewCommit(height, 0, blockID, sigs), votes } func makeState(t *testing.T, nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) { @@ -276,11 +278,11 @@ type testApp struct { var _ abci.Application = (*testApp)(nil) -func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) { - return abci.ResponseInfo{} +func (app *testApp) Info(_ context.Context, req *abci.RequestInfo) (*abci.ResponseInfo, error) { + return &abci.ResponseInfo{}, nil } -func (app *testApp) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFinalizeBlock { +func (app *testApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) { app.CommitVotes = req.DecidedLastCommit.Votes app.ByzantineValidators = req.ByzantineValidators @@ -293,7 +295,7 @@ func (app *testApp) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFi } } - return abci.ResponseFinalizeBlock{ + return &abci.ResponseFinalizeBlock{ ValidatorUpdates: app.ValidatorUpdates, ConsensusParamUpdates: &tmproto.ConsensusParams{ Version: &tmproto.VersionParams{ @@ -302,26 +304,26 @@ func (app *testApp) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFi }, Events: []abci.Event{}, TxResults: resTxs, - } + }, nil } -func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { - return abci.ResponseCheckTx{} +func (app *testApp) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) { + return &abci.ResponseCheckTx{}, nil } -func (app *testApp) Commit() abci.ResponseCommit { - return abci.ResponseCommit{RetainHeight: 1} +func (app *testApp) Commit(context.Context) (*abci.ResponseCommit, error) { + return &abci.ResponseCommit{RetainHeight: 1}, nil } -func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { - return +func (app *testApp) Query(_ context.Context, req *abci.RequestQuery) (*abci.ResponseQuery, error) { + return &abci.ResponseQuery{}, nil } -func (app *testApp) ProcessProposal(req abci.RequestProcessProposal) abci.ResponseProcessProposal { +func (app *testApp) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) { for _, tx := range req.Txs { if len(tx) == 0 { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} + return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil } } - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT} + return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil } diff --git a/internal/state/indexer/indexer_service_test.go b/internal/state/indexer/indexer_service_test.go index a71b204ec..6126ae259 100644 --- a/internal/state/indexer/indexer_service_test.go +++ b/internal/state/indexer/indexer_service_test.go @@ -54,8 +54,7 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) { assert.False(t, indexer.IndexingEnabled([]indexer.EventSink{})) // event sink setup - pool, err := setupDB(t) - assert.NoError(t, err) + pool := setupDB(t) store := dbm.NewMemDB() eventSinks := []indexer.EventSink{kv.NewEventSink(store), pSink} @@ -71,7 +70,7 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) { t.Cleanup(service.Wait) // publish block with txs - err = eventBus.PublishEventNewBlockHeader(ctx, types.EventDataNewBlockHeader{ + err = eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{ Header: types.Header{Height: 1}, NumTxs: int64(2), }) @@ -82,7 +81,7 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) { Tx: types.Tx("foo"), Result: abci.ExecTxResult{Code: 0}, } - err = eventBus.PublishEventTx(ctx, types.EventDataTx{TxResult: *txResult1}) + err = eventBus.PublishEventTx(types.EventDataTx{TxResult: *txResult1}) require.NoError(t, err) txResult2 := &abci.TxResult{ Height: 1, @@ -90,7 +89,7 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) { Tx: types.Tx("bar"), Result: abci.ExecTxResult{Code: 0}, } - err = eventBus.PublishEventTx(ctx, types.EventDataTx{TxResult: *txResult2}) + err = eventBus.PublishEventTx(types.EventDataTx{TxResult: *txResult2}) require.NoError(t, err) time.Sleep(100 * time.Millisecond) @@ -133,7 +132,7 @@ func resetDB(t *testing.T) { assert.NoError(t, err) } -func setupDB(t *testing.T) (*dockertest.Pool, error) { +func setupDB(t *testing.T) *dockertest.Pool { t.Helper() pool, err := dockertest.NewPool(os.Getenv("DOCKER_URL")) assert.NoError(t, err) @@ -187,7 +186,7 @@ func setupDB(t *testing.T) (*dockertest.Pool, error) { err = migrator.Apply(psqldb, sm) assert.NoError(t, err) - return pool, nil + return pool } func teardown(t *testing.T, pool *dockertest.Pool) error { diff --git a/internal/state/indexer/mocks/event_sink.go b/internal/state/indexer/mocks/event_sink.go index d5555a417..decf551ab 100644 --- a/internal/state/indexer/mocks/event_sink.go +++ b/internal/state/indexer/mocks/event_sink.go @@ -12,6 +12,8 @@ import ( tenderminttypes "github.com/tendermint/tendermint/types" + testing "testing" + types "github.com/tendermint/tendermint/abci/types" ) @@ -165,3 +167,13 @@ func (_m *EventSink) Type() indexer.EventSinkType { return r0 } + +// NewEventSink creates a new instance of EventSink. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewEventSink(t testing.TB) *EventSink { + mock := &EventSink{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/state/mocks/block_store.go b/internal/state/mocks/block_store.go index 563183437..7cc7fa883 100644 --- a/internal/state/mocks/block_store.go +++ b/internal/state/mocks/block_store.go @@ -5,6 +5,8 @@ package mocks import ( mock "github.com/stretchr/testify/mock" + testing "testing" + types "github.com/tendermint/tendermint/types" ) @@ -208,3 +210,13 @@ func (_m *BlockStore) Size() int64 { return r0 } + +// NewBlockStore creates a new instance of BlockStore. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewBlockStore(t testing.TB) *BlockStore { + mock := &BlockStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/state/mocks/evidence_pool.go b/internal/state/mocks/evidence_pool.go index 04e8be7bc..49633269b 100644 --- a/internal/state/mocks/evidence_pool.go +++ b/internal/state/mocks/evidence_pool.go @@ -8,6 +8,8 @@ import ( mock "github.com/stretchr/testify/mock" state "github.com/tendermint/tendermint/internal/state" + testing "testing" + types "github.com/tendermint/tendermint/types" ) @@ -71,3 +73,13 @@ func (_m *EvidencePool) PendingEvidence(maxBytes int64) ([]types.Evidence, int64 func (_m *EvidencePool) Update(_a0 context.Context, _a1 state.State, _a2 types.EvidenceList) { _m.Called(_a0, _a1, _a2) } + +// NewEvidencePool creates a new instance of EvidencePool. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewEvidencePool(t testing.TB) *EvidencePool { + mock := &EvidencePool{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/state/mocks/store.go b/internal/state/mocks/store.go index 02c69d3e0..9b41f3c1b 100644 --- a/internal/state/mocks/store.go +++ b/internal/state/mocks/store.go @@ -7,6 +7,8 @@ import ( state "github.com/tendermint/tendermint/internal/state" tendermintstate "github.com/tendermint/tendermint/proto/tendermint/state" + testing "testing" + types "github.com/tendermint/tendermint/types" ) @@ -186,3 +188,13 @@ func (_m *Store) SaveValidatorSets(_a0 int64, _a1 int64, _a2 *types.ValidatorSet return r0 } + +// NewStore creates a new instance of Store. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewStore(t testing.TB) *Store { + mock := &Store{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/statesync/mocks/state_provider.go b/internal/statesync/mocks/state_provider.go index b8d681631..582ebcd9c 100644 --- a/internal/statesync/mocks/state_provider.go +++ b/internal/statesync/mocks/state_provider.go @@ -8,6 +8,8 @@ import ( mock "github.com/stretchr/testify/mock" state "github.com/tendermint/tendermint/internal/state" + testing "testing" + types "github.com/tendermint/tendermint/types" ) @@ -82,3 +84,13 @@ func (_m *StateProvider) State(ctx context.Context, height uint64) (state.State, return r0, r1 } + +// NewStateProvider creates a new instance of StateProvider. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewStateProvider(t testing.TB) *StateProvider { + mock := &StateProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/statesync/reactor.go b/internal/statesync/reactor.go index ea2cac4f4..fb2749a02 100644 --- a/internal/statesync/reactor.go +++ b/internal/statesync/reactor.go @@ -333,7 +333,7 @@ func (r *Reactor) OnStop() { // of historical blocks before participating in consensus func (r *Reactor) Sync(ctx context.Context) (sm.State, error) { if r.eventBus != nil { - if err := r.eventBus.PublishEventStateSyncStatus(ctx, types.EventDataStateSyncStatus{ + if err := r.eventBus.PublishEventStateSyncStatus(types.EventDataStateSyncStatus{ Complete: false, Height: r.initialHeight, }); err != nil { @@ -387,7 +387,7 @@ func (r *Reactor) Sync(ctx context.Context) (sm.State, error) { } if r.eventBus != nil { - if err := r.eventBus.PublishEventStateSyncStatus(ctx, types.EventDataStateSyncStatus{ + if err := r.eventBus.PublishEventStateSyncStatus(types.EventDataStateSyncStatus{ Complete: true, Height: state.LastBlockHeight, }); err != nil { @@ -688,7 +688,7 @@ func (r *Reactor) handleChunkMessage(ctx context.Context, envelope *p2p.Envelope "chunk", msg.Index, "peer", envelope.From, ) - resp, err := r.conn.LoadSnapshotChunk(ctx, abci.RequestLoadSnapshotChunk{ + resp, err := r.conn.LoadSnapshotChunk(ctx, &abci.RequestLoadSnapshotChunk{ Height: msg.Height, Format: msg.Format, Chunk: msg.Index, @@ -1015,7 +1015,7 @@ func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerU // recentSnapshots fetches the n most recent snapshots from the app func (r *Reactor) recentSnapshots(ctx context.Context, n uint32) ([]*snapshot, error) { - resp, err := r.conn.ListSnapshots(ctx, abci.RequestListSnapshots{}) + resp, err := r.conn.ListSnapshots(ctx, &abci.RequestListSnapshots{}) if err != nil { return nil, err } diff --git a/internal/statesync/reactor_test.go b/internal/statesync/reactor_test.go index 86c84a9ed..c6b2c2d2b 100644 --- a/internal/statesync/reactor_test.go +++ b/internal/statesync/reactor_test.go @@ -204,15 +204,15 @@ func TestReactor_Sync(t *testing.T) { rts := setup(ctx, t, nil, nil, 100) chain := buildLightBlockChain(ctx, t, 1, 10, time.Now()) // app accepts any snapshot - rts.conn.On("OfferSnapshot", ctx, mock.AnythingOfType("types.RequestOfferSnapshot")). + rts.conn.On("OfferSnapshot", ctx, mock.IsType(&abci.RequestOfferSnapshot{})). Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}, nil) // app accepts every chunk - rts.conn.On("ApplySnapshotChunk", ctx, mock.AnythingOfType("types.RequestApplySnapshotChunk")). + rts.conn.On("ApplySnapshotChunk", ctx, mock.IsType(&abci.RequestApplySnapshotChunk{})). Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) // app query returns valid state app hash - rts.conn.On("Info", mock.Anything, proxy.RequestInfo).Return(&abci.ResponseInfo{ + rts.conn.On("Info", mock.Anything, &proxy.RequestInfo).Return(&abci.ResponseInfo{ AppVersion: testAppVersion, LastBlockHeight: snapshotHeight, LastBlockAppHash: chain[snapshotHeight+1].AppHash, @@ -307,7 +307,7 @@ func TestReactor_ChunkRequest(t *testing.T) { // mock ABCI connection to return local snapshots conn := &clientmocks.Client{} - conn.On("LoadSnapshotChunk", mock.Anything, abci.RequestLoadSnapshotChunk{ + conn.On("LoadSnapshotChunk", mock.Anything, &abci.RequestLoadSnapshotChunk{ Height: tc.request.Height, Format: tc.request.Format, Chunk: tc.request.Index, @@ -396,7 +396,7 @@ func TestReactor_SnapshotsRequest(t *testing.T) { // mock ABCI connection to return local snapshots conn := &clientmocks.Client{} - conn.On("ListSnapshots", mock.Anything, abci.RequestListSnapshots{}).Return(&abci.ResponseListSnapshots{ + conn.On("ListSnapshots", mock.Anything, &abci.RequestListSnapshots{}).Return(&abci.ResponseListSnapshots{ Snapshots: tc.snapshots, }, nil) diff --git a/internal/statesync/stateprovider.go b/internal/statesync/stateprovider.go index 3e58ca5f4..a796b0b2e 100644 --- a/internal/statesync/stateprovider.go +++ b/internal/statesync/stateprovider.go @@ -379,7 +379,7 @@ func (s *stateProviderP2P) consensusParams(ctx context.Context, height int64) (t } wg.Add(1) - go func(p *BlockProvider, peer types.NodeID) { + go func(peer types.NodeID) { defer wg.Done() timer := time.NewTimer(0) @@ -424,7 +424,7 @@ func (s *stateProviderP2P) consensusParams(ctx context.Context, height int64) (t } } - }(p, peer) + }(peer) } sig := make(chan struct{}) go func() { wg.Wait(); close(sig) }() diff --git a/internal/statesync/syncer.go b/internal/statesync/syncer.go index c2cca9a7c..47e058c19 100644 --- a/internal/statesync/syncer.go +++ b/internal/statesync/syncer.go @@ -337,7 +337,7 @@ func (s *syncer) Sync(ctx context.Context, snapshot *snapshot, chunks *chunkQueu func (s *syncer) offerSnapshot(ctx context.Context, snapshot *snapshot) error { s.logger.Info("Offering snapshot to ABCI app", "height", snapshot.Height, "format", snapshot.Format, "hash", snapshot.Hash) - resp, err := s.conn.OfferSnapshot(ctx, abci.RequestOfferSnapshot{ + resp, err := s.conn.OfferSnapshot(ctx, &abci.RequestOfferSnapshot{ Snapshot: &abci.Snapshot{ Height: snapshot.Height, Format: snapshot.Format, @@ -379,7 +379,7 @@ func (s *syncer) applyChunks(ctx context.Context, chunks *chunkQueue, start time return fmt.Errorf("failed to fetch chunk: %w", err) } - resp, err := s.conn.ApplySnapshotChunk(ctx, abci.RequestApplySnapshotChunk{ + resp, err := s.conn.ApplySnapshotChunk(ctx, &abci.RequestApplySnapshotChunk{ Index: chunk.Index, Chunk: chunk.Chunk, Sender: string(chunk.Sender), @@ -519,7 +519,7 @@ func (s *syncer) requestChunk(ctx context.Context, snapshot *snapshot, chunk uin // verifyApp verifies the sync, checking the app hash, last block height and app version func (s *syncer) verifyApp(ctx context.Context, snapshot *snapshot, appVersion uint64) error { - resp, err := s.conn.Info(ctx, proxy.RequestInfo) + resp, err := s.conn.Info(ctx, &proxy.RequestInfo) if err != nil { return fmt.Errorf("failed to query ABCI app for appHash: %w", err) } diff --git a/internal/statesync/syncer_test.go b/internal/statesync/syncer_test.go index e3bf49259..3fc3f0db4 100644 --- a/internal/statesync/syncer_test.go +++ b/internal/statesync/syncer_test.go @@ -109,7 +109,7 @@ func TestSyncer_SyncAny(t *testing.T) { // We start a sync, with peers sending back chunks when requested. We first reject the snapshot // with height 2 format 2, and accept the snapshot at height 1. - conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: &abci.Snapshot{ Height: 2, Format: 2, @@ -118,7 +118,7 @@ func TestSyncer_SyncAny(t *testing.T) { }, AppHash: []byte("app_hash_2"), }).Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT_FORMAT}, nil) - conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: &abci.Snapshot{ Height: s.Height, Format: s.Format, @@ -170,7 +170,7 @@ func TestSyncer_SyncAny(t *testing.T) { // The first time we're applying chunk 2 we tell it to retry the snapshot and discard chunk 1, // which should cause it to keep the existing chunk 0 and 2, and restart restoration from // beginning. We also wait for a little while, to exercise the retry logic in fetchChunks(). - conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 2, Chunk: []byte{1, 1, 2}, }).Once().Run(func(args mock.Arguments) { time.Sleep(1 * time.Second) }).Return( &abci.ResponseApplySnapshotChunk{ @@ -178,16 +178,16 @@ func TestSyncer_SyncAny(t *testing.T) { RefetchChunks: []uint32{1}, }, nil) - conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 0, Chunk: []byte{1, 1, 0}, }).Times(2).Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) - conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 1, Chunk: []byte{1, 1, 1}, }).Times(2).Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) - conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 2, Chunk: []byte{1, 1, 2}, }).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) - conn.On("Info", mock.Anything, proxy.RequestInfo).Return(&abci.ResponseInfo{ + conn.On("Info", mock.Anything, &proxy.RequestInfo).Return(&abci.ResponseInfo{ AppVersion: testAppVersion, LastBlockHeight: 1, LastBlockAppHash: []byte("app_hash"), @@ -247,7 +247,7 @@ func TestSyncer_SyncAny_abort(t *testing.T) { _, err := rts.syncer.AddSnapshot(peerID, s) require.NoError(t, err) - rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: toABCI(s), AppHash: []byte("app_hash"), }).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT}, nil) @@ -281,15 +281,15 @@ func TestSyncer_SyncAny_reject(t *testing.T) { _, err = rts.syncer.AddSnapshot(peerID, s11) require.NoError(t, err) - rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: toABCI(s22), AppHash: []byte("app_hash"), }).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}, nil) - rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: toABCI(s12), AppHash: []byte("app_hash"), }).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}, nil) - rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: toABCI(s11), AppHash: []byte("app_hash"), }).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}, nil) @@ -323,11 +323,11 @@ func TestSyncer_SyncAny_reject_format(t *testing.T) { _, err = rts.syncer.AddSnapshot(peerID, s11) require.NoError(t, err) - rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: toABCI(s22), AppHash: []byte("app_hash"), }).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT_FORMAT}, nil) - rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: toABCI(s11), AppHash: []byte("app_hash"), }).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT}, nil) @@ -372,11 +372,11 @@ func TestSyncer_SyncAny_reject_sender(t *testing.T) { _, err = rts.syncer.AddSnapshot(peerCID, sbc) require.NoError(t, err) - rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: toABCI(sbc), AppHash: []byte("app_hash"), }).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT_SENDER}, nil) - rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: toABCI(sa), AppHash: []byte("app_hash"), }).Once().Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}, nil) @@ -402,7 +402,7 @@ func TestSyncer_SyncAny_abciError(t *testing.T) { _, err := rts.syncer.AddSnapshot(peerID, s) require.NoError(t, err) - rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: toABCI(s), AppHash: []byte("app_hash"), }).Once().Return(nil, errBoom) @@ -445,7 +445,7 @@ func TestSyncer_offerSnapshot(t *testing.T) { rts := setup(ctx, t, nil, stateProvider, 2) s := &snapshot{Height: 1, Format: 1, Chunks: 3, Hash: []byte{1, 2, 3}, trustedAppHash: []byte("app_hash")} - rts.conn.On("OfferSnapshot", mock.Anything, abci.RequestOfferSnapshot{ + rts.conn.On("OfferSnapshot", mock.Anything, &abci.RequestOfferSnapshot{ Snapshot: toABCI(s), AppHash: []byte("app_hash"), }).Return(&abci.ResponseOfferSnapshot{Result: tc.result}, tc.err) @@ -506,11 +506,11 @@ func TestSyncer_applyChunks_Results(t *testing.T) { _, err = chunks.Add(&chunk{Height: 1, Format: 1, Index: 0, Chunk: body}) require.NoError(t, err) - rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 0, Chunk: body, }).Once().Return(&abci.ResponseApplySnapshotChunk{Result: tc.result}, tc.err) if tc.result == abci.ResponseApplySnapshotChunk_RETRY { - rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 0, Chunk: body, }).Once().Return(&abci.ResponseApplySnapshotChunk{ Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) @@ -573,13 +573,13 @@ func TestSyncer_applyChunks_RefetchChunks(t *testing.T) { require.NoError(t, err) // The first two chunks are accepted, before the last one asks for 1 to be refetched - rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 0, Chunk: []byte{0}, }).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) - rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 1, Chunk: []byte{1}, }).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) - rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 2, Chunk: []byte{2}, }).Once().Return(&abci.ResponseApplySnapshotChunk{ Result: tc.result, @@ -673,13 +673,13 @@ func TestSyncer_applyChunks_RejectSenders(t *testing.T) { require.NoError(t, err) // The first two chunks are accepted, before the last one asks for b sender to be rejected - rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 0, Chunk: []byte{0}, Sender: "aa", }).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) - rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 1, Chunk: []byte{1}, Sender: "bb", }).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) - rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 2, Chunk: []byte{2}, Sender: "cc", }).Once().Return(&abci.ResponseApplySnapshotChunk{ Result: tc.result, @@ -688,7 +688,7 @@ func TestSyncer_applyChunks_RejectSenders(t *testing.T) { // On retry, the last chunk will be tried again, so we just accept it then. if tc.result == abci.ResponseApplySnapshotChunk_RETRY { - rts.conn.On("ApplySnapshotChunk", mock.Anything, abci.RequestApplySnapshotChunk{ + rts.conn.On("ApplySnapshotChunk", mock.Anything, &abci.RequestApplySnapshotChunk{ Index: 2, Chunk: []byte{2}, Sender: "cc", }).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) } @@ -761,7 +761,7 @@ func TestSyncer_verifyApp(t *testing.T) { rts := setup(ctx, t, nil, nil, 2) - rts.conn.On("Info", mock.Anything, proxy.RequestInfo).Return(tc.response, tc.err) + rts.conn.On("Info", mock.Anything, &proxy.RequestInfo).Return(tc.response, tc.err) err := rts.syncer.verifyApp(ctx, s, appVersion) unwrapped := errors.Unwrap(err) if unwrapped != nil { diff --git a/internal/store/store_test.go b/internal/store/store_test.go index 82949b103..4fa577cc4 100644 --- a/internal/store/store_test.go +++ b/internal/store/store_test.go @@ -17,7 +17,6 @@ import ( "github.com/tendermint/tendermint/crypto" sm "github.com/tendermint/tendermint/internal/state" "github.com/tendermint/tendermint/internal/state/test/factory" - "github.com/tendermint/tendermint/libs/log" tmrand "github.com/tendermint/tendermint/libs/rand" tmtime "github.com/tendermint/tendermint/libs/time" "github.com/tendermint/tendermint/types" @@ -46,7 +45,7 @@ func makeTestCommit(height int64, timestamp time.Time) *types.Commit { commitSigs) } -func makeStateAndBlockStore(dir string, logger log.Logger) (sm.State, *BlockStore, cleanupFunc, error) { +func makeStateAndBlockStore(dir string) (sm.State, *BlockStore, cleanupFunc, error) { cfg, err := config.ResetTestRoot(dir, "blockchain_reactor_test") if err != nil { return sm.State{}, nil, nil, err @@ -81,7 +80,7 @@ func TestMain(m *testing.M) { } var cleanup cleanupFunc - state, _, cleanup, err = makeStateAndBlockStore(dir, log.NewNopLogger()) + state, _, cleanup, err = makeStateAndBlockStore(dir) if err != nil { stdlog.Fatal(err) } @@ -103,7 +102,7 @@ func TestMain(m *testing.M) { // TODO: This test should be simplified ... func TestBlockStoreSaveLoadBlock(t *testing.T) { - state, bs, cleanup, err := makeStateAndBlockStore(t.TempDir(), log.NewNopLogger()) + state, bs, cleanup, err := makeStateAndBlockStore(t.TempDir()) defer cleanup() require.NoError(t, err) require.Equal(t, bs.Base(), int64(0), "initially the base should be zero") @@ -492,7 +491,7 @@ func TestLoadBlockMeta(t *testing.T) { } func TestBlockFetchAtHeight(t *testing.T) { - state, bs, cleanup, err := makeStateAndBlockStore(t.TempDir(), log.NewNopLogger()) + state, bs, cleanup, err := makeStateAndBlockStore(t.TempDir()) defer cleanup() require.NoError(t, err) require.Equal(t, bs.Height(), int64(0), "initially the height should be zero") diff --git a/internal/test/factory/commit.go b/internal/test/factory/commit.go index 1a8691855..bc4022499 100644 --- a/internal/test/factory/commit.go +++ b/internal/test/factory/commit.go @@ -31,6 +31,7 @@ func MakeCommit(ctx context.Context, blockID types.BlockID, height int64, round return nil, err } vote.Signature = v.Signature + vote.ExtensionSignature = v.ExtensionSignature if _, err := voteSet.AddVote(vote); err != nil { return nil, err } diff --git a/libs/bits/bit_array_test.go b/libs/bits/bit_array_test.go index b76085bee..e4bf45c38 100644 --- a/libs/bits/bit_array_test.go +++ b/libs/bits/bit_array_test.go @@ -13,25 +13,25 @@ import ( tmprotobits "github.com/tendermint/tendermint/proto/tendermint/libs/bits" ) -func randBitArray(bits int) (*BitArray, []byte) { +func randBitArray(bits int) *BitArray { src := tmrand.Bytes((bits + 7) / 8) bA := NewBitArray(bits) for i := 0; i < len(src); i++ { for j := 0; j < 8; j++ { if i*8+j >= bits { - return bA, src + return bA } setBit := src[i]&(1< 0 bA.SetIndex(i*8+j, setBit) } } - return bA, src + return bA } func TestAnd(t *testing.T) { - bA1, _ := randBitArray(51) - bA2, _ := randBitArray(31) + bA1 := randBitArray(51) + bA2 := randBitArray(31) bA3 := bA1.And(bA2) var bNil *BitArray @@ -54,9 +54,8 @@ func TestAnd(t *testing.T) { } func TestOr(t *testing.T) { - - bA1, _ := randBitArray(51) - bA2, _ := randBitArray(31) + bA1 := randBitArray(51) + bA2 := randBitArray(31) bA3 := bA1.Or(bA2) bNil := (*BitArray)(nil) @@ -191,10 +190,7 @@ func TestEmptyFull(t *testing.T) { } func TestUpdateNeverPanics(t *testing.T) { - newRandBitArray := func(n int) *BitArray { - ba, _ := randBitArray(n) - return ba - } + newRandBitArray := func(n int) *BitArray { return randBitArray(n) } pairs := []struct { a, b *BitArray }{ diff --git a/libs/bytes/bytes.go b/libs/bytes/bytes.go index dd8e39737..a6be32f05 100644 --- a/libs/bytes/bytes.go +++ b/libs/bytes/bytes.go @@ -1,21 +1,16 @@ package bytes import ( - "bytes" + "encoding/base64" "encoding/hex" - "encoding/json" "fmt" "strings" ) -// HexBytes enables HEX-encoding for json/encoding. +// HexBytes is a wrapper around []byte that encodes data as hexadecimal strings +// for use in JSON. type HexBytes []byte -var ( - _ json.Marshaler = HexBytes{} - _ json.Unmarshaler = &HexBytes{} -) - // Marshal needed for protobuf compatibility func (bz HexBytes) Marshal() ([]byte, error) { return bz, nil @@ -27,41 +22,30 @@ func (bz *HexBytes) Unmarshal(data []byte) error { return nil } -// MarshalJSON implements the json.Marshaler interface. The encoding is a JSON -// quoted string of hexadecimal digits. -func (bz HexBytes) MarshalJSON() ([]byte, error) { - size := hex.EncodedLen(len(bz)) + 2 // +2 for quotation marks - buf := make([]byte, size) - hex.Encode(buf[1:], []byte(bz)) - buf[0] = '"' - buf[size-1] = '"' - - // Ensure letter digits are capitalized. - for i := 1; i < size-1; i++ { - if buf[i] >= 'a' && buf[i] <= 'f' { - buf[i] = 'A' + (buf[i] - 'a') - } - } - return buf, nil +// MarshalText encodes a HexBytes value as hexadecimal digits. +// This method is used by json.Marshal. +func (bz HexBytes) MarshalText() ([]byte, error) { + enc := hex.EncodeToString([]byte(bz)) + return []byte(strings.ToUpper(enc)), nil } -// UnmarshalJSON implements the json.Umarshaler interface. -func (bz *HexBytes) UnmarshalJSON(data []byte) error { - if bytes.Equal(data, []byte("null")) { +// UnmarshalText handles decoding of HexBytes from JSON strings. +// This method is used by json.Unmarshal. +// It allows decoding of both hex and base64-encoded byte arrays. +func (bz *HexBytes) UnmarshalText(data []byte) error { + input := string(data) + if input == "" || input == "null" { return nil } - - if len(data) < 2 || data[0] != '"' || data[len(data)-1] != '"' { - return fmt.Errorf("invalid hex string: %s", data) - } - - bz2, err := hex.DecodeString(string(data[1 : len(data)-1])) + dec, err := hex.DecodeString(input) if err != nil { - return err + dec, err = base64.StdEncoding.DecodeString(input) + + if err != nil { + return err + } } - - *bz = bz2 - + *bz = HexBytes(dec) return nil } diff --git a/libs/bytes/bytes_test.go b/libs/bytes/bytes_test.go index ce39935e8..fb1200a04 100644 --- a/libs/bytes/bytes_test.go +++ b/libs/bytes/bytes_test.go @@ -61,7 +61,7 @@ func TestJSONMarshal(t *testing.T) { t.Fatal(err) } assert.Equal(t, ts2.B1, tc.input) - assert.Equal(t, ts2.B2, HexBytes(tc.input)) + assert.Equal(t, string(ts2.B2), string(tc.input)) }) } } diff --git a/libs/events/events.go b/libs/events/events.go index 5ad1170a9..1b8db09c4 100644 --- a/libs/events/events.go +++ b/libs/events/events.go @@ -2,7 +2,6 @@ package events import ( - "context" "sync" ) @@ -20,7 +19,7 @@ type Eventable interface { // // FireEvent fires an event with the given name and data. type Fireable interface { - FireEvent(ctx context.Context, eventValue string, data EventData) + FireEvent(eventValue string, data EventData) } // EventSwitch is the interface for synchronous pubsub, where listeners @@ -62,7 +61,7 @@ func (evsw *eventSwitch) AddListenerForEvent(listenerID, eventValue string, cb E return nil } -func (evsw *eventSwitch) FireEvent(ctx context.Context, event string, data EventData) { +func (evsw *eventSwitch) FireEvent(event string, data EventData) { // Get the eventCell evsw.mtx.RLock() eventCell := evsw.eventCells[event] @@ -73,12 +72,12 @@ func (evsw *eventSwitch) FireEvent(ctx context.Context, event string, data Event } // Fire event for all listeners in eventCell - eventCell.fireEvent(ctx, data) + eventCell.fireEvent(data) } //----------------------------------------------------------------------------- -type EventCallback func(ctx context.Context, data EventData) error +type EventCallback func(data EventData) error // eventCell handles keeping track of listener callbacks for a given event. type eventCell struct { @@ -98,7 +97,7 @@ func (cell *eventCell) addListener(listenerID string, cb EventCallback) { cell.listeners[listenerID] = cb } -func (cell *eventCell) fireEvent(ctx context.Context, data EventData) { +func (cell *eventCell) fireEvent(data EventData) { cell.mtx.RLock() eventCallbacks := make([]EventCallback, 0, len(cell.listeners)) for _, cb := range cell.listeners { @@ -107,7 +106,7 @@ func (cell *eventCell) fireEvent(ctx context.Context, data EventData) { cell.mtx.RUnlock() for _, cb := range eventCallbacks { - if err := cb(ctx, data); err != nil { + if err := cb(data); err != nil { // should we log or abort here? continue } diff --git a/libs/events/events_test.go b/libs/events/events_test.go index fba2feba8..17f8c56d1 100644 --- a/libs/events/events_test.go +++ b/libs/events/events_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/fortytw2/leaktest" "github.com/stretchr/testify/require" ) @@ -20,7 +21,7 @@ func TestAddListenerForEventFireOnce(t *testing.T) { messages := make(chan EventData) require.NoError(t, evsw.AddListenerForEvent("listener", "event", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case messages <- data: return nil @@ -28,7 +29,7 @@ func TestAddListenerForEventFireOnce(t *testing.T) { return ctx.Err() } })) - go evsw.FireEvent(ctx, "event", "data") + go evsw.FireEvent("event", "data") received := <-messages if received != "data" { t.Errorf("message received does not match: %v", received) @@ -48,7 +49,7 @@ func TestAddListenerForEventFireMany(t *testing.T) { numbers := make(chan uint64, 4) // subscribe one listener for one event require.NoError(t, evsw.AddListenerForEvent("listener", "event", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers <- data.(uint64): return nil @@ -75,6 +76,8 @@ func TestAddListenerForDifferentEvents(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + t.Cleanup(leaktest.Check(t)) + evsw := NewEventSwitch() doneSum := make(chan uint64) @@ -84,7 +87,7 @@ func TestAddListenerForDifferentEvents(t *testing.T) { numbers := make(chan uint64, 4) // subscribe one listener to three events require.NoError(t, evsw.AddListenerForEvent("listener", "event1", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers <- data.(uint64): return nil @@ -93,7 +96,7 @@ func TestAddListenerForDifferentEvents(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener", "event2", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers <- data.(uint64): return nil @@ -102,7 +105,7 @@ func TestAddListenerForDifferentEvents(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener", "event3", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers <- data.(uint64): return nil @@ -135,6 +138,8 @@ func TestAddDifferentListenerForDifferentEvents(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + t.Cleanup(leaktest.Check(t)) + evsw := NewEventSwitch() doneSum1 := make(chan uint64) @@ -146,7 +151,7 @@ func TestAddDifferentListenerForDifferentEvents(t *testing.T) { numbers2 := make(chan uint64, 4) // subscribe two listener to three events require.NoError(t, evsw.AddListenerForEvent("listener1", "event1", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers1 <- data.(uint64): return nil @@ -155,7 +160,7 @@ func TestAddDifferentListenerForDifferentEvents(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener1", "event2", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers1 <- data.(uint64): return nil @@ -164,7 +169,7 @@ func TestAddDifferentListenerForDifferentEvents(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener1", "event3", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers1 <- data.(uint64): return nil @@ -173,7 +178,7 @@ func TestAddDifferentListenerForDifferentEvents(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener2", "event2", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers2 <- data.(uint64): return nil @@ -182,7 +187,7 @@ func TestAddDifferentListenerForDifferentEvents(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener2", "event3", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers2 <- data.(uint64): return nil @@ -238,7 +243,7 @@ func TestManageListenersAsync(t *testing.T) { numbers2 := make(chan uint64, 4) // subscribe two listener to three events require.NoError(t, evsw.AddListenerForEvent("listener1", "event1", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers1 <- data.(uint64): return nil @@ -247,7 +252,7 @@ func TestManageListenersAsync(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener1", "event2", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers1 <- data.(uint64): return nil @@ -256,7 +261,7 @@ func TestManageListenersAsync(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener1", "event3", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers1 <- data.(uint64): return nil @@ -265,7 +270,7 @@ func TestManageListenersAsync(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener2", "event1", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers2 <- data.(uint64): return nil @@ -274,7 +279,7 @@ func TestManageListenersAsync(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener2", "event2", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers2 <- data.(uint64): return nil @@ -283,7 +288,7 @@ func TestManageListenersAsync(t *testing.T) { } })) require.NoError(t, evsw.AddListenerForEvent("listener2", "event3", - func(ctx context.Context, data EventData) error { + func(data EventData) error { select { case numbers2 <- data.(uint64): return nil @@ -303,7 +308,7 @@ func TestManageListenersAsync(t *testing.T) { eventNumber := r1.Intn(3) + 1 go evsw.AddListenerForEvent(fmt.Sprintf("listener%v", listenerNumber), //nolint:errcheck // ignore for tests fmt.Sprintf("event%v", eventNumber), - func(context.Context, EventData) error { return nil }) + func(EventData) error { return nil }) } } addListenersStress() @@ -358,7 +363,7 @@ func fireEvents(ctx context.Context, evsw Fireable, event string, doneChan chan break } - evsw.FireEvent(ctx, event, i) + evsw.FireEvent(event, i) sentSum += i } diff --git a/libs/time/mocks/source.go b/libs/time/mocks/source.go index a8e49b314..7878d86f5 100644 --- a/libs/time/mocks/source.go +++ b/libs/time/mocks/source.go @@ -3,9 +3,11 @@ package mocks import ( - time "time" + testing "testing" mock "github.com/stretchr/testify/mock" + + time "time" ) // Source is an autogenerated mock type for the Source type @@ -26,3 +28,13 @@ func (_m *Source) Now() time.Time { return r0 } + +// NewSource creates a new instance of Source. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewSource(t testing.TB) *Source { + mock := &Source{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/light/client_benchmark_test.go b/light/client_benchmark_test.go index 4a54e2b54..c92b70990 100644 --- a/light/client_benchmark_test.go +++ b/light/client_benchmark_test.go @@ -69,7 +69,7 @@ func BenchmarkSequence(b *testing.B) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - headers, vals, _ := genLightBlocksWithKeys(b, chainID, 1000, 100, 1, bTime) + headers, vals, _ := genLightBlocksWithKeys(b, 1000, 100, 1, bTime) benchmarkFullNode := newProviderBenchmarkImpl(headers, vals) genesisBlock, _ := benchmarkFullNode.LightBlock(ctx, 1) @@ -106,7 +106,7 @@ func BenchmarkBisection(b *testing.B) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - headers, vals, _ := genLightBlocksWithKeys(b, chainID, 1000, 100, 1, bTime) + headers, vals, _ := genLightBlocksWithKeys(b, 1000, 100, 1, bTime) benchmarkFullNode := newProviderBenchmarkImpl(headers, vals) genesisBlock, _ := benchmarkFullNode.LightBlock(ctx, 1) @@ -142,7 +142,7 @@ func BenchmarkBackwards(b *testing.B) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - headers, vals, _ := genLightBlocksWithKeys(b, chainID, 1000, 100, 1, bTime) + headers, vals, _ := genLightBlocksWithKeys(b, 1000, 100, 1, bTime) benchmarkFullNode := newProviderBenchmarkImpl(headers, vals) trustedBlock, _ := benchmarkFullNode.LightBlock(ctx, 0) diff --git a/light/client_test.go b/light/client_test.go index fbf8536ba..8ecd842c9 100644 --- a/light/client_test.go +++ b/light/client_test.go @@ -387,7 +387,7 @@ func TestClient(t *testing.T) { // the appropriate range numBlocks := int64(300) - mockHeaders, mockVals, _ := genLightBlocksWithKeys(t, chainID, numBlocks, 101, 2, bTime) + mockHeaders, mockVals, _ := genLightBlocksWithKeys(t, numBlocks, 101, 2, bTime) lastBlock := &types.LightBlock{SignedHeader: mockHeaders[numBlocks], ValidatorSet: mockVals[numBlocks]} mockNode := &provider_mocks.Provider{} @@ -773,7 +773,7 @@ func TestClient(t *testing.T) { logger := log.NewNopLogger() { - headers, vals, _ := genLightBlocksWithKeys(t, chainID, 9, 3, 0, bTime) + headers, vals, _ := genLightBlocksWithKeys(t, 9, 3, 0, bTime) delete(headers, 1) delete(headers, 2) delete(vals, 1) diff --git a/light/detector_test.go b/light/detector_test.go index 82b0d2de6..4a86b5b87 100644 --- a/light/detector_test.go +++ b/light/detector_test.go @@ -35,7 +35,7 @@ func TestLightClientAttackEvidence_Lunatic(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - witnessHeaders, witnessValidators, chainKeys := genLightBlocksWithKeys(t, chainID, latestHeight, valSize, 2, bTime) + witnessHeaders, witnessValidators, chainKeys := genLightBlocksWithKeys(t, latestHeight, valSize, 2, bTime) forgedKeys := chainKeys[divergenceHeight-1].ChangeKeys(3) // we change 3 out of the 5 validators (still 2/5 remain) forgedVals := forgedKeys.ToValidators(2, 0) @@ -153,7 +153,7 @@ func TestLightClientAttackEvidence_Equivocation(t *testing.T) { // validators don't change in this network (however we still use a map just for convenience) primaryValidators = make(map[int64]*types.ValidatorSet, testCase.latestHeight) ) - witnessHeaders, witnessValidators, chainKeys := genLightBlocksWithKeys(t, chainID, + witnessHeaders, witnessValidators, chainKeys := genLightBlocksWithKeys(t, testCase.latestHeight+1, valSize, 2, bTime) for height := int64(1); height <= testCase.latestHeight; height++ { if height < testCase.divergenceHeight { @@ -250,7 +250,7 @@ func TestLightClientAttackEvidence_ForwardLunatic(t *testing.T) { defer cancel() logger := log.NewNopLogger() - witnessHeaders, witnessValidators, chainKeys := genLightBlocksWithKeys(t, chainID, latestHeight, valSize, 2, bTime) + witnessHeaders, witnessValidators, chainKeys := genLightBlocksWithKeys(t, latestHeight, valSize, 2, bTime) for _, unusedHeader := range []int64{3, 5, 6, 8} { delete(witnessHeaders, unusedHeader) } @@ -401,13 +401,13 @@ func TestClientDivergentTraces1(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - headers, vals, _ := genLightBlocksWithKeys(t, chainID, 1, 5, 2, bTime) + headers, vals, _ := genLightBlocksWithKeys(t, 1, 5, 2, bTime) mockPrimary := mockNodeFromHeadersAndVals(headers, vals) mockPrimary.On("ID").Return("mockPrimary") firstBlock, err := mockPrimary.LightBlock(ctx, 1) require.NoError(t, err) - headers, vals, _ = genLightBlocksWithKeys(t, chainID, 1, 5, 2, bTime) + headers, vals, _ = genLightBlocksWithKeys(t, 1, 5, 2, bTime) mockWitness := mockNodeFromHeadersAndVals(headers, vals) mockWitness.On("ID").Return("mockWitness") @@ -439,7 +439,7 @@ func TestClientDivergentTraces2(t *testing.T) { defer cancel() logger := log.NewNopLogger() - headers, vals, _ := genLightBlocksWithKeys(t, chainID, 2, 5, 2, bTime) + headers, vals, _ := genLightBlocksWithKeys(t, 2, 5, 2, bTime) mockPrimaryNode := mockNodeFromHeadersAndVals(headers, vals) mockPrimaryNode.On("ID").Return("mockPrimaryNode") @@ -485,7 +485,7 @@ func TestClientDivergentTraces3(t *testing.T) { logger := log.NewNopLogger() // - primaryHeaders, primaryVals, _ := genLightBlocksWithKeys(t, chainID, 2, 5, 2, bTime) + primaryHeaders, primaryVals, _ := genLightBlocksWithKeys(t, 2, 5, 2, bTime) mockPrimary := mockNodeFromHeadersAndVals(primaryHeaders, primaryVals) mockPrimary.On("ID").Return("mockPrimary") @@ -495,7 +495,7 @@ func TestClientDivergentTraces3(t *testing.T) { firstBlock, err := mockPrimary.LightBlock(ctx, 1) require.NoError(t, err) - mockHeaders, mockVals, _ := genLightBlocksWithKeys(t, chainID, 2, 5, 2, bTime) + mockHeaders, mockVals, _ := genLightBlocksWithKeys(t, 2, 5, 2, bTime) mockHeaders[1] = primaryHeaders[1] mockVals[1] = primaryVals[1] mockWitness := mockNodeFromHeadersAndVals(mockHeaders, mockVals) @@ -530,7 +530,7 @@ func TestClientDivergentTraces4(t *testing.T) { logger := log.NewNopLogger() // - primaryHeaders, primaryVals, _ := genLightBlocksWithKeys(t, chainID, 2, 5, 2, bTime) + primaryHeaders, primaryVals, _ := genLightBlocksWithKeys(t, 2, 5, 2, bTime) mockPrimary := mockNodeFromHeadersAndVals(primaryHeaders, primaryVals) mockPrimary.On("ID").Return("mockPrimary") @@ -540,7 +540,7 @@ func TestClientDivergentTraces4(t *testing.T) { firstBlock, err := mockPrimary.LightBlock(ctx, 1) require.NoError(t, err) - witnessHeaders, witnessVals, _ := genLightBlocksWithKeys(t, chainID, 2, 5, 2, bTime) + witnessHeaders, witnessVals, _ := genLightBlocksWithKeys(t, 2, 5, 2, bTime) primaryHeaders[2] = witnessHeaders[2] primaryVals[2] = witnessVals[2] mockWitness := mockNodeFromHeadersAndVals(primaryHeaders, primaryVals) diff --git a/light/helpers_test.go b/light/helpers_test.go index 73ef2ae65..4d002d936 100644 --- a/light/helpers_test.go +++ b/light/helpers_test.go @@ -160,7 +160,6 @@ func (pkz privKeys) ChangeKeys(delta int) privKeys { // NOTE: Expected to have a large validator set size ~ 100 validators. func genLightBlocksWithKeys( t testing.TB, - chainID string, numBlocks int64, valSize int, valVariation float32, diff --git a/light/provider/mocks/provider.go b/light/provider/mocks/provider.go index 1b4e583de..e136046f9 100644 --- a/light/provider/mocks/provider.go +++ b/light/provider/mocks/provider.go @@ -7,6 +7,8 @@ import ( mock "github.com/stretchr/testify/mock" + testing "testing" + types "github.com/tendermint/tendermint/types" ) @@ -65,3 +67,13 @@ func (_m *Provider) ReportEvidence(_a0 context.Context, _a1 types.Evidence) erro return r0 } + +// NewProvider creates a new instance of Provider. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewProvider(t testing.TB) *Provider { + mock := &Provider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/light/rpc/mocks/light_client.go b/light/rpc/mocks/light_client.go index 347d14707..ea6d6a2d4 100644 --- a/light/rpc/mocks/light_client.go +++ b/light/rpc/mocks/light_client.go @@ -7,6 +7,8 @@ import ( mock "github.com/stretchr/testify/mock" + testing "testing" + time "time" types "github.com/tendermint/tendermint/types" @@ -115,3 +117,13 @@ func (_m *LightClient) VerifyLightBlockAtHeight(ctx context.Context, height int6 return r0, r1 } + +// NewLightClient creates a new instance of LightClient. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewLightClient(t testing.TB) *LightClient { + mock := &LightClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/node/node.go b/node/node.go index 8a4da29f2..5c63a3253 100644 --- a/node/node.go +++ b/node/node.go @@ -29,6 +29,7 @@ import ( rpccore "github.com/tendermint/tendermint/internal/rpc/core" sm "github.com/tendermint/tendermint/internal/state" "github.com/tendermint/tendermint/internal/state/indexer" + "github.com/tendermint/tendermint/internal/state/indexer/sink" "github.com/tendermint/tendermint/internal/statesync" "github.com/tendermint/tendermint/internal/store" "github.com/tendermint/tendermint/libs/log" @@ -168,15 +169,19 @@ func makeNode( Metrics: nodeMetrics.eventlog, }) if err != nil { - return nil, fmt.Errorf("initializing event log: %w", err) + return nil, combineCloseError(fmt.Errorf("initializing event log: %w", err), makeCloser(closers)) } } - - indexerService, eventSinks, err := createIndexerService( - cfg, dbProvider, eventBus, logger, genDoc.ChainID, nodeMetrics.indexer) + eventSinks, err := sink.EventSinksFromConfig(cfg, dbProvider, genDoc.ChainID) if err != nil { return nil, combineCloseError(err, makeCloser(closers)) } + indexerService := indexer.NewService(indexer.ServiceArgs{ + Sinks: eventSinks, + EventBus: eventBus, + Logger: logger.With("module", "txindex"), + Metrics: nodeMetrics.indexer, + }) privValidator, err := createPrivval(ctx, logger, cfg, genDoc, filePrivval) if err != nil { @@ -260,11 +265,8 @@ func makeNode( node.rpcEnv.EvidencePool = evPool node.evPool = evPool - mpReactor, mp, err := createMempoolReactor(logger, cfg, proxyApp, stateStore, nodeMetrics.mempool, + mpReactor, mp := createMempoolReactor(logger, cfg, proxyApp, stateStore, nodeMetrics.mempool, peerManager.Subscribe, node.router.OpenChannel, peerManager.GetHeight) - if err != nil { - return nil, combineCloseError(err, makeCloser(closers)) - } node.rpcEnv.Mempool = mp node.services = append(node.services, mpReactor) @@ -292,16 +294,32 @@ func makeNode( blockSync := !onlyValidatorIsUs(state, pubKey) waitSync := stateSync || blockSync - csReactor, csState, err := createConsensusReactor(ctx, - cfg, stateStore, blockExec, blockStore, mp, evPool, - privValidator, nodeMetrics.consensus, waitSync, eventBus, - peerManager, node.router.OpenChannel, logger, + csState, err := consensus.NewState(logger.With("module", "consensus"), + cfg.Consensus, + stateStore, + blockExec, + blockStore, + mp, + evPool, + eventBus, + consensus.StateMetrics(nodeMetrics.consensus), + consensus.SkipStateStoreBootstrap, ) if err != nil { return nil, combineCloseError(err, makeCloser(closers)) } - node.services = append(node.services, csReactor) node.rpcEnv.ConsensusState = csState + + csReactor := consensus.NewReactor( + logger, + csState, + node.router.OpenChannel, + peerManager.Subscribe, + eventBus, + waitSync, + nodeMetrics.consensus, + ) + node.services = append(node.services, csReactor) node.rpcEnv.ConsensusReactor = csReactor // Create the blockchain reactor. Note, we do not start block sync if we're @@ -370,6 +388,9 @@ func makeNode( )) if cfg.Mode == config.ModeValidator { + if privValidator != nil { + csState.SetPrivValidator(ctx, privValidator) + } node.rpcEnv.PubKey = pubKey } @@ -423,7 +444,7 @@ func (n *nodeImpl) OnStart(ctx context.Context) error { // Start Internal Services if n.config.RPC.PprofListenAddress != "" { - rpcCtx, rpcCancel := context.WithCancel(ctx) + signal := make(chan struct{}) srv := &http.Server{Addr: n.config.RPC.PprofListenAddress, Handler: nil} go func() { select { @@ -431,7 +452,7 @@ func (n *nodeImpl) OnStart(ctx context.Context) error { sctx, scancel := context.WithTimeout(context.Background(), time.Second) defer scancel() _ = srv.Shutdown(sctx) - case <-rpcCtx.Done(): + case <-signal: } }() @@ -440,7 +461,7 @@ func (n *nodeImpl) OnStart(ctx context.Context) error { if err := srv.ListenAndServe(); err != nil { n.logger.Error("pprof server error", "err", err) - rpcCancel() + close(signal) } }() } @@ -562,21 +583,21 @@ func (n *nodeImpl) startPrometheusServer(ctx context.Context, addr string) *http ), } - promCtx, promCancel := context.WithCancel(ctx) + signal := make(chan struct{}) go func() { select { case <-ctx.Done(): sctx, scancel := context.WithTimeout(context.Background(), time.Second) defer scancel() _ = srv.Shutdown(sctx) - case <-promCtx.Done(): + case <-signal: } }() go func() { if err := srv.ListenAndServe(); err != nil { n.logger.Error("Prometheus HTTP server ListenAndServe", "err", err) - promCancel() + close(signal) } }() @@ -699,7 +720,7 @@ func getRouterConfig(conf *config.Config, appClient abciclient.Client) p2p.Route if conf.FilterPeers && appClient != nil { opts.FilterPeerByID = func(ctx context.Context, id types.NodeID) error { - res, err := appClient.Query(ctx, abci.RequestQuery{ + res, err := appClient.Query(ctx, &abci.RequestQuery{ Path: fmt.Sprintf("/p2p/filter/id/%s", id), }) if err != nil { @@ -713,7 +734,7 @@ func getRouterConfig(conf *config.Config, appClient abciclient.Client) p2p.Route } opts.FilterPeerByIP = func(ctx context.Context, ip net.IP, port uint16) error { - res, err := appClient.Query(ctx, abci.RequestQuery{ + res, err := appClient.Query(ctx, &abci.RequestQuery{ Path: fmt.Sprintf("/p2p/filter/addr/%s", net.JoinHostPort(ip.String(), strconv.Itoa(int(port)))), }) if err != nil { diff --git a/node/node_test.go b/node/node_test.go index 2736ca818..cb6f65add 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -28,6 +28,7 @@ import ( "github.com/tendermint/tendermint/internal/pubsub" sm "github.com/tendermint/tendermint/internal/state" "github.com/tendermint/tendermint/internal/state/indexer" + "github.com/tendermint/tendermint/internal/state/indexer/sink" "github.com/tendermint/tendermint/internal/store" "github.com/tendermint/tendermint/internal/test/factory" "github.com/tendermint/tendermint/libs/log" @@ -454,7 +455,8 @@ func TestMaxProposalBlockSize(t *testing.T) { err = proxyApp.Start(ctx) require.NoError(t, err) - state, stateDB, _ := state(t, types.MaxVotesCount, int64(1)) + state, stateDB, privVals := state(t, types.MaxVotesCount, int64(1)) + stateStore := sm.NewStore(stateDB) blockStore := store.NewBlockStore(dbm.NewMemDB()) const maxBytes int64 = 1024 * 1024 * 2 @@ -537,17 +539,25 @@ func TestMaxProposalBlockSize(t *testing.T) { BlockID: blockID, } + votes := make([]*types.Vote, types.MaxVotesCount) + // add maximum amount of signatures to a single commit for i := 0; i < types.MaxVotesCount; i++ { + pubKey, err := privVals[i].GetPubKey(ctx) + require.NoError(t, err) + votes[i] = &types.Vote{ + ValidatorAddress: pubKey.Address(), + } commit.Signatures = append(commit.Signatures, cs) } block, err := blockExec.CreateProposalBlock( ctx, math.MaxInt64, - state, commit, + state, + commit, proposerAddr, - nil, + votes, ) require.NoError(t, err) partSet, err := block.MakePartSet(types.BlockPartSizeBytes) @@ -627,11 +637,9 @@ func TestNodeSetEventSink(t *testing.T) { genDoc, err := types.GenesisDocFromFile(cfg.GenesisFile()) require.NoError(t, err) - indexService, eventSinks, err := createIndexerService(cfg, - config.DefaultDBProvider, eventBus, logger, genDoc.ChainID, - indexer.NopMetrics()) + eventSinks, err := sink.EventSinksFromConfig(cfg, config.DefaultDBProvider, genDoc.ChainID) require.NoError(t, err) - t.Cleanup(indexService.Wait) + return eventSinks } cleanup := func(ns service.Service) func() { diff --git a/node/setup.go b/node/setup.go index 1057fb6fa..512b02901 100644 --- a/node/setup.go +++ b/node/setup.go @@ -23,7 +23,6 @@ import ( "github.com/tendermint/tendermint/internal/p2p/pex" sm "github.com/tendermint/tendermint/internal/state" "github.com/tendermint/tendermint/internal/state/indexer" - "github.com/tendermint/tendermint/internal/state/indexer/sink" "github.com/tendermint/tendermint/internal/statesync" "github.com/tendermint/tendermint/internal/store" "github.com/tendermint/tendermint/libs/log" @@ -95,29 +94,6 @@ func initDBs( return blockStore, stateDB, makeCloser(closers), nil } -func createIndexerService( - cfg *config.Config, - dbProvider config.DBProvider, - eventBus *eventbus.EventBus, - logger log.Logger, - chainID string, - metrics *indexer.Metrics, -) (*indexer.Service, []indexer.EventSink, error) { - eventSinks, err := sink.EventSinksFromConfig(cfg, dbProvider, chainID) - if err != nil { - return nil, nil, err - } - - indexerService := indexer.NewService(indexer.ServiceArgs{ - Sinks: eventSinks, - EventBus: eventBus, - Logger: logger.With("module", "txindex"), - Metrics: metrics, - }) - - return indexerService, eventSinks, nil -} - func logNodeStartupInfo(state sm.State, pubKey crypto.PubKey, logger log.Logger, mode string) { // Log the version info. logger.Info("Version info", @@ -172,7 +148,7 @@ func createMempoolReactor( peerEvents p2p.PeerEventSubscriber, chCreator p2p.ChannelCreator, peerHeight func(types.NodeID) int64, -) (service.Service, mempool.Mempool, error) { +) (service.Service, mempool.Mempool) { logger = logger.With("module", "mempool") mp := mempool.NewTxMempool( @@ -197,7 +173,7 @@ func createMempoolReactor( mp.EnableTxsAvailable() } - return reactor, mp, nil + return reactor, mp } func createEvidenceReactor( @@ -215,70 +191,13 @@ func createEvidenceReactor( if err != nil { return nil, nil, func() error { return nil }, fmt.Errorf("unable to initialize evidence db: %w", err) } - dbCloser := evidenceDB.Close logger = logger.With("module", "evidence") evidencePool := evidence.NewPool(logger, evidenceDB, store, blockStore, metrics, eventBus) + evidenceReactor := evidence.NewReactor(logger, chCreator, peerEvents, evidencePool) - evidenceReactor := evidence.NewReactor( - logger, - chCreator, - peerEvents, - evidencePool, - ) - - return evidenceReactor, evidencePool, dbCloser, nil -} - -func createConsensusReactor( - ctx context.Context, - cfg *config.Config, - store sm.Store, - blockExec *sm.BlockExecutor, - blockStore sm.BlockStore, - mp mempool.Mempool, - evidencePool *evidence.Pool, - privValidator types.PrivValidator, - csMetrics *consensus.Metrics, - waitSync bool, - eventBus *eventbus.EventBus, - peerManager *p2p.PeerManager, - chCreator p2p.ChannelCreator, - logger log.Logger, -) (*consensus.Reactor, *consensus.State, error) { - logger = logger.With("module", "consensus") - - consensusState, err := consensus.NewState(ctx, - logger, - cfg.Consensus, - store, - blockExec, - blockStore, - mp, - evidencePool, - eventBus, - consensus.StateMetrics(csMetrics), - consensus.SkipStateStoreBootstrap, - ) - if err != nil { - return nil, nil, err - } - - if privValidator != nil && cfg.Mode == config.ModeValidator { - consensusState.SetPrivValidator(ctx, privValidator) - } - - reactor := consensus.NewReactor( - logger, - consensusState, - chCreator, - peerManager.Subscribe, - eventBus, - waitSync, - csMetrics, - ) - return reactor, consensusState, nil + return evidenceReactor, evidencePool, evidenceDB.Close, nil } func createPeerManager( diff --git a/privval/file.go b/privval/file.go index 728e0dc67..1e31b8c78 100644 --- a/privval/file.go +++ b/privval/file.go @@ -117,6 +117,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. @@ -328,12 +336,7 @@ func (pv *FilePV) Save() error { // Reset resets all fields in the FilePV. // NOTE: Unsafe! func (pv *FilePV) Reset() error { - 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() return pv.Save() } @@ -369,6 +372,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. @@ -379,6 +388,8 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error { if bytes.Equal(signBytes, lss.SignBytes) { vote.Signature = lss.Signature } else { + // Compares the canonicalized votes (i.e. without vote extensions + // or vote extension signatures). timestamp, ok, err := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes) if err != nil { return err @@ -390,6 +401,12 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error { vote.Timestamp = timestamp vote.Signature = lss.Signature } + + // 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 nil } @@ -402,6 +419,8 @@ func (pv *FilePV) signVote(chainID string, vote *tmproto.Vote) error { return err } vote.Signature = sig + vote.ExtensionSignature = extSig + return nil } @@ -453,8 +472,10 @@ func (pv *FilePV) saveSigned(height int64, round int32, step int8, signBytes []b //----------------------------------------------------------------------------------------- -// 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, error) { 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 91c2e2a9b..30df335a2 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -21,20 +21,14 @@ import ( ) func TestGenLoadValidator(t *testing.T) { - 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, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "") - require.NoError(t, err) + privVal, tempKeyFileName, tempStateFileName := newTestFilePV(t) height := int64(100) privVal.LastSignState.Height = height require.NoError(t, privVal.Save()) addr := privVal.GetAddress() - privVal, err = LoadFilePV(tempKeyFile.Name(), tempStateFile.Name()) + privVal, err := LoadFilePV(tempKeyFileName, tempStateFileName) assert.NoError(t, err) 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") @@ -44,14 +38,8 @@ func TestResetValidator(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - 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, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "") - require.NoError(t, err) - 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) @@ -61,8 +49,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(ctx, "mychainid", vote.ToProto()) + vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID, nil) + err := privVal.SignVote(ctx, "mychainid", vote.ToProto()) assert.NoError(t, err, "expected no error signing vote") // priv val after signing is not same as empty @@ -160,13 +148,7 @@ func TestSignVote(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - 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, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "") - require.NoError(t, err) + privVal, _, _ := newTestFilePV(t) randbytes := tmrand.Bytes(tmhash.Size) randbytes2 := tmrand.Bytes(tmhash.Size) @@ -180,10 +162,10 @@ 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(ctx, "mychainid", v) + err := privVal.SignVote(ctx, "mychainid", v) assert.NoError(t, err, "expected no error signing vote") // try to sign the same vote again; should be fine @@ -192,10 +174,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 { @@ -215,13 +197,7 @@ func TestSignProposal(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - 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, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "") - require.NoError(t, err) + privVal, _, _ := newTestFilePV(t) randbytes := tmrand.Bytes(tmhash.Size) randbytes2 := tmrand.Bytes(tmhash.Size) @@ -237,7 +213,7 @@ func TestSignProposal(t *testing.T) { proposal := newProposal(height, round, block1, ts) pbp := proposal.ToProto() - err = privVal.SignProposal(ctx, "mychainid", pbp) + err := privVal.SignProposal(ctx, "mychainid", pbp) assert.NoError(t, err, "expected no error signing proposal") // try to sign the same proposal again; should be fine @@ -278,7 +254,7 @@ 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(ctx, "mychainid", v) require.NoError(t, err, "expected no error signing vote") @@ -300,8 +276,69 @@ func TestDifferByTimestamp(t *testing.T) { } } +func TestVoteExtensionsAreAlwaysSigned(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + privVal, _, _ := newTestFilePV(t) + pubKey, err := privVal.GetPubKey(ctx) + 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(ctx, "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(ctx, "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(ctx, "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, @@ -310,6 +347,7 @@ func newVote(addr types.Address, idx int32, height int64, round int32, Type: typ, Timestamp: tmtime.Now(), BlockID: blockID, + Extension: extension, } } @@ -321,3 +359,15 @@ func newProposal(height int64, round int32, blockID types.BlockID, t time.Time) Timestamp: t, } } + +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, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "") + require.NoError(t, err) + + return privVal, tempKeyFile.Name(), tempStateFile.Name() +} diff --git a/privval/msgs_test.go b/privval/msgs_test.go index bbd3f6319..20e73762c 100644 --- a/privval/msgs_test.go +++ b/privval/msgs_test.go @@ -22,23 +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, - VoteExtension: types.VoteExtension{ - AppDataToSign: []byte("app_data_signed"), - AppDataSelfAuthenticating: []byte("app_data_self_authenticating"), - }, + Extension: []byte("extension"), } } @@ -87,8 +78,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}, "1aa8010aa501080110031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a2a0608f49a8ded0532146af1f4111082efb388211bc72c55bcd61e9ac3d538d5bb034a2f0a0f6170705f646174615f7369676e6564121c6170705f646174615f73656c665f61757468656e7469636174696e67"}, - {"Vote Response", &privproto.SignedVoteResponse{Vote: *votepb, Error: nil}, "22a8010aa501080110031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a2a0608f49a8ded0532146af1f4111082efb388211bc72c55bcd61e9ac3d538d5bb034a2f0a0f6170705f646174615f7369676e6564121c6170705f646174615f73656c665f61757468656e7469636174696e67"}, + {"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 f9fc73b48..d8143feb3 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -3,8 +3,6 @@ package tendermint.abci; option go_package = "github.com/tendermint/tendermint/abci/types"; -// For more information on gogo.proto, see: -// https://github.com/gogo/protobuf/blob/master/extensions.md import "tendermint/crypto/proof.proto"; import "tendermint/types/types.proto"; import "tendermint/crypto/keys.proto"; @@ -354,7 +352,6 @@ message ResponseExtendVote { bytes vote_extension = 1; } - message ResponseVerifyVoteExtension { VerifyStatus status = 1; @@ -383,8 +380,14 @@ 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 { - int32 round = 1; + // The round at which the block proposer decided in the previous height. + int32 round = 1; + // List of validators' addresses in the last validator set with their voting + // information, including vote extensions. repeated ExtendedVoteInfo votes = 2 [(gogoproto.nullable) = false]; } @@ -465,22 +468,14 @@ message VoteInfo { // ExtendedVoteInfo message ExtendedVoteInfo { - Validator validator = 1 [(gogoproto.nullable) = false]; - bool signed_last_block = 2; - bytes vote_extension = 3; + // 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; } -// CanonicalVoteExtension -// TODO: move this to core Tendermint data structures -message CanonicalVoteExtension { - bytes extension = 1; - int64 height = 2; - int32 round = 3; - string chain_id = 4; - bytes address = 5; -} - - enum MisbehaviorType { UNKNOWN = 0; DUPLICATE_VOTE = 1; diff --git a/proto/tendermint/abci/types.proto.intermediate b/proto/tendermint/abci/types.proto.intermediate deleted file mode 100644 index c752cd87b..000000000 --- a/proto/tendermint/abci/types.proto.intermediate +++ /dev/null @@ -1,521 +0,0 @@ -syntax = "proto3"; -package tendermint.abci; - -import "tendermint/crypto/proof.proto"; -import "tendermint/types/types.proto"; -import "tendermint/crypto/keys.proto"; -import "tendermint/types/params.proto"; -import "google/protobuf/timestamp.proto"; -import "gogoproto/gogo.proto"; - -// This file is a temporary workaround to enable development during the ABCI++ -// project. This file should be deleted and any references to it removed when -// the ongoing work on ABCI++ is completed. -// -// For the duration of ABCI++, this file should be able to build the `abci/types/types.pb.go` -// file. Any changes that update that file must come as a result of a change in -// this .proto file. -// For more information, see https://github.com/tendermint/tendermint/issues/8066 - -//---------------------------------------- -// Request types - -message Request { - oneof value { - RequestEcho echo = 1; - RequestFlush flush = 2; - RequestInfo info = 3; - RequestInitChain init_chain = 4; - RequestQuery query = 5; - RequestBeginBlock begin_block = 6 [deprecated = true]; - RequestCheckTx check_tx = 7; - RequestDeliverTx deliver_tx = 8 [deprecated = true]; - RequestEndBlock end_block = 9 [deprecated = true]; - RequestCommit commit = 10; - RequestListSnapshots list_snapshots = 11; - RequestOfferSnapshot offer_snapshot = 12; - RequestLoadSnapshotChunk load_snapshot_chunk = 13; - RequestApplySnapshotChunk apply_snapshot_chunk = 14; - RequestPrepareProposal prepare_proposal = 15; - RequestProcessProposal process_proposal = 16; - RequestExtendVote extend_vote = 17; - RequestVerifyVoteExtension verify_vote_extension = 18; - RequestFinalizeBlock finalize_block = 19; - } -} - -message RequestEcho { - string message = 1; -} - -message RequestFlush {} - -message RequestInfo { - string version = 1; - uint64 block_version = 2; - uint64 p2p_version = 3; - string abci_version = 4; -} - -message RequestInitChain { - google.protobuf.Timestamp time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - string chain_id = 2; - tendermint.types.ConsensusParams consensus_params = 3; - repeated ValidatorUpdate validators = 4 [(gogoproto.nullable) = false]; - bytes app_state_bytes = 5; - int64 initial_height = 6; -} - -message RequestQuery { - bytes data = 1; - string path = 2; - int64 height = 3; - bool prove = 4; -} - -message RequestBeginBlock { - bytes hash = 1; - tendermint.types.Header header = 2 [(gogoproto.nullable) = false]; - CommitInfo last_commit_info = 3 [(gogoproto.nullable) = false]; - repeated Misbehavior byzantine_validators = 4 [(gogoproto.nullable) = false]; -} - -enum CheckTxType { - NEW = 0 [(gogoproto.enumvalue_customname) = "New"]; - RECHECK = 1 [(gogoproto.enumvalue_customname) = "Recheck"]; -} - -message RequestCheckTx { - bytes tx = 1; - CheckTxType type = 2; -} - -message RequestDeliverTx { - bytes tx = 1; -} - -message RequestEndBlock { - int64 height = 1; -} - -message RequestCommit {} - -// lists available snapshots -message RequestListSnapshots {} - -// offers a snapshot to the application -message RequestOfferSnapshot { - Snapshot snapshot = 1; // snapshot offered by peers - bytes app_hash = 2; // light client-verified app hash for snapshot height -} - -// loads a snapshot chunk -message RequestLoadSnapshotChunk { - uint64 height = 1; - uint32 format = 2; - uint32 chunk = 3; -} - -// Applies a snapshot chunk -message RequestApplySnapshotChunk { - uint32 index = 1; - bytes chunk = 2; - string sender = 3; -} - -message RequestPrepareProposal { - // the modified transactions cannot exceed this size. - int64 max_tx_bytes = 1; - // txs is an array of transactions that will be included in a block, - // sent to the app for possible modifications. - repeated bytes txs = 2; - ExtendedCommitInfo local_last_commit = 3 [(gogoproto.nullable) = false]; - repeated Misbehavior byzantine_validators = 4 [(gogoproto.nullable) = false]; - int64 height = 5; - google.protobuf.Timestamp time = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes next_validators_hash = 7; - // address of the public key of the validator proposing the block. - bytes proposer_address = 8; -} - -message RequestProcessProposal { - repeated bytes txs = 1; - CommitInfo proposed_last_commit = 2 [(gogoproto.nullable) = false]; - repeated Misbehavior byzantine_validators = 3 [(gogoproto.nullable) = false]; - // hash is the merkle root hash of the fields of the proposed block. - bytes hash = 4; - int64 height = 5; - google.protobuf.Timestamp time = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes next_validators_hash = 7; - // address of the public key of the original proposer of the block. - bytes proposer_address = 8; -} - -// Extends a vote with application-side injection -message RequestExtendVote { - types.Vote vote = 1; -} - -// Verify the vote extension -message RequestVerifyVoteExtension { - types.Vote vote = 1; -} - -message RequestFinalizeBlock { - repeated bytes txs = 1; - CommitInfo decided_last_commit = 2 [(gogoproto.nullable) = false]; - repeated Misbehavior byzantine_validators = 3 [(gogoproto.nullable) = false]; - // hash is the merkle root hash of the fields of the proposed block. - bytes hash = 4; - int64 height = 5; - google.protobuf.Timestamp time = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes next_validators_hash = 7; - // proposer_address is the address of the public key of the original proposer of the block. - bytes proposer_address = 8; -} - -//---------------------------------------- -// Response types - -message Response { - oneof value { - ResponseException exception = 1; - ResponseEcho echo = 2; - ResponseFlush flush = 3; - ResponseInfo info = 4; - ResponseInitChain init_chain = 5; - ResponseQuery query = 6; - ResponseBeginBlock begin_block = 7 [deprecated = true]; - ResponseCheckTx check_tx = 8; - ResponseDeliverTx deliver_tx = 9 [deprecated = true]; - ResponseEndBlock end_block = 10 [deprecated = true]; - ResponseCommit commit = 11; - ResponseListSnapshots list_snapshots = 12; - ResponseOfferSnapshot offer_snapshot = 13; - ResponseLoadSnapshotChunk load_snapshot_chunk = 14; - ResponseApplySnapshotChunk apply_snapshot_chunk = 15; - ResponsePrepareProposal prepare_proposal = 16; - ResponseProcessProposal process_proposal = 17; - ResponseExtendVote extend_vote = 18; - ResponseVerifyVoteExtension verify_vote_extension = 19; - ResponseFinalizeBlock finalize_block = 20; - } -} - -// nondeterministic -message ResponseException { - string error = 1; -} - -message ResponseEcho { - string message = 1; -} - -message ResponseFlush {} - -message ResponseInfo { - string data = 1; - - // this is the software version of the application. TODO: remove? - string version = 2; - uint64 app_version = 3; - - int64 last_block_height = 4; - bytes last_block_app_hash = 5; -} - -message ResponseInitChain { - tendermint.types.ConsensusParams consensus_params = 1; - repeated ValidatorUpdate validators = 2 [(gogoproto.nullable) = false]; - bytes app_hash = 3; -} - -message ResponseQuery { - uint32 code = 1; - // bytes data = 2; // use "value" instead. - string log = 3; // nondeterministic - string info = 4; // nondeterministic - int64 index = 5; - bytes key = 6; - bytes value = 7; - tendermint.crypto.ProofOps proof_ops = 8; - int64 height = 9; - string codespace = 10; -} - -message ResponseBeginBlock { - repeated Event events = 1 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; -} - -message ResponseCheckTx { - uint32 code = 1; - bytes data = 2; - string log = 3; // nondeterministic - string info = 4; // nondeterministic - int64 gas_wanted = 5; - int64 gas_used = 6; - repeated Event events = 7 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; - string codespace = 8; - string sender = 9; - int64 priority = 10; - - // mempool_error is set by Tendermint. - - // ABCI applications creating a ResponseCheckTX should not set mempool_error. - string mempool_error = 11; -} - -message ResponseDeliverTx { - uint32 code = 1; - bytes data = 2; - string log = 3; // nondeterministic - string info = 4; // nondeterministic - int64 gas_wanted = 5 [json_name = "gas_wanted"]; - int64 gas_used = 6 [json_name = "gas_used"]; - repeated Event events = 7 - [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; // nondeterministic - string codespace = 8; -} - -message ResponseEndBlock { - repeated ValidatorUpdate validator_updates = 1 [(gogoproto.nullable) = false]; - tendermint.types.ConsensusParams consensus_param_updates = 2; - repeated Event events = 3 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; -} - -message ResponseCommit { - // reserve 1 - bytes data = 2; - int64 retain_height = 3; -} - -message ResponseListSnapshots { - repeated Snapshot snapshots = 1; -} - -message ResponseOfferSnapshot { - Result result = 1; - - enum Result { - UNKNOWN = 0; // Unknown result, abort all snapshot restoration - ACCEPT = 1; // Snapshot accepted, apply chunks - ABORT = 2; // Abort all snapshot restoration - REJECT = 3; // Reject this specific snapshot, try others - REJECT_FORMAT = 4; // Reject all snapshots of this format, try others - REJECT_SENDER = 5; // Reject all snapshots from the sender(s), try others - } -} - -message ResponseLoadSnapshotChunk { - bytes chunk = 1; -} - -message ResponseApplySnapshotChunk { - Result result = 1; - repeated uint32 refetch_chunks = 2; // Chunks to refetch and reapply - repeated string reject_senders = 3; // Chunk senders to reject and ban - - enum Result { - UNKNOWN = 0; // Unknown result, abort all snapshot restoration - ACCEPT = 1; // Chunk successfully accepted - ABORT = 2; // Abort all snapshot restoration - RETRY = 3; // Retry chunk (combine with refetch and reject) - RETRY_SNAPSHOT = 4; // Retry snapshot (combine with refetch and reject) - REJECT_SNAPSHOT = 5; // Reject this snapshot, try others - } -} - -message ResponsePrepareProposal { - repeated TxRecord tx_records = 1; - bytes app_hash = 2; - repeated ExecTxResult tx_results = 3; - repeated ValidatorUpdate validator_updates = 4; - tendermint.types.ConsensusParams consensus_param_updates = 5; -} - -message ResponseProcessProposal { - ProposalStatus status = 1; - bytes app_hash = 2; - repeated ExecTxResult tx_results = 3; - repeated ValidatorUpdate validator_updates = 4; - tendermint.types.ConsensusParams consensus_param_updates = 5; - - enum ProposalStatus { - UNKNOWN = 0; - ACCEPT = 1; - REJECT = 2; - } -} - -message ResponseExtendVote { - tendermint.types.VoteExtension vote_extension = 1; -} - -message ResponseVerifyVoteExtension { - VerifyStatus status = 1; - - enum VerifyStatus { - UNKNOWN = 0; - ACCEPT = 1; - REJECT = 2; - } -} - -message ResponseFinalizeBlock { - repeated Event events = 1 - [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; - repeated ExecTxResult tx_results = 2; - repeated ValidatorUpdate validator_updates = 3 [(gogoproto.nullable) = false]; - tendermint.types.ConsensusParams consensus_param_updates = 4; - bytes app_hash = 5; - int64 retain_height = 6; -} - -//---------------------------------------- -// Misc. - -message CommitInfo { - int32 round = 1; - repeated VoteInfo votes = 2 [(gogoproto.nullable) = false]; -} - -message ExtendedCommitInfo { - int32 round = 1; - repeated ExtendedVoteInfo votes = 2 [(gogoproto.nullable) = false]; -} - -// Event allows application developers to attach additional information to -// ResponseBeginBlock, ResponseEndBlock, ResponseCheckTx and ResponseDeliverTx. -// Later, transactions may be queried using these events. -message Event { - string type = 1; - repeated EventAttribute attributes = 2 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "attributes,omitempty"]; -} - -// EventAttribute is a single key-value pair, associated with an event. -message EventAttribute { - string key = 1; - string value = 2; - bool index = 3; // nondeterministic -} - -// ExecTxResult contains results of executing one individual transaction. -// -// * Its structure is equivalent to #ResponseDeliverTx which will be deprecated/deleted -message ExecTxResult { - uint32 code = 1; - bytes data = 2; - string log = 3; // nondeterministic - string info = 4; // nondeterministic - int64 gas_wanted = 5; - int64 gas_used = 6; - repeated Event events = 7 - [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; // nondeterministic - string codespace = 8; -} - -// TxResult contains results of executing the transaction. -// -// One usage is indexing transaction results. -message TxResult { - int64 height = 1; - uint32 index = 2; - bytes tx = 3; - ExecTxResult result = 4 [(gogoproto.nullable) = false]; -} - -message TxRecord { - TxAction action = 1; - bytes tx = 2; - - // TxAction contains App-provided information on what to do with a transaction that is part of a raw proposal - enum TxAction { - UNKNOWN = 0; // Unknown action - UNMODIFIED = 1; // The Application did not modify this transaction. - ADDED = 2; // The Application added this transaction. - REMOVED = 3; // The Application wants this transaction removed from the proposal and the mempool. - } -} - -//---------------------------------------- -// 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; - reserved 3; // Placeholder for tendermint_signed_extension in v0.37 - reserved 4; // Placeholder for app_signed_extension in v0.37 -} - -message ExtendedVoteInfo { - Validator validator = 1 [(gogoproto.nullable) = false]; - bool signed_last_block = 2; - bytes vote_extension = 3; -} - -enum MisbehaviorType { - UNKNOWN = 0; - DUPLICATE_VOTE = 1; - LIGHT_CLIENT_ATTACK = 2; -} - -message Misbehavior { - MisbehaviorType type = 1; - // The offending validator - Validator validator = 2 [(gogoproto.nullable) = false]; - // The height when the offense occurred - int64 height = 3; - // The corresponding time where the offense occurred - google.protobuf.Timestamp time = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - // Total voting power of the validator set in case the ABCI application does - // not store historical validators. - // https://github.com/tendermint/tendermint/issues/4581 - int64 total_voting_power = 5; -} - -//---------------------------------------- -// State Sync Types - -message Snapshot { - uint64 height = 1; // The height at which the snapshot was taken - uint32 format = 2; // The application-specific snapshot format - uint32 chunks = 3; // Number of chunks in the snapshot - bytes hash = 4; // Arbitrary snapshot hash, equal only if identical - bytes metadata = 5; // Arbitrary application metadata -} - -//---------------------------------------- -// Service Definition - -service ABCIApplication { - rpc Echo(RequestEcho) returns (ResponseEcho); - rpc Flush(RequestFlush) returns (ResponseFlush); - rpc Info(RequestInfo) returns (ResponseInfo); - rpc CheckTx(RequestCheckTx) returns (ResponseCheckTx); - rpc Query(RequestQuery) returns (ResponseQuery); - rpc Commit(RequestCommit) returns (ResponseCommit); - rpc InitChain(RequestInitChain) returns (ResponseInitChain); - rpc ListSnapshots(RequestListSnapshots) returns (ResponseListSnapshots); - rpc OfferSnapshot(RequestOfferSnapshot) returns (ResponseOfferSnapshot); - rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) returns (ResponseLoadSnapshotChunk); - rpc ApplySnapshotChunk(RequestApplySnapshotChunk) 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); -} diff --git a/proto/tendermint/types/canonical.pb.go b/proto/tendermint/types/canonical.pb.go index 0cd7386f7..50c0c84fa 100644 --- a/proto/tendermint/types/canonical.pb.go +++ b/proto/tendermint/types/canonical.pb.go @@ -225,13 +225,12 @@ 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"` - VoteExtension *VoteExtensionToSign `protobuf:"bytes,7,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,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"` } func (m *CanonicalVote) Reset() { *m = CanonicalVote{} } @@ -309,57 +308,121 @@ func (m *CanonicalVote) GetChainID() string { return "" } -func (m *CanonicalVote) GetVoteExtension() *VoteExtensionToSign { +// 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.VoteExtension + 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{ - // 522 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x94, 0x3f, 0x6f, 0xd3, 0x40, - 0x18, 0xc6, 0xe3, 0xd4, 0x49, 0x9c, 0x4b, 0x53, 0xc2, 0xa9, 0xaa, 0xac, 0x08, 0xd9, 0x96, 0x25, - 0x90, 0x59, 0x6c, 0x29, 0x1d, 0xd8, 0x5d, 0x90, 0x08, 0x2a, 0xa2, 0x5c, 0xa3, 0x0e, 0x2c, 0xd6, - 0xc5, 0x3e, 0x6c, 0x0b, 0xc7, 0x67, 0xd9, 0x97, 0x8a, 0x2e, 0x7c, 0x86, 0x7e, 0xac, 0x8e, 0x1d, - 0x61, 0x09, 0xc8, 0xf9, 0x12, 0x8c, 0xe8, 0xce, 0x49, 0x1c, 0x25, 0xc0, 0x02, 0xea, 0x12, 0xbd, - 0x7f, 0x1e, 0xbf, 0xef, 0xa3, 0xdf, 0xab, 0x1c, 0x30, 0x18, 0x49, 0x03, 0x92, 0xcf, 0xe2, 0x94, - 0x39, 0xec, 0x26, 0x23, 0x85, 0xe3, 0xe3, 0x94, 0xa6, 0xb1, 0x8f, 0x13, 0x3b, 0xcb, 0x29, 0xa3, - 0x70, 0x50, 0x2b, 0x6c, 0xa1, 0x18, 0x1e, 0x87, 0x34, 0xa4, 0xa2, 0xe9, 0xf0, 0xa8, 0xd2, 0x0d, - 0x9f, 0xec, 0x4d, 0x12, 0xbf, 0xab, 0xae, 0x1e, 0x52, 0x1a, 0x26, 0xc4, 0x11, 0xd9, 0x74, 0xfe, - 0xd1, 0x61, 0xf1, 0x8c, 0x14, 0x0c, 0xcf, 0xb2, 0x4a, 0x60, 0x7e, 0x01, 0x83, 0xb3, 0xf5, 0x66, - 0x37, 0xa1, 0xfe, 0xa7, 0xf1, 0x4b, 0x08, 0x81, 0x1c, 0xe1, 0x22, 0x52, 0x25, 0x43, 0xb2, 0x0e, - 0x91, 0x88, 0xe1, 0x15, 0x78, 0x94, 0xe1, 0x9c, 0x79, 0x05, 0x61, 0x5e, 0x44, 0x70, 0x40, 0x72, - 0xb5, 0x69, 0x48, 0x56, 0x6f, 0x64, 0xd9, 0xbb, 0x46, 0xed, 0xcd, 0xc0, 0x0b, 0x9c, 0xb3, 0x4b, - 0xc2, 0x5e, 0x0b, 0xbd, 0x2b, 0xdf, 0x2d, 0xf4, 0x06, 0xea, 0x67, 0xdb, 0x45, 0xd3, 0x05, 0x27, - 0xbf, 0x97, 0xc3, 0x63, 0xd0, 0x62, 0x94, 0xe1, 0x44, 0xd8, 0xe8, 0xa3, 0x2a, 0xd9, 0x78, 0x6b, - 0xd6, 0xde, 0xcc, 0x6f, 0x4d, 0xf0, 0xb8, 0x1e, 0x92, 0xd3, 0x8c, 0x16, 0x38, 0x81, 0xa7, 0x40, - 0xe6, 0x76, 0xc4, 0xe7, 0x47, 0x23, 0x7d, 0xdf, 0xe6, 0x65, 0x1c, 0xa6, 0x24, 0x78, 0x5b, 0x84, - 0x93, 0x9b, 0x8c, 0x20, 0x21, 0x86, 0x27, 0xa0, 0x1d, 0x91, 0x38, 0x8c, 0x98, 0x58, 0x30, 0x40, - 0xab, 0x8c, 0x9b, 0xc9, 0xe9, 0x3c, 0x0d, 0xd4, 0x03, 0x51, 0xae, 0x12, 0xf8, 0x1c, 0x74, 0x33, - 0x9a, 0x78, 0x55, 0x47, 0x36, 0x24, 0xeb, 0xc0, 0x3d, 0x2c, 0x17, 0xba, 0x72, 0xf1, 0xee, 0x1c, - 0xf1, 0x1a, 0x52, 0x32, 0x9a, 0x88, 0x08, 0xbe, 0x01, 0xca, 0x94, 0xe3, 0xf5, 0xe2, 0x40, 0x6d, - 0x09, 0x70, 0xe6, 0x5f, 0xc0, 0xad, 0x2e, 0xe1, 0xf6, 0xca, 0x85, 0xde, 0x59, 0x25, 0xa8, 0x23, - 0x06, 0x8c, 0x03, 0xe8, 0x82, 0xee, 0xe6, 0x8c, 0x6a, 0x5b, 0x0c, 0x1b, 0xda, 0xd5, 0xa1, 0xed, - 0xf5, 0xa1, 0xed, 0xc9, 0x5a, 0xe1, 0x2a, 0x9c, 0xfb, 0xed, 0x77, 0x5d, 0x42, 0xf5, 0x67, 0xf0, - 0x19, 0x50, 0xfc, 0x08, 0xc7, 0x29, 0xf7, 0xd3, 0x31, 0x24, 0xab, 0x5b, 0xed, 0x3a, 0xe3, 0x35, - 0xbe, 0x4b, 0x34, 0xc7, 0x81, 0xf9, 0xb3, 0x09, 0xfa, 0x1b, 0x5b, 0x57, 0x94, 0x91, 0x87, 0xe0, - 0xba, 0x0d, 0x4b, 0xfe, 0x9f, 0xb0, 0x5a, 0xff, 0x0e, 0xab, 0xfd, 0x67, 0x58, 0xf0, 0x1c, 0x1c, - 0x5d, 0x53, 0x46, 0x3c, 0xf2, 0x99, 0x91, 0xb4, 0x88, 0x69, 0x2a, 0xd0, 0xf6, 0x46, 0x4f, 0xf7, - 0xdd, 0x73, 0x94, 0xaf, 0xd6, 0xb2, 0x09, 0xe5, 0xcc, 0x50, 0xff, 0x7a, 0xbb, 0xe8, 0xbe, 0xbf, - 0x2b, 0x35, 0xe9, 0xbe, 0xd4, 0xa4, 0x1f, 0xa5, 0x26, 0xdd, 0x2e, 0xb5, 0xc6, 0xfd, 0x52, 0x6b, - 0x7c, 0x5d, 0x6a, 0x8d, 0x0f, 0x2f, 0xc2, 0x98, 0x45, 0xf3, 0xa9, 0xed, 0xd3, 0x99, 0xb3, 0xfd, - 0xf7, 0xaf, 0xc3, 0xea, 0x99, 0xd8, 0x7d, 0x1a, 0xa6, 0x6d, 0x51, 0x3f, 0xfd, 0x15, 0x00, 0x00, - 0xff, 0xff, 0x4e, 0x61, 0xcb, 0xc4, 0x7f, 0x04, 0x00, 0x00, + // 523 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0xc1, 0x6e, 0xd3, 0x40, + 0x10, 0x8d, 0x53, 0x27, 0xb1, 0xb7, 0x0d, 0x84, 0x55, 0x55, 0x99, 0xa8, 0xb2, 0x2d, 0x1f, 0x90, + 0xb9, 0xd8, 0x52, 0x7b, 0xe0, 0xee, 0x82, 0x44, 0x10, 0x88, 0xe2, 0x56, 0x3d, 0x70, 0x89, 0x36, + 0xf6, 0x62, 0x5b, 0x38, 0xde, 0x95, 0xbd, 0x91, 0xe8, 0x05, 0x7e, 0xa1, 0xdf, 0xc1, 0x97, 0xf4, + 0xd8, 0x23, 0x5c, 0x02, 0x72, 0x7e, 0x04, 0xed, 0xda, 0xb1, 0xad, 0x16, 0x55, 0x42, 0x20, 0x2e, + 0xd1, 0xcc, 0x9b, 0xb7, 0x33, 0x4f, 0x6f, 0xe2, 0x01, 0x26, 0xc3, 0x59, 0x88, 0xf3, 0x65, 0x92, + 0x31, 0x97, 0x5d, 0x52, 0x5c, 0xb8, 0x01, 0xca, 0x48, 0x96, 0x04, 0x28, 0x75, 0x68, 0x4e, 0x18, + 0x81, 0x93, 0x96, 0xe1, 0x08, 0xc6, 0x74, 0x3f, 0x22, 0x11, 0x11, 0x45, 0x97, 0x47, 0x15, 0x6f, + 0x7a, 0x78, 0xa7, 0x93, 0xf8, 0xad, 0xab, 0x46, 0x44, 0x48, 0x94, 0x62, 0x57, 0x64, 0x8b, 0xd5, + 0x07, 0x97, 0x25, 0x4b, 0x5c, 0x30, 0xb4, 0xa4, 0x15, 0xc1, 0xfa, 0x0c, 0x26, 0x27, 0xdb, 0xc9, + 0x5e, 0x4a, 0x82, 0x8f, 0xb3, 0xe7, 0x10, 0x02, 0x39, 0x46, 0x45, 0xac, 0x49, 0xa6, 0x64, 0xef, + 0xf9, 0x22, 0x86, 0x17, 0xe0, 0x21, 0x45, 0x39, 0x9b, 0x17, 0x98, 0xcd, 0x63, 0x8c, 0x42, 0x9c, + 0x6b, 0x7d, 0x53, 0xb2, 0x77, 0x8f, 0x6c, 0xe7, 0xb6, 0x50, 0xa7, 0x69, 0x78, 0x8a, 0x72, 0x76, + 0x86, 0xd9, 0x4b, 0xc1, 0xf7, 0xe4, 0xeb, 0xb5, 0xd1, 0xf3, 0xc7, 0xb4, 0x0b, 0x5a, 0x1e, 0x38, + 0xf8, 0x3d, 0x1d, 0xee, 0x83, 0x01, 0x23, 0x0c, 0xa5, 0x42, 0xc6, 0xd8, 0xaf, 0x92, 0x46, 0x5b, + 0xbf, 0xd5, 0x66, 0x7d, 0xef, 0x83, 0x47, 0x6d, 0x93, 0x9c, 0x50, 0x52, 0xa0, 0x14, 0x1e, 0x03, + 0x99, 0xcb, 0x11, 0xcf, 0x1f, 0x1c, 0x19, 0x77, 0x65, 0x9e, 0x25, 0x51, 0x86, 0xc3, 0x37, 0x45, + 0x74, 0x7e, 0x49, 0xb1, 0x2f, 0xc8, 0xf0, 0x00, 0x0c, 0x63, 0x9c, 0x44, 0x31, 0x13, 0x03, 0x26, + 0x7e, 0x9d, 0x71, 0x31, 0x39, 0x59, 0x65, 0xa1, 0xb6, 0x23, 0xe0, 0x2a, 0x81, 0x4f, 0x81, 0x4a, + 0x49, 0x3a, 0xaf, 0x2a, 0xb2, 0x29, 0xd9, 0x3b, 0xde, 0x5e, 0xb9, 0x36, 0x94, 0xd3, 0xb7, 0xaf, + 0x7d, 0x8e, 0xf9, 0x0a, 0x25, 0xa9, 0x88, 0xe0, 0x2b, 0xa0, 0x2c, 0xb8, 0xbd, 0xf3, 0x24, 0xd4, + 0x06, 0xc2, 0x38, 0xeb, 0x1e, 0xe3, 0xea, 0x4d, 0x78, 0xbb, 0xe5, 0xda, 0x18, 0xd5, 0x89, 0x3f, + 0x12, 0x0d, 0x66, 0x21, 0xf4, 0x80, 0xda, 0xac, 0x51, 0x1b, 0x8a, 0x66, 0x53, 0xa7, 0x5a, 0xb4, + 0xb3, 0x5d, 0xb4, 0x73, 0xbe, 0x65, 0x78, 0x0a, 0xf7, 0xfd, 0xea, 0x87, 0x21, 0xf9, 0xed, 0x33, + 0xf8, 0x04, 0x28, 0x41, 0x8c, 0x92, 0x8c, 0xeb, 0x19, 0x99, 0x92, 0xad, 0x56, 0xb3, 0x4e, 0x38, + 0xc6, 0x67, 0x89, 0xe2, 0x2c, 0xb4, 0xbe, 0xf6, 0xc1, 0xb8, 0x91, 0x75, 0x41, 0x18, 0xfe, 0x1f, + 0xbe, 0x76, 0xcd, 0x92, 0xff, 0xa5, 0x59, 0x83, 0xbf, 0x37, 0x6b, 0x78, 0x8f, 0x59, 0x5f, 0x3a, + 0x7f, 0x66, 0xee, 0xd5, 0x8b, 0x4f, 0x0c, 0x67, 0x45, 0x42, 0x32, 0x78, 0x08, 0x54, 0xbc, 0x4d, + 0xea, 0xef, 0xaa, 0x05, 0xfe, 0xd0, 0x9d, 0xc7, 0x1d, 0x35, 0xdc, 0x1d, 0xb5, 0x11, 0xe0, 0xbd, + 0xbb, 0x2e, 0x75, 0xe9, 0xa6, 0xd4, 0xa5, 0x9f, 0xa5, 0x2e, 0x5d, 0x6d, 0xf4, 0xde, 0xcd, 0x46, + 0xef, 0x7d, 0xdb, 0xe8, 0xbd, 0xf7, 0xcf, 0xa2, 0x84, 0xc5, 0xab, 0x85, 0x13, 0x90, 0xa5, 0xdb, + 0xbd, 0x18, 0x6d, 0x58, 0x5d, 0x96, 0xdb, 0xd7, 0x64, 0x31, 0x14, 0xf8, 0xf1, 0xaf, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x2b, 0x89, 0x89, 0x5b, 0xb2, 0x04, 0x00, 0x00, } func (m *CanonicalBlockID) Marshal() (dAtA []byte, err error) { @@ -529,18 +592,6 @@ 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) @@ -548,12 +599,12 @@ func (m *CanonicalVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n5, err5 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) - if err5 != nil { - return 0, err5 + n4, err4 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err4 != nil { + return 0, err4 } - i -= n5 - i = encodeVarintCanonical(dAtA, i, uint64(n5)) + i -= n4 + i = encodeVarintCanonical(dAtA, i, uint64(n4)) i-- dAtA[i] = 0x2a if m.BlockID != nil { @@ -588,6 +639,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 @@ -686,8 +786,27 @@ func (m *CanonicalVote) Size() (n int) { if l > 0 { n += 1 + l + sovCanonical(uint64(l)) } - if m.VoteExtension != nil { - l = m.VoteExtension.Size() + 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 @@ -1297,11 +1416,61 @@ 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) + default: + iNdEx = preIndex + skippy, err := skipCanonical(dAtA[iNdEx:]) + if err != nil { + return err } - var msglen int + 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 @@ -1311,27 +1480,77 @@ func (m *CanonicalVote) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - msglen |= int(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } - if msglen < 0 { + if byteLen < 0 { return ErrInvalidLengthCanonical } - postIndex := iNdEx + msglen + postIndex := iNdEx + byteLen if postIndex < 0 { return ErrInvalidLengthCanonical } if postIndex > l { return io.ErrUnexpectedEOF } - if m.VoteExtension == nil { - m.VoteExtension = &VoteExtensionToSign{} + m.Extension = append(m.Extension[:0], dAtA[iNdEx:postIndex]...) + if m.Extension == nil { + m.Extension = []byte{} } - if err := m.VoteExtension.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err + 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 diff --git a/proto/tendermint/types/canonical.proto b/proto/tendermint/types/canonical.proto index e88fd6ffe..e9ed1e55d 100644 --- a/proto/tendermint/types/canonical.proto +++ b/proto/tendermint/types/canonical.proto @@ -35,3 +35,12 @@ message CanonicalVote { google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; string chain_id = 6 [(gogoproto.customname) = "ChainID"]; } + +// 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/params.pb.go b/proto/tendermint/types/params.pb.go index c8b8594e1..41d417b91 100644 --- a/proto/tendermint/types/params.pb.go +++ b/proto/tendermint/types/params.pb.go @@ -394,7 +394,7 @@ func (m *HashedParams) GetBlockMaxGas() int64 { // see the specification of proposer-based timestamps: // https://github.com/tendermint/tendermint/tree/master/spec/consensus/proposer-based-timestamp type SynchronyParams struct { - // message_delay bounds how long a proposal message may take to reach all validators on a newtork + // message_delay bounds how long a proposal message may take to reach all validators on a network // and still be considered valid. MessageDelay *time.Duration `protobuf:"bytes,1,opt,name=message_delay,json=messageDelay,proto3,stdduration" json:"message_delay,omitempty"` // precision bounds how skewed a proposer's clock may be from any validator diff --git a/proto/tendermint/types/types.pb.go b/proto/tendermint/types/types.pb.go index 5c1f99d23..1904afcd1 100644 --- a/proto/tendermint/types/types.pb.go +++ b/proto/tendermint/types/types.pb.go @@ -29,7 +29,7 @@ var _ = time.Kitchen // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// BlockIdFlag indicates which BlcokID the signature is for +// BlockIdFlag indicates which BlockID the signature is for type BlockIDFlag int32 const ( @@ -466,15 +466,22 @@ func (m *Data) GetTxs() [][]byte { // Vote represents a prevote, precommit, or commit vote from validators for // consensus. type Vote 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"` - Round int32 `protobuf:"varint,3,opt,name=round,proto3" json:"round,omitempty"` - BlockID BlockID `protobuf:"bytes,4,opt,name=block_id,json=blockId,proto3" json:"block_id"` - 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"` - VoteExtension *VoteExtension `protobuf:"bytes,9,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,omitempty"` + 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"` + Round int32 `protobuf:"varint,3,opt,name=round,proto3" json:"round,omitempty"` + BlockID BlockID `protobuf:"bytes,4,opt,name=block_id,json=blockId,proto3" json:"block_id"` + 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"` + // 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. Only valid for precommit messages. + ExtensionSignature []byte `protobuf:"bytes,10,opt,name=extension_signature,json=extensionSignature,proto3" json:"extension_signature,omitempty"` } func (m *Vote) Reset() { *m = Vote{} } @@ -566,108 +573,16 @@ func (m *Vote) GetSignature() []byte { return nil } -func (m *Vote) GetVoteExtension() *VoteExtension { +func (m *Vote) GetExtension() []byte { if m != nil { - return m.VoteExtension + return m.Extension } 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 { +func (m *Vote) GetExtensionSignature() []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 m.ExtensionSignature } return nil } @@ -685,7 +600,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{8} + return fileDescriptor_d3a6e55e2345de56, []int{6} } func (m *Commit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -744,18 +659,17 @@ 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"` - VoteExtension *VoteExtensionToSign `protobuf:"bytes,5,opt,name=vote_extension,json=voteExtension,proto3" json:"vote_extension,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"` } 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{9} + return fileDescriptor_d3a6e55e2345de56, []int{7} } func (m *CommitSig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -812,13 +726,6 @@ 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"` @@ -833,7 +740,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{10} + return fileDescriptor_d3a6e55e2345de56, []int{8} } func (m *Proposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -920,7 +827,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{11} + return fileDescriptor_d3a6e55e2345de56, []int{9} } func (m *SignedHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -972,7 +879,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{12} + return fileDescriptor_d3a6e55e2345de56, []int{10} } func (m *LightBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1026,7 +933,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{13} + return fileDescriptor_d3a6e55e2345de56, []int{11} } func (m *BlockMeta) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1095,7 +1002,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{14} + return fileDescriptor_d3a6e55e2345de56, []int{12} } func (m *TxProof) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1154,8 +1061,6 @@ 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") @@ -1168,96 +1073,91 @@ func init() { func init() { proto.RegisterFile("tendermint/types/types.proto", fileDescriptor_d3a6e55e2345de56) } var fileDescriptor_d3a6e55e2345de56 = []byte{ - // 1423 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x4f, 0x6f, 0xdb, 0x64, - 0x18, 0xaf, 0x13, 0xb7, 0x49, 0x9e, 0xc4, 0x6d, 0xfa, 0xd2, 0x6d, 0x69, 0xb6, 0xa6, 0x91, 0xd1, - 0x58, 0x37, 0x50, 0x3a, 0x3a, 0xc4, 0x9f, 0x03, 0xa0, 0x24, 0xcd, 0xb6, 0x68, 0x6d, 0x1a, 0x9c, - 0x6c, 0x08, 0x2e, 0x96, 0x93, 0xbc, 0x4d, 0xcc, 0x1c, 0xdb, 0xb2, 0xdf, 0x94, 0x76, 0x9f, 0x00, - 0xf5, 0xb4, 0x13, 0xb7, 0x9e, 0xe0, 0x80, 0xc4, 0x05, 0x89, 0x2f, 0x80, 0x38, 0xed, 0xb8, 0x1b, - 0x9c, 0x06, 0xea, 0x24, 0x3e, 0x07, 0x7a, 0xff, 0xc4, 0xb1, 0x9b, 0x86, 0x4d, 0xd5, 0xc4, 0x25, - 0xf2, 0xfb, 0x3c, 0xbf, 0xe7, 0xff, 0xcf, 0x8f, 0xdf, 0xc0, 0x35, 0x82, 0xed, 0x1e, 0xf6, 0x86, - 0xa6, 0x4d, 0x36, 0xc9, 0x91, 0x8b, 0x7d, 0xfe, 0x5b, 0x72, 0x3d, 0x87, 0x38, 0x28, 0x3b, 0xd1, - 0x96, 0x98, 0x3c, 0xbf, 0xd2, 0x77, 0xfa, 0x0e, 0x53, 0x6e, 0xd2, 0x27, 0x8e, 0xcb, 0xaf, 0xf7, - 0x1d, 0xa7, 0x6f, 0xe1, 0x4d, 0x76, 0xea, 0x8c, 0xf6, 0x37, 0x89, 0x39, 0xc4, 0x3e, 0x31, 0x86, - 0xae, 0x00, 0xac, 0x85, 0xc2, 0x74, 0xbd, 0x23, 0x97, 0x38, 0x14, 0xeb, 0xec, 0x0b, 0x75, 0x21, - 0xa4, 0x3e, 0xc0, 0x9e, 0x6f, 0x3a, 0x76, 0x38, 0x8f, 0x7c, 0x71, 0x2a, 0xcb, 0x03, 0xc3, 0x32, - 0x7b, 0x06, 0x71, 0x3c, 0x8e, 0x50, 0x3f, 0x01, 0xa5, 0x69, 0x78, 0xa4, 0x85, 0xc9, 0x7d, 0x6c, - 0xf4, 0xb0, 0x87, 0x56, 0x60, 0x9e, 0x38, 0xc4, 0xb0, 0x72, 0x52, 0x51, 0xda, 0x50, 0x34, 0x7e, - 0x40, 0x08, 0xe4, 0x81, 0xe1, 0x0f, 0x72, 0xb1, 0xa2, 0xb4, 0x91, 0xd1, 0xd8, 0xb3, 0x3a, 0x00, - 0x99, 0x9a, 0x52, 0x0b, 0xd3, 0xee, 0xe1, 0xc3, 0xb1, 0x05, 0x3b, 0x50, 0x69, 0xe7, 0x88, 0x60, - 0x5f, 0x98, 0xf0, 0x03, 0xfa, 0x00, 0xe6, 0x59, 0xfe, 0xb9, 0x78, 0x51, 0xda, 0x48, 0x6f, 0xe5, - 0x4a, 0xa1, 0x46, 0xf1, 0xfa, 0x4a, 0x4d, 0xaa, 0xaf, 0xc8, 0xcf, 0x5e, 0xac, 0xcf, 0x69, 0x1c, - 0xac, 0x5a, 0x90, 0xa8, 0x58, 0x4e, 0xf7, 0x71, 0x7d, 0x3b, 0x48, 0x44, 0x9a, 0x24, 0x82, 0x76, - 0x61, 0xc9, 0x35, 0x3c, 0xa2, 0xfb, 0x98, 0xe8, 0x03, 0x56, 0x05, 0x0b, 0x9a, 0xde, 0x5a, 0x2f, - 0x9d, 0x9d, 0x43, 0x29, 0x52, 0xac, 0x88, 0xa2, 0xb8, 0x61, 0xa1, 0xfa, 0x8f, 0x0c, 0x0b, 0xa2, - 0x19, 0x9f, 0x42, 0x42, 0xb4, 0x95, 0x05, 0x4c, 0x6f, 0xad, 0x85, 0x3d, 0x0a, 0x55, 0xa9, 0xea, - 0xd8, 0x3e, 0xb6, 0xfd, 0x91, 0x2f, 0xfc, 0x8d, 0x6d, 0xd0, 0x3b, 0x90, 0xec, 0x0e, 0x0c, 0xd3, - 0xd6, 0xcd, 0x1e, 0xcb, 0x28, 0x55, 0x49, 0x9f, 0xbe, 0x58, 0x4f, 0x54, 0xa9, 0xac, 0xbe, 0xad, - 0x25, 0x98, 0xb2, 0xde, 0x43, 0x97, 0x61, 0x61, 0x80, 0xcd, 0xfe, 0x80, 0xb0, 0xb6, 0xc4, 0x35, - 0x71, 0x42, 0x1f, 0x83, 0x4c, 0x09, 0x91, 0x93, 0x59, 0xec, 0x7c, 0x89, 0xb3, 0xa5, 0x34, 0x66, - 0x4b, 0xa9, 0x3d, 0x66, 0x4b, 0x25, 0x49, 0x03, 0x3f, 0xfd, 0x6b, 0x5d, 0xd2, 0x98, 0x05, 0xaa, - 0x82, 0x62, 0x19, 0x3e, 0xd1, 0x3b, 0xb4, 0x6d, 0x34, 0xfc, 0x3c, 0x73, 0xb1, 0x3a, 0xdd, 0x10, - 0xd1, 0x58, 0x91, 0x7a, 0x9a, 0x5a, 0x71, 0x51, 0x0f, 0x6d, 0x40, 0x96, 0x39, 0xe9, 0x3a, 0xc3, - 0xa1, 0x49, 0x74, 0xd6, 0xf7, 0x05, 0xd6, 0xf7, 0x45, 0x2a, 0xaf, 0x32, 0xf1, 0x7d, 0x3a, 0x81, - 0xab, 0x90, 0xea, 0x19, 0xc4, 0xe0, 0x90, 0x04, 0x83, 0x24, 0xa9, 0x80, 0x29, 0x6f, 0xc0, 0x52, - 0xc0, 0x3a, 0x9f, 0x43, 0x92, 0xdc, 0xcb, 0x44, 0xcc, 0x80, 0xb7, 0x61, 0xc5, 0xc6, 0x87, 0x44, - 0x3f, 0x8b, 0x4e, 0x31, 0x34, 0xa2, 0xba, 0x47, 0x51, 0x8b, 0xeb, 0xb0, 0xd8, 0x1d, 0x37, 0x9f, - 0x63, 0x81, 0x61, 0x95, 0x40, 0xca, 0x60, 0xab, 0x90, 0x34, 0x5c, 0x97, 0x03, 0xd2, 0x0c, 0x90, - 0x30, 0x5c, 0x97, 0xa9, 0x6e, 0xc1, 0x32, 0xab, 0xd1, 0xc3, 0xfe, 0xc8, 0x22, 0xc2, 0x49, 0x86, - 0x61, 0x96, 0xa8, 0x42, 0xe3, 0x72, 0x86, 0x7d, 0x1b, 0x14, 0x7c, 0x60, 0xf6, 0xb0, 0xdd, 0xc5, - 0x1c, 0xa7, 0x30, 0x5c, 0x66, 0x2c, 0x64, 0xa0, 0x9b, 0x90, 0x75, 0x3d, 0xc7, 0x75, 0x7c, 0xec, - 0xe9, 0x46, 0xaf, 0xe7, 0x61, 0xdf, 0xcf, 0x2d, 0x72, 0x7f, 0x63, 0x79, 0x99, 0x8b, 0xd5, 0x1c, - 0xc8, 0xdb, 0x06, 0x31, 0x50, 0x16, 0xe2, 0xe4, 0xd0, 0xcf, 0x49, 0xc5, 0xf8, 0x46, 0x46, 0xa3, - 0x8f, 0xea, 0x2f, 0x71, 0x90, 0x1f, 0x39, 0x04, 0xa3, 0x3b, 0x20, 0xd3, 0x31, 0x31, 0xf6, 0x2d, - 0x9e, 0xc7, 0xe7, 0x96, 0xd9, 0xb7, 0x71, 0x6f, 0xd7, 0xef, 0xb7, 0x8f, 0x5c, 0xac, 0x31, 0x70, - 0x88, 0x4e, 0xb1, 0x08, 0x9d, 0x56, 0x60, 0xde, 0x73, 0x46, 0x76, 0x8f, 0xb1, 0x6c, 0x5e, 0xe3, - 0x07, 0x54, 0x83, 0x64, 0xc0, 0x12, 0xf9, 0x55, 0x2c, 0x59, 0xa2, 0x2c, 0xa1, 0x1c, 0x16, 0x02, - 0x2d, 0xd1, 0x11, 0x64, 0xa9, 0x40, 0x2a, 0x58, 0x5e, 0x82, 0x6d, 0xaf, 0x47, 0xd8, 0x89, 0x19, - 0x7a, 0x17, 0x96, 0x83, 0xd9, 0x07, 0xcd, 0xe3, 0x8c, 0xcb, 0x06, 0x0a, 0xd1, 0xbd, 0x08, 0xad, - 0x74, 0xbe, 0x80, 0x12, 0xac, 0xae, 0x09, 0xad, 0xea, 0x6c, 0x13, 0x5d, 0x83, 0x94, 0x6f, 0xf6, - 0x6d, 0x83, 0x8c, 0x3c, 0x2c, 0x98, 0x37, 0x11, 0xa0, 0xbb, 0xb0, 0x78, 0xe0, 0x10, 0xac, 0xe3, - 0x43, 0x82, 0x6d, 0xf6, 0xa6, 0xa7, 0x66, 0xed, 0x0e, 0x3a, 0x91, 0xda, 0x18, 0xa6, 0x29, 0x07, - 0xe1, 0xa3, 0x7a, 0x04, 0x4a, 0x44, 0x8f, 0x6e, 0x40, 0x96, 0x92, 0x8e, 0xbd, 0x17, 0xc4, 0xd1, - 0x69, 0x44, 0xb1, 0xb5, 0x14, 0xc3, 0x75, 0xe9, 0xe0, 0xdb, 0x0e, 0x9d, 0x1e, 0xfa, 0x1c, 0xae, - 0x05, 0x40, 0x1f, 0x5b, 0xfb, 0xba, 0x31, 0x22, 0x03, 0x6c, 0x13, 0xb3, 0x6b, 0x10, 0xd3, 0xee, - 0x8b, 0x05, 0xba, 0x2a, 0x8c, 0x5a, 0xd8, 0xda, 0x2f, 0x47, 0x00, 0xea, 0x67, 0xf0, 0x56, 0x24, - 0xb4, 0xf0, 0xfb, 0xba, 0x09, 0xa8, 0xbf, 0x49, 0xb0, 0xc0, 0x5f, 0xe6, 0x10, 0x75, 0xa4, 0xf3, - 0xa9, 0x13, 0x9b, 0x45, 0x9d, 0xf8, 0xc5, 0xa9, 0x53, 0x06, 0x08, 0xe6, 0xe1, 0xe7, 0xe4, 0x62, - 0x7c, 0x23, 0xbd, 0x75, 0x75, 0xda, 0x11, 0x4f, 0xb1, 0x65, 0xf6, 0xc5, 0xae, 0x0a, 0x19, 0xa9, - 0x3f, 0xc7, 0x20, 0x15, 0xe8, 0x51, 0x19, 0x94, 0x71, 0x5e, 0xfa, 0xbe, 0x65, 0xf4, 0xc5, 0xeb, - 0xb3, 0x36, 0x33, 0xb9, 0xbb, 0x96, 0xd1, 0xd7, 0xd2, 0x22, 0x1f, 0x7a, 0x38, 0x9f, 0x8a, 0xb1, - 0x19, 0x54, 0x8c, 0x70, 0x3f, 0x7e, 0x31, 0xee, 0x47, 0x58, 0x2a, 0x9f, 0x65, 0xe9, 0xce, 0x14, - 0x4b, 0xf9, 0x2b, 0x76, 0xfd, 0x15, 0x2c, 0xe5, 0x13, 0x3e, 0xcb, 0xd5, 0x5f, 0x63, 0x90, 0x6c, - 0xb2, 0x65, 0x64, 0x58, 0xff, 0xc7, 0x8a, 0xb9, 0x0a, 0x29, 0xd7, 0xb1, 0x74, 0xae, 0x91, 0x99, - 0x26, 0xe9, 0x3a, 0x96, 0x36, 0x45, 0xa2, 0xf9, 0x37, 0xb4, 0x7f, 0x16, 0xde, 0xc0, 0x0c, 0x12, - 0x67, 0x66, 0xa0, 0x7a, 0x90, 0xe1, 0xad, 0x10, 0x97, 0x83, 0xdb, 0xb4, 0x07, 0xec, 0xb6, 0x21, - 0x4d, 0x5f, 0x66, 0x78, 0xda, 0x1c, 0xa9, 0x09, 0x1c, 0xb5, 0xe0, 0xdf, 0x52, 0x71, 0x3f, 0xc9, - 0xcd, 0x22, 0xb9, 0x26, 0x70, 0xea, 0xf7, 0x12, 0xc0, 0x0e, 0xed, 0x2c, 0xab, 0x97, 0x7e, 0xd6, - 0x7d, 0x96, 0x82, 0x1e, 0x89, 0x5c, 0x98, 0x35, 0x34, 0x11, 0x3f, 0xe3, 0x87, 0xf3, 0xae, 0x82, - 0x32, 0xa1, 0xb6, 0x8f, 0xc7, 0xc9, 0x9c, 0xe3, 0x24, 0xf8, 0xda, 0xb6, 0x30, 0xd1, 0x32, 0x07, - 0xa1, 0x93, 0xfa, 0xbb, 0x04, 0x29, 0x96, 0xd3, 0x2e, 0x26, 0x46, 0x64, 0x86, 0xd2, 0xc5, 0x67, - 0xb8, 0x06, 0xc0, 0xdd, 0xf8, 0xe6, 0x13, 0x2c, 0x98, 0x95, 0x62, 0x92, 0x96, 0xf9, 0x04, 0xa3, - 0x0f, 0x83, 0x86, 0xc7, 0xff, 0xbb, 0xe1, 0x62, 0x41, 0x8c, 0xdb, 0x7e, 0x05, 0x12, 0xf6, 0x68, - 0xa8, 0xd3, 0x6f, 0xac, 0xcc, 0xd9, 0x6a, 0x8f, 0x86, 0xed, 0x43, 0x5f, 0xfd, 0x06, 0x12, 0xed, - 0x43, 0x76, 0xdf, 0xa4, 0x14, 0xf5, 0x1c, 0x47, 0x5c, 0x72, 0xf8, 0x96, 0x4c, 0x52, 0x01, 0xfb, - 0xa6, 0x23, 0x90, 0xe9, 0x16, 0x1d, 0xdf, 0x7e, 0xe9, 0x33, 0x2a, 0xbd, 0xe6, 0x4d, 0x56, 0xdc, - 0x61, 0x6f, 0xfd, 0x21, 0x41, 0x3a, 0xb4, 0x6d, 0xd0, 0xfb, 0x70, 0xa9, 0xb2, 0xb3, 0x57, 0x7d, - 0xa0, 0xd7, 0xb7, 0xf5, 0xbb, 0x3b, 0xe5, 0x7b, 0xfa, 0xc3, 0xc6, 0x83, 0xc6, 0xde, 0x97, 0x8d, - 0xec, 0x5c, 0xfe, 0xf2, 0xf1, 0x49, 0x11, 0x85, 0xb0, 0x0f, 0xed, 0xc7, 0xb6, 0xf3, 0xad, 0x8d, - 0x36, 0x61, 0x25, 0x6a, 0x52, 0xae, 0xb4, 0x6a, 0x8d, 0x76, 0x56, 0xca, 0x5f, 0x3a, 0x3e, 0x29, - 0x2e, 0x87, 0x2c, 0xca, 0x1d, 0x1f, 0xdb, 0x64, 0xda, 0xa0, 0xba, 0xb7, 0xbb, 0x5b, 0x6f, 0x67, - 0x63, 0x53, 0x06, 0x62, 0xfd, 0xdf, 0x84, 0xe5, 0xa8, 0x41, 0xa3, 0xbe, 0x93, 0x8d, 0xe7, 0xd1, - 0xf1, 0x49, 0x71, 0x31, 0x84, 0x6e, 0x98, 0x56, 0x3e, 0xf9, 0xdd, 0x0f, 0x85, 0xb9, 0x9f, 0x7e, - 0x2c, 0x48, 0xb4, 0x32, 0x25, 0xb2, 0x23, 0xd0, 0x7b, 0x70, 0xa5, 0x55, 0xbf, 0xd7, 0xa8, 0x6d, - 0xeb, 0xbb, 0xad, 0x7b, 0x7a, 0xfb, 0xab, 0x66, 0x2d, 0x54, 0xdd, 0xd2, 0xf1, 0x49, 0x31, 0x2d, - 0x4a, 0x9a, 0x85, 0x6e, 0x6a, 0xb5, 0x47, 0x7b, 0xed, 0x5a, 0x56, 0xe2, 0xe8, 0xa6, 0x87, 0xe9, - 0x02, 0x63, 0xe8, 0xdb, 0xb0, 0x7a, 0x0e, 0x3a, 0x28, 0x6c, 0xf9, 0xf8, 0xa4, 0xa8, 0x34, 0x3d, - 0xcc, 0xdf, 0x1f, 0x66, 0x51, 0x82, 0xdc, 0xb4, 0xc5, 0x5e, 0x73, 0xaf, 0x55, 0xde, 0xc9, 0x16, - 0xf3, 0xd9, 0xe3, 0x93, 0x62, 0x66, 0xbc, 0x0c, 0x29, 0x7e, 0x52, 0x59, 0xe5, 0x8b, 0x67, 0xa7, - 0x05, 0xe9, 0xf9, 0x69, 0x41, 0xfa, 0xfb, 0xb4, 0x20, 0x3d, 0x7d, 0x59, 0x98, 0x7b, 0xfe, 0xb2, - 0x30, 0xf7, 0xe7, 0xcb, 0xc2, 0xdc, 0xd7, 0x1f, 0xf5, 0x4d, 0x32, 0x18, 0x75, 0x4a, 0x5d, 0x67, - 0xb8, 0x19, 0xfe, 0x8f, 0x35, 0x79, 0xe4, 0xff, 0xf5, 0xce, 0xfe, 0xff, 0xea, 0x2c, 0x30, 0xf9, - 0x9d, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x38, 0xfc, 0x14, 0xb1, 0x40, 0x0e, 0x00, 0x00, + // 1341 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xcf, 0x73, 0xdb, 0xc4, + 0x17, 0x8f, 0x62, 0x25, 0xb6, 0x9f, 0xed, 0xc4, 0xd9, 0x6f, 0xda, 0xba, 0x6e, 0xe3, 0x68, 0xfc, + 0x1d, 0x20, 0x2d, 0x8c, 0x52, 0x52, 0x86, 0x1f, 0x07, 0x0e, 0xb6, 0x93, 0xb6, 0x9e, 0x26, 0x8e, + 0x91, 0xdd, 0x32, 0x70, 0xd1, 0xc8, 0xd6, 0xd6, 0x16, 0x95, 0x25, 0x8d, 0x76, 0x1d, 0x92, 0xfe, + 0x05, 0x4c, 0x4e, 0x3d, 0x71, 0xcb, 0x09, 0x0e, 0xdc, 0x39, 0x70, 0x65, 0x38, 0xf5, 0xd8, 0x1b, + 0x5c, 0x28, 0x4c, 0x3a, 0xc3, 0xdf, 0xc1, 0xec, 0x0f, 0xc9, 0x72, 0x9c, 0x40, 0xa7, 0xd3, 0xe1, + 0xe2, 0xd1, 0xbe, 0xf7, 0x79, 0x6f, 0xdf, 0x8f, 0xcf, 0xee, 0x5b, 0xc3, 0x75, 0x8a, 0x3d, 0x1b, + 0x87, 0x23, 0xc7, 0xa3, 0x9b, 0xf4, 0x28, 0xc0, 0x44, 0xfc, 0xea, 0x41, 0xe8, 0x53, 0x1f, 0x15, + 0x27, 0x5a, 0x9d, 0xcb, 0xcb, 0xab, 0x03, 0x7f, 0xe0, 0x73, 0xe5, 0x26, 0xfb, 0x12, 0xb8, 0xf2, + 0xfa, 0xc0, 0xf7, 0x07, 0x2e, 0xde, 0xe4, 0xab, 0xde, 0xf8, 0xd1, 0x26, 0x75, 0x46, 0x98, 0x50, + 0x6b, 0x14, 0x48, 0xc0, 0x5a, 0x62, 0x9b, 0x7e, 0x78, 0x14, 0x50, 0x9f, 0x61, 0xfd, 0x47, 0x52, + 0x5d, 0x49, 0xa8, 0x0f, 0x70, 0x48, 0x1c, 0xdf, 0x4b, 0xc6, 0x51, 0xd6, 0x66, 0xa2, 0x3c, 0xb0, + 0x5c, 0xc7, 0xb6, 0xa8, 0x1f, 0x0a, 0x44, 0xf5, 0x13, 0x28, 0xb4, 0xad, 0x90, 0x76, 0x30, 0xbd, + 0x87, 0x2d, 0x1b, 0x87, 0x68, 0x15, 0x16, 0xa8, 0x4f, 0x2d, 0xb7, 0xa4, 0x68, 0xca, 0x46, 0xc1, + 0x10, 0x0b, 0x84, 0x40, 0x1d, 0x5a, 0x64, 0x58, 0x9a, 0xd7, 0x94, 0x8d, 0xbc, 0xc1, 0xbf, 0xab, + 0x43, 0x50, 0x99, 0x29, 0xb3, 0x70, 0x3c, 0x1b, 0x1f, 0x46, 0x16, 0x7c, 0xc1, 0xa4, 0xbd, 0x23, + 0x8a, 0x89, 0x34, 0x11, 0x0b, 0xf4, 0x01, 0x2c, 0xf0, 0xf8, 0x4b, 0x29, 0x4d, 0xd9, 0xc8, 0x6d, + 0x95, 0xf4, 0x44, 0xa1, 0x44, 0x7e, 0x7a, 0x9b, 0xe9, 0xeb, 0xea, 0xb3, 0x17, 0xeb, 0x73, 0x86, + 0x00, 0x57, 0x5d, 0x48, 0xd7, 0x5d, 0xbf, 0xff, 0xb8, 0xb9, 0x1d, 0x07, 0xa2, 0x4c, 0x02, 0x41, + 0x7b, 0xb0, 0x1c, 0x58, 0x21, 0x35, 0x09, 0xa6, 0xe6, 0x90, 0x67, 0xc1, 0x37, 0xcd, 0x6d, 0xad, + 0xeb, 0x67, 0xfb, 0xa0, 0x4f, 0x25, 0x2b, 0x77, 0x29, 0x04, 0x49, 0x61, 0xf5, 0x2f, 0x15, 0x16, + 0x65, 0x31, 0x3e, 0x85, 0xb4, 0x2c, 0x2b, 0xdf, 0x30, 0xb7, 0xb5, 0x96, 0xf4, 0x28, 0x55, 0x7a, + 0xc3, 0xf7, 0x08, 0xf6, 0xc8, 0x98, 0x48, 0x7f, 0x91, 0x0d, 0x7a, 0x1b, 0x32, 0xfd, 0xa1, 0xe5, + 0x78, 0xa6, 0x63, 0xf3, 0x88, 0xb2, 0xf5, 0xdc, 0xe9, 0x8b, 0xf5, 0x74, 0x83, 0xc9, 0x9a, 0xdb, + 0x46, 0x9a, 0x2b, 0x9b, 0x36, 0xba, 0x0c, 0x8b, 0x43, 0xec, 0x0c, 0x86, 0x94, 0x97, 0x25, 0x65, + 0xc8, 0x15, 0xfa, 0x18, 0x54, 0x46, 0x88, 0x92, 0xca, 0xf7, 0x2e, 0xeb, 0x82, 0x2d, 0x7a, 0xc4, + 0x16, 0xbd, 0x1b, 0xb1, 0xa5, 0x9e, 0x61, 0x1b, 0x3f, 0xfd, 0x63, 0x5d, 0x31, 0xb8, 0x05, 0x6a, + 0x40, 0xc1, 0xb5, 0x08, 0x35, 0x7b, 0xac, 0x6c, 0x6c, 0xfb, 0x05, 0xee, 0xe2, 0xea, 0x6c, 0x41, + 0x64, 0x61, 0x65, 0xe8, 0x39, 0x66, 0x25, 0x44, 0x36, 0xda, 0x80, 0x22, 0x77, 0xd2, 0xf7, 0x47, + 0x23, 0x87, 0x9a, 0xbc, 0xee, 0x8b, 0xbc, 0xee, 0x4b, 0x4c, 0xde, 0xe0, 0xe2, 0x7b, 0xac, 0x03, + 0xd7, 0x20, 0x6b, 0x5b, 0xd4, 0x12, 0x90, 0x34, 0x87, 0x64, 0x98, 0x80, 0x2b, 0xdf, 0x81, 0xe5, + 0x98, 0x75, 0x44, 0x40, 0x32, 0xc2, 0xcb, 0x44, 0xcc, 0x81, 0xb7, 0x60, 0xd5, 0xc3, 0x87, 0xd4, + 0x3c, 0x8b, 0xce, 0x72, 0x34, 0x62, 0xba, 0x87, 0xd3, 0x16, 0x6f, 0xc1, 0x52, 0x3f, 0x2a, 0xbe, + 0xc0, 0x02, 0xc7, 0x16, 0x62, 0x29, 0x87, 0x5d, 0x85, 0x8c, 0x15, 0x04, 0x02, 0x90, 0xe3, 0x80, + 0xb4, 0x15, 0x04, 0x5c, 0x75, 0x13, 0x56, 0x78, 0x8e, 0x21, 0x26, 0x63, 0x97, 0x4a, 0x27, 0x79, + 0x8e, 0x59, 0x66, 0x0a, 0x43, 0xc8, 0x39, 0xf6, 0xff, 0x50, 0xc0, 0x07, 0x8e, 0x8d, 0xbd, 0x3e, + 0x16, 0xb8, 0x02, 0xc7, 0xe5, 0x23, 0x21, 0x07, 0xdd, 0x80, 0x62, 0x10, 0xfa, 0x81, 0x4f, 0x70, + 0x68, 0x5a, 0xb6, 0x1d, 0x62, 0x42, 0x4a, 0x4b, 0xc2, 0x5f, 0x24, 0xaf, 0x09, 0x71, 0xb5, 0x04, + 0xea, 0xb6, 0x45, 0x2d, 0x54, 0x84, 0x14, 0x3d, 0x24, 0x25, 0x45, 0x4b, 0x6d, 0xe4, 0x0d, 0xf6, + 0x59, 0xfd, 0x29, 0x05, 0xea, 0x43, 0x9f, 0x62, 0x74, 0x1b, 0x54, 0xd6, 0x26, 0xce, 0xbe, 0xa5, + 0xf3, 0xf8, 0xdc, 0x71, 0x06, 0x1e, 0xb6, 0xf7, 0xc8, 0xa0, 0x7b, 0x14, 0x60, 0x83, 0x83, 0x13, + 0x74, 0x9a, 0x9f, 0xa2, 0xd3, 0x2a, 0x2c, 0x84, 0xfe, 0xd8, 0xb3, 0x39, 0xcb, 0x16, 0x0c, 0xb1, + 0x40, 0x3b, 0x90, 0x89, 0x59, 0xa2, 0xfe, 0x1b, 0x4b, 0x96, 0x19, 0x4b, 0x18, 0x87, 0xa5, 0xc0, + 0x48, 0xf7, 0x24, 0x59, 0xea, 0x90, 0x8d, 0x2f, 0x2f, 0xc9, 0xb6, 0x57, 0x23, 0xec, 0xc4, 0x0c, + 0xbd, 0x0b, 0x2b, 0x71, 0xef, 0xe3, 0xe2, 0x09, 0xc6, 0x15, 0x63, 0x85, 0xac, 0xde, 0x14, 0xad, + 0x4c, 0x71, 0x01, 0xa5, 0x79, 0x5e, 0x13, 0x5a, 0x35, 0xf9, 0x4d, 0x74, 0x1d, 0xb2, 0xc4, 0x19, + 0x78, 0x16, 0x1d, 0x87, 0x58, 0x32, 0x6f, 0x22, 0x60, 0x5a, 0x7c, 0x48, 0xb1, 0xc7, 0x0f, 0xb9, + 0x60, 0xda, 0x44, 0x80, 0x36, 0xe1, 0x7f, 0xf1, 0xc2, 0x9c, 0x78, 0x11, 0x2c, 0x43, 0xb1, 0xaa, + 0x13, 0x69, 0xaa, 0x3f, 0x2b, 0xb0, 0x28, 0x0e, 0x46, 0xa2, 0x0d, 0xca, 0xf9, 0x6d, 0x98, 0xbf, + 0xa8, 0x0d, 0xa9, 0xd7, 0x6f, 0x43, 0x0d, 0x20, 0x0e, 0x93, 0x94, 0x54, 0x2d, 0xb5, 0x91, 0xdb, + 0xba, 0x36, 0xeb, 0x48, 0x84, 0xd8, 0x71, 0x06, 0xf2, 0xdc, 0x27, 0x8c, 0xaa, 0xbf, 0x2b, 0x90, + 0x8d, 0xf5, 0xa8, 0x06, 0x85, 0x28, 0x2e, 0xf3, 0x91, 0x6b, 0x0d, 0x24, 0x15, 0xd7, 0x2e, 0x0c, + 0xee, 0x8e, 0x6b, 0x0d, 0x8c, 0x9c, 0x8c, 0x87, 0x2d, 0xce, 0x6f, 0xeb, 0xfc, 0x05, 0x6d, 0x9d, + 0xe2, 0x51, 0xea, 0xf5, 0x78, 0x34, 0xd5, 0x71, 0xf5, 0x4c, 0xc7, 0xab, 0x3f, 0xce, 0x43, 0xa6, + 0xcd, 0x8f, 0xa2, 0xe5, 0xfe, 0x17, 0x07, 0xec, 0x1a, 0x64, 0x03, 0xdf, 0x35, 0x85, 0x46, 0xe5, + 0x9a, 0x4c, 0xe0, 0xbb, 0xc6, 0x4c, 0xdb, 0x17, 0xde, 0xd0, 0xe9, 0x5b, 0x7c, 0x03, 0x55, 0x4b, + 0x9f, 0xad, 0x5a, 0x08, 0x79, 0x51, 0x0a, 0x39, 0x1a, 0x6f, 0xb1, 0x1a, 0xf0, 0x59, 0xab, 0xcc, + 0x8e, 0x72, 0x11, 0xb6, 0x40, 0x1a, 0x12, 0xc7, 0x2c, 0xc4, 0x24, 0x91, 0xd3, 0xb9, 0x74, 0x11, + 0x2d, 0x0d, 0x89, 0xab, 0x7e, 0xab, 0x00, 0xec, 0xb2, 0xca, 0xf2, 0x7c, 0xd9, 0x50, 0x23, 0x3c, + 0x04, 0x73, 0x6a, 0xe7, 0xca, 0x45, 0x4d, 0x93, 0xfb, 0xe7, 0x49, 0x32, 0xee, 0x06, 0x14, 0x26, + 0x64, 0x24, 0x38, 0x0a, 0xe6, 0x1c, 0x27, 0xf1, 0xac, 0xe9, 0x60, 0x6a, 0xe4, 0x0f, 0x12, 0xab, + 0xea, 0x2f, 0x0a, 0x64, 0x79, 0x4c, 0x7b, 0x98, 0x5a, 0x53, 0x3d, 0x54, 0x5e, 0xbf, 0x87, 0x6b, + 0x00, 0xc2, 0x0d, 0x71, 0x9e, 0x60, 0xc9, 0xac, 0x2c, 0x97, 0x74, 0x9c, 0x27, 0x18, 0x7d, 0x18, + 0x17, 0x3c, 0xf5, 0xcf, 0x05, 0x97, 0x47, 0x3a, 0x2a, 0xfb, 0x15, 0x48, 0x7b, 0xe3, 0x91, 0xc9, + 0x26, 0x8c, 0x2a, 0xd8, 0xea, 0x8d, 0x47, 0xdd, 0x43, 0x52, 0xfd, 0x0a, 0xd2, 0xdd, 0x43, 0xfe, + 0xda, 0x62, 0x14, 0x0d, 0x7d, 0x5f, 0x8e, 0x78, 0xf1, 0xb4, 0xca, 0x30, 0x01, 0x9f, 0x68, 0x08, + 0x54, 0x36, 0xcb, 0xa3, 0xb7, 0x1f, 0xfb, 0x46, 0xfa, 0x2b, 0xbe, 0xe3, 0xe4, 0x0b, 0xee, 0xe6, + 0xaf, 0x0a, 0xe4, 0x12, 0xf7, 0x03, 0x7a, 0x1f, 0x2e, 0xd5, 0x77, 0xf7, 0x1b, 0xf7, 0xcd, 0xe6, + 0xb6, 0x79, 0x67, 0xb7, 0x76, 0xd7, 0x7c, 0xd0, 0xba, 0xdf, 0xda, 0xff, 0xbc, 0x55, 0x9c, 0x2b, + 0x5f, 0x3e, 0x3e, 0xd1, 0x50, 0x02, 0xfb, 0xc0, 0x7b, 0xec, 0xf9, 0x5f, 0xb3, 0xab, 0x78, 0x75, + 0xda, 0xa4, 0x56, 0xef, 0xec, 0xb4, 0xba, 0x45, 0xa5, 0x7c, 0xe9, 0xf8, 0x44, 0x5b, 0x49, 0x58, + 0xd4, 0x7a, 0x04, 0x7b, 0x74, 0xd6, 0xa0, 0xb1, 0xbf, 0xb7, 0xd7, 0xec, 0x16, 0xe7, 0x67, 0x0c, + 0xe4, 0x85, 0x7d, 0x03, 0x56, 0xa6, 0x0d, 0x5a, 0xcd, 0xdd, 0x62, 0xaa, 0x8c, 0x8e, 0x4f, 0xb4, + 0xa5, 0x04, 0xba, 0xe5, 0xb8, 0xe5, 0xcc, 0x37, 0xdf, 0x55, 0xe6, 0x7e, 0xf8, 0xbe, 0xa2, 0xb0, + 0xcc, 0x0a, 0x53, 0x77, 0x04, 0x7a, 0x0f, 0xae, 0x74, 0x9a, 0x77, 0x5b, 0x3b, 0xdb, 0xe6, 0x5e, + 0xe7, 0xae, 0xd9, 0xfd, 0xa2, 0xbd, 0x93, 0xc8, 0x6e, 0xf9, 0xf8, 0x44, 0xcb, 0xc9, 0x94, 0x2e, + 0x42, 0xb7, 0x8d, 0x9d, 0x87, 0xfb, 0xdd, 0x9d, 0xa2, 0x22, 0xd0, 0xed, 0x10, 0x1f, 0xf8, 0x14, + 0x73, 0xf4, 0x2d, 0xb8, 0x7a, 0x0e, 0x3a, 0x4e, 0x6c, 0xe5, 0xf8, 0x44, 0x2b, 0xb4, 0x43, 0x2c, + 0xce, 0x0f, 0xb7, 0xd0, 0xa1, 0x34, 0x6b, 0xb1, 0xdf, 0xde, 0xef, 0xd4, 0x76, 0x8b, 0x5a, 0xb9, + 0x78, 0x7c, 0xa2, 0xe5, 0xa3, 0xcb, 0x90, 0xe1, 0x27, 0x99, 0xd5, 0x3f, 0x7b, 0x76, 0x5a, 0x51, + 0x9e, 0x9f, 0x56, 0x94, 0x3f, 0x4f, 0x2b, 0xca, 0xd3, 0x97, 0x95, 0xb9, 0xe7, 0x2f, 0x2b, 0x73, + 0xbf, 0xbd, 0xac, 0xcc, 0x7d, 0xf9, 0xd1, 0xc0, 0xa1, 0xc3, 0x71, 0x4f, 0xef, 0xfb, 0xa3, 0xcd, + 0xe4, 0x3f, 0x8c, 0xc9, 0xa7, 0xf8, 0xa7, 0x73, 0xf6, 0xdf, 0x47, 0x6f, 0x91, 0xcb, 0x6f, 0xff, + 0x1d, 0x00, 0x00, 0xff, 0xff, 0xbb, 0xc0, 0x81, 0x37, 0x3e, 0x0d, 0x00, 0x00, } func (m *PartSetHeader) Marshal() (dAtA []byte, err error) { @@ -1558,15 +1458,17 @@ func (m *Vote) 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)) - } + 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 } @@ -1589,12 +1491,12 @@ func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) - if err7 != nil { - return 0, err7 + n6, err6 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err6 != nil { + return 0, err6 } - i -= n7 - i = encodeVarintTypes(dAtA, i, uint64(n7)) + i -= n6 + i = encodeVarintTypes(dAtA, i, uint64(n6)) i-- dAtA[i] = 0x2a { @@ -1625,73 +1527,6 @@ 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) @@ -1769,18 +1604,6 @@ 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) @@ -1788,12 +1611,12 @@ func (m *CommitSig) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x22 } - n11, err11 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) - if err11 != nil { - return 0, err11 + n9, err9 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err9 != nil { + return 0, err9 } - i -= n11 - i = encodeVarintTypes(dAtA, i, uint64(n11)) + i -= n9 + i = encodeVarintTypes(dAtA, i, uint64(n9)) i-- dAtA[i] = 0x1a if len(m.ValidatorAddress) > 0 { @@ -1838,12 +1661,12 @@ func (m *Proposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x3a } - n12, err12 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) - if err12 != nil { - return 0, err12 + n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err10 != nil { + return 0, err10 } - i -= n12 - i = encodeVarintTypes(dAtA, i, uint64(n12)) + i -= n10 + i = encodeVarintTypes(dAtA, i, uint64(n10)) i-- dAtA[i] = 0x32 { @@ -2238,37 +2061,11 @@ func (m *Vote) 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 -} - -func (m *VoteExtension) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.AppDataToSign) + l = len(m.Extension) 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) + l = len(m.ExtensionSignature) if l > 0 { n += 1 + l + sovTypes(uint64(l)) } @@ -2317,10 +2114,6 @@ 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 } @@ -3618,93 +3411,7 @@ func (m *Vote) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 9: 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 = &VoteExtension{} - } - if err := m.VoteExtension.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipTypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthTypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *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) + return fmt.Errorf("proto: wrong wireType = %d for field Extension", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -3731,14 +3438,14 @@ func (m *VoteExtension) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.AppDataToSign = append(m.AppDataToSign[:0], dAtA[iNdEx:postIndex]...) - if m.AppDataToSign == nil { - m.AppDataToSign = []byte{} + m.Extension = append(m.Extension[:0], dAtA[iNdEx:postIndex]...) + if m.Extension == nil { + m.Extension = []byte{} } iNdEx = postIndex - case 2: + case 10: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field AppDataSelfAuthenticating", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ExtensionSignature", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -3765,93 +3472,9 @@ func (m *VoteExtension) Unmarshal(dAtA []byte) error { 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{} + m.ExtensionSignature = append(m.ExtensionSignature[:0], dAtA[iNdEx:postIndex]...) + if m.ExtensionSignature == nil { + m.ExtensionSignature = []byte{} } iNdEx = postIndex default: @@ -4179,42 +3802,6 @@ 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 bc2c53196..e2b8a46c8 100644 --- a/proto/tendermint/types/types.proto +++ b/proto/tendermint/types/types.proto @@ -9,7 +9,7 @@ import "tendermint/crypto/proof.proto"; import "tendermint/version/types.proto"; import "tendermint/types/validator.proto"; -// BlockIdFlag indicates which BlcokID the signature is for +// BlockIdFlag indicates which BlockID the signature is for enum BlockIDFlag { option (gogoproto.goproto_enum_stringer) = true; option (gogoproto.goproto_enum_prefix) = false; @@ -112,7 +112,15 @@ 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. Only valid for precommit messages. + bytes extension_signature = 10; } // Commit contains the evidence that a block was committed by a set of diff --git a/proto/tendermint/types/types.proto.intermediate b/proto/tendermint/types/types.proto.intermediate deleted file mode 100644 index 280cd7133..000000000 --- a/proto/tendermint/types/types.proto.intermediate +++ /dev/null @@ -1,192 +0,0 @@ -syntax = "proto3"; -package tendermint.types; - -import "gogoproto/gogo.proto"; -import "google/protobuf/timestamp.proto"; -import "tendermint/crypto/proof.proto"; -import "tendermint/version/types.proto"; -import "tendermint/types/validator.proto"; - -// This file is a temporary workaround to enable development during the ABCI++ -// project. This file should be deleted and any references to it removed when -// the ongoing work on ABCI++ is completed. -// -// This file supports building of the `tendermint.abci` proto package. -// For more information, see https://github.com/tendermint/tendermint/issues/8066 - -// BlockIdFlag indicates which BlockID the signature is for -enum BlockIDFlag { - option (gogoproto.goproto_enum_stringer) = true; - option (gogoproto.goproto_enum_prefix) = false; - - BLOCK_ID_FLAG_UNKNOWN = 0 - [(gogoproto.enumvalue_customname) = "BlockIDFlagUnknown"]; - BLOCK_ID_FLAG_ABSENT = 1 - [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; - BLOCK_ID_FLAG_COMMIT = 2 - [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; - BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; -} - -// SignedMsgType is a type of signed message in the consensus. -enum SignedMsgType { - option (gogoproto.goproto_enum_stringer) = true; - option (gogoproto.goproto_enum_prefix) = false; - - SIGNED_MSG_TYPE_UNKNOWN = 0 - [(gogoproto.enumvalue_customname) = "UnknownType"]; - // Votes - SIGNED_MSG_TYPE_PREVOTE = 1 - [(gogoproto.enumvalue_customname) = "PrevoteType"]; - SIGNED_MSG_TYPE_PRECOMMIT = 2 - [(gogoproto.enumvalue_customname) = "PrecommitType"]; - - // Proposals - SIGNED_MSG_TYPE_PROPOSAL = 32 - [(gogoproto.enumvalue_customname) = "ProposalType"]; -} - -// PartsetHeader -message PartSetHeader { - uint32 total = 1; - bytes hash = 2; -} - -message Part { - uint32 index = 1; - bytes bytes = 2; - tendermint.crypto.Proof proof = 3 [(gogoproto.nullable) = false]; -} - -// BlockID -message BlockID { - bytes hash = 1; - PartSetHeader part_set_header = 2 [(gogoproto.nullable) = false]; -} - -// -------------------------------- - -// Header defines the structure of a Tendermint block header. -message Header { - // basic block info - tendermint.version.Consensus version = 1 [(gogoproto.nullable) = false]; - string chain_id = 2 [(gogoproto.customname) = "ChainID"]; - int64 height = 3; - google.protobuf.Timestamp time = 4 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - - // prev block info - BlockID last_block_id = 5 [(gogoproto.nullable) = false]; - - // hashes of block data - bytes last_commit_hash = 6; // commit from validators from the last block - bytes data_hash = 7; // transactions - - // hashes from the app output from the prev block - bytes validators_hash = 8; // validators for the current block - bytes next_validators_hash = 9; // validators for the next block - bytes consensus_hash = 10; // consensus params for current block - bytes app_hash = 11; // state after txs from the previous block - bytes last_results_hash = - 12; // root hash of all results from the txs from the previous block - - // consensus info - bytes evidence_hash = 13; // evidence included in the block - bytes proposer_address = 14; // original proposer of the block -} - -// Data contains the set of transactions included in the block -message Data { - // Txs that will be applied by state @ block.Height+1. - // NOTE: not all txs here are valid. We're just agreeing on the order first. - // This means that block.AppHash does not include these txs. - repeated bytes txs = 1; -} - -// Vote represents a prevote, precommit, or commit vote from validators for -// consensus. -message Vote { - SignedMsgType type = 1; - int64 height = 2; - int32 round = 3; - BlockID block_id = 4 [ - (gogoproto.nullable) = false, - (gogoproto.customname) = "BlockID" - ]; // zero if vote is nil. - google.protobuf.Timestamp timestamp = 5 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes validator_address = 6; - int32 validator_index = 7; - bytes signature = 8; - VoteExtension vote_extension = 9; -} - -// 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. -message Commit { - int64 height = 1; - int32 round = 2; - BlockID block_id = 3 - [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; - repeated CommitSig signatures = 4 [(gogoproto.nullable) = false]; -} - -// CommitSig is a part of the Vote included in a Commit. -message CommitSig { - BlockIDFlag block_id_flag = 1; - bytes validator_address = 2; - google.protobuf.Timestamp timestamp = 3 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes signature = 4; - VoteExtensionToSign vote_extension = 5; -} - -message Proposal { - SignedMsgType type = 1; - int64 height = 2; - int32 round = 3; - int32 pol_round = 4; - BlockID block_id = 5 - [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; - google.protobuf.Timestamp timestamp = 6 - [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; - bytes signature = 7; -} - -message SignedHeader { - Header header = 1; - Commit commit = 2; -} - -message LightBlock { - SignedHeader signed_header = 1; - tendermint.types.ValidatorSet validator_set = 2; -} - -message BlockMeta { - BlockID block_id = 1 - [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; - int64 block_size = 2; - Header header = 3 [(gogoproto.nullable) = false]; - int64 num_txs = 4; -} - -// TxProof represents a Merkle proof of the presence of a transaction in the -// Merkle tree. -message TxProof { - bytes root_hash = 1; - bytes data = 2; - tendermint.crypto.Proof proof = 3; -} diff --git a/rpc/client/eventstream/eventstream.go b/rpc/client/eventstream/eventstream.go index 887e723ce..59cfc8b5f 100644 --- a/rpc/client/eventstream/eventstream.go +++ b/rpc/client/eventstream/eventstream.go @@ -189,6 +189,5 @@ type MissedItemsError struct { // Error satisfies the error interface. func (e *MissedItemsError) Error() string { - return fmt.Sprintf("missed events matching %q between %q and %q", - e.Query, e.NewestSeen, e.OldestPresent) + return fmt.Sprintf("missed events matching %q between %q and %q", e.Query, e.NewestSeen, e.OldestPresent) } diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index 2a9507f8e..435f80a5c 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -207,19 +207,11 @@ func (c *baseRPCClient) ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo return result, nil } -func (c *baseRPCClient) ABCIQuery( - ctx context.Context, - path string, - data bytes.HexBytes, -) (*coretypes.ResultABCIQuery, error) { +func (c *baseRPCClient) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*coretypes.ResultABCIQuery, error) { return c.ABCIQueryWithOptions(ctx, path, data, rpcclient.DefaultABCIQueryOptions) } -func (c *baseRPCClient) ABCIQueryWithOptions( - ctx context.Context, - path string, - data bytes.HexBytes, - opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { +func (c *baseRPCClient) ABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes, opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { result := new(coretypes.ResultABCIQuery) if err := c.caller.Call(ctx, "abci_query", abciQueryArgs{ Path: path, @@ -232,10 +224,7 @@ func (c *baseRPCClient) ABCIQueryWithOptions( return result, nil } -func (c *baseRPCClient) BroadcastTxCommit( - ctx context.Context, - tx types.Tx, -) (*coretypes.ResultBroadcastTxCommit, error) { +func (c *baseRPCClient) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) { result := new(coretypes.ResultBroadcastTxCommit) if err := c.caller.Call(ctx, "broadcast_tx_commit", txArgs{Tx: tx}, result); err != nil { return nil, err @@ -243,25 +232,15 @@ func (c *baseRPCClient) BroadcastTxCommit( return result, nil } -func (c *baseRPCClient) BroadcastTxAsync( - ctx context.Context, - tx types.Tx, -) (*coretypes.ResultBroadcastTx, error) { +func (c *baseRPCClient) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) { return c.broadcastTX(ctx, "broadcast_tx_async", tx) } -func (c *baseRPCClient) BroadcastTxSync( - ctx context.Context, - tx types.Tx, -) (*coretypes.ResultBroadcastTx, error) { +func (c *baseRPCClient) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) { return c.broadcastTX(ctx, "broadcast_tx_sync", tx) } -func (c *baseRPCClient) broadcastTX( - ctx context.Context, - route string, - tx types.Tx, -) (*coretypes.ResultBroadcastTx, error) { +func (c *baseRPCClient) broadcastTX(ctx context.Context, route string, tx types.Tx) (*coretypes.ResultBroadcastTx, error) { result := new(coretypes.ResultBroadcastTx) if err := c.caller.Call(ctx, route, txArgs{Tx: tx}, result); err != nil { return nil, err @@ -269,11 +248,7 @@ func (c *baseRPCClient) broadcastTX( return result, nil } -func (c *baseRPCClient) UnconfirmedTxs( - ctx context.Context, - page *int, - perPage *int, -) (*coretypes.ResultUnconfirmedTxs, error) { +func (c *baseRPCClient) UnconfirmedTxs(ctx context.Context, page *int, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) { result := new(coretypes.ResultUnconfirmedTxs) if err := c.caller.Call(ctx, "unconfirmed_txs", unconfirmedArgs{Page: page, PerPage: perPage}, result); err != nil { @@ -329,10 +304,7 @@ func (c *baseRPCClient) ConsensusState(ctx context.Context) (*coretypes.ResultCo return result, nil } -func (c *baseRPCClient) ConsensusParams( - ctx context.Context, - height *int64, -) (*coretypes.ResultConsensusParams, error) { +func (c *baseRPCClient) ConsensusParams(ctx context.Context, height *int64) (*coretypes.ResultConsensusParams, error) { result := new(coretypes.ResultConsensusParams) if err := c.caller.Call(ctx, "consensus_params", heightArgs{Height: height}, result); err != nil { return nil, err @@ -356,11 +328,7 @@ func (c *baseRPCClient) Health(ctx context.Context) (*coretypes.ResultHealth, er return result, nil } -func (c *baseRPCClient) BlockchainInfo( - ctx context.Context, - minHeight, - maxHeight int64, -) (*coretypes.ResultBlockchainInfo, error) { +func (c *baseRPCClient) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) { result := new(coretypes.ResultBlockchainInfo) if err := c.caller.Call(ctx, "blockchain", blockchainInfoArgs{ MinHeight: minHeight, @@ -403,10 +371,7 @@ func (c *baseRPCClient) BlockByHash(ctx context.Context, hash bytes.HexBytes) (* return result, nil } -func (c *baseRPCClient) BlockResults( - ctx context.Context, - height *int64, -) (*coretypes.ResultBlockResults, error) { +func (c *baseRPCClient) BlockResults(ctx context.Context, height *int64) (*coretypes.ResultBlockResults, error) { result := new(coretypes.ResultBlockResults) if err := c.caller.Call(ctx, "block_results", heightArgs{Height: height}, result); err != nil { return nil, err @@ -446,14 +411,7 @@ func (c *baseRPCClient) Tx(ctx context.Context, hash bytes.HexBytes, prove bool) return result, nil } -func (c *baseRPCClient) TxSearch( - ctx context.Context, - query string, - prove bool, - page, - perPage *int, - orderBy string, -) (*coretypes.ResultTxSearch, error) { +func (c *baseRPCClient) TxSearch(ctx context.Context, query string, prove bool, page, perPage *int, orderBy string) (*coretypes.ResultTxSearch, error) { result := new(coretypes.ResultTxSearch) if err := c.caller.Call(ctx, "tx_search", searchArgs{ Query: query, @@ -468,12 +426,7 @@ func (c *baseRPCClient) TxSearch( return result, nil } -func (c *baseRPCClient) BlockSearch( - ctx context.Context, - query string, - page, perPage *int, - orderBy string, -) (*coretypes.ResultBlockSearch, error) { +func (c *baseRPCClient) BlockSearch(ctx context.Context, query string, page, perPage *int, orderBy string) (*coretypes.ResultBlockSearch, error) { result := new(coretypes.ResultBlockSearch) if err := c.caller.Call(ctx, "block_search", searchArgs{ Query: query, @@ -487,12 +440,7 @@ func (c *baseRPCClient) BlockSearch( return result, nil } -func (c *baseRPCClient) Validators( - ctx context.Context, - height *int64, - page, - perPage *int, -) (*coretypes.ResultValidators, error) { +func (c *baseRPCClient) Validators(ctx context.Context, height *int64, page, perPage *int) (*coretypes.ResultValidators, error) { result := new(coretypes.ResultValidators) if err := c.caller.Call(ctx, "validators", validatorArgs{ Height: height, @@ -504,10 +452,7 @@ func (c *baseRPCClient) Validators( return result, nil } -func (c *baseRPCClient) BroadcastEvidence( - ctx context.Context, - ev types.Evidence, -) (*coretypes.ResultBroadcastEvidence, error) { +func (c *baseRPCClient) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) { result := new(coretypes.ResultBroadcastEvidence) if err := c.caller.Call(ctx, "broadcast_evidence", evidenceArgs{ Evidence: coretypes.Evidence{Value: ev}, diff --git a/rpc/client/http/ws.go b/rpc/client/http/ws.go index 2f188a24d..e9a5ac829 100644 --- a/rpc/client/http/ws.go +++ b/rpc/client/http/ws.go @@ -107,6 +107,7 @@ func (w *wsEvents) Unsubscribe(ctx context.Context, subscriber, query string) er } w.mtx.Lock() + defer w.mtx.Unlock() info, ok := w.subscriptions[query] if ok { if info.id != "" { @@ -114,7 +115,6 @@ func (w *wsEvents) Unsubscribe(ctx context.Context, subscriber, query string) er } delete(w.subscriptions, info.query) } - w.mtx.Unlock() return nil } @@ -129,8 +129,8 @@ func (w *wsEvents) UnsubscribeAll(ctx context.Context, subscriber string) error } w.mtx.Lock() + defer w.mtx.Unlock() w.subscriptions = make(map[string]*wsSubscription) - w.mtx.Unlock() return nil } diff --git a/rpc/client/local/local.go b/rpc/client/local/local.go index b9091efac..24a9a6d7e 100644 --- a/rpc/client/local/local.go +++ b/rpc/client/local/local.go @@ -78,11 +78,7 @@ func (c *Local) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) return c.ABCIQueryWithOptions(ctx, path, data, rpcclient.DefaultABCIQueryOptions) } -func (c *Local) ABCIQueryWithOptions( - ctx context.Context, - path string, - data bytes.HexBytes, - opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { +func (c *Local) ABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes, opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { return c.env.ABCIQuery(ctx, path, data, opts.Height, opts.Prove) } @@ -189,23 +185,11 @@ func (c *Local) Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coret return c.env.Tx(ctx, hash, prove) } -func (c *Local) TxSearch( - ctx context.Context, - queryString string, - prove bool, - page, - perPage *int, - orderBy string, -) (*coretypes.ResultTxSearch, error) { +func (c *Local) TxSearch(ctx context.Context, queryString string, prove bool, page, perPage *int, orderBy string) (*coretypes.ResultTxSearch, error) { return c.env.TxSearch(ctx, queryString, prove, page, perPage, orderBy) } -func (c *Local) BlockSearch( - ctx context.Context, - queryString string, - page, perPage *int, - orderBy string, -) (*coretypes.ResultBlockSearch, error) { +func (c *Local) BlockSearch(ctx context.Context, queryString string, page, perPage *int, orderBy string) (*coretypes.ResultBlockSearch, error) { return c.env.BlockSearch(ctx, queryString, page, perPage, orderBy) } @@ -213,11 +197,7 @@ func (c *Local) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*core return c.env.BroadcastEvidence(ctx, coretypes.Evidence{Value: ev}) } -func (c *Local) Subscribe( - ctx context.Context, - subscriber, - queryString string, - capacity ...int) (out <-chan coretypes.ResultEvent, err error) { +func (c *Local) Subscribe(ctx context.Context, subscriber, queryString string, capacity ...int) (<-chan coretypes.ResultEvent, error) { q, err := query.New(queryString) if err != nil { return nil, fmt.Errorf("failed to parse query: %w", err) @@ -251,12 +231,7 @@ func (c *Local) Subscribe( return outc, nil } -func (c *Local) eventsRoutine( - ctx context.Context, - sub eventbus.Subscription, - subArgs pubsub.SubscribeArgs, - outc chan<- coretypes.ResultEvent, -) { +func (c *Local) eventsRoutine(ctx context.Context, sub eventbus.Subscription, subArgs pubsub.SubscribeArgs, outc chan<- coretypes.ResultEvent) { qstr := subArgs.Query.String() for { msg, err := sub.Next(ctx) @@ -271,17 +246,24 @@ func (c *Local) eventsRoutine( } continue } - outc <- coretypes.ResultEvent{ + select { + case outc <- coretypes.ResultEvent{ SubscriptionID: msg.SubscriptionID(), Query: qstr, Data: msg.Data(), Events: msg.Events(), + }: + case <-ctx.Done(): + return } } } // Try to resubscribe with exponential backoff. func (c *Local) resubscribe(ctx context.Context, subArgs pubsub.SubscribeArgs) eventbus.Subscription { + timer := time.NewTimer(0) + defer timer.Stop() + attempts := 0 for { if !c.IsRunning() { @@ -294,7 +276,13 @@ func (c *Local) resubscribe(ctx context.Context, subArgs pubsub.SubscribeArgs) e } attempts++ - time.Sleep((10 << uint(attempts)) * time.Millisecond) // 10ms -> 20ms -> 40ms + timer.Reset((10 << uint(attempts)) * time.Millisecond) // 10ms -> 20ms -> 40ms + select { + case <-timer.C: + continue + case <-ctx.Done(): + return nil + } } } diff --git a/rpc/client/mock/abci.go b/rpc/client/mock/abci.go index 8c652444b..a6aebdb14 100644 --- a/rpc/client/mock/abci.go +++ b/rpc/client/mock/abci.go @@ -25,47 +25,65 @@ var ( ) func (a ABCIApp) ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo, error) { - return &coretypes.ResultABCIInfo{Response: a.App.Info(proxy.RequestInfo)}, nil + res, err := a.App.Info(ctx, &proxy.RequestInfo) + if err != nil { + return nil, err + } + + return &coretypes.ResultABCIInfo{Response: *res}, nil } func (a ABCIApp) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*coretypes.ResultABCIQuery, error) { return a.ABCIQueryWithOptions(ctx, path, data, client.DefaultABCIQueryOptions) } -func (a ABCIApp) ABCIQueryWithOptions( - ctx context.Context, - path string, - data bytes.HexBytes, - opts client.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { - q := a.App.Query(abci.RequestQuery{ +func (a ABCIApp) ABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes, opts client.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { + q, err := a.App.Query(ctx, &abci.RequestQuery{ Data: data, Path: path, Height: opts.Height, Prove: opts.Prove, }) - return &coretypes.ResultABCIQuery{Response: q}, nil + if err != nil { + return nil, err + } + + return &coretypes.ResultABCIQuery{Response: *q}, nil } // NOTE: Caller should call a.App.Commit() separately, // this function does not actually wait for a commit. // TODO: Make it wait for a commit and set res.Height appropriately. func (a ABCIApp) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) { - res := coretypes.ResultBroadcastTxCommit{} - res.CheckTx = a.App.CheckTx(abci.RequestCheckTx{Tx: tx}) - if res.CheckTx.IsErr() { - return &res, nil + resp, err := a.App.CheckTx(ctx, &abci.RequestCheckTx{Tx: tx}) + if err != nil { + return nil, err } - fb := a.App.FinalizeBlock(abci.RequestFinalizeBlock{Txs: [][]byte{tx}}) + + res := &coretypes.ResultBroadcastTxCommit{CheckTx: *resp} + if res.CheckTx.IsErr() { + return res, nil + } + + fb, err := a.App.FinalizeBlock(ctx, &abci.RequestFinalizeBlock{Txs: [][]byte{tx}}) + if err != nil { + return nil, err + } + res.TxResult = *fb.TxResults[0] res.Height = -1 // TODO - return &res, nil + return res, nil } func (a ABCIApp) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) { - c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx}) + c, err := a.App.CheckTx(ctx, &abci.RequestCheckTx{Tx: tx}) + if err != nil { + return nil, err + } + // and this gets written in a background thread... if !c.IsErr() { - go func() { a.App.FinalizeBlock(abci.RequestFinalizeBlock{Txs: [][]byte{tx}}) }() + go func() { _, _ = a.App.FinalizeBlock(ctx, &abci.RequestFinalizeBlock{Txs: [][]byte{tx}}) }() } return &coretypes.ResultBroadcastTx{ Code: c.Code, @@ -77,10 +95,14 @@ func (a ABCIApp) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes. } func (a ABCIApp) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) { - c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx}) + c, err := a.App.CheckTx(ctx, &abci.RequestCheckTx{Tx: tx}) + if err != nil { + return nil, err + } + // and this gets written in a background thread... if !c.IsErr() { - go func() { a.App.FinalizeBlock(abci.RequestFinalizeBlock{Txs: [][]byte{tx}}) }() + go func() { _, _ = a.App.FinalizeBlock(ctx, &abci.RequestFinalizeBlock{Txs: [][]byte{tx}}) }() } return &coretypes.ResultBroadcastTx{ Code: c.Code, @@ -113,11 +135,7 @@ func (m ABCIMock) ABCIQuery(ctx context.Context, path string, data bytes.HexByte return m.ABCIQueryWithOptions(ctx, path, data, client.DefaultABCIQueryOptions) } -func (m ABCIMock) ABCIQueryWithOptions( - ctx context.Context, - path string, - data bytes.HexBytes, - opts client.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { +func (m ABCIMock) ABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes, opts client.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) { res, err := m.Query.GetResponse(QueryArgs{path, data, opts.Height, opts.Prove}) if err != nil { return nil, err @@ -185,11 +203,7 @@ func (r *ABCIRecorder) ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo, return res, err } -func (r *ABCIRecorder) ABCIQuery( - ctx context.Context, - path string, - data bytes.HexBytes, -) (*coretypes.ResultABCIQuery, error) { +func (r *ABCIRecorder) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*coretypes.ResultABCIQuery, error) { return r.ABCIQueryWithOptions(ctx, path, data, client.DefaultABCIQueryOptions) } diff --git a/rpc/client/mock/abci_test.go b/rpc/client/mock/abci_test.go index 489133a5b..e35ccf29c 100644 --- a/rpc/client/mock/abci_test.go +++ b/rpc/client/mock/abci_test.go @@ -185,7 +185,8 @@ func TestABCIApp(t *testing.T) { // commit // TODO: This may not be necessary in the future if res.Height == -1 { - m.App.Commit() + _, err := m.App.Commit(ctx) + require.NoError(t, err) } // check the key diff --git a/rpc/client/mocks/client.go b/rpc/client/mocks/client.go index d9049ea44..9a286eaf2 100644 --- a/rpc/client/mocks/client.go +++ b/rpc/client/mocks/client.go @@ -12,6 +12,8 @@ import ( mock "github.com/stretchr/testify/mock" + testing "testing" + types "github.com/tendermint/tendermint/types" ) @@ -795,3 +797,13 @@ func (_m *Client) Validators(ctx context.Context, height *int64, page *int, perP return r0, r1 } + +// NewClient creates a new instance of Client. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewClient(t testing.TB) *Client { + mock := &Client{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/rpc/jsonrpc/server/http_server.go b/rpc/jsonrpc/server/http_server.go index 32917b8cb..0b715835d 100644 --- a/rpc/jsonrpc/server/http_server.go +++ b/rpc/jsonrpc/server/http_server.go @@ -46,13 +46,7 @@ func DefaultConfig() *Config { // Serve creates a http.Server and calls Serve with the given listener. It // wraps handler to recover panics and limit the request body size. -func Serve( - ctx context.Context, - listener net.Listener, - handler http.Handler, - logger log.Logger, - config *Config, -) error { +func Serve(ctx context.Context, listener net.Listener, handler http.Handler, logger log.Logger, config *Config) error { logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s", listener.Addr())) h := recoverAndLogHandler(MaxBytesHandler(handler, config.MaxBodyBytes), logger) s := &http.Server{ @@ -83,19 +77,14 @@ func Serve( // Serve creates a http.Server and calls ServeTLS with the given listener, // certFile and keyFile. It wraps handler to recover panics and limit the // request body size. -func ServeTLS( - ctx context.Context, - listener net.Listener, - handler http.Handler, - certFile, keyFile string, - logger log.Logger, - config *Config, -) error { - logger.Info(fmt.Sprintf("Starting RPC HTTPS server on %s (cert: %q, key: %q)", - listener.Addr(), certFile, keyFile)) - h := recoverAndLogHandler(MaxBytesHandler(handler, config.MaxBodyBytes), logger) +func ServeTLS(ctx context.Context, listener net.Listener, handler http.Handler, certFile, keyFile string, logger log.Logger, config *Config) error { + logger.Info("Starting RPC HTTPS server", + "listenterAddr", listener.Addr(), + "certFile", certFile, + "keyFile", keyFile) + s := &http.Server{ - Handler: h, + Handler: recoverAndLogHandler(MaxBytesHandler(handler, config.MaxBodyBytes), logger), ReadTimeout: config.ReadTimeout, WriteTimeout: config.WriteTimeout, MaxHeaderBytes: config.MaxHeaderBytes, diff --git a/rpc/jsonrpc/server/ws_handler.go b/rpc/jsonrpc/server/ws_handler.go index 3fc86b86f..7f2221b24 100644 --- a/rpc/jsonrpc/server/ws_handler.go +++ b/rpc/jsonrpc/server/ws_handler.go @@ -36,11 +36,7 @@ type WebsocketManager struct { // NewWebsocketManager returns a new WebsocketManager that passes a map of // functions, connection options and logger to new WS connections. -func NewWebsocketManager( - logger log.Logger, - funcMap map[string]*RPCFunc, - wsConnOptions ...func(*wsConnection), -) *WebsocketManager { +func NewWebsocketManager(logger log.Logger, funcMap map[string]*RPCFunc, wsConnOptions ...func(*wsConnection)) *WebsocketManager { return &WebsocketManager{ funcMap: funcMap, Upgrader: websocket.Upgrader{ @@ -137,12 +133,7 @@ type wsConnection struct { // description of how to configure ping period and pong wait time. NOTE: if the // write buffer is full, pongs may be dropped, which may cause clients to // disconnect. see https://github.com/gorilla/websocket/issues/97 -func newWSConnection( - baseConn *websocket.Conn, - funcMap map[string]*RPCFunc, - logger log.Logger, - options ...func(*wsConnection), -) *wsConnection { +func newWSConnection(baseConn *websocket.Conn, funcMap map[string]*RPCFunc, logger log.Logger, options ...func(*wsConnection)) *wsConnection { wsc := &wsConnection{ Logger: logger, remoteAddr: baseConn.RemoteAddr().String(), diff --git a/scripts/abci-gen.sh b/scripts/abci-gen.sh deleted file mode 100755 index f4666dee9..000000000 --- a/scripts/abci-gen.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -# This file was added during development of ABCI++. This file is a script to allow -# the intermediate proto files to be built while active development proceeds -# on ABCI++. -# This file should be removed when work on ABCI++ is complete. -# For more information, see https://github.com/tendermint/tendermint/issues/8066. -set -euo pipefail - -cp ./proto/tendermint/abci/types.proto.intermediate ./proto/tendermint/abci/types.proto -cp ./proto/tendermint/types/types.proto.intermediate ./proto/tendermint/types/types.proto - -MODNAME="$(go list -m)" -find ./proto/tendermint -name '*.proto' -not -path "./proto/tendermint/abci/types.proto" \ - -exec sh ./scripts/protopackage.sh {} "$MODNAME" ';' - -sh ./scripts/protopackage.sh ./proto/tendermint/abci/types.proto $MODNAME "abci/types" - -make proto-gen - -echo "proto files have been compiled" - -echo "checking out copied files" - -find proto/tendermint/ -name '*.proto' -not -path "*.intermediate"\ - | xargs -I {} git checkout {} - -find proto/tendermint/ -name '*.pb.go' \ - | xargs -I {} git checkout {} diff --git a/scripts/confix/confix_test.go b/scripts/confix/confix_test.go index b7be1247d..ec258f4ca 100644 --- a/scripts/confix/confix_test.go +++ b/scripts/confix/confix_test.go @@ -25,6 +25,7 @@ func TestGuessConfigVersion(t *testing.T) { path, want string }{ {"testdata/non-config.toml", ""}, + {"testdata/v30-config.toml", ""}, {"testdata/v31-config.toml", ""}, {"testdata/v32-config.toml", "v0.32"}, {"testdata/v33-config.toml", "v0.33"}, diff --git a/scripts/confix/testdata/README.md b/scripts/confix/testdata/README.md new file mode 100644 index 000000000..5bbfa795f --- /dev/null +++ b/scripts/confix/testdata/README.md @@ -0,0 +1,52 @@ +# Test data for `confix` and `condiff` + +The files in this directory are stock Tendermint configuration files generated +by the last point release of each version series from v0.26 to present, along +with diffs between consecutive versions. + +## Config Samples + +The files named `vXX-config.toml` were generated by checking out and building +the corresponding version of Tendermint v0.xx.y and initializing a new node in +an empty home directory. The resulting `config.toml` file was copied here. +The exact build instructions vary a bit, but a general repro looks like: + +```shell +# This example uses v0.31, substitute the version of your choice. +# Note that the branch names and tags may differ. +# Versions prior to v0.26 may not build. +git checkout v0.31.9 +git clean -fdx + +# Versions prior to v0.32 do not have Go module files. +# Those that do may need some dependencies manually updated. +go mod init github.com/tendermint/tendermint +go mod tidy +go get golang.org/x/sys + +# Once you sort out the dependencies, this should usually work. +make build + +# Confirm you go the version you expected, and generate the file. +./build/tendermint --home=tmhome version +./build/tendermint --home=tmhome init + +# Copy the file out. +cp ./tmhome/config/config.toml v31-config.toml +``` + +## Version Diffs + +The files named `diff-XX-YY.txt` were generated by using the `condiff` tool on +the config samples for versions v0.XX and v0.YY: + +```shell +go run ./scripts/confix/condiff -desnake vXX-config vYY-config.toml > diff-XX-YY.txt +``` + +The `baseline.txt` was computed in the same way, but using an empty starting +file so that we capture all the settings in the target: + +```shell +go run ./scripts/confix/condiff -desnake /dev/null v26-config.toml > baseline.txt +``` diff --git a/scripts/confix/testdata/baseline.txt b/scripts/confix/testdata/baseline.txt new file mode 100644 index 000000000..421343704 --- /dev/null +++ b/scripts/confix/testdata/baseline.txt @@ -0,0 +1,73 @@ ++M abci ++M db-backend ++M db-dir ++M fast-sync ++M filter-peers ++M genesis-file ++M log-format ++M log-level ++M moniker ++M node-key-file ++M priv-validator-file ++M priv-validator-laddr ++M prof-laddr ++M proxy-app ++S consensus ++M consensus.wal-file ++M consensus.timeout-propose ++M consensus.timeout-propose-delta ++M consensus.timeout-prevote ++M consensus.timeout-prevote-delta ++M consensus.timeout-precommit ++M consensus.timeout-precommit-delta ++M consensus.timeout-commit ++M consensus.skip-timeout-commit ++M consensus.create-empty-blocks ++M consensus.create-empty-blocks-interval ++M consensus.peer-gossip-sleep-duration ++M consensus.peer-query-maj23-sleep-duration ++M consensus.blocktime-iota ++S instrumentation ++M instrumentation.prometheus ++M instrumentation.prometheus-listen-addr ++M instrumentation.max-open-connections ++M instrumentation.namespace ++S mempool ++M mempool.recheck ++M mempool.broadcast ++M mempool.wal-dir ++M mempool.size ++M mempool.cache-size ++S p2p ++M p2p.laddr ++M p2p.external-address ++M p2p.seeds ++M p2p.persistent-peers ++M p2p.upnp ++M p2p.addr-book-file ++M p2p.addr-book-strict ++M p2p.max-num-inbound-peers ++M p2p.max-num-outbound-peers ++M p2p.flush-throttle-timeout ++M p2p.max-packet-msg-payload-size ++M p2p.send-rate ++M p2p.recv-rate ++M p2p.pex ++M p2p.seed-mode ++M p2p.private-peer-ids ++M p2p.allow-duplicate-ip ++M p2p.handshake-timeout ++M p2p.dial-timeout ++S rpc ++M rpc.laddr ++M rpc.cors-allowed-origins ++M rpc.cors-allowed-methods ++M rpc.cors-allowed-headers ++M rpc.grpc-laddr ++M rpc.grpc-max-open-connections ++M rpc.unsafe ++M rpc.max-open-connections ++S tx-index ++M tx-index.indexer ++M tx-index.index-tags ++M tx-index.index-all-tags diff --git a/test/fuzz/mempool/testdata/cases/empty b/scripts/confix/testdata/diff-26-27.txt similarity index 100% rename from test/fuzz/mempool/testdata/cases/empty rename to scripts/confix/testdata/diff-26-27.txt diff --git a/scripts/confix/testdata/diff-27-28.txt b/scripts/confix/testdata/diff-27-28.txt new file mode 100644 index 000000000..c5c00449d --- /dev/null +++ b/scripts/confix/testdata/diff-27-28.txt @@ -0,0 +1,3 @@ +-M priv-validator-file ++M priv-validator-key-file ++M priv-validator-state-file diff --git a/test/fuzz/p2p/secretconnection/testdata/cases/empty b/scripts/confix/testdata/diff-28-29.txt similarity index 100% rename from test/fuzz/p2p/secretconnection/testdata/cases/empty rename to scripts/confix/testdata/diff-28-29.txt diff --git a/test/fuzz/rpc/jsonrpc/server/testdata/cases/empty b/scripts/confix/testdata/diff-29-30.txt similarity index 100% rename from test/fuzz/rpc/jsonrpc/server/testdata/cases/empty rename to scripts/confix/testdata/diff-29-30.txt diff --git a/scripts/confix/testdata/diff-30-31.txt b/scripts/confix/testdata/diff-30-31.txt new file mode 100644 index 000000000..0f93b761e --- /dev/null +++ b/scripts/confix/testdata/diff-30-31.txt @@ -0,0 +1,7 @@ +-M consensus.blocktime-iota ++M mempool.max-txs-bytes ++M rpc.max-subscription-clients ++M rpc.max-subscriptions-per-client ++M rpc.timeout-broadcast-tx-commit ++M rpc.tls-cert-file ++M rpc.tls-key-file diff --git a/scripts/confix/testdata/v26-config.toml b/scripts/confix/testdata/v26-config.toml new file mode 100644 index 000000000..a9237056b --- /dev/null +++ b/scripts/confix/testdata/v26-config.toml @@ -0,0 +1,249 @@ +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base config options ##### + +# TCP or UNIX socket address of the ABCI application, +# or the name of an ABCI application compiled in with the Tendermint binary +proxy_app = "tcp://127.0.0.1:26658" + +# A custom human readable name for this node +moniker = "localhost" + +# If this node is many blocks behind the tip of the chain, FastSync +# allows them to catchup quickly by downloading blocks in parallel +# and verifying their commits +fast_sync = true + +# Database backend: leveldb | memdb | cleveldb +db_backend = "leveldb" + +# Database directory +db_dir = "data" + +# Output level for logging, including package level options +log_level = "main:info,state:info,*:error" + +# Output format: 'plain' (colored text) or 'json' +log_format = "plain" + +##### additional base config options ##### + +# Path to the JSON file containing the initial validator set and other meta data +genesis_file = "config/genesis.json" + +# Path to the JSON file containing the private key to use as a validator in the consensus protocol +priv_validator_file = "config/priv_validator.json" + +# TCP or UNIX socket address for Tendermint to listen on for +# connections from an external PrivValidator process +priv_validator_laddr = "" + +# Path to the JSON file containing the private key to use for node authentication in the p2p protocol +node_key_file = "config/node_key.json" + +# Mechanism to connect to the ABCI application: socket | grpc +abci = "socket" + +# TCP or UNIX socket address for the profiling server to listen on +prof_laddr = "" + +# If true, query the ABCI app on connecting to a new peer +# so the app can decide if we should keep the connection or not +filter_peers = false + +##### advanced configuration options ##### + +##### rpc server configuration options ##### +[rpc] + +# TCP or UNIX socket address for the RPC server to listen on +laddr = "tcp://0.0.0.0:26657" + +# A list of origins a cross-domain request can be executed from +# Default value '[]' disables cors support +# Use '["*"]' to allow any origin +cors_allowed_origins = "[]" + +# A list of methods the client is allowed to use with cross-domain requests +cors_allowed_methods = "[HEAD GET POST]" + +# A list of non simple headers the client is allowed to use with cross-domain requests +cors_allowed_headers = "[Origin Accept Content-Type X-Requested-With X-Server-Time]" + +# TCP or UNIX socket address for the gRPC server to listen on +# NOTE: This server only supports /broadcast_tx_commit +grpc_laddr = "" + +# Maximum number of simultaneous connections. +# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections +# If you want to accept more significant number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +grpc_max_open_connections = 900 + +# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool +unsafe = false + +# Maximum number of simultaneous connections (including WebSocket). +# Does not include gRPC connections. See grpc_max_open_connections +# If you want to accept more significant number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +max_open_connections = 900 + +##### peer to peer configuration options ##### +[p2p] + +# Address to listen for incoming connections +laddr = "tcp://0.0.0.0:26656" + +# Address to advertise to peers for them to dial +# If empty, will use the same port as the laddr, +# and will introspect on the listener or use UPnP +# to figure out the address. +external_address = "" + +# Comma separated list of seed nodes to connect to +seeds = "" + +# Comma separated list of nodes to keep persistent connections to +persistent_peers = "" + +# UPNP port forwarding +upnp = false + +# Path to address book +addr_book_file = "config/addrbook.json" + +# Set true for strict address routability rules +# Set false for private or local networks +addr_book_strict = true + +# Maximum number of inbound peers +max_num_inbound_peers = 40 + +# Maximum number of outbound peers to connect to, excluding persistent peers +max_num_outbound_peers = 10 + +# Time to wait before flushing messages out on the connection +flush_throttle_timeout = "100ms" + +# Maximum size of a message packet payload, in bytes +max_packet_msg_payload_size = 1024 + +# Rate at which packets can be sent, in bytes/second +send_rate = 5120000 + +# Rate at which packets can be received, in bytes/second +recv_rate = 5120000 + +# Set true to enable the peer-exchange reactor +pex = true + +# Seed mode, in which node constantly crawls the network and looks for +# peers. If another node asks it for addresses, it responds and disconnects. +# +# Does not work if the peer-exchange reactor is disabled. +seed_mode = false + +# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) +private_peer_ids = "" + +# Toggle to disable guard against peers connecting from the same ip. +allow_duplicate_ip = true + +# Peer connection configuration. +handshake_timeout = "20s" +dial_timeout = "3s" + +##### mempool configuration options ##### +[mempool] + +recheck = true +broadcast = true +wal_dir = "" + +# size of the mempool +size = 5000 + +# size of the cache (used to filter transactions we saw earlier) +cache_size = 10000 + +##### consensus configuration options ##### +[consensus] + +wal_file = "data/cs.wal/wal" + +timeout_propose = "3s" +timeout_propose_delta = "500ms" +timeout_prevote = "1s" +timeout_prevote_delta = "500ms" +timeout_precommit = "1s" +timeout_precommit_delta = "500ms" +timeout_commit = "1s" + +# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) +skip_timeout_commit = false + +# EmptyBlocks mode and possible interval between empty blocks +create_empty_blocks = true +create_empty_blocks_interval = "0s" + +# Reactor sleep duration parameters +peer_gossip_sleep_duration = "100ms" +peer_query_maj23_sleep_duration = "2s" + +# Block time parameters. Corresponds to the minimum time increment between consecutive blocks. +blocktime_iota = "1s" + +##### transactions indexer configuration options ##### +[tx_index] + +# What indexer to use for transactions +# +# Options: +# 1) "null" (default) +# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). +indexer = "kv" + +# Comma-separated list of tags to index (by default the only tag is "tx.hash") +# +# You can also index transactions by height by adding "tx.height" tag here. +# +# It's recommended to index only a subset of tags due to possible memory +# bloat. This is, of course, depends on the indexer's DB and the volume of +# transactions. +index_tags = "" + +# When set to true, tells indexer to index all tags (predefined tags: +# "tx.hash", "tx.height" and all tags from DeliverTx responses). +# +# Note this may be not desirable (see the comment above). IndexTags has a +# precedence over IndexAllTags (i.e. when given both, IndexTags will be +# indexed). +index_all_tags = false + +##### instrumentation configuration options ##### +[instrumentation] + +# When true, Prometheus metrics are served under /metrics on +# PrometheusListenAddr. +# Check out the documentation for the list of available metrics. +prometheus = false + +# Address to listen for Prometheus collector(s) connections +prometheus_listen_addr = ":26660" + +# Maximum number of simultaneous connections. +# If you want to accept more significant number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max_open_connections = 3 + +# Instrumentation namespace +namespace = "tendermint" diff --git a/scripts/confix/testdata/v27-config.toml b/scripts/confix/testdata/v27-config.toml new file mode 100644 index 000000000..25e3b582f --- /dev/null +++ b/scripts/confix/testdata/v27-config.toml @@ -0,0 +1,249 @@ +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base config options ##### + +# TCP or UNIX socket address of the ABCI application, +# or the name of an ABCI application compiled in with the Tendermint binary +proxy_app = "tcp://127.0.0.1:26658" + +# A custom human readable name for this node +moniker = "localhost" + +# If this node is many blocks behind the tip of the chain, FastSync +# allows them to catchup quickly by downloading blocks in parallel +# and verifying their commits +fast_sync = true + +# Database backend: leveldb | memdb | cleveldb +db_backend = "leveldb" + +# Database directory +db_dir = "data" + +# Output level for logging, including package level options +log_level = "main:info,state:info,*:error" + +# Output format: 'plain' (colored text) or 'json' +log_format = "plain" + +##### additional base config options ##### + +# Path to the JSON file containing the initial validator set and other meta data +genesis_file = "config/genesis.json" + +# Path to the JSON file containing the private key to use as a validator in the consensus protocol +priv_validator_file = "config/priv_validator.json" + +# TCP or UNIX socket address for Tendermint to listen on for +# connections from an external PrivValidator process +priv_validator_laddr = "" + +# Path to the JSON file containing the private key to use for node authentication in the p2p protocol +node_key_file = "config/node_key.json" + +# Mechanism to connect to the ABCI application: socket | grpc +abci = "socket" + +# TCP or UNIX socket address for the profiling server to listen on +prof_laddr = "" + +# If true, query the ABCI app on connecting to a new peer +# so the app can decide if we should keep the connection or not +filter_peers = false + +##### advanced configuration options ##### + +##### rpc server configuration options ##### +[rpc] + +# TCP or UNIX socket address for the RPC server to listen on +laddr = "tcp://0.0.0.0:26657" + +# A list of origins a cross-domain request can be executed from +# Default value '[]' disables cors support +# Use '["*"]' to allow any origin +cors_allowed_origins = [] + +# A list of methods the client is allowed to use with cross-domain requests +cors_allowed_methods = ["HEAD", "GET", "POST", ] + +# A list of non simple headers the client is allowed to use with cross-domain requests +cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time", ] + +# TCP or UNIX socket address for the gRPC server to listen on +# NOTE: This server only supports /broadcast_tx_commit +grpc_laddr = "" + +# Maximum number of simultaneous connections. +# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +grpc_max_open_connections = 900 + +# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool +unsafe = false + +# Maximum number of simultaneous connections (including WebSocket). +# Does not include gRPC connections. See grpc_max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +max_open_connections = 900 + +##### peer to peer configuration options ##### +[p2p] + +# Address to listen for incoming connections +laddr = "tcp://0.0.0.0:26656" + +# Address to advertise to peers for them to dial +# If empty, will use the same port as the laddr, +# and will introspect on the listener or use UPnP +# to figure out the address. +external_address = "" + +# Comma separated list of seed nodes to connect to +seeds = "" + +# Comma separated list of nodes to keep persistent connections to +persistent_peers = "" + +# UPNP port forwarding +upnp = false + +# Path to address book +addr_book_file = "config/addrbook.json" + +# Set true for strict address routability rules +# Set false for private or local networks +addr_book_strict = true + +# Maximum number of inbound peers +max_num_inbound_peers = 40 + +# Maximum number of outbound peers to connect to, excluding persistent peers +max_num_outbound_peers = 10 + +# Time to wait before flushing messages out on the connection +flush_throttle_timeout = "100ms" + +# Maximum size of a message packet payload, in bytes +max_packet_msg_payload_size = 1024 + +# Rate at which packets can be sent, in bytes/second +send_rate = 5120000 + +# Rate at which packets can be received, in bytes/second +recv_rate = 5120000 + +# Set true to enable the peer-exchange reactor +pex = true + +# Seed mode, in which node constantly crawls the network and looks for +# peers. If another node asks it for addresses, it responds and disconnects. +# +# Does not work if the peer-exchange reactor is disabled. +seed_mode = false + +# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) +private_peer_ids = "" + +# Toggle to disable guard against peers connecting from the same ip. +allow_duplicate_ip = true + +# Peer connection configuration. +handshake_timeout = "20s" +dial_timeout = "3s" + +##### mempool configuration options ##### +[mempool] + +recheck = true +broadcast = true +wal_dir = "" + +# size of the mempool +size = 5000 + +# size of the cache (used to filter transactions we saw earlier) +cache_size = 10000 + +##### consensus configuration options ##### +[consensus] + +wal_file = "data/cs.wal/wal" + +timeout_propose = "3s" +timeout_propose_delta = "500ms" +timeout_prevote = "1s" +timeout_prevote_delta = "500ms" +timeout_precommit = "1s" +timeout_precommit_delta = "500ms" +timeout_commit = "1s" + +# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) +skip_timeout_commit = false + +# EmptyBlocks mode and possible interval between empty blocks +create_empty_blocks = true +create_empty_blocks_interval = "0s" + +# Reactor sleep duration parameters +peer_gossip_sleep_duration = "100ms" +peer_query_maj23_sleep_duration = "2s" + +# Block time parameters. Corresponds to the minimum time increment between consecutive blocks. +blocktime_iota = "1s" + +##### transactions indexer configuration options ##### +[tx_index] + +# What indexer to use for transactions +# +# Options: +# 1) "null" +# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). +indexer = "kv" + +# Comma-separated list of tags to index (by default the only tag is "tx.hash") +# +# You can also index transactions by height by adding "tx.height" tag here. +# +# It's recommended to index only a subset of tags due to possible memory +# bloat. This is, of course, depends on the indexer's DB and the volume of +# transactions. +index_tags = "" + +# When set to true, tells indexer to index all tags (predefined tags: +# "tx.hash", "tx.height" and all tags from DeliverTx responses). +# +# Note this may be not desirable (see the comment above). IndexTags has a +# precedence over IndexAllTags (i.e. when given both, IndexTags will be +# indexed). +index_all_tags = false + +##### instrumentation configuration options ##### +[instrumentation] + +# When true, Prometheus metrics are served under /metrics on +# PrometheusListenAddr. +# Check out the documentation for the list of available metrics. +prometheus = false + +# Address to listen for Prometheus collector(s) connections +prometheus_listen_addr = ":26660" + +# Maximum number of simultaneous connections. +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max_open_connections = 3 + +# Instrumentation namespace +namespace = "tendermint" diff --git a/scripts/confix/testdata/v28-config.toml b/scripts/confix/testdata/v28-config.toml new file mode 100644 index 000000000..b4aaa5aae --- /dev/null +++ b/scripts/confix/testdata/v28-config.toml @@ -0,0 +1,252 @@ +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base config options ##### + +# TCP or UNIX socket address of the ABCI application, +# or the name of an ABCI application compiled in with the Tendermint binary +proxy_app = "tcp://127.0.0.1:26658" + +# A custom human readable name for this node +moniker = "localhost" + +# If this node is many blocks behind the tip of the chain, FastSync +# allows them to catchup quickly by downloading blocks in parallel +# and verifying their commits +fast_sync = true + +# Database backend: leveldb | memdb | cleveldb +db_backend = "leveldb" + +# Database directory +db_dir = "data" + +# Output level for logging, including package level options +log_level = "main:info,state:info,*:error" + +# Output format: 'plain' (colored text) or 'json' +log_format = "plain" + +##### additional base config options ##### + +# Path to the JSON file containing the initial validator set and other meta data +genesis_file = "config/genesis.json" + +# Path to the JSON file containing the private key to use as a validator in the consensus protocol +priv_validator_key_file = "config/priv_validator_key.json" + +# Path to the JSON file containing the last sign state of a validator +priv_validator_state_file = "data/priv_validator_state.json" + +# TCP or UNIX socket address for Tendermint to listen on for +# connections from an external PrivValidator process +priv_validator_laddr = "" + +# Path to the JSON file containing the private key to use for node authentication in the p2p protocol +node_key_file = "config/node_key.json" + +# Mechanism to connect to the ABCI application: socket | grpc +abci = "socket" + +# TCP or UNIX socket address for the profiling server to listen on +prof_laddr = "" + +# If true, query the ABCI app on connecting to a new peer +# so the app can decide if we should keep the connection or not +filter_peers = false + +##### advanced configuration options ##### + +##### rpc server configuration options ##### +[rpc] + +# TCP or UNIX socket address for the RPC server to listen on +laddr = "tcp://0.0.0.0:26657" + +# A list of origins a cross-domain request can be executed from +# Default value '[]' disables cors support +# Use '["*"]' to allow any origin +cors_allowed_origins = [] + +# A list of methods the client is allowed to use with cross-domain requests +cors_allowed_methods = ["HEAD", "GET", "POST", ] + +# A list of non simple headers the client is allowed to use with cross-domain requests +cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time", ] + +# TCP or UNIX socket address for the gRPC server to listen on +# NOTE: This server only supports /broadcast_tx_commit +grpc_laddr = "" + +# Maximum number of simultaneous connections. +# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +grpc_max_open_connections = 900 + +# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool +unsafe = false + +# Maximum number of simultaneous connections (including WebSocket). +# Does not include gRPC connections. See grpc_max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +max_open_connections = 900 + +##### peer to peer configuration options ##### +[p2p] + +# Address to listen for incoming connections +laddr = "tcp://0.0.0.0:26656" + +# Address to advertise to peers for them to dial +# If empty, will use the same port as the laddr, +# and will introspect on the listener or use UPnP +# to figure out the address. +external_address = "" + +# Comma separated list of seed nodes to connect to +seeds = "" + +# Comma separated list of nodes to keep persistent connections to +persistent_peers = "" + +# UPNP port forwarding +upnp = false + +# Path to address book +addr_book_file = "config/addrbook.json" + +# Set true for strict address routability rules +# Set false for private or local networks +addr_book_strict = true + +# Maximum number of inbound peers +max_num_inbound_peers = 40 + +# Maximum number of outbound peers to connect to, excluding persistent peers +max_num_outbound_peers = 10 + +# Time to wait before flushing messages out on the connection +flush_throttle_timeout = "100ms" + +# Maximum size of a message packet payload, in bytes +max_packet_msg_payload_size = 1024 + +# Rate at which packets can be sent, in bytes/second +send_rate = 5120000 + +# Rate at which packets can be received, in bytes/second +recv_rate = 5120000 + +# Set true to enable the peer-exchange reactor +pex = true + +# Seed mode, in which node constantly crawls the network and looks for +# peers. If another node asks it for addresses, it responds and disconnects. +# +# Does not work if the peer-exchange reactor is disabled. +seed_mode = false + +# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) +private_peer_ids = "" + +# Toggle to disable guard against peers connecting from the same ip. +allow_duplicate_ip = false + +# Peer connection configuration. +handshake_timeout = "20s" +dial_timeout = "3s" + +##### mempool configuration options ##### +[mempool] + +recheck = true +broadcast = true +wal_dir = "" + +# size of the mempool +size = 5000 + +# size of the cache (used to filter transactions we saw earlier) +cache_size = 10000 + +##### consensus configuration options ##### +[consensus] + +wal_file = "data/cs.wal/wal" + +timeout_propose = "3s" +timeout_propose_delta = "500ms" +timeout_prevote = "1s" +timeout_prevote_delta = "500ms" +timeout_precommit = "1s" +timeout_precommit_delta = "500ms" +timeout_commit = "1s" + +# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) +skip_timeout_commit = false + +# EmptyBlocks mode and possible interval between empty blocks +create_empty_blocks = true +create_empty_blocks_interval = "0s" + +# Reactor sleep duration parameters +peer_gossip_sleep_duration = "100ms" +peer_query_maj23_sleep_duration = "2s" + +# Block time parameters. Corresponds to the minimum time increment between consecutive blocks. +blocktime_iota = "1s" + +##### transactions indexer configuration options ##### +[tx_index] + +# What indexer to use for transactions +# +# Options: +# 1) "null" +# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). +indexer = "kv" + +# Comma-separated list of tags to index (by default the only tag is "tx.hash") +# +# You can also index transactions by height by adding "tx.height" tag here. +# +# It's recommended to index only a subset of tags due to possible memory +# bloat. This is, of course, depends on the indexer's DB and the volume of +# transactions. +index_tags = "" + +# When set to true, tells indexer to index all tags (predefined tags: +# "tx.hash", "tx.height" and all tags from DeliverTx responses). +# +# Note this may be not desirable (see the comment above). IndexTags has a +# precedence over IndexAllTags (i.e. when given both, IndexTags will be +# indexed). +index_all_tags = false + +##### instrumentation configuration options ##### +[instrumentation] + +# When true, Prometheus metrics are served under /metrics on +# PrometheusListenAddr. +# Check out the documentation for the list of available metrics. +prometheus = false + +# Address to listen for Prometheus collector(s) connections +prometheus_listen_addr = ":26660" + +# Maximum number of simultaneous connections. +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max_open_connections = 3 + +# Instrumentation namespace +namespace = "tendermint" diff --git a/scripts/confix/testdata/v29-config.toml b/scripts/confix/testdata/v29-config.toml new file mode 100644 index 000000000..b4aaa5aae --- /dev/null +++ b/scripts/confix/testdata/v29-config.toml @@ -0,0 +1,252 @@ +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base config options ##### + +# TCP or UNIX socket address of the ABCI application, +# or the name of an ABCI application compiled in with the Tendermint binary +proxy_app = "tcp://127.0.0.1:26658" + +# A custom human readable name for this node +moniker = "localhost" + +# If this node is many blocks behind the tip of the chain, FastSync +# allows them to catchup quickly by downloading blocks in parallel +# and verifying their commits +fast_sync = true + +# Database backend: leveldb | memdb | cleveldb +db_backend = "leveldb" + +# Database directory +db_dir = "data" + +# Output level for logging, including package level options +log_level = "main:info,state:info,*:error" + +# Output format: 'plain' (colored text) or 'json' +log_format = "plain" + +##### additional base config options ##### + +# Path to the JSON file containing the initial validator set and other meta data +genesis_file = "config/genesis.json" + +# Path to the JSON file containing the private key to use as a validator in the consensus protocol +priv_validator_key_file = "config/priv_validator_key.json" + +# Path to the JSON file containing the last sign state of a validator +priv_validator_state_file = "data/priv_validator_state.json" + +# TCP or UNIX socket address for Tendermint to listen on for +# connections from an external PrivValidator process +priv_validator_laddr = "" + +# Path to the JSON file containing the private key to use for node authentication in the p2p protocol +node_key_file = "config/node_key.json" + +# Mechanism to connect to the ABCI application: socket | grpc +abci = "socket" + +# TCP or UNIX socket address for the profiling server to listen on +prof_laddr = "" + +# If true, query the ABCI app on connecting to a new peer +# so the app can decide if we should keep the connection or not +filter_peers = false + +##### advanced configuration options ##### + +##### rpc server configuration options ##### +[rpc] + +# TCP or UNIX socket address for the RPC server to listen on +laddr = "tcp://0.0.0.0:26657" + +# A list of origins a cross-domain request can be executed from +# Default value '[]' disables cors support +# Use '["*"]' to allow any origin +cors_allowed_origins = [] + +# A list of methods the client is allowed to use with cross-domain requests +cors_allowed_methods = ["HEAD", "GET", "POST", ] + +# A list of non simple headers the client is allowed to use with cross-domain requests +cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time", ] + +# TCP or UNIX socket address for the gRPC server to listen on +# NOTE: This server only supports /broadcast_tx_commit +grpc_laddr = "" + +# Maximum number of simultaneous connections. +# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +grpc_max_open_connections = 900 + +# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool +unsafe = false + +# Maximum number of simultaneous connections (including WebSocket). +# Does not include gRPC connections. See grpc_max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +max_open_connections = 900 + +##### peer to peer configuration options ##### +[p2p] + +# Address to listen for incoming connections +laddr = "tcp://0.0.0.0:26656" + +# Address to advertise to peers for them to dial +# If empty, will use the same port as the laddr, +# and will introspect on the listener or use UPnP +# to figure out the address. +external_address = "" + +# Comma separated list of seed nodes to connect to +seeds = "" + +# Comma separated list of nodes to keep persistent connections to +persistent_peers = "" + +# UPNP port forwarding +upnp = false + +# Path to address book +addr_book_file = "config/addrbook.json" + +# Set true for strict address routability rules +# Set false for private or local networks +addr_book_strict = true + +# Maximum number of inbound peers +max_num_inbound_peers = 40 + +# Maximum number of outbound peers to connect to, excluding persistent peers +max_num_outbound_peers = 10 + +# Time to wait before flushing messages out on the connection +flush_throttle_timeout = "100ms" + +# Maximum size of a message packet payload, in bytes +max_packet_msg_payload_size = 1024 + +# Rate at which packets can be sent, in bytes/second +send_rate = 5120000 + +# Rate at which packets can be received, in bytes/second +recv_rate = 5120000 + +# Set true to enable the peer-exchange reactor +pex = true + +# Seed mode, in which node constantly crawls the network and looks for +# peers. If another node asks it for addresses, it responds and disconnects. +# +# Does not work if the peer-exchange reactor is disabled. +seed_mode = false + +# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) +private_peer_ids = "" + +# Toggle to disable guard against peers connecting from the same ip. +allow_duplicate_ip = false + +# Peer connection configuration. +handshake_timeout = "20s" +dial_timeout = "3s" + +##### mempool configuration options ##### +[mempool] + +recheck = true +broadcast = true +wal_dir = "" + +# size of the mempool +size = 5000 + +# size of the cache (used to filter transactions we saw earlier) +cache_size = 10000 + +##### consensus configuration options ##### +[consensus] + +wal_file = "data/cs.wal/wal" + +timeout_propose = "3s" +timeout_propose_delta = "500ms" +timeout_prevote = "1s" +timeout_prevote_delta = "500ms" +timeout_precommit = "1s" +timeout_precommit_delta = "500ms" +timeout_commit = "1s" + +# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) +skip_timeout_commit = false + +# EmptyBlocks mode and possible interval between empty blocks +create_empty_blocks = true +create_empty_blocks_interval = "0s" + +# Reactor sleep duration parameters +peer_gossip_sleep_duration = "100ms" +peer_query_maj23_sleep_duration = "2s" + +# Block time parameters. Corresponds to the minimum time increment between consecutive blocks. +blocktime_iota = "1s" + +##### transactions indexer configuration options ##### +[tx_index] + +# What indexer to use for transactions +# +# Options: +# 1) "null" +# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). +indexer = "kv" + +# Comma-separated list of tags to index (by default the only tag is "tx.hash") +# +# You can also index transactions by height by adding "tx.height" tag here. +# +# It's recommended to index only a subset of tags due to possible memory +# bloat. This is, of course, depends on the indexer's DB and the volume of +# transactions. +index_tags = "" + +# When set to true, tells indexer to index all tags (predefined tags: +# "tx.hash", "tx.height" and all tags from DeliverTx responses). +# +# Note this may be not desirable (see the comment above). IndexTags has a +# precedence over IndexAllTags (i.e. when given both, IndexTags will be +# indexed). +index_all_tags = false + +##### instrumentation configuration options ##### +[instrumentation] + +# When true, Prometheus metrics are served under /metrics on +# PrometheusListenAddr. +# Check out the documentation for the list of available metrics. +prometheus = false + +# Address to listen for Prometheus collector(s) connections +prometheus_listen_addr = ":26660" + +# Maximum number of simultaneous connections. +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max_open_connections = 3 + +# Instrumentation namespace +namespace = "tendermint" diff --git a/scripts/confix/testdata/v30-config.toml b/scripts/confix/testdata/v30-config.toml new file mode 100644 index 000000000..b4aaa5aae --- /dev/null +++ b/scripts/confix/testdata/v30-config.toml @@ -0,0 +1,252 @@ +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base config options ##### + +# TCP or UNIX socket address of the ABCI application, +# or the name of an ABCI application compiled in with the Tendermint binary +proxy_app = "tcp://127.0.0.1:26658" + +# A custom human readable name for this node +moniker = "localhost" + +# If this node is many blocks behind the tip of the chain, FastSync +# allows them to catchup quickly by downloading blocks in parallel +# and verifying their commits +fast_sync = true + +# Database backend: leveldb | memdb | cleveldb +db_backend = "leveldb" + +# Database directory +db_dir = "data" + +# Output level for logging, including package level options +log_level = "main:info,state:info,*:error" + +# Output format: 'plain' (colored text) or 'json' +log_format = "plain" + +##### additional base config options ##### + +# Path to the JSON file containing the initial validator set and other meta data +genesis_file = "config/genesis.json" + +# Path to the JSON file containing the private key to use as a validator in the consensus protocol +priv_validator_key_file = "config/priv_validator_key.json" + +# Path to the JSON file containing the last sign state of a validator +priv_validator_state_file = "data/priv_validator_state.json" + +# TCP or UNIX socket address for Tendermint to listen on for +# connections from an external PrivValidator process +priv_validator_laddr = "" + +# Path to the JSON file containing the private key to use for node authentication in the p2p protocol +node_key_file = "config/node_key.json" + +# Mechanism to connect to the ABCI application: socket | grpc +abci = "socket" + +# TCP or UNIX socket address for the profiling server to listen on +prof_laddr = "" + +# If true, query the ABCI app on connecting to a new peer +# so the app can decide if we should keep the connection or not +filter_peers = false + +##### advanced configuration options ##### + +##### rpc server configuration options ##### +[rpc] + +# TCP or UNIX socket address for the RPC server to listen on +laddr = "tcp://0.0.0.0:26657" + +# A list of origins a cross-domain request can be executed from +# Default value '[]' disables cors support +# Use '["*"]' to allow any origin +cors_allowed_origins = [] + +# A list of methods the client is allowed to use with cross-domain requests +cors_allowed_methods = ["HEAD", "GET", "POST", ] + +# A list of non simple headers the client is allowed to use with cross-domain requests +cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time", ] + +# TCP or UNIX socket address for the gRPC server to listen on +# NOTE: This server only supports /broadcast_tx_commit +grpc_laddr = "" + +# Maximum number of simultaneous connections. +# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +grpc_max_open_connections = 900 + +# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool +unsafe = false + +# Maximum number of simultaneous connections (including WebSocket). +# Does not include gRPC connections. See grpc_max_open_connections +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} +# 1024 - 40 - 10 - 50 = 924 = ~900 +max_open_connections = 900 + +##### peer to peer configuration options ##### +[p2p] + +# Address to listen for incoming connections +laddr = "tcp://0.0.0.0:26656" + +# Address to advertise to peers for them to dial +# If empty, will use the same port as the laddr, +# and will introspect on the listener or use UPnP +# to figure out the address. +external_address = "" + +# Comma separated list of seed nodes to connect to +seeds = "" + +# Comma separated list of nodes to keep persistent connections to +persistent_peers = "" + +# UPNP port forwarding +upnp = false + +# Path to address book +addr_book_file = "config/addrbook.json" + +# Set true for strict address routability rules +# Set false for private or local networks +addr_book_strict = true + +# Maximum number of inbound peers +max_num_inbound_peers = 40 + +# Maximum number of outbound peers to connect to, excluding persistent peers +max_num_outbound_peers = 10 + +# Time to wait before flushing messages out on the connection +flush_throttle_timeout = "100ms" + +# Maximum size of a message packet payload, in bytes +max_packet_msg_payload_size = 1024 + +# Rate at which packets can be sent, in bytes/second +send_rate = 5120000 + +# Rate at which packets can be received, in bytes/second +recv_rate = 5120000 + +# Set true to enable the peer-exchange reactor +pex = true + +# Seed mode, in which node constantly crawls the network and looks for +# peers. If another node asks it for addresses, it responds and disconnects. +# +# Does not work if the peer-exchange reactor is disabled. +seed_mode = false + +# Comma separated list of peer IDs to keep private (will not be gossiped to other peers) +private_peer_ids = "" + +# Toggle to disable guard against peers connecting from the same ip. +allow_duplicate_ip = false + +# Peer connection configuration. +handshake_timeout = "20s" +dial_timeout = "3s" + +##### mempool configuration options ##### +[mempool] + +recheck = true +broadcast = true +wal_dir = "" + +# size of the mempool +size = 5000 + +# size of the cache (used to filter transactions we saw earlier) +cache_size = 10000 + +##### consensus configuration options ##### +[consensus] + +wal_file = "data/cs.wal/wal" + +timeout_propose = "3s" +timeout_propose_delta = "500ms" +timeout_prevote = "1s" +timeout_prevote_delta = "500ms" +timeout_precommit = "1s" +timeout_precommit_delta = "500ms" +timeout_commit = "1s" + +# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0) +skip_timeout_commit = false + +# EmptyBlocks mode and possible interval between empty blocks +create_empty_blocks = true +create_empty_blocks_interval = "0s" + +# Reactor sleep duration parameters +peer_gossip_sleep_duration = "100ms" +peer_query_maj23_sleep_duration = "2s" + +# Block time parameters. Corresponds to the minimum time increment between consecutive blocks. +blocktime_iota = "1s" + +##### transactions indexer configuration options ##### +[tx_index] + +# What indexer to use for transactions +# +# Options: +# 1) "null" +# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). +indexer = "kv" + +# Comma-separated list of tags to index (by default the only tag is "tx.hash") +# +# You can also index transactions by height by adding "tx.height" tag here. +# +# It's recommended to index only a subset of tags due to possible memory +# bloat. This is, of course, depends on the indexer's DB and the volume of +# transactions. +index_tags = "" + +# When set to true, tells indexer to index all tags (predefined tags: +# "tx.hash", "tx.height" and all tags from DeliverTx responses). +# +# Note this may be not desirable (see the comment above). IndexTags has a +# precedence over IndexAllTags (i.e. when given both, IndexTags will be +# indexed). +index_all_tags = false + +##### instrumentation configuration options ##### +[instrumentation] + +# When true, Prometheus metrics are served under /metrics on +# PrometheusListenAddr. +# Check out the documentation for the list of available metrics. +prometheus = false + +# Address to listen for Prometheus collector(s) connections +prometheus_listen_addr = ":26660" + +# Maximum number of simultaneous connections. +# If you want to accept a larger number than the default, make sure +# you increase your OS limits. +# 0 - unlimited. +max_open_connections = 3 + +# Instrumentation namespace +namespace = "tendermint" diff --git a/scripts/protopackage.sh b/scripts/protopackage.sh deleted file mode 100755 index 5eace2752..000000000 --- a/scripts/protopackage.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/sh -set -eo pipefail - -# This script appends the "option go_package" proto option to the file located at $FNAME. -# This option specifies what the package will be named when imported by other packages. -# This option is not directly included in the proto files to allow the files to more easily -# be hosted in github.com/tendermint/spec and shared between other repos. -# If the option is already specified in the file, it will be replaced using the -# arguments passed to this script. - -FNAME="${1:?missing required .proto filename}" -MODNAME=$(echo $2| sed 's/\//\\\//g') -PACKAGE="$(dirname $FNAME | sed 's/^\.\/\(.*\)/\1/g' | sed 's/\//\\\//g')" -if [[ ! -z "$3" ]]; then - PACKAGE="$(echo $3 | sed 's/\//\\\//g')" -fi - - -if ! grep -q 'option\s\+go_package\s\+=\s\+.*;' $FNAME; then - sed -i "s/\(package tendermint.*\)/\1\n\noption go_package = \"$MODNAME\/$PACKAGE\";/g" $FNAME -else - sed -i "s/option\s\+go_package\s\+=\s\+.*;/option go_package = \"$MODNAME\/$PACKAGE\";/g" $FNAME -fi diff --git a/scripts/scmigrate/migrate.go b/scripts/scmigrate/migrate.go index ba3de2698..c8e1d9490 100644 --- a/scripts/scmigrate/migrate.go +++ b/scripts/scmigrate/migrate.go @@ -115,7 +115,7 @@ func getAllSeenCommits(ctx context.Context, db dbm.DB) ([]toMigrate, error) { return scData, nil } -func renameRecord(ctx context.Context, db dbm.DB, keep toMigrate) error { +func renameRecord(db dbm.DB, keep toMigrate) error { wantKey := makeKeyFromPrefix(prefixSeenCommit) if bytes.Equal(keep.key, wantKey) { return nil // we already did this conversion @@ -143,7 +143,7 @@ func renameRecord(ctx context.Context, db dbm.DB, keep toMigrate) error { return cerr } -func deleteRecords(ctx context.Context, db dbm.DB, scData []toMigrate) error { +func deleteRecords(db dbm.DB, scData []toMigrate) error { // delete all the remaining stale values in a single batch batch := db.NewBatch() @@ -179,7 +179,7 @@ func Migrate(ctx context.Context, db dbm.DB) error { // retain only the latest. keep, remove := scData[0], scData[1:] - if err := renameRecord(ctx, db, keep); err != nil { + if err := renameRecord(db, keep); err != nil { return fmt.Errorf("renaming seen commit record: %w", err) } @@ -189,7 +189,7 @@ func Migrate(ctx context.Context, db dbm.DB) error { // Remove any older seen commits. Prior to v0.35, we kept these records for // all heights, but v0.35 keeps only the latest. - if err := deleteRecords(ctx, db, remove); err != nil { + if err := deleteRecords(db, remove); err != nil { return fmt.Errorf("writing data: %w", err) } diff --git a/spec/abci/abci.md b/spec/abci/abci.md index 2a027d1d0..5d9d59b71 100644 --- a/spec/abci/abci.md +++ b/spec/abci/abci.md @@ -13,35 +13,11 @@ replication engine. When run within the same process, Tendermint will call the A application methods directly as Go method calls. When Tendermint and the ABCI application are run as separate processes, Tendermint -opens four connections to the application for ABCI methods. The connections each -handle a subset of the ABCI method calls. These subsets are defined as follows: +opens maintains a connection over either a native socket protocol or +gRPC. -#### **Consensus** connection - -* Driven by a consensus protocol and is responsible for block execution. -* Handles the `InitChain`, `BeginBlock`, `DeliverTx`, `EndBlock`, and `Commit` method -calls. - -#### **Mempool** connection - -* For validating new transactions, before they're shared or included in a block. -* Handles the `CheckTx` calls. - -#### **Info** connection - -* For initialization and for queries from the user. -* Handles the `Info` and `Query` calls. - -#### **Snapshot** connection - -* For serving and restoring [state sync snapshots](apps.md#state-sync). -* Handles the `ListSnapshots`, `LoadSnapshotChunk`, `OfferSnapshot`, and `ApplySnapshotChunk` calls. - -Additionally, there is a `Flush` method that is called on every connection, -and an `Echo` method that is just for debugging. - -More details on managing state across connections can be found in the section on -[ABCI Applications](apps.md). +More details on managing state across connections can be found in the +section on [ABCI Applications](apps.md). ## Errors @@ -54,13 +30,17 @@ These methods also return a `Codespace` string to Tendermint. This field is used to disambiguate `Code` values returned by different domains of the application. The `Codespace` is a namespace for the `Code`. -The `Echo`, `Info`, `InitChain`, `BeginBlock`, `EndBlock`, `Commit` methods -do not return errors. An error in any of these methods represents a critical -issue that Tendermint has no reasonable way to handle. If there is an error in one -of these methods, the application must crash to ensure that the error is safely -handled by an operator. +The handling of non-zero response codes by Tendermint is described +below. -The handling of non-zero response codes by Tendermint is described below +Applications should always terminate if they encounter an issue in a +method where continuing would corrupt their own state, or for which +tendermint should not continue. + +In the Go implementation these methods take a context and may return +an error. The context exists so that applications can terminate +gracefully during shutdown, and the error return value makes it +possible for applications to singal transient errors to Tendermint. ### CheckTx diff --git a/test/e2e/app/app.go b/test/e2e/app/app.go index 2736dad04..64ccd2e28 100644 --- a/test/e2e/app/app.go +++ b/test/e2e/app/app.go @@ -2,21 +2,31 @@ package app import ( "bytes" + "context" "encoding/base64" + "encoding/binary" "errors" "fmt" + "math/rand" "path/filepath" "sort" "strconv" + "strings" "sync" "github.com/tendermint/tendermint/abci/example/code" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/proto/tendermint/types" "github.com/tendermint/tendermint/version" ) +const ( + 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 // to disk as JSON, taking state sync snapshots if requested. @@ -104,20 +114,20 @@ func NewApplication(cfg *Config) (*Application, error) { } // Info implements ABCI. -func (app *Application) Info(req abci.RequestInfo) abci.ResponseInfo { +func (app *Application) Info(_ context.Context, req *abci.RequestInfo) (*abci.ResponseInfo, error) { app.mu.Lock() defer app.mu.Unlock() - return abci.ResponseInfo{ + return &abci.ResponseInfo{ Version: version.ABCIVersion, AppVersion: 1, LastBlockHeight: int64(app.state.Height), LastBlockAppHash: app.state.Hash, - } + }, nil } // Info implements ABCI. -func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { +func (app *Application) InitChain(_ context.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) { app.mu.Lock() defer app.mu.Unlock() @@ -129,7 +139,7 @@ func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitCh panic(err) } } - resp := abci.ResponseInitChain{ + resp := &abci.ResponseInitChain{ AppHash: app.state.Hash, ConsensusParams: &types.ConsensusParams{ Version: &types.VersionParams{ @@ -140,26 +150,26 @@ func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitCh if resp.Validators, err = app.validatorUpdates(0); err != nil { panic(err) } - return resp + return resp, nil } // CheckTx implements ABCI. -func (app *Application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { +func (app *Application) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) { app.mu.Lock() defer app.mu.Unlock() _, _, err := parseTx(req.Tx) if err != nil { - return abci.ResponseCheckTx{ + return &abci.ResponseCheckTx{ Code: code.CodeTypeEncodingError, Log: err.Error(), - } + }, nil } - return abci.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1} + return &abci.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}, nil } // FinalizeBlock implements ABCI. -func (app *Application) FinalizeBlock(req abci.RequestFinalizeBlock) abci.ResponseFinalizeBlock { +func (app *Application) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) { var txs = make([]*abci.ExecTxResult, len(req.Txs)) app.mu.Lock() @@ -180,7 +190,7 @@ func (app *Application) FinalizeBlock(req abci.RequestFinalizeBlock) abci.Respon panic(err) } - return abci.ResponseFinalizeBlock{ + return &abci.ResponseFinalizeBlock{ TxResults: txs, ValidatorUpdates: valUpdates, Events: []abci.Event{ @@ -198,11 +208,11 @@ func (app *Application) FinalizeBlock(req abci.RequestFinalizeBlock) abci.Respon }, }, }, - } + }, nil } // Commit implements ABCI. -func (app *Application) Commit() abci.ResponseCommit { +func (app *Application) Commit(_ context.Context) (*abci.ResponseCommit, error) { app.mu.Lock() defer app.mu.Unlock() @@ -215,36 +225,36 @@ func (app *Application) Commit() abci.ResponseCommit { if err != nil { panic(err) } - app.logger.Info("Created state sync snapshot", "height", snapshot.Height) + app.logger.Info("created state sync snapshot", "height", snapshot.Height) err = app.snapshots.Prune(maxSnapshotCount) if err != nil { - app.logger.Error("Failed to prune snapshots", "err", err) + app.logger.Error("failed to prune snapshots", "err", err) } } retainHeight := int64(0) if app.cfg.RetainBlocks > 0 { retainHeight = int64(height - app.cfg.RetainBlocks + 1) } - return abci.ResponseCommit{ + return &abci.ResponseCommit{ Data: hash, RetainHeight: retainHeight, - } + }, nil } // Query implements ABCI. -func (app *Application) Query(req abci.RequestQuery) abci.ResponseQuery { +func (app *Application) Query(_ context.Context, req *abci.RequestQuery) (*abci.ResponseQuery, error) { app.mu.Lock() defer app.mu.Unlock() - return abci.ResponseQuery{ + return &abci.ResponseQuery{ Height: int64(app.state.Height), Key: req.Data, Value: []byte(app.state.Get(string(req.Data))), - } + }, nil } // ListSnapshots implements ABCI. -func (app *Application) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots { +func (app *Application) ListSnapshots(_ context.Context, req *abci.RequestListSnapshots) (*abci.ResponseListSnapshots, error) { app.mu.Lock() defer app.mu.Unlock() @@ -252,11 +262,11 @@ func (app *Application) ListSnapshots(req abci.RequestListSnapshots) abci.Respon if err != nil { panic(err) } - return abci.ResponseListSnapshots{Snapshots: snapshots} + return &abci.ResponseListSnapshots{Snapshots: snapshots}, nil } // LoadSnapshotChunk implements ABCI. -func (app *Application) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk { +func (app *Application) LoadSnapshotChunk(_ context.Context, req *abci.RequestLoadSnapshotChunk) (*abci.ResponseLoadSnapshotChunk, error) { app.mu.Lock() defer app.mu.Unlock() @@ -264,11 +274,11 @@ func (app *Application) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abc if err != nil { panic(err) } - return abci.ResponseLoadSnapshotChunk{Chunk: chunk} + return &abci.ResponseLoadSnapshotChunk{Chunk: chunk}, nil } // OfferSnapshot implements ABCI. -func (app *Application) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot { +func (app *Application) OfferSnapshot(_ context.Context, req *abci.RequestOfferSnapshot) (*abci.ResponseOfferSnapshot, error) { app.mu.Lock() defer app.mu.Unlock() @@ -277,11 +287,11 @@ func (app *Application) OfferSnapshot(req abci.RequestOfferSnapshot) abci.Respon } app.restoreSnapshot = req.Snapshot app.restoreChunks = [][]byte{} - return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT} + return &abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}, nil } // ApplySnapshotChunk implements ABCI. -func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk { +func (app *Application) ApplySnapshotChunk(_ context.Context, req *abci.RequestApplySnapshotChunk) (*abci.ResponseApplySnapshotChunk, error) { app.mu.Lock() defer app.mu.Unlock() @@ -301,10 +311,77 @@ func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) a app.restoreSnapshot = nil app.restoreChunks = nil } - return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT} + return &abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil } -func (app *Application) PrepareProposal(req abci.RequestPrepareProposal) abci.ResponsePrepareProposal { +// 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++ + } + // 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. + txRecords := make([]*abci.TxRecord, len(req.Txs)+1) + for i, tx := range req.Txs { + if strings.HasPrefix(string(tx), extTxPrefix) { + txRecords[i] = &abci.TxRecord{ + Action: abci.TxRecord_REMOVED, + Tx: tx, + } + } else { + txRecords[i] = &abci.TxRecord{ + Action: abci.TxRecord_UNMODIFIED, + Tx: tx, + } + totalBytes += int64(len(tx)) + } + } + if totalBytes+int64(len(extTx)) < req.MaxTxBytes { + txRecords[len(req.Txs)] = &abci.TxRecord{ + Action: abci.TxRecord_ADDED, + Tx: 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{ + TxRecords: txRecords, + }, nil + } // None of the transactions are modified by this application. trs := make([]*abci.TxRecord, 0, len(req.Txs)) var totalBytes int64 @@ -318,19 +395,92 @@ func (app *Application) PrepareProposal(req abci.RequestPrepareProposal) abci.Re Tx: tx, }) } - return abci.ResponsePrepareProposal{TxRecords: trs} + return &abci.ResponsePrepareProposal{TxRecords: trs}, nil } // ProcessProposal implements part of the Application interface. // It accepts any proposal that does not contain a malformed transaction. -func (app *Application) ProcessProposal(req abci.RequestProcessProposal) abci.ResponseProcessProposal { +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 { - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT} + 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 + } } } - return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT} + 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 { + app.logger.Error( + "got unexpected height in VerifyVoteExtension request", + "expectedHeight", app.state.Height, + "requestHeight", req.Height, + ) + return &abci.ResponseVerifyVoteExtension{ + Status: abci.ResponseVerifyVoteExtension_REJECT, + }, nil + } + + 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 { @@ -378,3 +528,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/runner/evidence.go b/test/e2e/runner/evidence.go index b29b574a2..10db5d58b 100644 --- a/test/e2e/runner/evidence.go +++ b/test/e2e/runner/evidence.go @@ -63,7 +63,6 @@ func InjectEvidence(ctx context.Context, logger log.Logger, r *rand.Rand, testne if err != nil { return err } - valSet, err := types.ValidatorSetFromExistingValidators(valRes.Validators) if err != nil { return err @@ -289,8 +288,7 @@ func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.Bloc } } -func mutateValidatorSet(ctx context.Context, privVals []types.MockPV, vals *types.ValidatorSet, -) ([]types.PrivValidator, *types.ValidatorSet, error) { +func mutateValidatorSet(ctx context.Context, privVals []types.MockPV, vals *types.ValidatorSet) ([]types.PrivValidator, *types.ValidatorSet, error) { newVal, newPrivVal, err := factory.Validator(ctx, 10) if err != nil { return nil, nil, err diff --git a/test/e2e/runner/main.go b/test/e2e/runner/main.go index 7d147f411..c4a73d33f 100644 --- a/test/e2e/runner/main.go +++ b/test/e2e/runner/main.go @@ -134,7 +134,7 @@ func NewCLI(logger log.Logger) *CLI { if err = Wait(ctx, logger, cli.testnet, 5); err != nil { // wait for network to settle before tests return err } - if err := Test(logger, cli.testnet); err != nil { + if err := Test(cli.testnet); err != nil { return err } return nil @@ -259,7 +259,7 @@ func NewCLI(logger log.Logger) *CLI { Use: "test", Short: "Runs test cases against a running testnet", RunE: func(cmd *cobra.Command, args []string) error { - return Test(logger, cli.testnet) + return Test(cli.testnet) }, }) diff --git a/test/e2e/runner/test.go b/test/e2e/runner/test.go index 22fcd730e..2237588a1 100644 --- a/test/e2e/runner/test.go +++ b/test/e2e/runner/test.go @@ -3,12 +3,11 @@ package main import ( "os" - "github.com/tendermint/tendermint/libs/log" e2e "github.com/tendermint/tendermint/test/e2e/pkg" ) // Test runs test cases under tests/ -func Test(logger log.Logger, testnet *e2e.Testnet) error { +func Test(testnet *e2e.Testnet) error { err := os.Setenv("E2E_MANIFEST", testnet.File) if err != nil { return err diff --git a/test/e2e/tests/app_test.go b/test/e2e/tests/app_test.go index 36115346b..ed041e186 100644 --- a/test/e2e/tests/app_test.go +++ b/test/e2e/tests/app_test.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "math/rand" + "strconv" "testing" "time" @@ -45,9 +46,11 @@ func TestApp_Hash(t *testing.T) { testNode(t, func(ctx context.Context, 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") + // In next-block execution, the app hash is stored in the next block blockHeight := info.Response.LastBlockHeight + 1 @@ -60,7 +63,6 @@ func TestApp_Hash(t *testing.T) { block, err := client.Block(ctx, &blockHeight) require.NoError(t, err) - require.Equal(t, blockHeight, block.Block.Height) require.Equal(t, fmt.Sprintf("%x", info.Response.LastBlockAppHash), @@ -185,3 +187,18 @@ func TestApp_Tx(t *testing.T) { } } + +func TestApp_VoteExtensions(t *testing.T) { + testNode(t, func(ctx context.Context, 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/test/fuzz/Makefile b/test/fuzz/Makefile deleted file mode 100644 index 3bf4486b8..000000000 --- a/test/fuzz/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/make -f - -.PHONY: fuzz-mempool -fuzz-mempool: - cd mempool && \ - rm -f *-fuzz.zip && \ - go-fuzz-build && \ - go-fuzz - -.PHONY: fuzz-p2p-sc -fuzz-p2p-sc: - cd p2p/secretconnection && \ - rm -f *-fuzz.zip && \ - go run ./init-corpus/main.go && \ - go-fuzz-build && \ - go-fuzz - -.PHONY: fuzz-rpc-server -fuzz-rpc-server: - cd rpc/jsonrpc/server && \ - rm -f *-fuzz.zip && \ - go-fuzz-build && \ - go-fuzz - -clean: - find . -name corpus -type d -exec rm -rf {} +; - find . -name crashers -type d -exec rm -rf {} +; - find . -name suppressions -type d -exec rm -rf {} +; - find . -name *\.zip -type f -delete diff --git a/test/fuzz/README.md b/test/fuzz/README.md index 707217afd..11ec9d521 100644 --- a/test/fuzz/README.md +++ b/test/fuzz/README.md @@ -5,68 +5,18 @@ Fuzzing for various packages in Tendermint using [go-fuzz](https://github.com/dv Inputs: - mempool `CheckTx` (using kvstore in-process ABCI app) -- p2p `Addrbook#AddAddress` -- p2p `pex.Reactor#Receive` - p2p `SecretConnection#Read` and `SecretConnection#Write` - rpc jsonrpc server -## Directory structure - -``` -| test -| |- corpus/ -| |- crashers/ -| |- init-corpus/ -| |- suppressions/ -| |- testdata/ -| |- .go -``` - -`/corpus` directory contains corpus data. The idea is to help the fuzzier to -understand what bytes sequences are semantically valid (e.g. if we're testing -PNG decoder, then we would put black-white PNG into corpus directory; with -blockchain reactor - we would put blockchain messages into corpus). - -`/init-corpus` (if present) contains a script for generating corpus data. - -`/testdata` directory may contain an additional data (like `addrbook.json`). - -Upon running the fuzzier, `/crashers` and `/suppressions` dirs will be created, -along with .zip archive. `/crashers` will show any inputs, which have -lead to panics (plus a trace). `/suppressions` will show any suppressed inputs. - ## Running -```sh -make fuzz-mempool -make fuzz-p2p-addrbook -make fuzz-p2p-pex -make fuzz-p2p-sc -make fuzz-rpc-server -``` - -Each command will create corpus data (if needed), generate a fuzz archive and -call `go-fuzz` executable. - -Then watch out for the respective outputs in the fuzzer output to announce new -crashers which can be found in the directory `crashers`. - -For example if we find +The fuzz tests are in native Go fuzzing format. Use the `go` +tool to run them: ```sh -ls crashers/ -61bde465f47c93254d64d643c3b2480e0a54666e -61bde465f47c93254d64d643c3b2480e0a54666e.output -61bde465f47c93254d64d643c3b2480e0a54666e.quoted -da39a3ee5e6b4b0d3255bfef95601890afd80709 -da39a3ee5e6b4b0d3255bfef95601890afd80709.output -da39a3ee5e6b4b0d3255bfef95601890afd80709.quoted +go test -fuzz Mempool ./tests +go test -fuzz P2PSecretConnection ./tests +go test -fuzz RPCJSONRPCServer ./tests ``` -the crashing bytes generated by the fuzzer will be in -`61bde465f47c93254d64d643c3b2480e0a54666e` the respective crash report in -`61bde465f47c93254d64d643c3b2480e0a54666e.output` - -and the bug report can be created by retrieving the bytes in -`61bde465f47c93254d64d643c3b2480e0a54666e` and feeding those back into the -`Fuzz` function. +See [the Go Fuzzing introduction](https://go.dev/doc/fuzz/) for more information. diff --git a/test/fuzz/mempool/fuzz_test.go b/test/fuzz/mempool/fuzz_test.go deleted file mode 100644 index 69f34db64..000000000 --- a/test/fuzz/mempool/fuzz_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package mempool_test - -import ( - "io" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - - mempool "github.com/tendermint/tendermint/test/fuzz/mempool" -) - -const testdataCasesDir = "testdata/cases" - -func TestMempoolTestdataCases(t *testing.T) { - entries, err := os.ReadDir(testdataCasesDir) - require.NoError(t, err) - - for _, e := range entries { - entry := e - t.Run(entry.Name(), func(t *testing.T) { - defer func() { - r := recover() - require.Nilf(t, r, "testdata/cases test panic") - }() - f, err := os.Open(filepath.Join(testdataCasesDir, entry.Name())) - require.NoError(t, err) - input, err := io.ReadAll(f) - require.NoError(t, err) - mempool.Fuzz(input) - }) - } -} diff --git a/test/fuzz/oss-fuzz-build.sh b/test/fuzz/oss-fuzz-build.sh index ef2052614..836253d4d 100755 --- a/test/fuzz/oss-fuzz-build.sh +++ b/test/fuzz/oss-fuzz-build.sh @@ -1,10 +1,22 @@ -#!/bin/bash -eu +#!/bin/bash + +set -euo pipefail export FUZZ_ROOT="github.com/tendermint/tendermint" -(cd test/fuzz/p2p/secretconnection; go run ./init-corpus/main.go) -compile_go_fuzzer "$FUZZ_ROOT"/test/fuzz/p2p/secretconnection Fuzz fuzz_p2p_secretconnection fuzz +build_go_fuzzer() { + local function="$1" + local fuzzer="$2" -compile_go_fuzzer "$FUZZ_ROOT"/test/fuzz/mempool Fuzz fuzz_mempool fuzz + gotip run github.com/orijtech/otils/corpus2ossfuzz@latest -o "$OUT"/"$fuzzer"_seed_corpus.zip -corpus test/fuzz/tests/testdata/fuzz/"$function" + compile_native_go_fuzzer "$FUZZ_ROOT"/test/fuzz/tests "$function" "$fuzzer" +} -compile_go_fuzzer "$FUZZ_ROOT"/test/fuzz/rpc/jsonrpc/server Fuzz fuzz_rpc_jsonrpc_server fuzz +gotip get github.com/AdamKorcz/go-118-fuzz-build/utils +gotip get github.com/prometheus/common/expfmt@v0.32.1 + +build_go_fuzzer FuzzP2PSecretConnection fuzz_p2p_secretconnection + +build_go_fuzzer FuzzMempool fuzz_mempool + +build_go_fuzzer FuzzRPCJSONRPCServer fuzz_rpc_jsonrpc_server diff --git a/test/fuzz/p2p/secretconnection/fuzz_test.go b/test/fuzz/p2p/secretconnection/fuzz_test.go deleted file mode 100644 index 6fe19b03b..000000000 --- a/test/fuzz/p2p/secretconnection/fuzz_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package secretconnection_test - -import ( - "io" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/tendermint/tendermint/test/fuzz/p2p/secretconnection" -) - -const testdataCasesDir = "testdata/cases" - -func TestSecretConnectionTestdataCases(t *testing.T) { - entries, err := os.ReadDir(testdataCasesDir) - require.NoError(t, err) - - for _, e := range entries { - entry := e - t.Run(entry.Name(), func(t *testing.T) { - defer func() { - r := recover() - require.Nilf(t, r, "testdata/cases test panic") - }() - f, err := os.Open(filepath.Join(testdataCasesDir, entry.Name())) - require.NoError(t, err) - input, err := io.ReadAll(f) - require.NoError(t, err) - secretconnection.Fuzz(input) - }) - } -} diff --git a/test/fuzz/p2p/secretconnection/init-corpus/main.go b/test/fuzz/p2p/secretconnection/init-corpus/main.go deleted file mode 100644 index 3a2537ff7..000000000 --- a/test/fuzz/p2p/secretconnection/init-corpus/main.go +++ /dev/null @@ -1,47 +0,0 @@ -// nolint: gosec -package main - -import ( - "flag" - "fmt" - "log" - "os" - "path/filepath" -) - -func main() { - baseDir := flag.String("base", ".", `where the "corpus" directory will live`) - flag.Parse() - - initCorpus(*baseDir) -} - -func initCorpus(baseDir string) { - log.SetFlags(0) - - corpusDir := filepath.Join(baseDir, "corpus") - if err := os.MkdirAll(corpusDir, 0755); err != nil { - log.Fatal(err) - } - - data := []string{ - "dadc04c2-cfb1-4aa9-a92a-c0bf780ec8b6", - "", - " ", - " a ", - `{"a": 12, "tsp": 999, k: "blue"}`, - `9999.999`, - `""`, - `Tendermint fuzzing`, - } - - for i, datum := range data { - filename := filepath.Join(corpusDir, fmt.Sprintf("%d", i)) - - if err := os.WriteFile(filename, []byte(datum), 0644); err != nil { - log.Fatalf("can't write %v to %q: %v", datum, filename, err) - } - - log.Printf("wrote %q", filename) - } -} diff --git a/test/fuzz/rpc/jsonrpc/server/fuzz_test.go b/test/fuzz/rpc/jsonrpc/server/fuzz_test.go deleted file mode 100644 index 8a34da8a6..000000000 --- a/test/fuzz/rpc/jsonrpc/server/fuzz_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package server_test - -import ( - "io" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/tendermint/tendermint/test/fuzz/rpc/jsonrpc/server" -) - -const testdataCasesDir = "testdata/cases" - -func TestServerTestdataCases(t *testing.T) { - entries, err := os.ReadDir(testdataCasesDir) - require.NoError(t, err) - - for _, e := range entries { - entry := e - t.Run(entry.Name(), func(t *testing.T) { - defer func() { - r := recover() - require.Nilf(t, r, "testdata/cases test panic") - }() - f, err := os.Open(filepath.Join(testdataCasesDir, entry.Name())) - require.NoError(t, err) - input, err := io.ReadAll(f) - require.NoError(t, err) - server.Fuzz(input) - }) - } -} diff --git a/test/fuzz/rpc/jsonrpc/server/handler.go b/test/fuzz/rpc/jsonrpc/server/handler.go deleted file mode 100644 index c9203e9f5..000000000 --- a/test/fuzz/rpc/jsonrpc/server/handler.go +++ /dev/null @@ -1,65 +0,0 @@ -package server - -import ( - "bytes" - "context" - "encoding/json" - "io" - "net/http" - "net/http/httptest" - - "github.com/tendermint/tendermint/libs/log" - rs "github.com/tendermint/tendermint/rpc/jsonrpc/server" - "github.com/tendermint/tendermint/rpc/jsonrpc/types" -) - -var rpcFuncMap = map[string]*rs.RPCFunc{ - "c": rs.NewRPCFunc(func(ctx context.Context, s string, i int) (string, error) { - return "foo", nil - }, "s", "i"), -} -var mux *http.ServeMux - -func init() { - mux = http.NewServeMux() - rs.RegisterRPCFuncs(mux, rpcFuncMap, log.NewNopLogger()) -} - -func Fuzz(data []byte) int { - if len(data) == 0 { - return -1 - } - - req, _ := http.NewRequest("POST", "http://localhost/", bytes.NewReader(data)) - rec := httptest.NewRecorder() - mux.ServeHTTP(rec, req) - res := rec.Result() - blob, err := io.ReadAll(res.Body) - if err != nil { - panic(err) - } - if err := res.Body.Close(); err != nil { - panic(err) - } - if len(blob) == 0 { - return 1 - } - - if outputJSONIsSlice(blob) { - recv := []types.RPCResponse{} - if err := json.Unmarshal(blob, &recv); err != nil { - panic(err) - } - return 1 - } - recv := &types.RPCResponse{} - if err := json.Unmarshal(blob, recv); err != nil { - panic(err) - } - return 1 -} - -func outputJSONIsSlice(input []byte) bool { - slice := []interface{}{} - return json.Unmarshal(input, &slice) == nil -} diff --git a/test/fuzz/rpc/jsonrpc/server/testdata/1184f5b8d4b6dd08709cf1513f26744167065e0d b/test/fuzz/rpc/jsonrpc/server/testdata/1184f5b8d4b6dd08709cf1513f26744167065e0d deleted file mode 100644 index 6e7ea636e..000000000 --- a/test/fuzz/rpc/jsonrpc/server/testdata/1184f5b8d4b6dd08709cf1513f26744167065e0d +++ /dev/null @@ -1 +0,0 @@ -[0] \ No newline at end of file diff --git a/test/fuzz/rpc/jsonrpc/server/testdata/cases/1184f5b8d4b6dd08709cf1513f26744167065e0d b/test/fuzz/rpc/jsonrpc/server/testdata/cases/1184f5b8d4b6dd08709cf1513f26744167065e0d deleted file mode 100644 index 6e7ea636e..000000000 --- a/test/fuzz/rpc/jsonrpc/server/testdata/cases/1184f5b8d4b6dd08709cf1513f26744167065e0d +++ /dev/null @@ -1 +0,0 @@ -[0] \ No newline at end of file diff --git a/test/fuzz/rpc/jsonrpc/server/testdata/cases/bbcffb1cdb2cea50fd3dd8c1524905551d0b2e79 b/test/fuzz/rpc/jsonrpc/server/testdata/cases/bbcffb1cdb2cea50fd3dd8c1524905551d0b2e79 deleted file mode 100644 index e0be2aa4b..000000000 --- a/test/fuzz/rpc/jsonrpc/server/testdata/cases/bbcffb1cdb2cea50fd3dd8c1524905551d0b2e79 +++ /dev/null @@ -1 +0,0 @@ -[0,0] \ No newline at end of file diff --git a/test/fuzz/rpc/jsonrpc/server/testdata/cases/clusterfuzz-testcase-minimized-fuzz_rpc_jsonrpc_server-4738572803506176 b/test/fuzz/rpc/jsonrpc/server/testdata/cases/clusterfuzz-testcase-minimized-fuzz_rpc_jsonrpc_server-4738572803506176 deleted file mode 100644 index 0f7836d2f..000000000 --- a/test/fuzz/rpc/jsonrpc/server/testdata/cases/clusterfuzz-testcase-minimized-fuzz_rpc_jsonrpc_server-4738572803506176 +++ /dev/null @@ -1 +0,0 @@ -[{"iD":7},{"iD":7}] \ No newline at end of file diff --git a/test/fuzz/rpc/jsonrpc/server/testdata/clusterfuzz-testcase-minimized-fuzz_rpc_jsonrpc_server-4738572803506176 b/test/fuzz/rpc/jsonrpc/server/testdata/clusterfuzz-testcase-minimized-fuzz_rpc_jsonrpc_server-4738572803506176 deleted file mode 100644 index 0f7836d2f..000000000 --- a/test/fuzz/rpc/jsonrpc/server/testdata/clusterfuzz-testcase-minimized-fuzz_rpc_jsonrpc_server-4738572803506176 +++ /dev/null @@ -1 +0,0 @@ -[{"iD":7},{"iD":7}] \ No newline at end of file diff --git a/test/fuzz/mempool/checktx.go b/test/fuzz/tests/mempool_test.go similarity index 74% rename from test/fuzz/mempool/checktx.go rename to test/fuzz/tests/mempool_test.go index 8be90f0c2..a76b059ca 100644 --- a/test/fuzz/mempool/checktx.go +++ b/test/fuzz/tests/mempool_test.go @@ -1,7 +1,10 @@ -package mempool +//go:build gofuzz || go1.18 + +package tests import ( "context" + "testing" abciclient "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/abci/example/kvstore" @@ -33,11 +36,11 @@ func init() { } } -func Fuzz(data []byte) int { - err := getMp().CheckTx(context.Background(), data, nil, mempool.TxInfo{}) - if err != nil { - return 0 - } - - return 1 +func FuzzMempool(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + err := getMp().CheckTx(context.Background(), data, nil, mempool.TxInfo{}) + if err != nil { + panic(err) + } + }) } diff --git a/test/fuzz/p2p/secretconnection/read_write.go b/test/fuzz/tests/p2p_secretconnection_test.go similarity index 94% rename from test/fuzz/p2p/secretconnection/read_write.go rename to test/fuzz/tests/p2p_secretconnection_test.go index 87d547e55..65f268a7b 100644 --- a/test/fuzz/p2p/secretconnection/read_write.go +++ b/test/fuzz/tests/p2p_secretconnection_test.go @@ -1,19 +1,28 @@ -package secretconnection +//go:build gofuzz || go1.18 + +package tests import ( "bytes" "fmt" "io" "log" + "testing" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/internal/libs/async" sc "github.com/tendermint/tendermint/internal/p2p/conn" ) -func Fuzz(data []byte) int { +func FuzzP2PSecretConnection(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + fuzz(data) + }) +} + +func fuzz(data []byte) { if len(data) == 0 { - return -1 + return } fooConn, barConn := makeSecretConnPair() @@ -44,14 +53,11 @@ func Fuzz(data []byte) int { } copy(dataRead[totalRead:], buf[:m]) totalRead += m - log.Printf("total read: %d", totalRead) } if !bytes.Equal(data, dataRead) { panic("bytes written != read") } - - return 1 } type kvstoreConn struct { diff --git a/test/fuzz/tests/rpc_jsonrpc_server_test.go b/test/fuzz/tests/rpc_jsonrpc_server_test.go new file mode 100644 index 000000000..bc4e90881 --- /dev/null +++ b/test/fuzz/tests/rpc_jsonrpc_server_test.go @@ -0,0 +1,68 @@ +//go:build gofuzz || go1.18 + +package tests + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/tendermint/tendermint/libs/log" + rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server" + "github.com/tendermint/tendermint/rpc/jsonrpc/types" +) + +func FuzzRPCJSONRPCServer(f *testing.F) { + var rpcFuncMap = map[string]*rpcserver.RPCFunc{ + "c": rpcserver.NewRPCFunc(func(ctx context.Context, s string, i int) (string, error) { + return "foo", nil + }, "s", "i"), + } + + mux := http.NewServeMux() + rpcserver.RegisterRPCFuncs(mux, rpcFuncMap, log.NewNopLogger()) + f.Fuzz(func(t *testing.T, data []byte) { + if len(data) == 0 { + return + } + + req, err := http.NewRequest("POST", "http://localhost/", bytes.NewReader(data)) + if err != nil { + panic(err) + } + rec := httptest.NewRecorder() + mux.ServeHTTP(rec, req) + res := rec.Result() + blob, err := io.ReadAll(res.Body) + if err != nil { + panic(err) + } + if err := res.Body.Close(); err != nil { + panic(err) + } + if len(blob) == 0 { + return + } + + if outputJSONIsSlice(blob) { + var recv []types.RPCResponse + if err := json.Unmarshal(blob, &recv); err != nil { + panic(err) + } + return + } + var recv types.RPCResponse + if err := json.Unmarshal(blob, &recv); err != nil { + panic(err) + } + }) +} + +func outputJSONIsSlice(input []byte) bool { + var slice []json.RawMessage + return json.Unmarshal(input, &slice) == nil +} diff --git a/test/fuzz/tests/testdata/fuzz/FuzzMempool/1daffc1033a0bfc7f0c2bccb7440674e67a9e2cc0a4531863076254ada059863 b/test/fuzz/tests/testdata/fuzz/FuzzMempool/1daffc1033a0bfc7f0c2bccb7440674e67a9e2cc0a4531863076254ada059863 new file mode 100644 index 000000000..88467017a --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzMempool/1daffc1033a0bfc7f0c2bccb7440674e67a9e2cc0a4531863076254ada059863 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("S1") diff --git a/test/fuzz/tests/testdata/fuzz/FuzzMempool/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 b/test/fuzz/tests/testdata/fuzz/FuzzMempool/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 new file mode 100644 index 000000000..a96f5599e --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzMempool/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("0") diff --git a/test/fuzz/tests/testdata/fuzz/FuzzMempool/d40a98862ed393eb712e47a91bcef18e6f24cf368bb4bd248c7a7101ef8e178d b/test/fuzz/tests/testdata/fuzz/FuzzMempool/d40a98862ed393eb712e47a91bcef18e6f24cf368bb4bd248c7a7101ef8e178d new file mode 100644 index 000000000..e0f2da225 --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzMempool/d40a98862ed393eb712e47a91bcef18e6f24cf368bb4bd248c7a7101ef8e178d @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/0f1a3d10e4d642e42a3ccd9bad652d355431f5824327271aca6f648e8cd4e786 b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/0f1a3d10e4d642e42a3ccd9bad652d355431f5824327271aca6f648e8cd4e786 new file mode 100644 index 000000000..f0b8ea88b --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/0f1a3d10e4d642e42a3ccd9bad652d355431f5824327271aca6f648e8cd4e786 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte(" ") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/172c521d1c5e7a5cce55e39b235928fc1c8c4adbb4635913c204c4724cf47d20 b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/172c521d1c5e7a5cce55e39b235928fc1c8c4adbb4635913c204c4724cf47d20 new file mode 100644 index 000000000..a3668a6db --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/172c521d1c5e7a5cce55e39b235928fc1c8c4adbb4635913c204c4724cf47d20 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("{\"a\": 12, \"tsp\": 999, k: \"blue\"}") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/a9481542b8154bfe8fe868c8907cb66557347cb9b45709b17da861997d7cabea b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/a9481542b8154bfe8fe868c8907cb66557347cb9b45709b17da861997d7cabea new file mode 100644 index 000000000..98241189c --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/a9481542b8154bfe8fe868c8907cb66557347cb9b45709b17da861997d7cabea @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("\"\"") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/ba3758980fe724f83bdf1cb97caa73657b4a78d48e5fd6fc3b1590d24799e803 b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/ba3758980fe724f83bdf1cb97caa73657b4a78d48e5fd6fc3b1590d24799e803 new file mode 100644 index 000000000..c479f2604 --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/ba3758980fe724f83bdf1cb97caa73657b4a78d48e5fd6fc3b1590d24799e803 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("9999.999") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/c22ff3cdf5145a03ecc6a2c18a7ec4eb3c9e1384af92cfa14cf50951535b6c85 b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/c22ff3cdf5145a03ecc6a2c18a7ec4eb3c9e1384af92cfa14cf50951535b6c85 new file mode 100644 index 000000000..280f15bf7 --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/c22ff3cdf5145a03ecc6a2c18a7ec4eb3c9e1384af92cfa14cf50951535b6c85 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte(" a ") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/d40a98862ed393eb712e47a91bcef18e6f24cf368bb4bd248c7a7101ef8e178d b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/d40a98862ed393eb712e47a91bcef18e6f24cf368bb4bd248c7a7101ef8e178d new file mode 100644 index 000000000..e0f2da225 --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/d40a98862ed393eb712e47a91bcef18e6f24cf368bb4bd248c7a7101ef8e178d @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/dc7304b2cddeadd08647d30b1d027f749960376c338e14a81e0396ffc6e6d6bd b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/dc7304b2cddeadd08647d30b1d027f749960376c338e14a81e0396ffc6e6d6bd new file mode 100644 index 000000000..017f8d03f --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzP2PSecretConnection/dc7304b2cddeadd08647d30b1d027f749960376c338e14a81e0396ffc6e6d6bd @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("Tendermint fuzzing") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/058ae08103537df220789dea46edb8b7cf7368e90da0cb35888a1452f4d114a2 b/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/058ae08103537df220789dea46edb8b7cf7368e90da0cb35888a1452f4d114a2 new file mode 100644 index 000000000..53742f182 --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/058ae08103537df220789dea46edb8b7cf7368e90da0cb35888a1452f4d114a2 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("[{\"iD\":7},{\"iD\":7}]") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/2ab633cb322fca9e76fc965b430076844ebd0b3c4f30f5263b94a3d50f00bce6 b/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/2ab633cb322fca9e76fc965b430076844ebd0b3c4f30f5263b94a3d50f00bce6 new file mode 100644 index 000000000..ef2bd593a --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/2ab633cb322fca9e76fc965b430076844ebd0b3c4f30f5263b94a3d50f00bce6 @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("[0,0]") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/aadb440fa55da05c1185e3e64b33c804d994cce06781e8c39481411793a8a73f b/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/aadb440fa55da05c1185e3e64b33c804d994cce06781e8c39481411793a8a73f new file mode 100644 index 000000000..fb9f33963 --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/aadb440fa55da05c1185e3e64b33c804d994cce06781e8c39481411793a8a73f @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("[0]") \ No newline at end of file diff --git a/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/d40a98862ed393eb712e47a91bcef18e6f24cf368bb4bd248c7a7101ef8e178d b/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/d40a98862ed393eb712e47a91bcef18e6f24cf368bb4bd248c7a7101ef8e178d new file mode 100644 index 000000000..e0f2da225 --- /dev/null +++ b/test/fuzz/tests/testdata/fuzz/FuzzRPCJSONRPCServer/d40a98862ed393eb712e47a91bcef18e6f24cf368bb4bd248c7a7101ef8e178d @@ -0,0 +1,2 @@ +go test fuzz v1 +[]byte("") \ No newline at end of file diff --git a/types/block.go b/types/block.go index d6e45af6a..578ac43ee 100644 --- a/types/block.go +++ b/types/block.go @@ -603,21 +603,19 @@ const ( // CommitSig is a part of the Vote included in a Commit. type CommitSig struct { - BlockIDFlag BlockIDFlag `json:"block_id_flag"` - ValidatorAddress Address `json:"validator_address"` - Timestamp time.Time `json:"timestamp"` - Signature []byte `json:"signature"` - VoteExtension VoteExtensionToSign `json:"vote_extension"` + BlockIDFlag BlockIDFlag `json:"block_id_flag"` + ValidatorAddress Address `json:"validator_address"` + Timestamp time.Time `json:"timestamp"` + Signature []byte `json:"signature"` } // NewCommitSigForBlock returns new CommitSig with BlockIDFlagCommit. -func NewCommitSigForBlock(signature []byte, valAddr Address, ts time.Time, ext VoteExtensionToSign) CommitSig { +func NewCommitSigForBlock(signature []byte, valAddr Address, ts time.Time) CommitSig { return CommitSig{ BlockIDFlag: BlockIDFlagCommit, ValidatorAddress: valAddr, Timestamp: ts, Signature: signature, - VoteExtension: ext, } } @@ -650,14 +648,12 @@ func (cs CommitSig) Absent() bool { // 1. first 6 bytes of signature // 2. first 6 bytes of validator address // 3. block ID flag -// 4. first 6 bytes of the vote extension -// 5. timestamp +// 4. timestamp func (cs CommitSig) String() string { - return fmt.Sprintf("CommitSig{%X by %X on %v with %X @ %s}", + return fmt.Sprintf("CommitSig{%X by %X on %v @ %s}", tmbytes.Fingerprint(cs.Signature), tmbytes.Fingerprint(cs.ValidatorAddress), cs.BlockIDFlag, - tmbytes.Fingerprint(cs.VoteExtension.BytesPacked()), CanonicalTime(cs.Timestamp)) } @@ -729,7 +725,6 @@ func (cs *CommitSig) ToProto() *tmproto.CommitSig { ValidatorAddress: cs.ValidatorAddress, Timestamp: cs.Timestamp, Signature: cs.Signature, - VoteExtension: cs.VoteExtension.ToProto(), } } @@ -741,7 +736,6 @@ func (cs *CommitSig) FromProto(csp tmproto.CommitSig) error { cs.ValidatorAddress = csp.ValidatorAddress cs.Timestamp = csp.Timestamp cs.Signature = csp.Signature - cs.VoteExtension = VoteExtensionToSignFromProto(csp.VoteExtension) return cs.ValidateBasic() } @@ -808,7 +802,6 @@ func (commit *Commit) GetVote(valIdx int32) *Vote { ValidatorAddress: commitSig.ValidatorAddress, ValidatorIndex: valIdx, Signature: commitSig.Signature, - VoteExtension: commitSig.VoteExtension.ToVoteExtension(), } } diff --git a/types/block_test.go b/types/block_test.go index 4ed47dd9d..97b12cdba 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -274,7 +274,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()) } @@ -571,7 +571,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 2e2063e36..9a3d995ec 100644 --- a/types/canonical.go +++ b/types/canonical.go @@ -51,26 +51,29 @@ func CanonicalizeProposal(chainID string, proposal *tmproto.Proposal) tmproto.Ca } } -func GetVoteExtensionToSign(ext *tmproto.VoteExtension) *tmproto.VoteExtensionToSign { - if ext == nil { - return nil - } - return &tmproto.VoteExtensionToSign{ - AppDataToSign: ext.AppDataToSign, +// CanonicalizeVote transforms the given Vote to a CanonicalVote, which does +// 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, + Height: vote.Height, // encoded as sfixed64 + Round: int64(vote.Round), // encoded as sfixed64 + BlockID: CanonicalizeBlockID(vote.BlockID), + Timestamp: vote.Timestamp, + ChainID: chainID, } } -// CanonicalizeVote transforms the given Vote to a CanonicalVote, which does -// not contain ValidatorIndex and ValidatorAddress fields. -func CanonicalizeVote(chainID string, vote *tmproto.Vote) tmproto.CanonicalVote { - return tmproto.CanonicalVote{ - Type: vote.Type, - Height: vote.Height, // encoded as sfixed64 - Round: int64(vote.Round), // encoded as sfixed64 - BlockID: CanonicalizeBlockID(vote.BlockID), - Timestamp: vote.Timestamp, - ChainID: chainID, - VoteExtension: GetVoteExtensionToSign(vote.VoteExtension), +// 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, } } diff --git a/types/events.go b/types/events.go index cd535e71b..d87b74cb8 100644 --- a/types/events.go +++ b/types/events.go @@ -1,7 +1,6 @@ package types import ( - "context" "fmt" "strings" @@ -308,15 +307,15 @@ func QueryForEvent(eventValue string) *tmquery.Query { // BlockEventPublisher publishes all block related events type BlockEventPublisher interface { - PublishEventNewBlock(ctx context.Context, block EventDataNewBlock) error - PublishEventNewBlockHeader(ctx context.Context, header EventDataNewBlockHeader) error - PublishEventNewEvidence(ctx context.Context, evidence EventDataNewEvidence) error - PublishEventTx(context.Context, EventDataTx) error - PublishEventValidatorSetUpdates(context.Context, EventDataValidatorSetUpdates) error + PublishEventNewBlock(EventDataNewBlock) error + PublishEventNewBlockHeader(EventDataNewBlockHeader) error + PublishEventNewEvidence(EventDataNewEvidence) error + PublishEventTx(EventDataTx) error + PublishEventValidatorSetUpdates(EventDataValidatorSetUpdates) error } type TxEventPublisher interface { - PublishEventTx(context.Context, EventDataTx) error + PublishEventTx(EventDataTx) error } // eventWithAttr constructs a single abci.Event with a single attribute. diff --git a/types/evidence_test.go b/types/evidence_test.go index dd3b54d08..e284c2fca 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -295,7 +295,14 @@ func TestMockEvidenceValidateBasic(t *testing.T) { func makeVote( ctx context.Context, - t *testing.T, val PrivValidator, chainID string, valIndex int32, height int64, round int32, step int, blockID BlockID, + t *testing.T, + val PrivValidator, + chainID string, + valIndex int32, + height int64, + round int32, + step int, + blockID BlockID, time time.Time, ) *Vote { pubKey, err := val.GetPubKey(ctx) diff --git a/types/params.go b/types/params.go index c0e5e3a00..e8ee6fcdf 100644 --- a/types/params.go +++ b/types/params.go @@ -1,6 +1,7 @@ package types import ( + "crypto/sha256" "errors" "fmt" "time" @@ -8,7 +9,6 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/crypto/sr25519" - "github.com/tendermint/tendermint/crypto/tmhash" tmstrings "github.com/tendermint/tendermint/libs/strings" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) @@ -328,8 +328,6 @@ func (params ConsensusParams) ValidateConsensusParams() error { // This allows the ConsensusParams to evolve more without breaking the block // protocol. No need for a Merkle tree here, just a small struct to hash. func (params ConsensusParams) HashConsensusParams() []byte { - hasher := tmhash.New() - hp := tmproto.HashedParams{ BlockMaxBytes: params.Block.MaxBytes, BlockMaxGas: params.Block.MaxGas, @@ -340,11 +338,9 @@ func (params ConsensusParams) HashConsensusParams() []byte { panic(err) } - _, err = hasher.Write(bz) - if err != nil { - panic(err) - } - return hasher.Sum(nil) + sum := sha256.Sum256(bz) + + return sum[:] } func (params *ConsensusParams) Equals(params2 *ConsensusParams) bool { diff --git a/types/priv_validator.go b/types/priv_validator.go index 5a9b27cb6..29620f829 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -90,11 +90,17 @@ func (pv MockPV) SignVote(ctx context.Context, chainID string, vote *tmproto.Vot } signBytes := VoteSignBytes(useChainID, vote) + extSignBytes := VoteExtensionSignBytes(useChainID, vote) sig, err := pv.PrivKey.Sign(signBytes) if err != nil { return err } vote.Signature = sig + extSig, err := pv.PrivKey.Sign(extSignBytes) + if err != nil { + return err + } + vote.ExtensionSignature = extSig return nil } diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 3ab095eee..c482153c1 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -14,11 +14,11 @@ import ( func TestABCIPubKey(t *testing.T) { pkEd := ed25519.GenPrivKey().PubKey() - err := testABCIPubKey(t, pkEd, ABCIPubKeyTypeEd25519) + err := testABCIPubKey(t, pkEd) assert.NoError(t, err) } -func testABCIPubKey(t *testing.T, pk crypto.PubKey, typeStr string) error { +func testABCIPubKey(t *testing.T, pk crypto.PubKey) error { abciPubKey, err := encoding.PubKeyToProto(pk) require.NoError(t, err) pk2, err := encoding.PubKeyFromProto(abciPubKey) diff --git a/types/test_util.go b/types/test_util.go index dbd3f81ec..8aea2f02c 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -43,5 +43,16 @@ func signAddVote(ctx context.Context, privVal PrivValidator, vote *Vote, voteSet return false, err } vote.Signature = v.Signature + vote.ExtensionSignature = v.ExtensionSignature return voteSet.AddVote(vote) } + +// Votes constructed from commits don't have extensions, because we don't store +// the extensions themselves in the commit. This method is used to construct a +// copy of a vote, but nil its extension and signature. +func voteWithoutExtension(v *Vote) *Vote { + vc := v.Copy() + vc.Extension = nil + vc.ExtensionSignature = nil + return vc +} diff --git a/types/vote.go b/types/vote.go index 7333f98fc..e66f40396 100644 --- a/types/vote.go +++ b/types/vote.go @@ -46,86 +46,19 @@ func NewConflictingVoteError(vote1, vote2 *Vote) *ErrVoteConflictingVotes { // Address is hex bytes. type Address = crypto.Address -// VoteExtensionToSign is a subset of VoteExtension -// that is signed by the validators private key -type VoteExtensionToSign struct { - AppDataToSign []byte `json:"app_data_to_sign"` -} - -func (ext VoteExtensionToSign) ToProto() *tmproto.VoteExtensionToSign { - if ext.IsEmpty() { - return nil - } - return &tmproto.VoteExtensionToSign{ - AppDataToSign: ext.AppDataToSign, - } -} - -func VoteExtensionToSignFromProto(pext *tmproto.VoteExtensionToSign) VoteExtensionToSign { - if pext == nil { - return VoteExtensionToSign{} - } - return VoteExtensionToSign{ - AppDataToSign: pext.AppDataToSign, - } -} - -func (ext VoteExtensionToSign) IsEmpty() bool { - return len(ext.AppDataToSign) == 0 -} - -// BytesPacked returns a bytes-packed representation for -// debugging and human identification. This function should -// not be used for any logical operations. -func (ext VoteExtensionToSign) BytesPacked() []byte { - res := []byte{} - res = append(res, ext.AppDataToSign...) - return res -} - -// ToVoteExtension constructs a VoteExtension from a VoteExtensionToSign -func (ext VoteExtensionToSign) ToVoteExtension() VoteExtension { - return VoteExtension{ - AppDataToSign: ext.AppDataToSign, - } -} - -// VoteExtension is a set of data provided by the application -// that is additionally included in the vote -type VoteExtension struct { - AppDataToSign []byte `json:"app_data_to_sign"` - AppDataSelfAuthenticating []byte `json:"app_data_self_authenticating"` -} - -// ToSign constructs a VoteExtensionToSign from a VoteExtenstion -func (ext VoteExtension) ToSign() VoteExtensionToSign { - return VoteExtensionToSign{ - AppDataToSign: ext.AppDataToSign, - } -} - -// BytesPacked returns a bytes-packed representation for -// debugging and human identification. This function should -// not be used for any logical operations. -func (ext VoteExtension) BytesPacked() []byte { - res := []byte{} - res = append(res, ext.AppDataToSign...) - res = append(res, ext.AppDataSelfAuthenticating...) - return res -} - // Vote represents a prevote, precommit, or commit vote from validators for // consensus. type Vote struct { - Type tmproto.SignedMsgType `json:"type"` - Height int64 `json:"height,string"` - 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"` - VoteExtension VoteExtension `json:"vote_extension"` + Type tmproto.SignedMsgType `json:"type"` + Height int64 `json:"height,string"` + 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. @@ -149,12 +82,11 @@ func (vote *Vote) CommitSig() CommitSig { ValidatorAddress: vote.ValidatorAddress, Timestamp: vote.Timestamp, Signature: vote.Signature, - VoteExtension: vote.VoteExtension.ToSign(), } } // 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 @@ -171,9 +103,23 @@ 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 - voteCopy.VoteExtension = vote.VoteExtension.Copy() return &voteCopy } @@ -213,7 +159,7 @@ func (vote *Vote) String() string { typeString, tmbytes.Fingerprint(vote.BlockID.Hash), tmbytes.Fingerprint(vote.Signature), - tmbytes.Fingerprint(vote.VoteExtension.BytesPacked()), + tmbytes.Fingerprint(vote.Extension), CanonicalTime(vote.Timestamp), ) } @@ -226,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 } @@ -272,40 +224,15 @@ func (vote *Vote) ValidateBasic() error { return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize) } - // XXX: add length verification for vote extension? + // 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 } -func (ext VoteExtension) Copy() VoteExtension { - res := VoteExtension{ - AppDataToSign: ext.AppDataToSign, - AppDataSelfAuthenticating: ext.AppDataSelfAuthenticating, - } - return res -} - -func (ext VoteExtension) IsEmpty() bool { - if len(ext.AppDataToSign) != 0 { - return false - } - if len(ext.AppDataSelfAuthenticating) != 0 { - return false - } - return true -} - -func (ext VoteExtension) ToProto() *tmproto.VoteExtension { - if ext.IsEmpty() { - return nil - } - - return &tmproto.VoteExtension{ - AppDataToSign: ext.AppDataToSign, - AppDataSelfAuthenticating: ext.AppDataSelfAuthenticating, - } -} - // ToProto converts the handwritten type to proto generated type // return type, nil if everything converts safely, otherwise nil, error func (vote *Vote) ToProto() *tmproto.Vote { @@ -314,15 +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, - VoteExtension: vote.VoteExtension.ToProto(), + 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, } } @@ -342,15 +270,6 @@ func VotesToProto(votes []*Vote) []*tmproto.Vote { return res } -func VoteExtensionFromProto(pext *tmproto.VoteExtension) VoteExtension { - ext := VoteExtension{} - if pext != nil { - ext.AppDataToSign = pext.AppDataToSign - ext.AppDataSelfAuthenticating = pext.AppDataSelfAuthenticating - } - return ext -} - // FromProto converts a proto generetad type to a handwritten type // return type, nil if everything converts safely, otherwise nil, error func VoteFromProto(pv *tmproto.Vote) (*Vote, error) { @@ -372,7 +291,8 @@ func VoteFromProto(pv *tmproto.Vote) (*Vote, error) { vote.ValidatorAddress = pv.ValidatorAddress vote.ValidatorIndex = pv.ValidatorIndex vote.Signature = pv.Signature - vote.VoteExtension = VoteExtensionFromProto(pv.VoteExtension) + 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 4de9b1837..1805b4c3e 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -127,6 +127,7 @@ func TestVoteSet_AddVote_Bad(t *testing.T) { t.Errorf("expected VoteSet.Add to fail, wrong type") } } + } func TestVoteSet_2_3Majority(t *testing.T) { @@ -509,7 +510,6 @@ func randVoteSet( ) (*VoteSet, *ValidatorSet, []PrivValidator) { t.Helper() valSet, privValidators := randValidatorPrivValSet(ctx, t, numValidators, votingPower) - return NewVoteSet("test_chain_id", height, round, signedMsgType, valSet), valSet, privValidators } diff --git a/types/vote_test.go b/types/vote_test.go index 4a852d81f..949c996d0 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -13,6 +13,7 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/internal/libs/protoio" + tmtime "github.com/tendermint/tendermint/libs/time" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) @@ -130,12 +131,13 @@ func TestVoteSignBytesTestVectors(t *testing.T) { }, // containing vote extension 5: { - "test_chain_id", &Vote{Height: 1, Round: 1, VoteExtension: VoteExtension{ - AppDataToSign: []byte("signed"), - AppDataSelfAuthenticating: []byte("auth"), - }}, + "test_chain_id", &Vote{ + Height: 1, + Round: 1, + Extension: []byte("extension"), + }, []byte{ - 0x38, // length + 0x2e, // length 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type @@ -146,13 +148,6 @@ func TestVoteSignBytesTestVectors(t *testing.T) { // (field_number << 3) | wire_type 0x32, 0xd, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, // chainID - // (field_number << 3) | wire_type - 0x3a, - 0x8, // length - 0xa, // (field_number << 3) | wire_type - 0x6, // length - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, // AppDataSigned - // SelfAuthenticating data is excluded on signing }, // chainID }, } @@ -208,6 +203,82 @@ 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) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + 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(ctx) + 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(ctx, "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