diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b6729552d2..9960106ffd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,14 +6,14 @@ updates: interval: daily time: "11:00" open-pull-requests-limit: 10 - - package-ecosystem: npm - directory: "/docs" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 10 - reviewers: - - fadeev +# - package-ecosystem: npm +# directory: "/docs" +# schedule: +# interval: daily +# time: "11:00" +# open-pull-requests-limit: 10 +# reviewers: +# - fadeev - package-ecosystem: gomod directory: "/" schedule: @@ -21,7 +21,8 @@ updates: time: "11:00" open-pull-requests-limit: 10 reviewers: - - melekes - - tessr + - shotonoff + - lklimek + - iammadab labels: - - T:dependencies + - dependencies diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 751a19d68e..b8d577e46c 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,3 +1,4 @@ +--- name: Test Coverage on: pull_request: @@ -5,6 +6,7 @@ on: branches: - master - release/** + - v0.*-dev jobs: bls-signatures: @@ -84,7 +86,7 @@ jobs: with: go-version: "^1.15.4" - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v5.0.1 + - uses: technote-space/get-diff-action@v5.0.2 with: PATTERNS: | **/**.go @@ -116,7 +118,7 @@ jobs: with: go-version: "^1.15.4" - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v5.0.1 + - uses: technote-space/get-diff-action@v5.0.2 with: PATTERNS: | **/**.go @@ -154,7 +156,7 @@ jobs: run: | echo "::set-output name=hash::$(git --git-dir=third_party/bls-signatures/src/.git rev-parse HEAD)" shell: bash - - uses: technote-space/get-diff-action@v5.0.1 + - uses: technote-space/get-diff-action@v5.0.2 with: PATTERNS: | **/**.go diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 506aa78d20..663072c1cf 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -36,7 +36,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set suffix to Docker tags - uses: actions/github-script@v4.1 + uses: actions/github-script@v5 id: suffix with: result-encoding: string diff --git a/.github/workflows/e2e-nightly-34x.yml b/.github/workflows/e2e-nightly-34x.yml index 0220388861..0fd2479013 100644 --- a/.github/workflows/e2e-nightly-34x.yml +++ b/.github/workflows/e2e-nightly-34x.yml @@ -49,7 +49,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Notify Slack on failure - uses: rtCamp/action-slack-notify@ecc1353ce30ef086ce3fc3d1ea9ac2e32e150402 + uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} SLACK_CHANNEL: tendermint-internal @@ -65,7 +65,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Notify Slack on success - uses: rtCamp/action-slack-notify@ae4223259071871559b6e9d08b24a63d71b3f0c0 + uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} SLACK_CHANNEL: tendermint-internal diff --git a/.github/workflows/e2e-nightly-master.yml b/.github/workflows/e2e-nightly-master.yml index 3a4e4fdaa0..c823d8994b 100644 --- a/.github/workflows/e2e-nightly-master.yml +++ b/.github/workflows/e2e-nightly-master.yml @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Notify Slack on failure - uses: rtCamp/action-slack-notify@ae4223259071871559b6e9d08b24a63d71b3f0c0 + uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} SLACK_CHANNEL: tendermint-internal @@ -62,7 +62,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Notify Slack on success - uses: rtCamp/action-slack-notify@ae4223259071871559b6e9d08b24a63d71b3f0c0 + uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} SLACK_CHANNEL: tendermint-internal diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ce2b31d9b7..325fd3c87a 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -45,10 +45,12 @@ jobs: key: ${{ runner.os }}-bls-${{ steps.bls-revision.outputs.hash }} if: steps.bls-cache.outputs.cache-hit != 'true' - e2e-test: + e2e-test-dashcore: runs-on: ubuntu-latest needs: bls-signatures timeout-minutes: 15 + env: + FULLNODE_PUBKEY_KEEP: false steps: - uses: actions/setup-go@v2 with: @@ -61,7 +63,7 @@ jobs: run: | echo "::set-output name=hash::$(git --git-dir=third_party/bls-signatures/src/.git rev-parse HEAD)" shell: bash - - uses: technote-space/get-diff-action@v5.0.1 + - uses: technote-space/get-diff-action@v5.0.2 with: PATTERNS: | **/**.go @@ -81,12 +83,60 @@ jobs: run: make -j2 docker runner if: "env.GIT_DIFF != ''" - - name: Run CI testnet + - name: Run CI dashcore testnet working-directory: test/e2e - run: ./build/runner -f networks/ci.toml + run: ./build/runner -f networks/dashcore.toml if: "env.GIT_DIFF != ''" - - name: Emit logs on failure + - name: Emit dashcore logs on failure if: ${{ failure() }} working-directory: test/e2e - run: ./build/runner -f networks/ci.toml logs + run: ./build/runner -f networks/dashcore.toml logs + + e2e-test-rotate: + runs-on: ubuntu-latest + needs: bls-signatures + timeout-minutes: 15 + env: + FULLNODE_PUBKEY_KEEP: false + steps: + - uses: actions/setup-go@v2 + with: + go-version: '^1.15.5' + - uses: actions/checkout@v2 + with: + submodules: true + - name: Get BLS library revision + id: bls-revision + run: | + echo "::set-output name=hash::$(git --git-dir=third_party/bls-signatures/src/.git rev-parse HEAD)" + shell: bash + - uses: technote-space/get-diff-action@v5.0.2 + with: + PATTERNS: | + **/**.go + go.mod + go.sum + - uses: actions/cache@v2 + with: + path: ~/bls-cache + key: ${{ runner.os }}-bls-${{ steps.bls-revision.outputs.hash }} + - name: Install BLS library + run: | + sudo cp -vr ~/bls-cache/include/* /usr/local/include/ + sudo cp -vr ~/bls-cache/libchiabls.a /usr/local/lib/ + - name: Build + working-directory: test/e2e + # Run two make jobs in parallel, since we can't run steps in parallel. + run: make -j2 docker runner + if: "env.GIT_DIFF != ''" + + - name: Run CI rotate testnet + working-directory: test/e2e + run: ./build/runner -f networks/rotate.toml + if: "env.GIT_DIFF != ''" + + - name: Emit rotate logs on failure + if: ${{ failure() }} + working-directory: test/e2e + run: ./build/runner -f networks/rotate.toml logs diff --git a/.github/workflows/fuzz-nightly.yml b/.github/workflows/fuzz-nightly.yml index 83f3abe273..5d2cb4175e 100644 --- a/.github/workflows/fuzz-nightly.yml +++ b/.github/workflows/fuzz-nightly.yml @@ -72,7 +72,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Notify Slack if any crashers - uses: rtCamp/action-slack-notify@ae4223259071871559b6e9d08b24a63d71b3f0c0 + uses: rtCamp/action-slack-notify@12e36fc18b0689399306c2e0b3e0f2978b7f1ee7 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} SLACK_CHANNEL: tendermint-internal diff --git a/.github/workflows/linkchecker.yml b/.github/workflows/linkchecker.yml index f7c5b3e7bb..0a698af595 100644 --- a/.github/workflows/linkchecker.yml +++ b/.github/workflows/linkchecker.yml @@ -7,6 +7,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - - uses: gaurav-nelson/github-action-markdown-link-check@1.0.8 + - uses: gaurav-nelson/github-action-markdown-link-check@1.0.13 with: folder-path: "docs" diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index addb299b8f..4a6af17954 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -58,7 +58,7 @@ jobs: run: | echo "::set-output name=hash::$(git --git-dir=third_party/bls-signatures/src/.git rev-parse HEAD)" shell: bash - - uses: technote-space/get-diff-action@v5.0.1 + - uses: technote-space/get-diff-action@v5.0.2 with: PATTERNS: | **/**.go diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 610e2e29ee..c7de0c550e 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,7 +7,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v3 + - uses: actions/stale@v4 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-pr-message: "This pull request has been automatically marked as stale because it has not had diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4d99c10998..40de11639c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -63,7 +63,7 @@ jobs: with: go-version: "^1.15.5" - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v5.0.1 + - uses: technote-space/get-diff-action@v5.0.2 with: PATTERNS: | **/**.go @@ -95,7 +95,7 @@ jobs: with: go-version: "^1.15.5" - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v5.0.1 + - uses: technote-space/get-diff-action@v5.0.2 with: PATTERNS: | **/**.go @@ -135,7 +135,7 @@ jobs: with: go-version: "^1.15.5" - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v5.0.1 + - uses: technote-space/get-diff-action@v5.0.2 with: PATTERNS: | **/**.go @@ -166,7 +166,7 @@ jobs: with: go-version: "^1.15.5" - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v5.0.1 + - uses: technote-space/get-diff-action@v5.0.2 with: PATTERNS: | **/**.go diff --git a/.golangci.yml b/.golangci.yml index 6840a3307e..342b41ca4f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,6 +6,7 @@ linters: - dogsled - dupl - errcheck + - exportloopref # - funlen # - gochecknoglobals # - gochecknoinits @@ -26,7 +27,7 @@ linters: # - maligned - nakedret - prealloc - - scopelint + # - scopelint - staticcheck - structcheck - stylecheck diff --git a/CHANGELOG.md b/CHANGELOG.md index c1bfe504aa..b6ed38b21d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3748 +1,3421 @@ -# Changelog +## [0.7.0] - 2022-01-24 -## v0.34.11 +### Miscellaneous Tasks -*June 18, 2021* +- Create only 1 proof block by default +- Release script and initial changelog (#250) +- [**breaking**] Bump ABCI version and update release.sh to change TMVersionDefault automatically (#253) -This release improves the robustness of statesync; tweaking channel priorities and timeouts and -adding two new parameters to the state sync config. +### Build -Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). +- Bump github.com/prometheus/client_golang (#249) +- Bump github.com/BurntSushi/toml from 0.4.1 to 1.0.0 -### BREAKING CHANGES +## [0.7.0-dev.6] - 2022-01-07 -- Apps - - [Version] \#6494 `TMCoreSemVer` is not required to be set as a ldflag any longer. +### Bug Fixes -### IMPROVEMENTS +- Change CI testnet config from ci.toml on dashcore.toml +- Update the title of pipeline task +- Ensure seed at least once connects to another seed (#200) +- Panic on precommits does not have any +2/3 votes +- Improved error handling in DashCoreSignerClient +- Don't disconnect already disconnected validators -- [statesync] \#6566 Allow state sync fetchers and request timeout to be configurable. (@alexanderbez) -- [statesync] \#6378 Retry requests for snapshots and add a minimum discovery time (5s) for new snapshots. (@tychoish) -- [statesync] \#6582 Increase chunk priority and add multiple retry chunk requests (@cmwaters) +### Documentation -### BUG FIXES +- Add description about how to keep validators public keys at full node +- Add information how to sue preset for network generation +- Change a type of code block -- [evidence] \#6375 Fix bug with inconsistent LightClientAttackEvidence hashing (@cmwaters) +### Features -## v0.34.10 +- Add two more CI pipeline tasks to run e2e rotate.toml +- Reset full-node pub-keys +- Manual backport the upstream commit b69ac23fd20bdc00dea00c7c8a69fa66f2e675a9 +- Update CHANGELOG_PENDING.md +- Improve logging for better elasticsearch compatibility (#220) +- InitChain can set initial core lock height (#222) +- Add empty block on h-1 and h-2 apphash change (#241) +- Inter-validator set communication (#187) +- Add create_proof_block_range config option (#243) -*April 14, 2021* +### Refactor -This release fixes a bug where peers would sometimes try to send messages -on incorrect channels. Special thanks to our friends at Oasis Labs for surfacing -this issue! +- Minor formatting improvements +- Apply peer review feedback -Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermint). +### Testing -- [p2p/node] [\#6339](https://github.com/tendermint/tendermint/issues/6339) Fix bug with using custom channels (@cmwaters) -- [light] [\#6346](https://github.com/tendermint/tendermint/issues/6346) Correctly handle too high errors to improve client robustness (@cmwaters) +- Regenerate remote_client mock +- Get rid of workarounds for issues fixed in 0.6.1 +- Clean up databases in tests (#6304) +- Improve cleanup for data and disk use (#6311) +- Close db in randConsensusNetWithPeers, just as it is in randConsensusNet +- Ensure commit stateid in wal is OK -## v0.34.9 +### Buf -*April 8, 2021* +- Modify buf.yml, add buf generate (#5653) -This release fixes a moderate severity security issue, Security Advisory Alderfly, -which impacts all networks that rely on Tendermint light clients. -Further details will be released once networks have upgraded. +### Build -This release also includes a small Go API-breaking change, to reduce panics in the RPC layer. +- Fix proto-lint step in Makefile +- Github workflows: fix dependabot and code coverage (#191) +- Bump github.com/adlio/schema from 1.1.13 to 1.1.14 +- Bump github.com/lib/pq from 1.10.3 to 1.10.4 +- Run e2e tests in parallel +- Bump technote-space/get-diff-action from 5.0.1 to 5.0.2 +- Bump github.com/adlio/schema from 1.1.14 to 1.1.15 +- Bump github.com/adlio/schema from 1.1.15 to 1.2.3 +- Bump github.com/rs/cors from 1.8.0 to 1.8.2 -Special thanks to our external contributors on this release: @gchaincl +### E2e -Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermint). +- Add option to dump and analyze core dumps -### BREAKING CHANGES +## [0.6.1-dev.1] - 2021-10-26 -- Go API - - [rpc/jsonrpc/server] [\#6204](https://github.com/tendermint/tendermint/issues/6204) Modify `WriteRPCResponseHTTP(Error)` to return an error (@melekes) +### Bug Fixes -### FEATURES +- Accessing validator state safetly +- Safe state access in TestValidProposalChainLocks +- Safe state access in TestReactorInvalidBlockChainLock +- Safe state access in TestReactorInvalidBlockChainLock +- Seeds should not hang when disconnected from all nodes -- [rpc] [\#6226](https://github.com/tendermint/tendermint/issues/6226) Index block events and expose a new RPC method, `/block_search`, to allow querying for blocks by `BeginBlock` and `EndBlock` events (@alexanderbez) +### Buf -### BUG FIXES +- Modify buf.yml, add buf generate (#5653) -- [rpc/jsonrpc/server] [\#6191](https://github.com/tendermint/tendermint/issues/6191) Correctly unmarshal `RPCRequest` when data is `null` (@melekes) -- [p2p] [\#6289](https://github.com/tendermint/tendermint/issues/6289) Fix "unknown channels" bug on CustomReactors (@gchaincl) -- [light/evidence] Adds logic to handle forward lunatic attacks (@cmwaters) +### Build -## v0.34.8 +- Bump rtCamp/action-slack-notify from 2.1.1 to 2.2.0 +- Fix proto-lint step in Makefile -*February 25, 2021* +### Light -This release, in conjunction with [a fix in the Cosmos SDK](https://github.com/cosmos/cosmos-sdk/pull/8641), -introduces changes that should mean the logs are much, much quieter. 🎉 +- Fix panic when empty commit is received from server -Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermint). +## [0.6.0] - 2021-10-14 -### IMPROVEMENTS +### Bug Fixes -- [libs/log] [\#6174](https://github.com/tendermint/tendermint/issues/6174) Include timestamp (`ts` field; `time.RFC3339Nano` format) in JSON logger output (@melekes) +- Amd64 build and arm build ci +- State sync locks when trying to retrieve AppHash +- Set correct LastStateID when updating block +- StateID - update tests (WIP, some still red) +- Ractor should validate StateID correctly + other fixes +- StateID in light client implementation +- Tests sometimes fail on connection attempt +- App hash size validation + remove unused code +- Invalid generation of tmproto.StateID request id +- State sync locks when trying to retrieve AppHash +- Correctly handle state ID of initial block +- Don't use state to verify blocks from mempool +- Incorrect state id for first block +- AppHashSize is inconsistent +- Support initial height != 1 +- E2e: workaround for "chain stalled at unknown height" +- Update dashcore network config, add validator01 to validator_update.0 and add all available validators to 1010 height +- Cleanup e2e Readme.md +- Remove height 1008 from dashcore +- Race condition in p2p_switch and pex_reactor (#7015) +- Fix MD after the lint +- To avoid potential race conditions the validator-set-update is needed to copy rather than using the pointer to the field at PersistentKVStoreApplication, 'cause it leads to a race condition +- Update a comment block -### BUG FIXES +### Documentation -- [abci] [\#6124](https://github.com/tendermint/tendermint/issues/6124) Fixes a panic condition during callback execution in `ReCheckTx` during high tx load. (@alexanderbez) +- State ID +- State-id.md typos and grammar +- Remove invalid info about initial state id +- Add some code comments +- ADR: Inter Validator Set Messaging +- Adr-d001: apllied feedback, added additional info +- Adr-d001 clarified abci protocol changes +- Adr-d001 describe 3 scenarios and minor restructure +- Adr-d001: clarify terms based on peer review +- Adr-d001 apply peer review comments +- StateID verification algorithm -## v0.34.7 +### Features -*February 18, 2021* +- Add ProposedBlockGTimeWindow in a config +- Fix coping of PubKey pointer +- Use proto.Copy function to copy a message -This release fixes a downstream security issue which impacts Cosmos SDK -users who are: +### Fix -* Using Cosmos SDK v0.40.0 or later, AND -* Running validator nodes, AND -* Using the file-based `FilePV` implementation for their consensus keys +- Benchmark tests slow down light client tests -Users who fulfill all the above criteria were susceptible to leaking -private key material in the logs. All other users are unaffected. +### Miscellaneous Tasks -The root cause was a discrepancy -between the Tendermint Core (untyped) logger and the Cosmos SDK (typed) logger: -Tendermint Core's logger automatically stringifies Go interfaces whenever possible; -however, the Cosmos SDK's logger uses reflection to log the fields within a Go interface. +- Bump version to 0.6.0 (#185) -The introduction of the typed logger meant that previously un-logged fields within -interfaces are now sometimes logged, including the private key material inside the -`FilePV` struct. +### Refactor -Tendermint Core v0.34.7 fixes this issue; however, we strongly recommend that all validators -use remote signer implementations instead of `FilePV` in production. +- E2e docker: build bls in separate layer +- Golangci-lint + minor test improvements +- Minor formatting updates +- E2e docker: build bls in separate layer +- Add ErrInvalidVoteSignature +- S/GetStateID()/StateID()/ +- Code style changes after peer review +- Move stateid to separate file +- Remove unused message CanonicalStateVote +- Use types instead of pb StateID in SignVote and Evidence +- Inverse behaviour of resetting fullnode pubkeys from FULLNODE_PUBKEY_RESET to FULLNODE_PUBKEY_KEEP env +- Add runner/rotate task to simplify running rotate network -Thank you to @joe-bowman for his assistance with this vulnerability and a particular -shout-out to @marbar3778 for diagnosing it quickly. +### Testing -Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermint). +- Add StateID unit tests +- Check if wrong state ID fails VoteAdd() +- Fix: TestStateBadProposal didn't copy slices correctly +- TestHandshakePanicsIfAppReturnsWrongAppHash fixed +- Change apphash for every message +- Workaround for e2e tests starting too fast +- Consensus tests use random initial height +- Non-nil genesis apphash in genesis tests +- Add tests for initial height != 1 to consensus +- Fix: replay_test.go fails due to invalid height processing +- Add some StateID AppHash and Height assertions +- StateID verify with blocks N and N+1 +- Install abci-cli when running make tests_integrations (#6834) -### BUG FIXES +### Abci -- [consensus] [\#6128](https://github.com/tendermint/tendermint/pull/6128) Remove privValidator from log call (@tessr) +- Change client to use multi-reader mutexes (backport #6306) (#6873) -## v0.34.6 +### Add -*February 18, 2021* +- Update e2e doc -_Tendermint Core v0.34.5 and v0.34.6 have been recalled due to build tooling problems._ +### Build -## v0.34.4 +- E2e docker app can be run with dlv debugger +- Improve e2e docker container debugging +- Update all deps to most recent version +- Replace github.com/go-kit/kit/log with github.com/go-kit/log -*February 11, 2021* +### Cleanup -This release includes a fix for a memory leak in the evidence reactor (see #6068, below). -All Tendermint clients are recommended to upgrade. -Thank you to our friends at Crypto.com for the initial report of this memory leak! +- Remove not needed binary test/app/grpc_client -Special thanks to other external contributors on this release: @yayajacky, @odidev, @laniehei, and @c29r3! +### Config -Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermint). +- Add example on external_address (backport #6621) (#6624) -### BUG FIXES +### E2e -- [light] [\#6022](https://github.com/tendermint/tendermint/pull/6022) Fix a bug when the number of validators equals 100 (@melekes) -- [light] [\#6026](https://github.com/tendermint/tendermint/pull/6026) Fix a bug when height isn't provided for the rpc calls: `/commit` and `/validators` (@cmwaters) -- [evidence] [\#6068](https://github.com/tendermint/tendermint/pull/6068) Terminate broadcastEvidenceRoutine when peer is stopped (@melekes) +- Disable app tests for light client (#6672) +- Avoid starting nodes from the future (#6835) (#6838) +- Cleanup node start function (#6842) (#6848) +### Internal/consensus -## v0.34.3 +- Update error log (#6863) (#6867) -*January 19, 2021* +### Light -This release includes a fix for a high-severity security vulnerability, -a DoS-vector that impacted Tendermint Core v0.34.0-v0.34.2. For more details, see -[Security Advisory Mulberry](https://github.com/tendermint/tendermint/security/advisories/GHSA-p658-8693-mhvg) -or https://nvd.nist.gov/vuln/detail/CVE-2021-21271. +- Fix early erroring (#6905) -Tendermint Core v0.34.3 also updates GoGo Protobuf to 1.3.2 in order to pick up the fix for -https://nvd.nist.gov/vuln/detail/CVE-2021-3121. +### Rpc -Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermint). +- Add chunked rpc interface (backport #6445) (#6717) -### BUG FIXES +### Statesync -- [evidence] [[security fix]](https://github.com/tendermint/tendermint/security/advisories/GHSA-p658-8693-mhvg) Use correct source of evidence time (@cmwaters) -- [proto] [\#5886](https://github.com/tendermint/tendermint/pull/5889) Bump gogoproto to 1.3.2 (@marbar3778) +- Improve stateprovider handling in the syncer (backport) (#6881) -## v0.34.2 +## [0.6.0-dev.2] - 2021-09-10 -*January 12, 2021* +### Features -This release fixes a substantial bug in evidence handling where evidence could -sometimes be broadcast before the block containing that evidence was fully committed, -resulting in some nodes panicking when trying to verify said evidence. +- Info field with arbitrary data to ResultBroadcastTx -Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). +### Abci -### BREAKING CHANGES +- Change client to use multi-reader mutexes (backport #6306) (#6873) -- Go API - - [libs/os] [\#5871](https://github.com/tendermint/tendermint/issues/5871) `EnsureDir` now propagates IO errors and checks the file type (@erikgrinaker) +### E2e -### BUG FIXES +- Cleanup node start function (#6842) (#6848) -- [evidence] [\#5890](https://github.com/tendermint/tendermint/pull/5890) Add a buffer to evidence from consensus to avoid broadcasting and proposing evidence before the - height of such an evidence has finished (@cmwaters) -- [statesync] [\#5889](https://github.com/tendermint/tendermint/issues/5889) Set `LastHeightConsensusParamsChanged` when bootstrapping Tendermint state (@cmwaters) +### Internal/consensus -## v0.34.1 +- Update error log (#6863) (#6867) -*January 6, 2021* +### Light -Special thanks to external contributors on this release: +- Fix early erroring (#6905) -@p4u from vocdoni.io reported that the mempool might behave incorrectly under a -high load. The consequences can range from pauses between blocks to the peers -disconnecting from this node. As a temporary remedy (until the mempool package -is refactored), the `max-batch-bytes` was disabled. Transactions will be sent -one by one without batching. +### Statesync -Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). +- Improve stateprovider handling in the syncer (backport) (#6881) -### BREAKING CHANGES +## [0.6.0-dev.1] - 2021-08-19 -- CLI/RPC/Config - - [cli] [\#5786](https://github.com/tendermint/tendermint/issues/5786) deprecate snake_case commands for hyphen-case (@cmwaters) +### Features -- Go API - - [libs/protoio] [\#5868](https://github.com/tendermint/tendermint/issues/5868) Return number of bytes read in `Reader.ReadMsg()` (@erikgrinaker) +- [**breaking**] Proposed app version (#148) -### IMPROVEMENTS +### Miscellaneous Tasks -- [mempool] [\#5813](https://github.com/tendermint/tendermint/issues/5813) Add `keep-invalid-txs-in-cache` config option. When set to true, mempool will keep invalid transactions in the cache (@p4u) +- Bump tenderdash version to 0.6.0-dev.1 -### BUG FIXES +### Testing -- [crypto] [\#5707](https://github.com/tendermint/tendermint/issues/5707) Fix infinite recursion in string formatting of Secp256k1 keys (@erikgrinaker) -- [mempool] [\#5800](https://github.com/tendermint/tendermint/issues/5800) Disable `max-batch-bytes` (@melekes) -- [p2p] [\#5868](https://github.com/tendermint/tendermint/issues/5868) Fix inbound traffic statistics and rate limiting in `MConnection` (@erikgrinaker) +- Install abci-cli when running make tests_integrations (#6834) -## v0.34.0 +### Changelog -*November 19, 2020* +- Prepare for v0.34.12 (#6831) -Holy smokes, this is a big one! For a more reader-friendly overview of the changes in 0.34.0 -(and of the changes you need to accommodate as a user), check out [UPGRADING.md](UPGRADING.md). +### Changelog_pending -Special thanks to external contributors on this release: @james-ray, @fedekunze, @favadi, @alessio, -@joe-bowman, @cuonglm, @SadPencil and @dongsam. +- Add missing entry (#6830) -And as always, friendly reminder, that we have a [bug bounty program](https://hackerone.com/tendermint). +### E2e -### BREAKING CHANGES +- Avoid starting nodes from the future (#6835) (#6838) -- CLI/RPC/Config +### Rpc - - [config] [\#5315](https://github.com/tendermint/tendermint/pull/5315) Rename `prof_laddr` to `pprof_laddr` and move it to `rpc` section (@melekes) - - [evidence] [\#4959](https://github.com/tendermint/tendermint/pull/4959) Add JSON tags to `DuplicateVoteEvidence` (@marbar3778) - - [light] [\#4946](https://github.com/tendermint/tendermint/pull/4946) `tendermint lite` command has been renamed to `tendermint light` (@marbar3778) - - [privval] [\#4582](https://github.com/tendermint/tendermint/pull/4582) `round` in private_validator_state.json is no longer JSON string; instead it is a number (@marbar3778) - - [rpc] [\#4792](https://github.com/tendermint/tendermint/pull/4792) `/validators` are now sorted by voting power (@melekes) - - [rpc] [\#4947](https://github.com/tendermint/tendermint/pull/4947) Return an error when `page` pagination param is 0 in `/validators`, `tx_search` (@melekes) - - [rpc] [\#5137](https://github.com/tendermint/tendermint/pull/5137) JSON tags of `gasWanted` and `gasUsed` in `ResponseCheckTx` and `ResponseDeliverTx` have been made snake_case (`gas_wanted` and `gas_used`) (@marbar3778) - - [rpc] [\#5315](https://github.com/tendermint/tendermint/pull/5315) Remove `/unsafe_start_cpu_profiler`, `/unsafe_stop_cpu_profiler` and `/unsafe_write_heap_profile`. Please use pprof functionality instead (@melekes) - - [rpc/client, rpc/jsonrpc/client] [\#5347](https://github.com/tendermint/tendermint/pull/5347) All client methods now accept `context.Context` as 1st param (@melekes) +- Log update (backport #6825) (#6826) -- Apps - - - [abci] [\#4704](https://github.com/tendermint/tendermint/pull/4704) Add ABCI methods `ListSnapshots`, `LoadSnapshotChunk`, `OfferSnapshot`, and `ApplySnapshotChunk` for state sync snapshots. `ABCIVersion` bumped to 0.17.0. (@erikgrinaker) - - [abci] [\#4989](https://github.com/tendermint/tendermint/pull/4989) `Proof` within `ResponseQuery` has been renamed to `ProofOps` (@marbar3778) - - [abci] [\#5096](https://github.com/tendermint/tendermint/pull/5096) `CheckTxType` Protobuf enum names are now uppercase, to follow Protobuf style guide (@erikgrinaker) - - [abci] [\#5324](https://github.com/tendermint/tendermint/pull/5324) ABCI evidence type is now an enum with two types of possible evidence (@cmwaters) - -- P2P Protocol - - - [blockchain] [\#4637](https://github.com/tendermint/tendermint/pull/4637) Migrate blockchain reactor(s) to Protobuf encoding (@marbar3778) - - [evidence] [\#4949](https://github.com/tendermint/tendermint/pull/4949) Migrate evidence reactor to Protobuf encoding (@marbar3778) - - [mempool] [\#4940](https://github.com/tendermint/tendermint/pull/4940) Migrate mempool from to Protobuf encoding (@marbar3778) - - [mempool] [\#5321](https://github.com/tendermint/tendermint/pull/5321) Batch transactions when broadcasting them to peers (@melekes) - - `MaxBatchBytes` new config setting defines the max size of one batch. - - [p2p/pex] [\#4973](https://github.com/tendermint/tendermint/pull/4973) Migrate `p2p/pex` reactor to Protobuf encoding (@marbar3778) - - [statesync] [\#4943](https://github.com/tendermint/tendermint/pull/4943) Migrate state sync reactor to Protobuf encoding (@marbar3778) - -- Blockchain Protocol - - - [evidence] [\#4725](https://github.com/tendermint/tendermint/pull/4725) Remove `Pubkey` from `DuplicateVoteEvidence` (@marbar3778) - - [evidence] [\#5499](https://github.com/tendermint/tendermint/pull/5449) Cap evidence to a maximum number of bytes (supercedes [\#4780](https://github.com/tendermint/tendermint/pull/4780)) (@cmwaters) - - [merkle] [\#5193](https://github.com/tendermint/tendermint/pull/5193) Header hashes are no longer empty for empty inputs, notably `DataHash`, `EvidenceHash`, and `LastResultsHash` (@erikgrinaker) - - [state] [\#4845](https://github.com/tendermint/tendermint/pull/4845) Include `GasWanted` and `GasUsed` into `LastResultsHash` (@melekes) - - [types] [\#4792](https://github.com/tendermint/tendermint/pull/4792) Sort validators by voting power to enable faster commit verification (@melekes) - -- On-disk serialization - - - [state] [\#4679](https://github.com/tendermint/tendermint/pull/4679) Migrate state module to Protobuf encoding (@marbar3778) - - `BlockStoreStateJSON` is now `BlockStoreState` and is encoded as binary in the database - - [store] [\#4778](https://github.com/tendermint/tendermint/pull/4778) Migrate store module to Protobuf encoding (@marbar3778) - -- Light client, private validator - - - [light] [\#4964](https://github.com/tendermint/tendermint/pull/4964) Migrate light module migration to Protobuf encoding (@marbar3778) - - [privval] [\#4985](https://github.com/tendermint/tendermint/pull/4985) Migrate `privval` module to Protobuf encoding (@marbar3778) - -- Go API - - - [consensus] [\#4582](https://github.com/tendermint/tendermint/pull/4582) RoundState: `Round`, `LockedRound` & `CommitRound` are now `int32` (@marbar3778) - - [consensus] [\#4582](https://github.com/tendermint/tendermint/pull/4582) HeightVoteSet: `round` is now `int32` (@marbar3778) - - [crypto] [\#4721](https://github.com/tendermint/tendermint/pull/4721) Remove `SimpleHashFromMap()` and `SimpleProofsFromMap()` (@erikgrinaker) - - [crypto] [\#4940](https://github.com/tendermint/tendermint/pull/4940) All keys have become `[]byte` instead of `[]byte`. The byte method no longer returns the marshaled value but just the `[]byte` form of the data. (@marbar3778) - - [crypto] [\#4988](https://github.com/tendermint/tendermint/pull/4988) Removal of key type multisig (@marbar3778) - - The key has been moved to the [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk/blob/master/crypto/types/multisig/multisignature.go) - - [crypto] [\#4989](https://github.com/tendermint/tendermint/pull/4989) Remove `Simple` prefixes from `SimpleProof`, `SimpleValueOp` & `SimpleProofNode`. (@marbar3778) - - `merkle.Proof` has been renamed to `ProofOps`. - - Protobuf messages `Proof` & `ProofOp` has been moved to `proto/crypto/merkle` - - `SimpleHashFromByteSlices` has been renamed to `HashFromByteSlices` - - `SimpleHashFromByteSlicesIterative` has been renamed to `HashFromByteSlicesIterative` - - `SimpleProofsFromByteSlices` has been renamed to `ProofsFromByteSlices` - - [crypto] [\#4941](https://github.com/tendermint/tendermint/pull/4941) Remove suffixes from all keys. (@marbar3778) - - ed25519: type `PrivKeyEd25519` is now `PrivKey` - - ed25519: type `PubKeyEd25519` is now `PubKey` - - secp256k1: type`PrivKeySecp256k1` is now `PrivKey` - - secp256k1: type`PubKeySecp256k1` is now `PubKey` - - sr25519: type `PrivKeySr25519` is now `PrivKey` - - sr25519: type `PubKeySr25519` is now `PubKey` - - [crypto] [\#5214](https://github.com/tendermint/tendermint/pull/5214) Change `GenPrivKeySecp256k1` to `GenPrivKeyFromSecret` to be consistent with other keys (@marbar3778) - - [crypto] [\#5236](https://github.com/tendermint/tendermint/pull/5236) `VerifyBytes` is now `VerifySignature` on the `crypto.PubKey` interface (@marbar3778) - - [evidence] [\#5361](https://github.com/tendermint/tendermint/pull/5361) Add LightClientAttackEvidence and change evidence interface (@cmwaters) - - [libs] [\#4831](https://github.com/tendermint/tendermint/pull/4831) Remove `Bech32` pkg from Tendermint. This pkg now lives in the [cosmos-sdk](https://github.com/cosmos/cosmos-sdk/tree/4173ea5ebad906dd9b45325bed69b9c655504867/types/bech32) (@marbar3778) - - [light] [\#4946](https://github.com/tendermint/tendermint/pull/4946) Rename `lite2` pkg to `light`. Remove `lite` implementation. (@marbar3778) - - [light] [\#5347](https://github.com/tendermint/tendermint/pull/5347) `NewClient`, `NewHTTPClient`, `VerifyHeader` and `VerifyLightBlockAtHeight` now accept `context.Context` as 1st param (@melekes) - - [merkle] [\#5193](https://github.com/tendermint/tendermint/pull/5193) `HashFromByteSlices` and `ProofsFromByteSlices` now return a hash for empty inputs, following RFC6962 (@erikgrinaker) - - [proto] [\#5025](https://github.com/tendermint/tendermint/pull/5025) All proto files have been moved to `/proto` directory. (@marbar3778) - - Using the recommended the file layout from buf, [see here for more info](https://buf.build/docs/lint-checkers#file_layout) - - [rpc/client] [\#4947](https://github.com/tendermint/tendermint/pull/4947) `Validators`, `TxSearch` `page`/`per_page` params become pointers (@melekes) - - `UnconfirmedTxs` `limit` param is a pointer - - [rpc/jsonrpc/server] [\#5141](https://github.com/tendermint/tendermint/pull/5141) Remove `WriteRPCResponseArrayHTTP` (use `WriteRPCResponseHTTP` instead) (@melekes) - - [state] [\#4679](https://github.com/tendermint/tendermint/pull/4679) `TxResult` is a Protobuf type defined in `abci` types directory (@marbar3778) - - [state] [\#5191](https://github.com/tendermint/tendermint/pull/5191) Add `State.InitialHeight` field to record initial block height, must be `1` (not `0`) to start from 1 (@erikgrinaker) - - [state] [\#5231](https://github.com/tendermint/tendermint/pull/5231) `LoadStateFromDBOrGenesisFile()` and `LoadStateFromDBOrGenesisDoc()` no longer saves the state in the database if not found, the genesis state is simply returned (@erikgrinaker) - - [state] [\#5348](https://github.com/tendermint/tendermint/pull/5348) Define an Interface for the state store. (@marbar3778) - - [types] [\#4939](https://github.com/tendermint/tendermint/pull/4939) `SignedMsgType` has moved to a Protobuf enum types (@marbar3778) - - [types] [\#4962](https://github.com/tendermint/tendermint/pull/4962) `ConsensusParams`, `BlockParams`, `EvidenceParams`, `ValidatorParams` & `HashedParams` are now Protobuf types (@marbar3778) - - [types] [\#4852](https://github.com/tendermint/tendermint/pull/4852) Vote & Proposal `SignBytes` is now func `VoteSignBytes` & `ProposalSignBytes` (@marbar3778) - - [types] [\#4798](https://github.com/tendermint/tendermint/pull/4798) Simplify `VerifyCommitTrusting` func + remove extra validation (@melekes) - - [types] [\#4845](https://github.com/tendermint/tendermint/pull/4845) Remove `ABCIResult` (@melekes) - - [types] [\#5029](https://github.com/tendermint/tendermint/pull/5029) Rename all values from `PartsHeader` to `PartSetHeader` to have consistency (@marbar3778) - - [types] [\#4939](https://github.com/tendermint/tendermint/pull/4939) `Total` in `Parts` & `PartSetHeader` has been changed from a `int` to a `uint32` (@marbar3778) - - [types] [\#4939](https://github.com/tendermint/tendermint/pull/4939) Vote: `ValidatorIndex` & `Round` are now `int32` (@marbar3778) - - [types] [\#4939](https://github.com/tendermint/tendermint/pull/4939) Proposal: `POLRound` & `Round` are now `int32` (@marbar3778) - - [types] [\#4939](https://github.com/tendermint/tendermint/pull/4939) Block: `Round` is now `int32` (@marbar3778) - -### FEATURES - -- [abci] [\#5031](https://github.com/tendermint/tendermint/pull/5031) Add `AppVersion` to consensus parameters (@james-ray) - - This makes it possible to update your ABCI application version via `EndBlock` response -- [abci] [\#5174](https://github.com/tendermint/tendermint/pull/5174) Remove `MockEvidence` in favor of testing with actual evidence types (`DuplicateVoteEvidence` & `LightClientAttackEvidence`) (@cmwaters) -- [abci] [\#5191](https://github.com/tendermint/tendermint/pull/5191) Add `InitChain.InitialHeight` field giving the initial block height (@erikgrinaker) -- [abci] [\#5227](https://github.com/tendermint/tendermint/pull/5227) Add `ResponseInitChain.app_hash` which is recorded in genesis block (@erikgrinaker) -- [config] [\#5147](https://github.com/tendermint/tendermint/pull/5147) Add `--consensus.double_sign_check_height` flag and `DoubleSignCheckHeight` config variable. See [ADR-51](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-051-double-signing-risk-reduction.md) (@dongsam) -- [db] [\#5233](https://github.com/tendermint/tendermint/pull/5233) Add support for `badgerdb` database backend (@erikgrinaker) -- [evidence] [\#4532](https://github.com/tendermint/tendermint/pull/4532) Handle evidence from light clients (@melekes) -- [evidence] [#4821](https://github.com/tendermint/tendermint/pull/4821) Amnesia (light client attack) evidence can be detected, verified and committed (@cmwaters) -- [genesis] [\#5191](https://github.com/tendermint/tendermint/pull/5191) Add `initial_height` field to specify the initial chain height (defaults to `1`) (@erikgrinaker) -- [libs/math] [\#5665](https://github.com/tendermint/tendermint/pull/5665) Make fractions unsigned integers (uint64) (@cmwaters) -- [light] [\#5298](https://github.com/tendermint/tendermint/pull/5298) Morph validator set and signed header into light block (@cmwaters) -- [p2p] [\#4981](https://github.com/tendermint/tendermint/pull/4981) Expose `SaveAs` func on NodeKey (@melekes) -- [privval] [\#5239](https://github.com/tendermint/tendermint/pull/5239) Add `chainID` to requests from client. (@marbar3778) -- [rpc] [\#4532](https://github.com/tendermint/tendermint/pull/4923) Support `BlockByHash` query (@fedekunze) -- [rpc] [\#4979](https://github.com/tendermint/tendermint/pull/4979) Support EXISTS operator in `/tx_search` query (@melekes) -- [rpc] [\#5017](https://github.com/tendermint/tendermint/pull/5017) Add `/check_tx` endpoint to check transactions without executing them or adding them to the mempool (@melekes) -- [rpc] [\#5108](https://github.com/tendermint/tendermint/pull/5108) Subscribe using the websocket for new evidence events (@cmwaters) -- [statesync] Add state sync support, where a new node can be rapidly bootstrapped by fetching state snapshots from peers instead of replaying blocks. See the `[statesync]` config section. -- [evidence] [\#5361](https://github.com/tendermint/tendermint/pull/5361) Add LightClientAttackEvidence and refactor evidence lifecycle - for more information see [ADR-059](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-059-evidence-composition-and-lifecycle.md) (@cmwaters) - -### IMPROVEMENTS - -- [blockchain] [\#5278](https://github.com/tendermint/tendermint/pull/5278) Verify only +2/3 of the signatures in a block when fast syncing. (@marbar3778) -- [consensus] [\#4578](https://github.com/tendermint/tendermint/pull/4578) Attempt to repair the consensus WAL file (`data/cs.wal/wal`) automatically in case of corruption (@alessio) - - The original WAL file will be backed up to `data/cs.wal/wal.CORRUPTED`. -- [consensus] [\#5143](https://github.com/tendermint/tendermint/pull/5143) Only call `privValidator.GetPubKey` once per block (@melekes) -- [evidence] [\#4722](https://github.com/tendermint/tendermint/pull/4722) Consolidate evidence store and pool types to improve evidence DB (@cmwaters) -- [evidence] [\#4839](https://github.com/tendermint/tendermint/pull/4839) Reject duplicate evidence from being proposed (@cmwaters) -- [evidence] [\#5219](https://github.com/tendermint/tendermint/pull/5219) Change the source of evidence time to block time (@cmwaters) -- [libs] [\#5126](https://github.com/tendermint/tendermint/pull/5126) Add a sync package which wraps sync.(RW)Mutex & deadlock.(RW)Mutex and use a build flag (deadlock) in order to enable deadlock checking (@marbar3778) -- [light] [\#4935](https://github.com/tendermint/tendermint/pull/4935) Fetch and compare a new header with witnesses in parallel (@melekes) -- [light] [\#4929](https://github.com/tendermint/tendermint/pull/4929) Compare header with witnesses only when doing bisection (@melekes) -- [light] [\#4916](https://github.com/tendermint/tendermint/pull/4916) Validate basic for inbound validator sets and headers before further processing them (@cmwaters) -- [mempool] Add RemoveTxByKey() exported function for custom mempool cleaning (@p4u) -- [p2p/conn] [\#4795](https://github.com/tendermint/tendermint/pull/4795) Return err on `signChallenge()` instead of panic -- [privval] [\#5437](https://github.com/tendermint/tendermint/pull/5437) `NewSignerDialerEndpoint` can now be given `SignerServiceEndpointOption` (@erikgrinaker) -- [rpc] [\#4968](https://github.com/tendermint/tendermint/pull/4968) JSON encoding is now handled by `libs/json`, not Amino (@erikgrinaker) -- [rpc] [\#5293](https://github.com/tendermint/tendermint/pull/5293) `/dial_peers` has added `private` and `unconditional` as parameters. (@marbar3778) -- [state] [\#4781](https://github.com/tendermint/tendermint/pull/4781) Export `InitStateVersion` for the initial state version (@erikgrinaker) -- [txindex] [\#4466](https://github.com/tendermint/tendermint/pull/4466) Allow to index an event at runtime (@favadi) - - `abci.EventAttribute` replaces `KV.Pair` -- [types] [\#4905](https://github.com/tendermint/tendermint/pull/4905) Add `ValidateBasic` to validator and validator set (@cmwaters) -- [types] [\#5340](https://github.com/tendermint/tendermint/pull/5340) Add check in `Header.ValidateBasic()` for block protocol version (@marbar3778) -- [types] [\#5490](https://github.com/tendermint/tendermint/pull/5490) Use `Commit` and `CommitSig` max sizes instead of vote max size to calculate the maximum block size. (@cmwaters) - - -### BUG FIXES - -- [abci/grpc] [\#5520](https://github.com/tendermint/tendermint/pull/5520) Return async responses in order, to avoid mempool panics. (@erikgrinaker) -- [blockchain/v2] [\#4971](https://github.com/tendermint/tendermint/pull/4971) Correctly set block store base in status responses (@erikgrinaker) -- [blockchain/v2] [\#5499](https://github.com/tendermint/tendermint/pull/5499) Fix "duplicate block enqueued by processor" panic (@melekes) -- [blockchain/v2] [\#5530](https://github.com/tendermint/tendermint/pull/5530) Fix out of order block processing panic (@melekes) -- [blockchain/v2] [\#5553](https://github.com/tendermint/tendermint/pull/5553) Make the removal of an already removed peer a noop (@melekes) -- [consensus] [\#4895](https://github.com/tendermint/tendermint/pull/4895) Cache the address of the validator to reduce querying a remote KMS (@joe-bowman) -- [consensus] [\#4970](https://github.com/tendermint/tendermint/pull/4970) Don't allow `LastCommitRound` to be negative (@cuonglm) -- [consensus] [\#5329](https://github.com/tendermint/tendermint/pull/5329) Fix wrong proposer schedule for validators returned by `InitChain` (@erikgrinaker) -- [docker] [\#5385](https://github.com/tendermint/tendermint/pull/5385) Fix incorrect `time_iota_ms` default setting causing block timestamp drift (@erikgrinaker) -- [evidence] [\#5170](https://github.com/tendermint/tendermint/pull/5170) Change ABCI evidence time to the time the infraction happened not the time the evidence was committed on the block (@cmwaters) -- [evidence] [\#5610](https://github.com/tendermint/tendermint/pull/5610) Make it possible for ABCI evidence to be formed from Tendermint evidence (@cmwaters) -- [libs/rand] [\#5215](https://github.com/tendermint/tendermint/pull/5215) Fix out-of-memory error on unexpected argument of Str() (@SadPencil) -- [light] [\#5307](https://github.com/tendermint/tendermint/pull/5307) Persist correct proposer priority in light client validator sets (@cmwaters) -- [p2p] [\#5136](https://github.com/tendermint/tendermint/pull/5136) Fix error for peer with the same ID but different IPs (@valardragon) -- [privval] [\#5638](https://github.com/tendermint/tendermint/pull/5638) Increase read/write timeout to 5s and calculate ping interval based on it (@JoeKash) -- [proxy] [\#5078](https://github.com/tendermint/tendermint/pull/5078) Force Tendermint to exit when ABCI app crashes (@melekes) -- [rpc] [\#5660](https://github.com/tendermint/tendermint/pull/5660) Set `application/json` as the `Content-Type` header in RPC responses. (@alexanderbez) -- [store] [\#5382](https://github.com/tendermint/tendermint/pull/5382) Fix race conditions when loading/saving/pruning blocks (@erikgrinaker) - -## v0.33.8 - -*August 11, 2020* - -### Go security update - -Go reported a security vulnerability that affected the `encoding/binary` package. The most recent binary for tendermint is using 1.14.6, for this -reason the Tendermint engineering team has opted to conduct a release to aid users in using the correct version of Go. Read more about the security issue [here](https://github.com/golang/go/issues/40618). - - -## v0.33.7 - - *August 4, 2020* - - ### BUG FIXES: - - - [go] Build release binary using Go 1.14.4, to avoid halt caused by Go 1.14.1 (https://github.com/golang/go/issues/38223) - - [privval] [\#5140](https://github.com/tendermint/tendermint/pull/5140) `RemoteSignerError` from remote signers are no longer retried (@melekes) - - -## v0.33.6 - -*July 2, 2020* - -This security release fixes: - -### Denial of service - -Tendermint 0.33.0 and above allow block proposers to include signatures for the -wrong block. This may happen naturally if you start a network, have it run for -some time and restart it **without changing the chainID**. (It is a -[misconfiguration](https://docs.tendermint.com/master/tendermint-core/using-tendermint.html) -to reuse chainIDs.) Correct block proposers will accidentally include signatures -for the wrong block if they see these signatures, and then commits won't validate, -making all proposed blocks invalid. A malicious validator (even with a minimal -amount of stake) can use this vulnerability to completely halt the network. - -Tendermint 0.33.6 checks all the signatures are for the block with +2/3 -majority before creating a commit. - -### False Witness - -Tendermint 0.33.1 and above are no longer fully verifying commit signatures -during block execution - they stop after +2/3. This means proposers can propose -blocks that contain valid +2/3 signatures and then the rest of the signatures -can be whatever they want. They can claim that all the other validators signed -just by including a CommitSig with arbitrary signature data. While this doesn't -seem to impact safety of Tendermint per se, it means that Commits may contain a -lot of invalid data. +### Version -_This was already true of blocks, since they could include invalid txs filled -with garbage, but in that case the application knew that they are invalid and -could punish the proposer. But since applications didn't--and don't-- -verify commit signatures directly (they trust Tendermint to do that), -they won't be able to detect it._ +- Bump for 0.34.12 (#6832) -This can impact incentivization logic in the application that depends on the -LastCommitInfo sent in BeginBlock, which includes which validators signed. For -instance, Gaia incentivizes proposers with a bonus for including more than +2/3 -of the signatures. But a proposer can now claim that bonus just by including -arbitrary data for the final -1/3 of validators without actually waiting for -their signatures. There may be other tricks that can be played because of this. +## [0.5.11-dev.4] - 2021-07-31 -Tendermint 0.33.6 verifies all the signatures during block execution. +### State/privval -_Please note that the light client does not check nil votes and exits as soon -as 2/3+ of the signatures are checked._ +- Vote timestamp fix (backport #6748) (#6783) -**All clients are recommended to upgrade.** +## [0.5.10] - 2021-07-26 -Special thanks to @njmurarka at Bluzelle Networks for reporting this. +### Build -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). +- Bump golangci/golangci-lint-action from 2.3.0 to 2.5.2 -### SECURITY: +## [0.5.7] - 2021-07-16 -- [consensus] Do not allow signatures for a wrong block in commits (@ebuchman) -- [consensus] Verify all the signatures during block execution (@melekes) +### Bug Fixes -**Please note that the fix for the False Witness issue renames the `VerifyCommitTrusting` -function to `VerifyCommitLightTrusting`. If you were relying on the light client, you may -need to update your code.** +- Maverick compile issues (#104) +- Private validator key still automatically creating (#120) +- Getting pro tx hash from full node +- Incorrectly assume amd64 arch during docker build +- Image isn't pushed after build -## v0.33.5 +### Documentation -*May 28, 2020* +- Rename tenderdash and update target repo +- Update github issue and pr templates (#131) -Special thanks to external contributors on this release: @tau3, +### Features -Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). +- Improve initialisation (#117) +- Add arm64 arch for builds -### BREAKING CHANGES: +### Miscellaneous Tasks -- Go API +- Target production dockerhub org +- Use official docker action - - [privval] [\#4744](https://github.com/tendermint/tendermint/pull/4744) Remove deprecated `OldFilePV` (@melekes) - - [mempool] [\#4759](https://github.com/tendermint/tendermint/pull/4759) Modify `Mempool#InitWAL` to return an error (@melekes) - - [node] [\#4832](https://github.com/tendermint/tendermint/pull/4832) `ConfigureRPC` returns an error (@melekes) - - [rpc] [\#4836](https://github.com/tendermint/tendermint/pull/4836) Overhaul `lib` folder (@melekes) - Move lib/ folder to jsonrpc/. - Rename: - rpc package -> jsonrpc package - rpcclient package -> client package - rpcserver package -> server package - JSONRPCClient to Client - JSONRPCRequestBatch to RequestBatch - JSONRPCCaller to Caller - StartHTTPServer to Serve - StartHTTPAndTLSServer to ServeTLS - NewURIClient to NewURI - NewJSONRPCClient to New - NewJSONRPCClientWithHTTPClient to NewWithHTTPClient - NewWSClient to NewWS - Unexpose ResponseWriterWrapper - Remove unused http_params.go +### Ci +- Make ci consistent and push to docker hub +- Trigger docker build on release +- Disable arm build +- Always push to dockerhub +- Test enabling cache +- Test arm build +- Manually trigger build +- Disable arm64 builds +- Set release to workflow dispatch (manual) trigger +- Enable arm64 in CI -### FEATURES: +### Config -- [pex] [\#4439](https://github.com/tendermint/tendermint/pull/4439) Use highwayhash for pex buckets (@tau3) +- Add example on external_address (backport #6621) (#6624) -### IMPROVEMENTS: +### E2e -- [abci/server] [\#4719](https://github.com/tendermint/tendermint/pull/4719) Print panic & stack trace to STDERR if logger is not set (@melekes) -- [types] [\#4638](https://github.com/tendermint/tendermint/pull/4638) Implement `Header#ValidateBasic` (@alexanderbez) -- [buildsystem] [\#4378](https://github.com/tendermint/tendermint/pull/4738) Replace build_c and install_c with TENDERMINT_BUILD_OPTIONS parsing. The following options are available: - - nostrip: don't strip debugging symbols nor DWARF tables. - - cleveldb: use cleveldb as db backend instead of goleveldb. - - race: pass -race to go build and enable data race detection. -- [mempool] [\#4759](https://github.com/tendermint/tendermint/pull/4759) Allow ReapX and CheckTx functions to run in parallel (@melekes) -- [rpc/core] [\#4844](https://github.com/tendermint/tendermint/pull/4844) Do not lock consensus state in `/validators`, `/consensus_params` and `/status` (@melekes) +- Disable app tests for light client (#6672) -### BUG FIXES: +### Light -- [blockchain/v2] [\#4761](https://github.com/tendermint/tendermint/pull/4761) Fix excessive CPU usage caused by spinning on closed channels (@erikgrinaker) -- [blockchain/v2] Respect `fast_sync` option (@erikgrinaker) -- [light] [\#4741](https://github.com/tendermint/tendermint/pull/4741) Correctly return `ErrSignedHeaderNotFound` and `ErrValidatorSetNotFound` on corresponding RPC errors (@erikgrinaker) -- [rpc] [\#4805](https://github.com/tendermint/tendermint/issues/4805) Attempt to handle panics during panic recovery (@erikgrinaker) -- [types] [\#4764](https://github.com/tendermint/tendermint/pull/4764) Return an error if voting power overflows in `VerifyCommitTrusting` (@melekes) -- [privval] [\#4812](https://github.com/tendermint/tendermint/pull/4812) Retry `GetPubKey/SignVote/SignProposal` a few times before returning an error (@melekes) -- [p2p] [\#4847](https://github.com/tendermint/tendermint/pull/4847) Return masked IP (not the actual IP) in addrbook#groupKey (@melekes) +- Correctly handle contexts (backport -> v0.34.x) (#6685) +- Add case to catch cancelled contexts within the detector (backport #6701) (#6720) -## v0.33.4 +### Release -- Nodes are no longer guaranteed to contain all blocks up to the latest height. The ABCI app can now control which blocks to retain through the ABCI field `ResponseCommit.retain_height`, all blocks and associated data below this height will be removed. +- Prepare changelog for v0.34.11 (#6597) -*April 21, 2020* +### Rpc -Special thanks to external contributors on this release: @whylee259, @greg-szabo +- Add chunked rpc interface (backport #6445) (#6717) -Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). +### Statesync -### BREAKING CHANGES: +- Increase chunk priority and robustness (#6582) -- Go API +## [0.4.1] - 2021-06-09 - - [lite2] [\#4616](https://github.com/tendermint/tendermint/pull/4616) Make `maxClockDrift` an option `Verify/VerifyAdjacent/VerifyNonAdjacent` now accept `maxClockDrift time.Duration` (@melekes). - - [rpc/client] [\#4628](https://github.com/tendermint/tendermint/pull/4628) Split out HTTP and local clients into `http` and `local` packages (@erikgrinaker). +### .github -### FEATURES: +- Split the issue template into two seperate templates (#2073) +- Add markdown link checker (#4513) +- Move checklist from PR description into an auto-comment (#4745) +- Fix whitespace for autocomment (#4747) +- Fix whitespace for auto-comment (#4750) +- Rename crashers output (fuzz-nightly-test) (#5993) +- Archive crashers and fix set-crashers-count step (#5992) +- Fix fuzz-nightly job (#5965) +- Use job ID (not step ID) inside if condition (#6060) +- Remove erik as reviewer from dependapot (#6076) +- Make core team codeowners (#6384) -- [abci] [\#4588](https://github.com/tendermint/tendermint/issues/4588) Add `ResponseCommit.retain_height` field, which will automatically remove blocks below this height. This bumps the ABCI version to 0.16.2 (@erikgrinaker). -- [cmd] [\#4665](https://github.com/tendermint/tendermint/pull/4665) New `tendermint completion` command to generate Bash/Zsh completion scripts (@alessio). -- [rpc] [\#4588](https://github.com/tendermint/tendermint/issues/4588) Add `/status` response fields for the earliest block available on the node (@erikgrinaker). -- [rpc] [\#4611](https://github.com/tendermint/tendermint/pull/4611) Add `codespace` to `ResultBroadcastTx` (@whylee259). +### .github/codeowners -### IMPROVEMENTS: +- Add alexanderbez (#5913) -- [all] [\#4608](https://github.com/tendermint/tendermint/pull/4608) Give reactors descriptive names when they're initialized (@tessr). -- [blockchain] [\#4588](https://github.com/tendermint/tendermint/issues/4588) Add `Base` to blockchain reactor P2P messages `StatusRequest` and `StatusResponse` (@erikgrinaker). -- [Docker] [\#4569](https://github.com/tendermint/tendermint/issues/4569) Default configuration added to docker image (you can still mount your own config the same way) (@greg-szabo). -- [example/kvstore] [\#4588](https://github.com/tendermint/tendermint/issues/4588) Add `RetainBlocks` option to control block retention (@erikgrinaker). -- [evidence] [\#4632](https://github.com/tendermint/tendermint/pull/4632) Inbound evidence checked if already existing (@cmwaters). -- [lite2] [\#4575](https://github.com/tendermint/tendermint/pull/4575) Use bisection for within-range verification (@cmwaters). -- [lite2] [\#4562](https://github.com/tendermint/tendermint/pull/4562) Cache headers when using bisection (@cmwaters). -- [p2p] [\#4548](https://github.com/tendermint/tendermint/pull/4548) Add ban list to address book (@cmwaters). -- [privval] [\#4534](https://github.com/tendermint/tendermint/issues/4534) Add `error` as a return value on`GetPubKey()` (@marbar3778). -- [p2p] [\#4621](https://github.com/tendermint/tendermint/issues/4621) Ban peers when messages are unsolicited or too frequent (@cmwaters). -- [rpc] [\#4703](https://github.com/tendermint/tendermint/pull/4703) Add `count` and `total` to `/validators` response (@melekes). -- [tools] [\#4615](https://github.com/tendermint/tendermint/issues/4615) Allow developers to use Docker to generate proto stubs, via `make proto-gen-docker` (@erikgrinaker). +### .github/issue_template -### BUG FIXES: +- Update `/dump_consensus_state` request. (#5060) -- [rpc] [\#4568](https://github.com/tendermint/tendermint/issues/4568) Fix panic when `Subscribe` is called, but HTTP client is not running. `Subscribe`, `Unsubscribe(All)` methods return an error now (@melekes). +### .github/workflows -## v0.33.3 +- Try different e2e nightly test set (#6036) +- Separate e2e workflows for 0.34.x and master (#6041) +- Fix whitespace in e2e config file (#6043) +- Cleanup yaml for e2e nightlies (#6049) -*April 6, 2020* +### .golangci -This security release fixes: +- Disable new linters (#4024) -### Denial of service 1 +### .goreleaser -Tendermint 0.33.2 and earlier does not limit P2P connection requests number. -For each p2p connection, Tendermint allocates ~0.5MB. Even though this -memory is garbage collected once the connection is terminated (due to duplicate -IP or reaching a maximum number of inbound peers), temporary memory spikes can -lead to OOM (Out-Of-Memory) exceptions. +- Don't build linux/arm +- Build for windows +- Remove arm64 build instructions and bump changelog again (#6131) -Tendermint 0.33.3 (and 0.32.10) limits the total number of P2P incoming -connection requests to to `p2p.max_num_inbound_peers + -len(p2p.unconditional_peer_ids)`. +### ADR -Notes: +- Fix malleability problems in Secp256k1 signatures +- Add missing numbers as blank templates (#5154) -- Tendermint does not rate limit P2P connection requests per IP (an attacker - can saturate all the inbound slots); -- Tendermint does not rate limit HTTP(S) requests. If you expose any RPC - endpoints to the public, please make sure to put in place some protection - (https://www.nginx.com/blog/rate-limiting-nginx/). We may implement this in - the future ([\#1696](https://github.com/tendermint/tendermint/issues/1696)). +### ADR-016 -### Denial of service 2 +- Add versions to Block and State (#2644) +- Add protocol Version to NodeInfo (#2654) +- Update ABCI Info method for versions (#2662) -Tendermint 0.33.2 and earlier does not reclaim `activeID` of a peer after it's -removed in `Mempool` reactor. This does not happen all the time. It only -happens when a connection fails (for any reason) before the Peer is created and -added to all reactors. `RemovePeer` is therefore called before `AddPeer`, which -leads to always growing memory (`activeIDs` map). The `activeIDs` map has a -maximum size of 65535 and the node will panic if this map reaches the maximum. -An attacker can create a lot of connection attempts (exploiting Denial of -service 1), which ultimately will lead to the node panicking. +### ADR-037 -Tendermint 0.33.3 (and 0.32.10) claims `activeID` for a peer in `InitPeer`, -which is executed before `MConnection` is started. +- DeliverBlock (#3420) -Notes: +### ADR-053 -- `InitPeer` function was added to all reactors to combat a similar issue - - [\#3338](https://github.com/tendermint/tendermint/issues/3338); -- Denial of service 2 is independent of Denial of service 1 and can be executed - without it. +- Update with implementation plan after prototype (#4427) +- Strengthen and simplify the state sync ABCI interface (#4610) -**All clients are recommended to upgrade** +### ADR-057 -Special thanks to [fudongbai](https://hackerone.com/fudongbai) for finding -and reporting this. +- RPC (#4857) -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). +### BROKEN -### SECURITY: +- Attempt to replace go-wire.JSON with json.Unmarshall in rpc -- [mempool] Reserve IDs in InitPeer instead of AddPeer (@tessr) -- [p2p] Limit the number of incoming connections (@melekes) +### Backport -## v0.33.2 +- #6494 (#6506) -*March 11, 2020* +### Bech32 -Special thanks to external contributors on this release: -@antho1404, @michaelfig, @gterzian, @tau3, @Shivani912 +- Wrap error messages -Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). +### Bug Fixes -### BREAKING CHANGES: +- Fix spelling of comment (#4566) +- Jsonrpc url parsing and dial function (#6264) (#6288) -- CLI/RPC/Config - - [cli] [\#4505](https://github.com/tendermint/tendermint/pull/4505) `tendermint lite` sub-command new syntax (@melekes): - `lite cosmoshub-3 -p 52.57.29.196:26657 -w public-seed-node.cosmoshub.certus.one:26657 - --height 962118 --hash 28B97BE9F6DE51AC69F70E0B7BFD7E5C9CD1A595B7DC31AFF27C50D4948` +### CHANGELOG -- Go API - - [lite2] [\#4535](https://github.com/tendermint/tendermint/pull/4535) Remove `Start/Stop` (@melekes) - - [lite2] [\#4469](https://github.com/tendermint/tendermint/issues/4469) Remove `RemoveNoLongerTrustedHeaders` and `RemoveNoLongerTrustedHeadersPeriod` option (@cmwaters) - - [lite2] [\#4473](https://github.com/tendermint/tendermint/issues/4473) Return height as a 2nd param in `TrustedValidatorSet` (@melekes) - - [lite2] [\#4536](https://github.com/tendermint/tendermint/pull/4536) `Update` returns a signed header (1st param) (@melekes) +- Update release date +- Update release date +- Update release/v0.32.8 details (#4162) +- Update to reflect 0.33.5 (#4915) +- Add 0.32.12 changelog entry (#4918) +- Update for 0.34.0-rc4 (#5400) +- Prepare 0.34.1-rc1 (#5832) +### CHANGELOG_PENDING -### IMPROVEMENTS: +- Fix the upcoming release number (#5103) -- [blockchain/v2] [\#4361](https://github.com/tendermint/tendermint/pull/4361) Add reactor (@brapse) -- [cmd] [\#4515](https://github.com/tendermint/tendermint/issues/4515) Change `tendermint debug dump` sub-command archives filename's format (@melekes) -- [consensus] [\#3583](https://github.com/tendermint/tendermint/issues/3583) Reduce `non-deterministic signature` log noise (@tau3) -- [examples/kvstore] [\#4507](https://github.com/tendermint/tendermint/issues/4507) ABCI query now returns the proper height (@erikgrinaker) -- [lite2] [\#4462](https://github.com/tendermint/tendermint/issues/4462) Add `NewHTTPClient` and `NewHTTPClientFromTrustedStore` (@cmwaters) -- [lite2] [\#4329](https://github.com/tendermint/tendermint/issues/4329) modified bisection to loop (@cmwaters) -- [lite2] [\#4385](https://github.com/tendermint/tendermint/issues/4385) Disconnect from bad nodes (@melekes) -- [lite2] [\#4398](https://github.com/tendermint/tendermint/issues/4398) Add `VerifyAdjacent` and `VerifyNonAdjacent` funcs (@cmwaters) -- [lite2] [\#4426](https://github.com/tendermint/tendermint/issues/4426) Don't save intermediate headers (@cmwaters) -- [lite2] [\#4464](https://github.com/tendermint/tendermint/issues/4464) Cross-check first header (@cmwaters) -- [lite2] [\#4470](https://github.com/tendermint/tendermint/issues/4470) Fix inconsistent header-validatorset pairing (@melekes) -- [lite2] [\#4488](https://github.com/tendermint/tendermint/issues/4488) Allow local clock drift -10 sec. (@melekes) -- [p2p] [\#4449](https://github.com/tendermint/tendermint/pull/4449) Use `curve25519.X25519()` instead of `ScalarMult` (@erikgrinaker) -- [types] [\#4417](https://github.com/tendermint/tendermint/issues/4417) **VerifyCommitX() functions should return as soon as +2/3 threshold is reached** (@alessio). -- [libs/kv] [\#4542](https://github.com/tendermint/tendermint/pull/4542) remove unused type KI64Pair (@tessr) +### CODEOWNERS -### BUG FIXES: +- Specify more precise codeowners (#5333) +- Remove erikgrinaker (#6057) + +### CONTRIBUTING + +- Include instructions for installing protobuf +- Update minor release process (#4909) + +### CRandHex + +- Fix up doc to mention length of digits + +### Client + +- DumpConsensusState, not DialSeeds. Cleanup + +### Connect2Switches + +- Panic on err + +### Docs + +- Update description of seeds and persistent peers + +### Documentation + +- Move FROM to golang:1.4 because 1.4.2 broke +- Go-events -> tmlibs/events +- Update for 0.10.0 [ci skip]" +- Add docs from website +- Tons of minor improvements +- Add conf.py +- Test +- Add sphinx Makefile & requirements +- Consolidate ADRs +- Convert markdown to rst +- Organize the specification +- Rpc docs to be slate, see #526, #629 +- Use maxdepth 2 for spec +- Port website's intro for rtd +- Rst-ify the intro +- Fix image links +- Link fixes +- Clean a bunch of stuff up +- Logo, add readme, fixes +- Pretty-fy +- Give index a Tools section +- Update and clean up adr +- Use README.rst to be pulled from tendermint +- Re-add the images +- Add original README's from tools repo +- Convert from md to rst +- Update index.rst +- Move images in from tools repo +- Harmonize headers for tools docs +- Add kubes docs to mintnet doc, from tools +- Add original tm-bench/monitor files +- Organize tm-bench/monitor description +- Pull from tools on build +- Finish pull from tools +- Organize the directory, #656 +- Add software.json from website (ecosystem) +- Rename file +- Add and re-format the ecosystem from website +- Pull from tools' master branch +- Using ABCI-CLI +- Remove last section from ecosystem +- Organize install a bit better +- Add ABCI implementations +- Added passchain to the ecosystem.rst in the applications section; +- Fix build warnings +- Add stratumn +- Add py-tendermint to abci-servers +- Remove mention of type byte +- Add info about tm-migrate +- Update abci example details [ci skip] +- Typo +- Smaller logo (200px) +- Comb through step by step +- Fixup abci guide +- Fix links, closes #860 +- Add note about putting GOPATH/bin on PATH +- Correction, closes #910 +- Update ecosystem.rst (#1037) +- Add abci spec +- Add counter/dummy code snippets +- Updates from review (#1076) +- Tx formats: closes #1083, #536 +- Fix tx formats [ci skip] +- Update getting started [ci skip] +- Add document 'On Determinism' +- Wrong command-line flag +- The character for 1/3 fraction could not be rendered in PDF on readthedocs. (#1326) +- Update quick start guide (#1351) +- Build updates +- Add diagram, closes #1565 (#1577) +- Lil fixes +- Update install instructions, closes #1580 +- Blockchain and consensus dirs +- Fix dead links, closes #1608 +- Use absolute links (#1617) +- Update ABCI output (#1635) +- A link to quick install script +- Add BSD install script +- Start move back to md +- Cleanup/clarify build process +- Pretty fixes +- Some organizational cleanup +- DuplicateVoteEvidence +- Update abci links (#1796) +- Update js-abci example +- Minor fix for abci query peer filter +- Update address spec to sha2 for ed25519 +- Remove node* files +- Update getting started and remove old script (now in scripts/install) +- Md fixes & latest tm-bench/monitor +- Modify blockchain spec to reflect validator set changes (#2107) +- Fix links & other imrpvoements +- Note max outbound peers excludes persistent +- Fix img links, closes #2214 (#2282) +- Deprecate RTD (#2280) +- Fix indentation for genesis.validators +- Remove json tags, dont use HexBytes +- Update vote, signature, time +- Fix encoding JSON +- Bring blockchain.md up-to-date +- Specify consensus params in state.md +- Fix note about ChainID size +- Remove tags from result for now +- Move app-dev/abci-spec.md to spec/abci/abci.md +- Update spec +- Refactor ABCI docs +- Fixes and more from #2249 +- Add abci spec to config.js +- Improve docs on AppHash (#2363) +- Update link to rpc (#2361) +- Update README (#2393) +- Update secure-p2p doc to match the spec + current implementation +- Add missing changelog entry and comment (#2451) +- Add assets/instructions for local docs build (#2453) +- Consensus params and general merkle (#2524) +- Update config: ref #2800 & #2837 +- Prepend cp to /usr/local with sudo (#2885) +- Small improvements (#2933) +- Fix js-abci example (#2935) +- Add client.Start() to RPC WS examples (#2936) +- Update ecosystem.json: add Rust ABCI (#2945) +- Add client#Start/Stop to examples in RPC docs (#2939) +- Relative links in docs/spec/readme.md, js-amino lib (#2977) +- Fixes from 'first time' review (#2999) +- Enable full-text search (#3004) +- Add edit on Github links (#3014) +- Update DOCS_README (#3019) +- Networks/docker-compose: small fixes (#3017) +- Add rpc link to docs navbar and re-org sidebar (#3041) +- Fix p2p readme links (#3109) +- Update link for rpc docs (#3129) +- Fix broken link (#3142) +- Fix RPC links (#3141) +- Explain how someone can run his/her own ABCI app on localnet (#3195) +- Update pubsub ADR (#3131) +- Fix lite client formatting (#3198) +- Fix links (#3220) +- Fix rpc Tx() method docs (#3331) +- Fix typo (#3373) +- Fix the reverse of meaning in spec (#3387) +- Fix broken links (#3482) (#3488) +- Fix broken links (#3482) (#3488) +- Fix block.Header.Time description (#3529) +- Abci#Commit: better explain the possible deadlock (#3536) +- Fix typo in clist readme (#3574) +- Update contributing.md (#3503) +- Fix minor typo (#3681) +- Update RPC docs for /subscribe & /unsubscribe (#3705) +- Update /block_results RPC docs (#3708) +- Missing 'b' in python command (#3728) +- Fix some language issues and deprecated link (#3733) +- (rpc/broadcast_tx_*) write expectations for a client (#3749) +- Update JS section of abci-cli.md (#3747) +- Update to contributing.md (#3760) +- Add readme image (#3763) +- Remove confusing statement from contributing.md (#3764) +- Quick link fixes throughout docs and repo (#3776) +- Replace priv_validator.json with priv_validator_key.json (#3786) +- Fix consensus spec formatting (#3804) +- "Writing a built-in Tendermint Core application in Go" guide (#3608) +- Add guides to docs (#3830) +- Add a footer to guides (#3835) +- "Writing a Tendermint Core application in Kotlin (gRPC)" guide (#3838) +- "Writing a Tendermint Core application in Java (gRPC)" guide (#3887) +- Fix some typos and changelog entries (#3915) +- Switch the data in `/unconfirmed_txs` and `num_unconfirmed_txs` (#3933) +- Add dev sessions from YouTube (#3929) +- Move dev sessions into docs (#3934) +- Specify a fix for badger err on Windows (#3974) +- Remove traces of develop branch (#4022) +- Any path can be absolute or relative (#4035) +- Add previous dev sessions (#4040) +- Add ABCI Overview (2/2) dev session (#4044) +- Update fork-accountability.md (#4068) +- Add assumption to getting started with abci-cli (#4098) +- Fix build instructions (#4123) +- Add GA for docs.tendermint.com (#4149) +- Replace dead original whitepaper link (#4155) +- Update wording (#4174) +- Mention that Evidence votes are now sorted +- Fix broken links (#4186) +- Fix broken links in consensus/readme.md (#4200) +- Update ADR 43 with links to PRs (#4207) +- Add flag documentation (#4219) +- Fix broken rpc link (#4221) +- Fix broken ecosystem link (#4222) +- Add notes on architecture intro (#4175) +- Remove "0 means latest" from swagger docs (#4236) +- Update app-architecture.md (#4259) +- Link fixes in readme (#4268) +- Add link for installing Tendermint (#4307) +- Update theme version (#4315) +- Minor doc fixes (#4335) +- Update links to rpc (#4348) +- Update npm dependencies (#4364) +- Update guides proto paths (#4365) +- Fix incorrect link (#4377) +- Fix spec links (#4384) +- Update Light Client Protocol page (#4405) +- Adr-046 add bisection algorithm details (#4496) +- `tendermint node --help` dumps all supported flags (#4511) +- Write about debug kill and dump (#4516) +- Fix links (#4531) +- Validator setup & Key info (#4604) +- Add adr-55 for proto repo design (#4623) +- Amend adr-54 with changes in the sdk (#4684) +- Create adr 56: prove amnesia attack +- Mention unbonding period in MaxAgeNumBlocks/MaxAgeDuration +- State we don't support non constant time crypto +- Move tcp-window.png to imgs/ +- Document open file limit in production guide (#4945) +- Update amnesia adr (#4994) +- Update .vuepress/config.js (#5043) +- Add warning for chainid (#5072) +- Added further documentation to the subscribing to events page (#5110) +- Tweak light client documentation (#5121) +- Modify needed proto files for guides (#5123) +- EventAttribute#Index is not deterministic (#5132) +- Event hashing ADR 058 (#5134) +- Simplify choosing an ADR number (#5156) +- Add more details on Vote struct from /consensus_state (#5164) +- Document ConsensusParams (#5165) +- Document canonical field (#5166) +- Cleanup (#5252) +- Dont display duplicate (#5271) +- Rename swagger to openapi (#5263) +- Fix go tutorials (#5267) +- Versioned (#5241) +- Remove duplicate secure p2p (#5279) +- Remove interview transcript (#5282) +- Add block retention to upgrading.md (#5284) +- Updates to various sections (#5285) +- Add algolia docsearch configs (#5309) +- Add doc on state sync configuration (#5304) +- Move subscription to tendermint-core (#5323) +- Add missing metrics (#5325) +- Add more description to initial_height (#5350) +- Make rfc section disppear (#5353) +- Document max entries for `/blockchain` RPC (#5356) +- Fix incorrect time_iota_ms configuration (#5385) +- Specify 0.34 (#5823) +- Package-lock.json fix (#5948) +- Bump package-lock.json of v0.34.x (#5952) +- Change v0.33 version (#5950) +- Release Linux/ARM64 image (#5925) +- Dont login when in PR (#5961) +- Fix typo in state sync example (#5989) +- Fix sample code #6186 + +### Fix + +- Ansible playbook to deploy tendermint + +### GroupReader#Read + +- Return io.EOF if file is empty + +### Makefile + +- Go test --race +- Add gmt and lint +- Add 'build' target +- Add megacheck & some additional fixes +- Remove redundant lint +- Fix linter +- Parse TENDERMINT_BUILD_OPTIONS (#4738) +- Always pull image in proto-gen-docker. (#5953) + +### Optimize + +- Using parameters in func (#2845) + +### Proposal + +- New Makefile standard template (#168) + +### PubKeyFromBytes + +- Return zero value PubKey on error + +### Query + +- Height -> LastHeight +- LastHeight -> Height :) + +### R4R + +- Add timeouts to http servers (#2780) +- Swap start/end in ReverseIterator (#2913) +- Split immutable and mutable parts of priv_validator.json (#2870) +- Config TestRoot modification for LCD test (#3177) + +### README + +- Add godoc instead of tedious MD regeneration +- Document the minimum Go version +- Specify supported versions (#4660) +- Update chat link with Discord instead of Riot (#5071) +- Clean up README (#5391) + +### ResponseEndBlock + +- Ensure Address matches PubKey if provided + +### Security + +- Use bytes.Equal for key comparison +- Compile time assert to, and document sort.Interface +- Remove RipeMd160. +- Implement PeerTransport +- Refactor Remote signers (#3370) +- Cross-check new header with all witnesses (#4373) +- Bump google.golang.org/grpc from 1.28.1 to 1.29.0 +- Bump vuepress-theme-cosmos from 1.0.165 to 1.0.166 in /docs (#4920) +- [Security] Bump websocket-extensions from 0.1.3 to 0.1.4 in /docs (#4976) +- Bump github.com/prometheus/client_golang from 1.6.0 to 1.7.0 (#5027) +- [Security] Bump lodash from 4.17.15 to 4.17.19 in /docs +- [Security] Bump prismjs from 1.20.0 to 1.21.0 in /docs +- Bump vuepress-theme-cosmos from 1.0.169 to 1.0.172 in /docs (#5286) +- Bump google.golang.org/grpc from 1.31.0 to 1.31.1 (#5290) +- Bump github.com/golang/protobuf from 1.4.2 to 1.4.3 (#5506) +- Bump github.com/spf13/cobra from 1.0.0 to 1.1.0 (#5505) +- Bump github.com/prometheus/client_golang from 1.7.1 to 1.8.0 (#5515) +- Bump github.com/spf13/cobra from 1.1.0 to 1.1.1 (#5526) +- Bump google.golang.org/grpc from 1.33.1 to 1.33.2 (#5635) +- Bump github.com/stretchr/testify from 1.6.1 to 1.7.0 (#5897) +- Bump google.golang.org/grpc from 1.34.0 to 1.35.0 (#5902) +- Bump vuepress-theme-cosmos from 1.0.179 to 1.0.180 in /docs (#5915) +- Add security mailing list (#5916) +- Bump github.com/tendermint/tm-db from 0.6.3 to 0.6.4 (#6073) +- Bump watchpack from 2.1.0 to 2.1.1 in /docs (#6063) +- Update 0.34.3 changelog with details on security vuln (bp #6108) (#6110) + +### Testing + +- Broadcast_tx with tmsp; p2p +- Add throughput benchmark using mintnet and netmon +- Install mintnet, netmon +- Use MACH_PREFIX +- Cleanup +- Dont run cloud test on push to master +- README.md +- Refactor bash; test fastsync (failing) +- Name client conts so we dont need to rm them because circle +- Test dummy using rpc query +- Add xxd dep to dockerfile +- More verbosity +- Add killall to dockerfile. cleanup +- Codecov +- Use glide with mintnet/netmon +- Install glide for network test +- App persistence +- Tmsp query result is json +- Increase proposal timeout +- Cleanup and fix scripts +- Crank it to eleventy +- More cleanup on p2p +- RandConsensusNet takes more args +- Crank circle timeouts +- Automate building consensus/test_data +- Circle artifacts +- Dont start cs until all peers connected +- Shorten timeouts +- Remove codecov patch threshold +- Kill and restart all nodes +- Use PROXY_APP=persistent_dummy +- Use fail-test failure indices +- More unique container names +- Set log_level=info +- Always rebuild grpc_client +- Split up test/net/test.sh +- Unexport internal function. +- Update docker to 1.7.4 +- Dont use log files on circle +- Shellcheck +- Forward CIRCLECI var through docker +- Only use syslog on circle +- More logging +- Wait for tendermint proc +- Add extra kill after fail index triggered +- Wait for ports to be freed +- Use --pex on restart +- Install abci apps first +- Fix docker and apps +- Dial_seeds +- Docker exec doesnt work on circle +- Bump sleep to 5 for bound ports release +- Better client naming +- Use unix socket for rpc +- Shellcheck +- Check err on cmd.Wait +- Test_libs all use Makefile +- Jq .result[1] -> jq .result +- P2p.seeds and p2p.pex +- Add simple client/server test with no addr prefix +- Update for abci-cli consolidation. shell formatting +- Sunset tmlibs/process.Process +- Wait for node heights before checking app hash +- Fix ensureABCIIsUp +- Fix test/app/counter_test.sh +- Longer timeout +- Add some timeouts +- Use shasum to avoid rarer dependency +- Less bash +- More smoothness +- Test itr.Value in checkValuePanics (#2580) +- Add consensus_params to testnet config generation (#3781) +- Branch for fix of ci (#4266) +- Bind test servers to 127.0.0.1 (#4322) +- Simplified txsearch cancellation test (#4500) +- Fix p2p test build breakage caused by Debian testing +- Revert Go 1.13→1.14 bump +- Use random socket names to avoid collisions (#4885) +- Mitigate test data race (#4886) +- Use github.sha in binary cache key (#5062) +- Deflake TestAddAndRemoveListenerConcurrency and TestSyncer_SyncAny (#5101) +- Protobuf vectors for reactors (#5221) +- Add end-to-end testing framework (#5435) +- Add basic end-to-end test cases (#5450) +- Add GitHub action for end-to-end tests (#5452) +- Remove P2P tests (#5453) +- Add E2E test for node peering (#5465) +- Add random testnet generator (#5479) +- Clean up E2E test volumes using a container (#5509) +- Tweak E2E tests for nightly runs (#5512) +- Enable ABCI gRPC client in E2E testnets (#5521) +- Enable blockchain v2 in E2E testnet generator (#5533) +- Enable restart/kill perturbations in E2E tests (#5537) +- Run remaining E2E testnets on run-multiple.sh failure (#5557) +- Tag E2E Docker resources and autoremove them (#5558) +- Add evidence e2e tests (#5488) +- Fix handling of start height in generated E2E testnets (#5563) +- Disable E2E misbehaviors due to bugs (#5569) +- Fix various E2E test issues (#5576) +- Fix secp failures (#5649) +- Switched node keys back to edwards (#4) +- Fix TestByzantinePrevoteEquivocation flake (#5710) +- Fix integration tests and rename binary +- Improve WaitGroup handling in Byzantine tests (#5861) +- Disable abci/grpc and blockchain/v2 due to flake (#5854) +- Don't use foo-bar.net in TestHTTPClientMakeHTTPDialer (#5997) (#6047) +- Enable pprof server to help debugging failures (#6003) +- Increase sign/propose tolerances (#6033) +- Increase validator tolerances (#6037) +- Move fuzz tests into this repo (#5918) +- Fix `make test` (#5966) + +### UPGRADING + +- Polish upgrading instructions for 0.34 (#5398) + +### UPGRADING.md + +- Write about the LastResultsHash change (#5000) + +### Update + +- JTMSP -> jABCI + +### Vagrantfile + +- Update Go version + +### ValidatorSet#GetByAddress + +- Return -1 if no validator was found + +### WAL + +- Better errors and new fail point (#3246) + +### WIP + +- Begin parallel refactoring with go-wire Write methods and MConnection +- Fix rpc/core +- More empty struct examples +- Add implementation of mock/fake http-server +- Rename package name from fakeserver to mockcoreserver +- Change the method names of call structure, Fix adding headers +- Add mock of JRPCServer implementation on top of HTTServer mock + +### [Docs] + +- Minor doc touchups (#4171) + +### [docs + +- Typo fix] remove misplaced "the" +- Typo fix] add missing "have" + +### Abci + +- Remove old repo docs +- Remove nested .gitignore +- Remove LICENSE +- Add comment for doc update +- Remove fee (#2043) +- Change validators to last_commit_info in RequestBeginBlock (#2074) +- Update readme for building protoc (#2124) +- Add next_validators_hash to header +- VoteInfo, ValidatorUpdate. See ADR-018 +- Move round back from votes to commit +- Codespace (#2557) +- LocalClient improvements & bugfixes & pubsub Unsubscribe issues (#2748) +- Refactor tagging events using list of lists (#3643) +- Refactor ABCI CheckTx and DeliverTx signatures (#3735) +- Refactor CheckTx to notify of recheck (#3744) +- Minor cleanups in the socket client (#3758) +- Fix documentation regarding CheckTx type update (#3789) +- Remove TotalTxs and NumTxs from Header (#3783) +- Fix broken spec link (#4366) +- Fix protobuf lint issues +- Regenerate proto files +- Remove protoreplace script +- Remove python examples +- Proto files follow same path (#5039) +- Fix abci evidence types (#5174) +- Add ResponseInitChain.app_hash, check and record it (#5227) +- Update evidence (#5324) +- Fix socket client error for state sync responses (#5395) +- Fix ReCheckTx for Socket Client (bp #6124) (#6125) -- [cmd] [\#4303](https://github.com/tendermint/tendermint/issues/4303) Show useful error when Tendermint is not initialized (@melekes) -- [cmd] [\#4515](https://github.com/tendermint/tendermint/issues/4515) **Fix `tendermint debug kill` sub-command** (@melekes) -- [rpc] [\#3935](https://github.com/tendermint/tendermint/issues/3935) **Create buffered subscriptions on `/subscribe`** (@melekes) -- [rpc] [\#4375](https://github.com/tendermint/tendermint/issues/4375) Stop searching for txs in `/tx_search` upon client timeout (@gterzian) -- [rpc] [\#4406](https://github.com/tendermint/tendermint/pull/4406) Fix issue with multiple subscriptions on the websocket (@antho1404) -- [rpc] [\#4432](https://github.com/tendermint/tendermint/issues/4432) Fix `/tx_search` pagination with ordered results (@erikgrinaker) -- [rpc] [\#4492](https://github.com/tendermint/tendermint/issues/4492) Keep the original subscription "id" field when new RPCs come in (@michaelfig) +### Abci-cli +- Print OK if code is 0 +- Prefix flag variables with flag -## v0.33.1 +### Abci/client -*Feburary 13, 2020* +- Fix DATA RACE in gRPC client (#3798) -Special thanks to external contributors on this release: -@princesinha19 +### Abci/example/kvstore -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### FEATURES: - -- [rpc] [\#3333](https://github.com/tendermint/tendermint/issues/3333) Add `order_by` to `/tx_search` endpoint, allowing to change default ordering from asc to desc (@princesinha19) - -### IMPROVEMENTS: - -- [proto] [\#4369](https://github.com/tendermint/tendermint/issues/4369) Add [buf](https://buf.build/) for usage with linting and checking if there are breaking changes with the master branch. -- [proto] [\#4369](https://github.com/tendermint/tendermint/issues/4369) Add `make proto-gen` cmd to generate proto stubs outside of GOPATH. - -### BUG FIXES: - -- [node] [\#4311](https://github.com/tendermint/tendermint/issues/4311) Use `GRPCMaxOpenConnections` when creating the gRPC server, not `MaxOpenConnections` -- [rpc] [\#4319](https://github.com/tendermint/tendermint/issues/4319) Check `BlockMeta` is not nil in `/block` & `/block_by_hash` - -## v0.33 - -Special thanks to external contributors on this release: @mrekucci, @PSalant726, @princesinha19, @greg-szabo, @dongsam, @cuonglm, @jgimeno, @yenkhoon - -Friendly reminder, we have a [bug bounty -program.](https://hackerone.com/tendermint). - -*January 14, 2020* - -This release contains breaking changes to the `Block#Header`, specifically -`NumTxs` and `TotalTxs` were removed (\#2521). Here's how this change affects -different modules: - -- apps: it breaks the ABCI header field numbering -- state: it breaks the format of `State` on disk -- RPC: all RPC requests which expose the header broke -- Go API: the `Header` broke -- P2P: since blocks go over the wire, technically the P2P protocol broke - -Also, blocks are significantly smaller 🔥 because we got rid of the redundant -information in `Block#LastCommit`. `Commit` now mainly consists of a signature -and a validator address plus a timestamp. Note we may remove the validator -address & timestamp fields in the future (see ADR-25). - -`lite2` package has been added to solve `lite` issues and introduce weak -subjectivity interface. Refer to the [spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md) for complete details. -`lite` package is now deprecated and will be removed in v0.34 release. - -### BREAKING CHANGES: - -- CLI/RPC/Config - - - [rpc] [\#3471](https://github.com/tendermint/tendermint/issues/3471) Paginate `/validators` response (default: 30 vals per page) - - [rpc] [\#3188](https://github.com/tendermint/tendermint/issues/3188) Remove `BlockMeta` in `ResultBlock` in favor of `BlockId` for `/block` - - [rpc] `/block_results` response format updated (see RPC docs for details) - ``` - { - "jsonrpc": "2.0", - "id": "", - "result": { - "height": "2109", - "txs_results": null, - "begin_block_events": null, - "end_block_events": null, - "validator_updates": null, - "consensus_param_updates": null - } - } - ``` - - [rpc] [\#4141](https://github.com/tendermint/tendermint/pull/4141) Remove `#event` suffix from the ID in event responses. - `{"jsonrpc": "2.0", "id": 0, "result": ...}` - - [rpc] [\#4141](https://github.com/tendermint/tendermint/pull/4141) Switch to integer IDs instead of `json-client-XYZ` - ``` - id=0 method=/subscribe - id=0 result=... - id=1 method=/abci_query - id=1 result=... - ``` - - ID is unique for each request; - - Request.ID is now optional. Notification is a Request without an ID. Previously ID="" or ID=0 were considered as notifications. - - - [config] [\#4046](https://github.com/tendermint/tendermint/issues/4046) Rename tag(s) to CompositeKey & places where tag is still present it was renamed to event or events. Find how a compositeKey is constructed [here](https://github.com/tendermint/tendermint/blob/6d05c531f7efef6f0619155cf10ae8557dd7832f/docs/app-dev/indexing-transactions.md) - - You will have to generate a new config for your Tendermint node(s) - - [genesis] [\#2565](https://github.com/tendermint/tendermint/issues/2565) Add `consensus_params.evidence.max_age_duration`. Rename - `consensus_params.evidence.max_age` to `max_age_num_blocks`. - - [cli] [\#1771](https://github.com/tendermint/tendermint/issues/1771) `tendermint lite` now uses new light client package (`lite2`) - and has 3 more flags: `--trusting-period`, `--trusted-height` and - `--trusted-hash` - -- Apps - - - [tm-bench] Removed tm-bench in favor of [tm-load-test](https://github.com/informalsystems/tm-load-test) - -- Go API - - - [rpc] [\#3953](https://github.com/tendermint/tendermint/issues/3953) Modify NewHTTP, NewXXXClient functions to return an error on invalid remote instead of panicking (@mrekucci) - - [rpc/client] [\#3471](https://github.com/tendermint/tendermint/issues/3471) `Validators` now requires two more args: `page` and `perPage` - - [libs/common] [\#3262](https://github.com/tendermint/tendermint/issues/3262) Make error the last parameter of `Task` (@PSalant726) - - [cs/types] [\#3262](https://github.com/tendermint/tendermint/issues/3262) Rename `GotVoteFromUnwantedRoundError` to `ErrGotVoteFromUnwantedRound` (@PSalant726) - - [libs/common] [\#3862](https://github.com/tendermint/tendermint/issues/3862) Remove `errors.go` from `libs/common` - - [libs/common] [\#4230](https://github.com/tendermint/tendermint/issues/4230) Move `KV` out of common to its own pkg - - [libs/common] [\#4230](https://github.com/tendermint/tendermint/issues/4230) Rename `cmn.KVPair(s)` to `kv.Pair(s)`s - - [libs/common] [\#4232](https://github.com/tendermint/tendermint/issues/4232) Move `Service` & `BaseService` from `libs/common` to `libs/service` - - [libs/common] [\#4232](https://github.com/tendermint/tendermint/issues/4232) Move `common/nil.go` to `types/utils.go` & make the functions private - - [libs/common] [\#4231](https://github.com/tendermint/tendermint/issues/4231) Move random functions from `libs/common` into pkg `rand` - - [libs/common] [\#4237](https://github.com/tendermint/tendermint/issues/4237) Move byte functions from `libs/common` into pkg `bytes` - - [libs/common] [\#4237](https://github.com/tendermint/tendermint/issues/4237) Move throttletimer functions from `libs/common` into pkg `timer` - - [libs/common] [\#4237](https://github.com/tendermint/tendermint/issues/4237) Move tempfile functions from `libs/common` into pkg `tempfile` - - [libs/common] [\#4240](https://github.com/tendermint/tendermint/issues/4240) Move os functions from `libs/common` into pkg `os` - - [libs/common] [\#4240](https://github.com/tendermint/tendermint/issues/4240) Move net functions from `libs/common` into pkg `net` - - [libs/common] [\#4240](https://github.com/tendermint/tendermint/issues/4240) Move mathematical functions and types out of `libs/common` to `math` pkg - - [libs/common] [\#4240](https://github.com/tendermint/tendermint/issues/4240) Move string functions out of `libs/common` to `strings` pkg - - [libs/common] [\#4240](https://github.com/tendermint/tendermint/issues/4240) Move async functions out of `libs/common` to `async` pkg - - [libs/common] [\#4240](https://github.com/tendermint/tendermint/issues/4240) Move bit functions out of `libs/common` to `bits` pkg - - [libs/common] [\#4240](https://github.com/tendermint/tendermint/issues/4240) Move cmap functions out of `libs/common` to `cmap` pkg - - [libs/common] [\#4258](https://github.com/tendermint/tendermint/issues/4258) Remove `Rand` from all `rand` pkg functions - - [types] [\#2565](https://github.com/tendermint/tendermint/issues/2565) Remove `MockBadEvidence` & `MockGoodEvidence` in favor of `MockEvidence` - -- Blockchain Protocol - - - [abci] [\#2521](https://github.com/tendermint/tendermint/issues/2521) Remove `TotalTxs` and `NumTxs` from `Header` - - [types] [\#4151](https://github.com/tendermint/tendermint/pull/4151) Enforce ordering of votes in DuplicateVoteEvidence to be lexicographically sorted on BlockID - - [types] [\#1648](https://github.com/tendermint/tendermint/issues/1648) Change `Commit` to consist of just signatures - -- P2P Protocol - - - [p2p] [\#3668](https://github.com/tendermint/tendermint/pull/3668) Make `SecretConnection` non-malleable - -- [proto] [\#3986](https://github.com/tendermint/tendermint/pull/3986) Prefix protobuf types to avoid name conflicts. - - ABCI becomes `tendermint.abci.types` with the new API endpoint `/tendermint.abci.types.ABCIApplication/` - - core_grpc becomes `tendermint.rpc.grpc` with the new API endpoint `/tendermint.rpc.grpc.BroadcastAPI/` - - merkle becomes `tendermint.crypto.merkle` - - libs.common becomes `tendermint.libs.common` - - proto3 becomes `tendermint.types.proto3` - -### FEATURES: - -- [p2p] [\#4053](https://github.com/tendermint/tendermint/issues/4053) Add `unconditional_peer_ids` and `persistent_peers_max_dial_period` config variables (see ADR-050) (@dongsam) -- [tools] [\#4227](https://github.com/tendermint/tendermint/pull/4227) Implement `tendermint debug kill` and - `tendermint debug dump` commands for Tendermint node debugging functionality. See `--help` in both - commands for further documentation and usage. -- [cli] [\#4234](https://github.com/tendermint/tendermint/issues/4234) Add `--db_backend and --db_dir` flags (@princesinha19) -- [cli] [\#4113](https://github.com/tendermint/tendermint/issues/4113) Add optional `--genesis_hash` flag to check genesis hash upon startup -- [config] [\#3831](https://github.com/tendermint/tendermint/issues/3831) Add support for [RocksDB](https://rocksdb.org/) (@Stumble) -- [rpc] [\#3985](https://github.com/tendermint/tendermint/issues/3985) Add new `/block_by_hash` endpoint, which allows to fetch a block by its hash (@princesinha19) -- [metrics] [\#4263](https://github.com/tendermint/tendermint/issues/4263) Add - - `consensus_validator_power`: track your validators power - - `consensus_validator_last_signed_height`: track at which height the validator last signed - - `consensus_validator_missed_blocks`: total amount of missed blocks for a validator - as gauges in prometheus for validator specific metrics -- [rpc/lib] [\#4248](https://github.com/tendermint/tendermint/issues/4248) RPC client basic authentication support (@greg-szabo) -- [lite2] [\#1771](https://github.com/tendermint/tendermint/issues/1771) Light client with weak subjectivity - -### IMPROVEMENTS: - -- [rpc] [\#3188](https://github.com/tendermint/tendermint/issues/3188) Added `block_size` to `BlockMeta` this is reflected in `/blockchain` -- [types] [\#2521](https://github.com/tendermint/tendermint/issues/2521) Add `NumTxs` to `BlockMeta` and `EventDataNewBlockHeader` -- [p2p] [\#4185](https://github.com/tendermint/tendermint/pull/4185) Simplify `SecretConnection` handshake with merlin -- [cli] [\#4065](https://github.com/tendermint/tendermint/issues/4065) Add `--consensus.create_empty_blocks_interval` flag (@jgimeno) -- [docs] [\#4065](https://github.com/tendermint/tendermint/issues/4065) Document `--consensus.create_empty_blocks_interval` flag (@jgimeno) -- [crypto] [\#4190](https://github.com/tendermint/tendermint/pull/4190) Added SR25519 signature scheme -- [abci] [\#4177] kvstore: Return `LastBlockHeight` and `LastBlockAppHash` in `Info` (@princesinha19) -- [rpc] [\#2741](https://github.com/tendermint/tendermint/issues/2741) Add `proposer` to `/consensus_state` response (@princesinha19) -- [deps] [\#4289](https://github.com/tendermint/tendermint/pull/4289) Update tm-db to 0.4.0, this includes major breaking changes in the dep that change how errors are handled. - -### BUG FIXES: - -- [rpc/lib][\#4051](https://github.com/tendermint/tendermint/pull/4131) Fix RPC client, which was previously resolving https protocol to http (@yenkhoon) -- [rpc] [\#4141](https://github.com/tendermint/tendermint/pull/4141) JSONRPCClient: validate that Response.ID matches Request.ID -- [rpc] [\#4141](https://github.com/tendermint/tendermint/pull/4141) WSClient: check for unsolicited responses -- [types] [\4164](https://github.com/tendermint/tendermint/pull/4164) Prevent temporary power overflows on validator updates -- [cs] [\#4069](https://github.com/tendermint/tendermint/issues/4069) Don't panic when block meta is not found in store (@gregzaitsev) -- [types] [\#4164](https://github.com/tendermint/tendermint/issues/4164) Prevent temporary power overflows on validator updates (joint - efforts of @gchaincl and @ancazamfir) -- [p2p] [\#4140](https://github.com/tendermint/tendermint/issues/4140) `SecretConnection`: use the transcript solely for authentication (i.e. MAC) -- [consensus/types] [\#4243](https://github.com/tendermint/tendermint/issues/4243) fix BenchmarkRoundStateDeepCopy panics (@cuonglm) -- [rpc] [\#4256](https://github.com/tendermint/tendermint/issues/4256) Pass `outCapacity` to `eventBus#Subscribe` when subscribing using a local client +- Decrease val power by 1 upon equivocation (#5056) -## v0.32.13 +### Abci/examples -*August 5, 2020* +- Switch from hex to base64 pubkey in kvstore (#3641) - ### BUG FIXES +### Abci/grpc - - [privval] [\#5112](https://github.com/tendermint/tendermint/issues/5112) If remote signer errors, don't retry (@melekes) +- Return async responses in order (#5520) (#5531) +- Fix ordering of sync/async callback combinations (#5556) +- Fix invalid mutex handling in StopForError() (#5849) -## v0.32.12 +### Abci/kvstore -*May 19, 2020* +- Return `LastBlockHeight` and `LastBlockAppHash` in `Info` (#4233) -### BUG FIXES +### Abci/server -- [p2p] [\#4847](https://github.com/tendermint/tendermint/pull/4847) Return masked IP (not the actual IP) in addrbook#groupKey (@melekes) +- Recover from app panics in socket server (#3809) +- Print panic & stack trace to STDERR if logger is not set -## v0.32.11 +### Abci/types -*April 29, 2020* +- Update comment (#3612) +- Add comment for TotalVotingPower (#5081) -### BUG FIXES: +### Absent_validators -- [privval] [\#4275](https://github.com/tendermint/tendermint/issues/4275) Fix consensus failure when remote signer drops (@melekes) +- Repeated int -> repeated bytes -## v0.32.10 +### Addrbook -*April 6, 2020* +- Toggle strict routability -This security release fixes: +### Addrbook_test -### Denial of Service 1 +- Preallocate memory for bookSizes (#3268) -Tendermint 0.33.2 and earlier does not limit the number of P2P connection -requests. For each p2p connection, Tendermint allocates ~0.5MB. Even though -this memory is garbage collected once the connection is terminated (due to -duplicate IP or reaching a maximum number of inbound peers), temporary memory -spikes can lead to OOM (Out-Of-Memory) exceptions. +### Adr -Tendermint 0.33.3 (and 0.32.10) limits the total number of P2P incoming -connection requests to to `p2p.max_num_inbound_peers + -len(p2p.unconditional_peer_ids)`. +- Add 005 consensus params +- Update 007 trust metric usage +- Amend decisions for PrivValidator +- Update readme +- PeerTransport (#2069) +- Encoding for cryptography at launch (#2121) +- Protocol versioning +- Chain-versions +- Style fixes (#3206) +- Peer Behaviour (#3539) +- PeerBehaviour updates (#3558) +- [43] blockchain riri-org (#3753) +- ADR-052: Tendermint Mode (#4302) +- ADR-051: Double Signing Risk Reduction (#4262) +- Light client implementation (#4397) +- Crypto encoding for proto (#4481) +- Add API stability ADR (#5341) -Notes: +### Adr#50 -- Tendermint does not rate limit P2P connection requests per IP (an attacker - can saturate all the inbound slots); -- Tendermint does not rate limit HTTP(S) requests. If you expose any RPC - endpoints to the public, please make sure to put in place some protection - (https://www.nginx.com/blog/rate-limiting-nginx/). We may implement this in - the future ([\#1696](https://github.com/tendermint/tendermint/issues/1696)). +- Improve trusted peering (#4072) -### Denial of Service 2 +### Adr-009 -Tendermint 0.33.2 and earlier does not reclaim `activeID` of a peer after it's -removed in `Mempool` reactor. This does not happen all the time. It only -happens when a connection fails (for any reason) before the Peer is created and -added to all reactors. `RemovePeer` is therefore called before `AddPeer`, which -leads to always growing memory (`activeIDs` map). The `activeIDs` map has a -maximum size of 65535 and the node will panic if this map reaches the maximum. -An attacker can create a lot of connection attempts (exploiting Denial of -Service 1), which ultimately will lead to the node panicking. +- No pubkeys in beginblock +- Add references -Tendermint 0.33.3 (and 0.32.10) claims `activeID` for a peer in `InitPeer`, -which is executed before `MConnection` is started. +### Adr-016 -Notes: +- Update int64->uint64; add version to ConsensusParams (#2667) -- `InitPeer` function was added to all reactors to combat a similar issue - - [\#3338](https://github.com/tendermint/tendermint/issues/3338); -- Denial of Service 2 is independent of Denial of Service 1 and can be executed - without it. +### Adr-018 -**All clients are recommended to upgrade** +- Abci validators -Special thanks to [fudongbai](https://hackerone.com/fudongbai) for finding -and reporting this. +### Adr-021 -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). +- Note about tag spacers (#2362) -### SECURITY: +### Adr-029 -- [mempool] Reserve IDs in InitPeer instead of AddPeer (@tessr) -- [p2p] Limit the number of incoming connections (@melekes) +- Update CheckBlock -## v0.32.9 +### Adr-047 -_January, 9, 2020_ +- Evidence handling (#4429) -Special thanks to external contributors on this release: @greg-szabo, @gregzaitsev, @yenkhoon +### Adr-053 -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). +- Update after state sync merge (#4768) -### FEATURES: +### All -- [rpc/lib] [\#4248](https://github.com/tendermint/tendermint/issues/4248) RPC client basic authentication support (@greg-szabo) +- No more anonymous imports +- Fix vet issues with build tags, formatting +- Gofmt (#1743) +- Name reactors when they are initialized (#4608) -- [metrics] [\#4294](https://github.com/tendermint/tendermint/pull/4294) Add - - `consensus_validator_power`: track your validators power - - `consensus_validator_last_signed_height`: track at which height the validator last signed - - `consensus_validator_missed_blocks`: total amount of missed blocks for a validator - as gauges in prometheus for validator specific metrics +### Ansible -### BUG FIXES: +- Update tendermint and basecoin versions +- Added option to provide accounts for genesis generation, terraform: added option to secure DigitalOcean servers, devops: added DNS name creation to tendermint terraform -- [rpc/lib] [\#4131](https://github.com/tendermint/tendermint/pull/4131) Fix RPC client, which was previously resolving https protocol to http (@yenkhoon) -- [cs] [\#4069](https://github.com/tendermint/tendermint/issues/4069) Don't panic when block meta is not found in store (@gregzaitsev) +### Appveyor -## v0.32.8 +- Use make -*November 19, 2019* +### Arm -Special thanks to external contributors on this release: @erikgrinaker, @guagualvcha, @hsyis, @cosmostuba, @whunmr, @austinabell +- Add install script, fix Makefile (#2824) -Friendly reminder, we have a [bug bounty -program.](https://hackerone.com/tendermint). +### Autofile +- Ensure file is open in Sync +- Resolve relative paths (#4390) -### BREAKING CHANGES: +### Batch -- Go API +- Progress - - [libs/pubsub] [\#4070](https://github.com/tendermint/tendermint/pull/4070) `Query#(Matches|Conditions)` returns an error. +### Behaviour -### IMPROVEMENTS: +- Return correct reason in MessageOutOfOrder (#3772) +- Add simple doc.go (#5055) -- [mempool] [\#4083](https://github.com/tendermint/tendermint/pull/4083) Added TxInfo parameter to CheckTx(), and removed CheckTxWithInfo() (@erikgrinaker) -- [mempool] [\#4057](https://github.com/tendermint/tendermint/issues/4057) Include peer ID when logging rejected txns (@erikgrinaker) -- [tools] [\#4023](https://github.com/tendermint/tendermint/issues/4023) Improved `tm-monitor` formatting of start time and avg tx throughput (@erikgrinaker) -- [p2p] [\#3991](https://github.com/tendermint/tendermint/issues/3991) Log "has been established or dialed" as debug log instead of Error for connected peers (@whunmr) -- [rpc] [\#4077](https://github.com/tendermint/tendermint/pull/4077) Added support for `EXISTS` clause to the Websocket query interface. -- [privval] Add `SignerDialerEndpointRetryWaitInterval` option (@cosmostuba) -- [crypto] Add `RegisterKeyType` to amino to allow external key types registration (@austinabell) +### Binary -### BUG FIXES: +- Prevent runaway alloc -- [libs/pubsub] [\#4070](https://github.com/tendermint/tendermint/pull/4070) Strip out non-numeric characters when attempting to match numeric values. -- [libs/pubsub] [\#4070](https://github.com/tendermint/tendermint/pull/4070) No longer panic in Query#(Matches|Conditions) preferring to return an error instead. -- [tools] [\#4023](https://github.com/tendermint/tendermint/issues/4023) Refresh `tm-monitor` health when validator count is updated (@erikgrinaker) -- [state] [\#4104](https://github.com/tendermint/tendermint/pull/4104) txindex/kv: Fsync data to disk immediately after receiving it (@guagualvcha) -- [state] [\#4095](https://github.com/tendermint/tendermint/pull/4095) txindex/kv: Return an error if there's one when the user searches for a tx (hash=X) (@hsyis) +### Bit_array -## v0.32.7 +- Simplify subtraction -*October 18, 2019* +### Block -This security release fixes a vulnerability found in the `consensus` package, -where an attacker could construct a `BlockPartMessage` message in such a way -that it will lead to consensus failure. A few similar issues have been -identified and fixed here. +- Fix max commit sig size (#5567) -**All clients are recommended to upgrade** +### Block/state -Special thanks to [elvishacker](https://hackerone.com/elvishacker) for finding -and reporting this. +- Add CallTx type +- Gas price for block and tx -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). +### Blockchain -### BREAKING CHANGES: +- Use ApplyBlock +- Thread safe store.Height() +- Explain isCaughtUp logic +- Fixing reactor tests +- Add comment in AddPeer. closes #666 +- Add tests and more docs for BlockStore +- Update store comments +- Updated store docs/comments from review +- Deduplicate store header value tests +- Less fragile and involved tests for blockstore +- Block creator helper for compressing tests as per @ebuchman +- Note about store tests needing simplification ... +- Test fixes +- Update for new state +- Test wip for hard to test functionality [ci skip] +- Fix register concrete name. (#2213) +- Update the maxHeight when a peer is removed (#3350) +- Comment out logger in test code that causes a race condition (#3500) +- Dismiss request channel delay (#3459) +- Reorg reactor (#3561) +- Add v2 reactor (#4361) +- Enable v2 to be set (#4597) +- Proto migration (#4969) +- Test vectors for proto encoding (#5073) +- Fix fast sync halt with initial height > 1 (#5249) +- Verify +2/3 (#5278) -- Go API - - [consensus] Modify `WAL#Write` and `WAL#WriteSync` to return an error if - they fail to write a message +### Blockchain/pool -### SECURITY: +- Some comments and small changes -- [consensus] Validate incoming messages more throughly +### Blockchain/reactor -## v0.32.6 +- RespondWithNoResponseMessage for missing height -*October 8, 2019* - -The previous patch was insufficient because the attacker could still find a way -to submit a `nil` pubkey by constructing a `PubKeyMultisigThreshold` pubkey -with `nil` subpubkeys for example. - -This release provides multiple fixes, which include recovering from panics when -accepting new peers and only allowing `ed25519` pubkeys. - -**All clients are recommended to upgrade** - -Special thanks to [fudongbai](https://hackerone.com/fudongbai) for pointing -this out. - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### SECURITY: - -- [p2p] [\#4030](https://github.com/tendermint/tendermint/issues/4030) Only allow ed25519 pubkeys when connecting - -## v0.32.5 - -*October 1, 2019* - -This release fixes a major security vulnerability found in the `p2p` package. -All clients are recommended to upgrade. See -[\#4030](https://github.com/tendermint/tendermint/issues/4030) for details. - -Special thanks to [fudongbai](https://hackerone.com/fudongbai) for discovering -and reporting this issue. - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### SECURITY: - -- [p2p] [\#4030](https://github.com/tendermint/tendermint/issues/4030) Fix for panic on nil public key send to a peer - -## v0.32.4 - -*September 19, 2019* - -Special thanks to external contributors on this release: @jon-certik, @gracenoah, @PSalant726, @gchaincl - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### BREAKING CHANGES: - -- CLI/RPC/Config - - [rpc] [\#3984](https://github.com/tendermint/tendermint/issues/3984) Add `MempoolClient` interface to `Client` interface - -### IMPROVEMENTS: - -- [rpc] [\#2010](https://github.com/tendermint/tendermint/issues/2010) Add NewHTTPWithClient and NewJSONRPCClientWithHTTPClient (note these and NewHTTP, NewJSONRPCClient functions panic if remote is invalid) (@gracenoah) -- [rpc] [\#3882](https://github.com/tendermint/tendermint/issues/3882) Add custom marshalers to proto messages to disable `omitempty` -- [deps] [\#3952](https://github.com/tendermint/tendermint/pull/3952) bump github.com/go-kit/kit from 0.6.0 to 0.9.0 -- [deps] [\#3951](https://github.com/tendermint/tendermint/pull/3951) bump github.com/stretchr/testify from 1.3.0 to 1.4.0 -- [deps] [\#3945](https://github.com/tendermint/tendermint/pull/3945) bump github.com/gorilla/websocket from 1.2.0 to 1.4.1 -- [deps] [\#3948](https://github.com/tendermint/tendermint/pull/3948) bump github.com/libp2p/go-buffer-pool from 0.0.1 to 0.0.2 -- [deps] [\#3943](https://github.com/tendermint/tendermint/pull/3943) bump github.com/fortytw2/leaktest from 1.2.0 to 1.3.0 -- [deps] [\#3939](https://github.com/tendermint/tendermint/pull/3939) bump github.com/rs/cors from 1.6.0 to 1.7.0 -- [deps] [\#3937](https://github.com/tendermint/tendermint/pull/3937) bump github.com/magiconair/properties from 1.8.0 to 1.8.1 -- [deps] [\#3947](https://github.com/tendermint/tendermint/pull/3947) update gogo/protobuf version from v1.2.1 to v1.3.0 -- [deps] [\#4001](https://github.com/tendermint/tendermint/pull/4001) bump github.com/tendermint/tm-db from 0.1.1 to 0.2.0 - -### BUG FIXES: - -- [consensus] [\#3908](https://github.com/tendermint/tendermint/issues/3908) Wait `timeout_commit` to pass even if `create_empty_blocks` is `false` -- [mempool] [\#3968](https://github.com/tendermint/tendermint/issues/3968) Fix memory loading error on 32-bit machines (@jon-certik) - -## v0.32.3 - -*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) -guide. - -Special thanks to external contributors on this release: -@gchaincl, @bluele, @climber73 - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### IMPROVEMENTS: - -- [consensus] [\#3839](https://github.com/tendermint/tendermint/issues/3839) Reduce "Error attempting to add vote" message severity (Error -> Info) -- [mempool] [\#3877](https://github.com/tendermint/tendermint/pull/3877) Make `max_tx_bytes` configurable instead of `max_msg_bytes` (@bluele) -- [privval] [\#3370](https://github.com/tendermint/tendermint/issues/3370) Refactor and simplify validator/kms connection handling. Please refer to [this comment](https://github.com/tendermint/tendermint/pull/3370#issue-257360971) for details -- [rpc] [\#3880](https://github.com/tendermint/tendermint/issues/3880) Document endpoints with `swagger`, introduce contract tests of implementation against documentation - -### BUG FIXES: - -- [config] [\#3868](https://github.com/tendermint/tendermint/issues/3868) Move misplaced `max_msg_bytes` into mempool section (@bluele) -- [rpc] [\#3910](https://github.com/tendermint/tendermint/pull/3910) Fix DATA RACE in HTTP client (@gchaincl) -- [store] [\#3893](https://github.com/tendermint/tendermint/issues/3893) Fix "Unregistered interface types.Evidence" panic - -## v0.32.2 - -*July 31, 2019* - -Special thanks to external contributors on this release: -@ruseinov, @bluele, @guagualvcha - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### BREAKING CHANGES: - -- Go API - - [libs] [\#3811](https://github.com/tendermint/tendermint/issues/3811) Remove `db` from libs in favor of `https://github.com/tendermint/tm-db` - -### FEATURES: - -- [blockchain] [\#3561](https://github.com/tendermint/tendermint/issues/3561) Add early version of the new blockchain reactor, which is supposed to be more modular and testable compared to the old version. To try it, you'll have to change `version` in the config file, [here](https://github.com/tendermint/tendermint/blob/master/config/toml.go#L303) NOTE: It's not ready for a production yet. For further information, see [ADR-40](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-040-blockchain-reactor-refactor.md) & [ADR-43](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-043-blockchain-riri-org.md) -- [mempool] [\#3826](https://github.com/tendermint/tendermint/issues/3826) Make `max_msg_bytes` configurable(@bluele) -- [node] [\#3846](https://github.com/tendermint/tendermint/pull/3846) Allow replacing existing p2p.Reactor(s) using [`CustomReactors` - option](https://godoc.org/github.com/tendermint/tendermint/node#CustomReactors). - Warning: beware of accidental name clashes. Here is the list of existing - reactors: MEMPOOL, BLOCKCHAIN, CONSENSUS, EVIDENCE, PEX. -- [rpc] [\#3818](https://github.com/tendermint/tendermint/issues/3818) Make `max_body_bytes` and `max_header_bytes` configurable(@bluele) -- [rpc] [\#2252](https://github.com/tendermint/tendermint/issues/2252) Add `/broadcast_evidence` endpoint to submit double signing and other types of evidence - -### IMPROVEMENTS: - -- [abci] [\#3809](https://github.com/tendermint/tendermint/issues/3809) Recover from application panics in `server/socket_server.go` to allow socket cleanup (@ruseinov) -- [p2p] [\#3664](https://github.com/tendermint/tendermint/issues/3664) p2p/conn: reuse buffer when write/read from secret connection(@guagualvcha) -- [p2p] [\#3834](https://github.com/tendermint/tendermint/issues/3834) Do not write 'Couldn't connect to any seeds' error log if there are no seeds in config file -- [rpc] [\#3076](https://github.com/tendermint/tendermint/issues/3076) Improve transaction search performance - -### BUG FIXES: - -- [p2p] [\#3644](https://github.com/tendermint/tendermint/issues/3644) Fix error logging for connection stop (@defunctzombie) -- [rpc] [\#3813](https://github.com/tendermint/tendermint/issues/3813) Return err if page is incorrect (less than 0 or greater than total pages) - -## v0.32.1 - -*July 15, 2019* - -Special thanks to external contributors on this release: -@ParthDesai, @climber73, @jim380, @ashleyvega - -This release contains a minor enhancement to the ABCI and some breaking changes to our libs folder, namely: -- CheckTx requests include a `CheckTxType` enum that can be set to `Recheck` to indicate to the application that this transaction was already checked/validated and certain expensive operations (like checking signatures) can be skipped -- Removed various functions from `libs` pkgs - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### BREAKING CHANGES: - -- Go API - - - [abci] [\#2127](https://github.com/tendermint/tendermint/issues/2127) The CheckTx and DeliverTx methods in the ABCI `Application` interface now take structs as arguments (RequestCheckTx and RequestDeliverTx, respectively), instead of just the raw tx bytes. This allows more information to be passed to these methods, for instance, indicating whether a tx has already been checked. - - [libs] Remove unused `db/debugDB` and `common/colors.go` & `errors/errors.go` files (@marbar3778) - - [libs] [\#2432](https://github.com/tendermint/tendermint/issues/2432) Remove unused `common/heap.go` file (@marbar3778) - - [libs] Remove unused `date.go`, `io.go`. Remove `GoPath()`, `Prompt()` and `IsDirEmpty()` functions from `os.go` (@marbar3778) - - [libs] Remove unused `FailRand()` func and minor clean up to `fail.go`(@marbar3778) - -### FEATURES: - -- [node] Add variadic argument to `NewNode` to support functional options, allowing the Node to be more easily customized. -- [node][\#3730](https://github.com/tendermint/tendermint/pull/3730) Add `CustomReactors` option to `NewNode` allowing caller to pass - custom reactors to run inside Tendermint node (@ParthDesai) -- [abci] [\#2127](https://github.com/tendermint/tendermint/issues/2127)RequestCheckTx has a new field, `CheckTxType`, which can take values of `CheckTxType_New` and `CheckTxType_Recheck`, indicating whether this is a new tx being checked for the first time or whether this tx is being rechecked after a block commit. This allows applications to skip certain expensive operations, like signature checking, if they've already been done once. see [docs](https://github.com/tendermint/tendermint/blob/eddb433d7c082efbeaf8974413a36641519ee895/docs/spec/abci/apps.md#mempool-connection) - -### IMPROVEMENTS: - -- [rpc] [\#3700](https://github.com/tendermint/tendermint/issues/3700) Make possible to set absolute paths for TLS cert and key (@climber73) -- [abci] [\#3513](https://github.com/tendermint/tendermint/issues/3513) Call the reqRes callback after the resCb so they always happen in the same order - -### BUG FIXES: - -- [p2p] [\#3338](https://github.com/tendermint/tendermint/issues/3338) Prevent "sent next PEX request too soon" errors by not calling - ensurePeers outside of ensurePeersRoutine -- [behaviour] [\3772](https://github.com/tendermint/tendermint/pull/3772) Return correct reason in MessageOutOfOrder (@jim380) -- [config] [\#3723](https://github.com/tendermint/tendermint/issues/3723) Add consensus_params to testnet config generation; document time_iota_ms (@ashleyvega) - - -## v0.32.0 - -*June 25, 2019* - -Special thanks to external contributors on this release: -@needkane, @SebastianElvis, @andynog, @Yawning, @wooparadog - -This release contains breaking changes to our build and release processes, ABCI, -and the RPC, namely: -- Use Go modules instead of dep -- Bring active development to the `master` Github branch -- ABCI Tags are now Events - see - [docs](https://github.com/tendermint/tendermint/blob/60827f75623b92eff132dc0eff5b49d2025c591e/docs/spec/abci/abci.md#events) -- Bind RPC to localhost by default, not to the public interface [UPGRADING/RPC_Changes](./UPGRADING.md#rpc_changes) - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### BREAKING CHANGES: - -* CLI/RPC/Config - - [cli] [\#3613](https://github.com/tendermint/tendermint/issues/3613) Switch from golang/dep to Go Modules to resolve dependencies: - It is recommended to switch to Go Modules if your project has tendermint as - a dependency. Read more on Modules here: - https://github.com/golang/go/wiki/Modules - - [config] [\#3632](https://github.com/tendermint/tendermint/pull/3632) Removed `leveldb` as generic - option for `db_backend`. Must be `goleveldb` or `cleveldb`. - - [rpc] [\#3616](https://github.com/tendermint/tendermint/issues/3616) Fix field names for `/block_results` response (eg. `results.DeliverTx` - -> `results.deliver_tx`). See docs for details. - - [rpc] [\#3724](https://github.com/tendermint/tendermint/issues/3724) RPC now binds to `127.0.0.1` by default instead of `0.0.0.0` - -* Apps - - [abci] [\#1859](https://github.com/tendermint/tendermint/issues/1859) `ResponseCheckTx`, `ResponseDeliverTx`, `ResponseBeginBlock`, - and `ResponseEndBlock` now include `Events` instead of `Tags`. Each `Event` - contains a `type` and a list of `attributes` (list of key-value pairs) - allowing for inclusion of multiple distinct events in each response. - -* Go API - - [abci] [\#3193](https://github.com/tendermint/tendermint/issues/3193) Use RequestDeliverTx and RequestCheckTx in the ABCI - Application interface - - [libs/db] [\#3632](https://github.com/tendermint/tendermint/pull/3632) Removed deprecated `LevelDBBackend` const - If you have `db_backend` set to `leveldb` in your config file, please - change it to `goleveldb` or `cleveldb`. - - [p2p] [\#3521](https://github.com/tendermint/tendermint/issues/3521) Remove NewNetAddressStringWithOptionalID - -* Blockchain Protocol - -* P2P Protocol - -### FEATURES: - -### IMPROVEMENTS: -- [abci/examples] [\#3659](https://github.com/tendermint/tendermint/issues/3659) Change validator update tx format in the `persistent_kvstore` to use base64 for pubkeys instead of hex (@needkane) -- [consensus] [\#3656](https://github.com/tendermint/tendermint/issues/3656) Exit if SwitchToConsensus fails -- [p2p] [\#3666](https://github.com/tendermint/tendermint/issues/3666) Add per channel telemetry to improve reactor observability -- [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. (@wooparadog) - -### BUG FIXES: -- [libs/db] [\#3717](https://github.com/tendermint/tendermint/issues/3717) Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) -- [libs/db] [\#3718](https://github.com/tendermint/tendermint/issues/3718) Fixed the BoltDB backend's Get and Iterator implementation (@Yawning) -- [node] [\#3716](https://github.com/tendermint/tendermint/issues/3716) Fix a bug where `nil` is recorded as node's address -- [node] [\#3741](https://github.com/tendermint/tendermint/issues/3741) Fix profiler blocking the entire node - -*Tendermint 0.31 release series has reached End-Of-Life and is no longer supported.* - -## v0.31.12 - -*April 6, 2020* - -This security release fixes: - -### Denial of Service 1 - -Tendermint 0.33.2 and earlier does not limit the number of P2P connection requests. -For each p2p connection, Tendermint allocates ~0.5MB. Even though this -memory is garbage collected once the connection is terminated (due to duplicate -IP or reaching a maximum number of inbound peers), temporary memory spikes can -lead to OOM (Out-Of-Memory) exceptions. - -Tendermint 0.33.3, 0.32.10, and 0.31.12 limit the total number of P2P incoming -connection requests to to `p2p.max_num_inbound_peers + -len(p2p.unconditional_peer_ids)`. - -Notes: - -- Tendermint does not rate limit P2P connection requests per IP (an attacker - can saturate all the inbound slots); -- Tendermint does not rate limit HTTP(S) requests. If you expose any RPC - endpoints to the public, please make sure to put in place some protection - (https://www.nginx.com/blog/rate-limiting-nginx/). We may implement this in - the future ([\#1696](https://github.com/tendermint/tendermint/issues/1696)). - -### Denial of Service 2 - -Tendermint 0.33.2 and earlier does not reclaim `activeID` of a peer after it's -removed in `Mempool` reactor. This does not happen all the time. It only -happens when a connection fails (for any reason) before the Peer is created and -added to all reactors. `RemovePeer` is therefore called before `AddPeer`, which -leads to always growing memory (`activeIDs` map). The `activeIDs` map has a -maximum size of 65535 and the node will panic if this map reaches the maximum. -An attacker can create a lot of connection attempts (exploiting Denial of -Service 1), which ultimately will lead to the node panicking. - -Tendermint 0.33.3, 0.32.10, and 0.31.12 claim `activeID` for a peer in `InitPeer`, -which is executed before `MConnection` is started. - -Notes: - -- `InitPeer` function was added to all reactors to combat a similar issue - - [\#3338](https://github.com/tendermint/tendermint/issues/3338); -- Denial of Service 2 is independent of Denial of Service 1 and can be executed - without it. - -**All clients are recommended to upgrade** - -Special thanks to [fudongbai](https://hackerone.com/fudongbai) for finding -and reporting this. - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### SECURITY: - -- [mempool] Reserve IDs in InitPeer instead of AddPeer (@tessr) -- [p2p] Limit the number of incoming connections (@melekes) - -## v0.31.11 - -*October 18, 2019* - -This security release fixes a vulnerability found in the `consensus` package, -where an attacker could construct a `BlockPartMessage` message in such a way -that it will lead to consensus failure. A few similar issues have been -identified and fixed here. - -**All clients are recommended to upgrade** - -Special thanks to [elvishacker](https://hackerone.com/elvishacker) for finding -and reporting this. - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### BREAKING CHANGES: - -- Go API - - [consensus] Modify `WAL#Write` and `WAL#WriteSync` to return an error if - they fail to write a message - -### SECURITY: - -- [consensus] Validate incoming messages more throughly - -## v0.31.10 - -*October 8, 2019* - -The previous patch was insufficient because the attacker could still find a way -to submit a `nil` pubkey by constructing a `PubKeyMultisigThreshold` pubkey -with `nil` subpubkeys for example. - -This release provides multiple fixes, which include recovering from panics when -accepting new peers and only allowing `ed25519` pubkeys. - -**All clients are recommended to upgrade** - -Special thanks to [fudongbai](https://hackerone.com/fudongbai) for pointing -this out. - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### SECURITY: - -- [p2p] [\#4030](https://github.com/tendermint/tendermint/issues/4030) Only allow ed25519 pubkeys when connecting - -## v0.31.9 - -*October 1, 2019* - -This release fixes a major security vulnerability found in the `p2p` package. -All clients are recommended to upgrade. See -[\#4030](https://github.com/tendermint/tendermint/issues/4030) for details. - -Special thanks to [fudongbai](https://hackerone.com/fudongbai) for discovering -and reporting this issue. - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### SECURITY: - -- [p2p] [\#4030](https://github.com/tendermint/tendermint/issues/4030) Fix for panic on nil public key send to a peer - -### BUG FIXES: - -- [node] [\#3716](https://github.com/tendermint/tendermint/issues/3716) Fix a bug where `nil` is recorded as node's address -- [node] [\#3741](https://github.com/tendermint/tendermint/issues/3741) Fix profiler blocking the entire node - -## v0.31.8 - -*July 29, 2019* - -This releases fixes one bug in the PEX reactor and adds a `recover` to the Go's -ABCI server, which allows it to properly cleanup. - -### IMPROVEMENTS: -- [abci] [\#3809](https://github.com/tendermint/tendermint/issues/3809) Recover from application panics in `server/socket_server.go` to allow socket cleanup (@ruseinov) - -### BUG FIXES: -- [p2p] [\#3338](https://github.com/tendermint/tendermint/issues/3338) Prevent "sent next PEX request too soon" errors by not calling - ensurePeers outside of ensurePeersRoutine - -## v0.31.7 - -*June 3, 2019* - -This releases fixes a regression in the mempool introduced in v0.31.6. -The regression caused the invalid committed txs to be proposed in blocks over and -over again. - -### BUG FIXES: -- [mempool] [\#3699](https://github.com/tendermint/tendermint/issues/3699) Remove all committed txs from the mempool. - This reverts the change from v0.31.6 where we only remove valid txs from the mempool. - Note this means malicious proposals can cause txs to be dropped from the - mempools of other nodes by including them in blocks before they are valid. - See [\#3322](https://github.com/tendermint/tendermint/issues/3322). - -## v0.31.6 - -*May 31st, 2019* - -This release contains many fixes and improvements, primarily for p2p functionality. -It also fixes a security issue in the mempool package. - -With this release, Tendermint now supports [boltdb](https://github.com/etcd-io/bbolt), although -in experimental mode. Feel free to try and report to us any findings/issues. -Note also that the build tags for compiling CLevelDB have changed. - -Special thanks to external contributors on this release: -@guagualvcha, @james-ray, @gregdhill, @climber73, @yutianwu, -@carlosflrs, @defunctzombie, @leoluk, @needkane, @CrocdileChan - -### BREAKING CHANGES: - -* Go API - - [libs/common] Removed deprecated `PanicSanity`, `PanicCrisis`, - `PanicConsensus` and `PanicQ` - - [mempool, state] [\#2659](https://github.com/tendermint/tendermint/issues/2659) `Mempool` now an interface that lives in the mempool package. - See issue and PR for more details. - - [p2p] [\#3346](https://github.com/tendermint/tendermint/issues/3346) `Reactor#InitPeer` method is added to `Reactor` interface - - [types] [\#1648](https://github.com/tendermint/tendermint/issues/1648) `Commit#VoteSignBytes` signature was changed - -### FEATURES: -- [node] [\#2659](https://github.com/tendermint/tendermint/issues/2659) Add `node.Mempool()` method, which allows you to access mempool -- [libs/db] [\#3604](https://github.com/tendermint/tendermint/pull/3604) Add experimental support for bolt db (etcd's fork of bolt) (@CrocdileChan) - -### IMPROVEMENTS: -- [cli] [\#3585](https://github.com/tendermint/tendermint/issues/3585) Add `--keep-addr-book` option to `unsafe_reset_all` cmd to not - clear the address book (@climber73) -- [cli] [\#3160](https://github.com/tendermint/tendermint/issues/3160) Add - `--config=` option to `testnet` cmd (@gregdhill) -- [cli] [\#3661](https://github.com/tendermint/tendermint/pull/3661) Add - `--hostname-suffix`, `--hostname` and `--random-monikers` options to `testnet` - cmd for greater peer address/identity generation flexibility. -- [crypto] [\#3672](https://github.com/tendermint/tendermint/issues/3672) Return more info in the `AddSignatureFromPubKey` error -- [cs/replay] [\#3460](https://github.com/tendermint/tendermint/issues/3460) Check appHash for each block -- [libs/db] [\#3611](https://github.com/tendermint/tendermint/issues/3611) Conditional compilation - * Use `cleveldb` tag instead of `gcc` to compile Tendermint with CLevelDB or - use `make build_c` / `make install_c` (full instructions can be found at - https://docs.tendermint.com/master/introduction/install.html#compile-with-cleveldb-support) - * Use `boltdb` tag to compile Tendermint with bolt db -- [node] [\#3362](https://github.com/tendermint/tendermint/issues/3362) Return an error if `persistent_peers` list is invalid (except - when IP lookup fails) -- [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer (@guagualvcha) -- [p2p] [\#3531](https://github.com/tendermint/tendermint/issues/3531) Terminate session on nonce wrapping (@climber73) -- [pex] [\#3647](https://github.com/tendermint/tendermint/pull/3647) Dial seeds, if any, instead of crawling peers first (@defunctzombie) -- [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC -- [rpc] [\#3362](https://github.com/tendermint/tendermint/issues/3362) `/dial_seeds` & `/dial_peers` return errors if addresses are - incorrect (except when IP lookup fails) - -### BUG FIXES: -- [consensus] [\#3067](https://github.com/tendermint/tendermint/issues/3067) Fix replay from appHeight==0 with validator set changes (@james-ray) -- [consensus] [\#3304](https://github.com/tendermint/tendermint/issues/3304) Create a peer state in consensus reactor before the peer - is started (@guagualvcha) -- [lite] [\#3669](https://github.com/tendermint/tendermint/issues/3669) Add context parameter to RPC Handlers in proxy routes (@yutianwu) -- [mempool] [\#3322](https://github.com/tendermint/tendermint/issues/3322) When a block is committed, only remove committed txs from the mempool -that were valid (ie. `ResponseDeliverTx.Code == 0`) -- [p2p] [\#3338](https://github.com/tendermint/tendermint/issues/3338) Ensure `RemovePeer` is always called before `InitPeer` (upon a peer - reconnecting to our node) -- [p2p] [\#3532](https://github.com/tendermint/tendermint/issues/3532) Limit the number of attempts to connect to a peer in seed mode - to 16 (as a result, the node will stop retrying after a 35 hours time window) -- [p2p] [\#3362](https://github.com/tendermint/tendermint/issues/3362) Allow inbound peers to be persistent, including for seed nodes. -- [pex] [\#3603](https://github.com/tendermint/tendermint/pull/3603) Dial seeds when addrbook needs more addresses (@defunctzombie) - -### OTHERS: -- [networks] fixes ansible integration script (@carlosflrs) - -## v0.31.5 - -*April 16th, 2019* - -This release fixes a regression from v0.31.4 where, in existing chains that -were upgraded, `/validators` could return an empty validator set. This is true -for almost all heights, given the validator set remains the same. - -Special thanks to external contributors on this release: -@brapse, @guagualvcha, @dongsam, @phucc - -### IMPROVEMENTS: - -- [libs/common] `CMap`: slight optimization in `Keys()` and `Values()` (@phucc) -- [gitignore] gitignore: add .vendor-new (@dongsam) - -### BUG FIXES: - -- [state] [\#3537](https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833) - `LoadValidators`: do not return an empty validator set -- [blockchain] [\#3457](https://github.com/tendermint/tendermint/issues/3457) - Fix "peer did not send us anything" in `fast_sync` mode when under high pressure - -## v0.31.4 - -*April 12th, 2019* - -This release fixes a regression from v0.31.3 which used the peer's `SocketAddr` to add the peer to -the address book. This swallowed the peer's self-reported port which is important in case of reconnect. -It brings back `NetAddress()` to `NodeInfo` and uses it instead of `SocketAddr` for adding peers. -Additionally, it improves response time on the `/validators` or `/status` RPC endpoints. -As a side-effect it makes these RPC endpoint more difficult to DoS and fixes a performance degradation in `ExecCommitBlock`. -Also, it contains an [ADR](https://github.com/tendermint/tendermint/pull/3539) that proposes decoupling the -responsibility for peer behaviour from the `p2p.Switch` (by @brapse). - -Special thanks to external contributors on this release: -@brapse, @guagualvcha, @mydring - -### IMPROVEMENTS: - -- [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer -- [p2p] [\#3547](https://github.com/tendermint/tendermint/pull/3547) Fix a couple of annoying typos (@mdyring) - -### BUG FIXES: - -- [docs] [\#3514](https://github.com/tendermint/tendermint/issues/3514) Fix block.Header.Time description (@melekes) -- [p2p] [\#2716](https://github.com/tendermint/tendermint/issues/2716) Check if we're already connected to peer right before dialing it (@melekes) -- [p2p] [\#3545](https://github.com/tendermint/tendermint/issues/3545) Add back `NetAddress()` to `NodeInfo` and use it instead of peer's `SocketAddr()` when adding a peer to the `PEXReactor` (potential fix for [\#3532](https://github.com/tendermint/tendermint/issues/3532)) -- [state] [\#3438](https://github.com/tendermint/tendermint/pull/3438) - Persist validators every 100000 blocks even if no changes to the set - occurred (@guagualvcha). This - 1) Prevents possible DoS attack using `/validators` or `/status` RPC - endpoints. Before response time was growing linearly with height if no - changes were made to the validator set. - 2) Fixes performance degradation in `ExecCommitBlock` where we call - `LoadValidators` for each `Evidence` in the block. - -## v0.31.3 - -*April 1st, 2019* - -This release includes two security sensitive fixes: it ensures generated private -keys are valid, and it prevents certain DNS lookups that would cause the node to -panic if the lookup failed. - -### BREAKING CHANGES: -* Go API - - [crypto/secp256k1] [\#3439](https://github.com/tendermint/tendermint/issues/3439) - The `secp256k1.GenPrivKeySecp256k1` function has changed to guarantee that it returns a valid key, which means it - will return a different private key than in previous versions for the same secret. - -### BUG FIXES: - -- [crypto/secp256k1] [\#3439](https://github.com/tendermint/tendermint/issues/3439) - Ensure generated private keys are valid by randomly sampling until a valid key is found. - Previously, it was possible (though rare!) to generate keys that exceeded the curve order. - Such keys would lead to invalid signatures. -- [p2p] [\#3522](https://github.com/tendermint/tendermint/issues/3522) Memoize - socket address in peer connections to avoid DNS lookups. Previously, failed - DNS lookups could cause the node to panic. - -## v0.31.2 - -*March 30th, 2019* - -This release fixes a regression from v0.31.1 where Tendermint panics under -mempool load for external ABCI apps. - -Special thanks to external contributors on this release: -@guagualvcha - -### BREAKING CHANGES: - -* CLI/RPC/Config - -* Apps - -* Go API - - [libs/autofile] [\#3504](https://github.com/tendermint/tendermint/issues/3504) Remove unused code in autofile package. Deleted functions: `Group.Search`, `Group.FindLast`, `GroupReader.ReadLine`, `GroupReader.PushLine`, `MakeSimpleSearchFunc` (@guagualvcha) - -* Blockchain Protocol - -* P2P Protocol - -### FEATURES: - -### IMPROVEMENTS: - -- [circle] [\#3497](https://github.com/tendermint/tendermint/issues/3497) Move release management to CircleCI - -### BUG FIXES: - -- [mempool] [\#3512](https://github.com/tendermint/tendermint/issues/3512) Fix panic from concurrent access to txsMap, a regression for external ABCI apps introduced in v0.31.1 - -## v0.31.1 - -*March 27th, 2019* - -This release contains a major improvement for the mempool that reduce the amount of sent data by about 30% -(see some numbers below). -It also fixes a memory leak in the mempool and adds TLS support to the RPC server by providing a certificate and key in the config. - -Special thanks to external contributors on this release: -@brapse, @guagualvcha, @HaoyangLiu, @needkane, @TraceBundy - -### BREAKING CHANGES: - -* CLI/RPC/Config - -* Apps - -* Go API - - [crypto] [\#3426](https://github.com/tendermint/tendermint/pull/3426) Remove `Ripemd160` helper method (@needkane) - - [libs/common] [\#3429](https://github.com/tendermint/tendermint/pull/3429) Remove `RepeatTimer` (also `TimerMaker` and `Ticker` interface) - - [rpc/client] [\#3458](https://github.com/tendermint/tendermint/issues/3458) Include `NetworkClient` interface into `Client` interface - - [types] [\#3448](https://github.com/tendermint/tendermint/issues/3448) Remove method `PB2TM.ConsensusParams` - -* Blockchain Protocol - -* P2P Protocol - -### FEATURES: - - - [rpc] [\#3419](https://github.com/tendermint/tendermint/issues/3419) Start HTTPS server if `rpc.tls_cert_file` and `rpc.tls_key_file` are provided in the config (@guagualvcha) - -### IMPROVEMENTS: - -- [docs] [\#3140](https://github.com/tendermint/tendermint/issues/3140) Formalize proposer election algorithm properties -- [docs] [\#3482](https://github.com/tendermint/tendermint/issues/3482) Fix broken links (@brapse) -- [mempool] [\#2778](https://github.com/tendermint/tendermint/issues/2778) No longer send txs back to peers who sent it to you. -Also, limit to 65536 active peers. -This vastly improves the bandwidth consumption of nodes. -For instance, for a 4 node localnet, in a test sending 250byte txs for 120 sec. at 500 txs/sec (total of 15MB): - - total bytes received from 1st node: - - before: 42793967 (43MB) - - after: 30003256 (30MB) - - total bytes sent to 1st node: - - before: 30569339 (30MB) - - after: 19304964 (19MB) -- [p2p] [\#3475](https://github.com/tendermint/tendermint/issues/3475) Simplify `GetSelectionWithBias` for addressbook (@guagualvcha) -- [rpc/lib/client] [\#3430](https://github.com/tendermint/tendermint/issues/3430) Disable compression for HTTP client to prevent GZIP-bomb DoS attacks (@guagualvcha) - -### BUG FIXES: - -- [blockchain] [\#2699](https://github.com/tendermint/tendermint/issues/2699) Update the maxHeight when a peer is removed -- [mempool] [\#3478](https://github.com/tendermint/tendermint/issues/3478) Fix memory-leak related to `broadcastTxRoutine` (@HaoyangLiu) - - -## v0.31.0 - -*March 16th, 2019* - -Special thanks to external contributors on this release: -@danil-lashin, @guagualvcha, @siburu, @silasdavis, @srmo, @Stumble, @svenstaro - -This release is primarily about the new pubsub implementation, dubbed `pubsub 2.0`, and related changes, -like configurable limits on the number of active RPC subscriptions at a time (`max_subscription_clients`). -Pubsub 2.0 is an improved version of the older pubsub that is non-blocking and has a nicer API. -Note the improved pubsub API also resulted in some improvements to the HTTPClient interface and the API for WebSocket subscriptions. -This release also adds a configurable limit to the mempool size (`max_txs_bytes`, default 1GB) -and a configurable timeout for the `/broadcast_tx_commit` endpoint. - -See the [v0.31.0 -Milestone](https://github.com/tendermint/tendermint/milestone/19?closed=1) for -more details. - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### BREAKING CHANGES: - -* CLI/RPC/Config - - [config] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Remove `consensus.blocktime_iota` parameter - - [rpc] [\#3227](https://github.com/tendermint/tendermint/issues/3227) New PubSub design does not block on clients when publishing - messages. Slow clients may miss messages and receive an error, terminating - the subscription. - - [rpc] [\#3269](https://github.com/tendermint/tendermint/issues/2826) Limit number of unique clientIDs with open subscriptions. Configurable via `rpc.max_subscription_clients` - - [rpc] [\#3269](https://github.com/tendermint/tendermint/issues/2826) Limit number of unique queries a given client can subscribe to at once. Configurable via `rpc.max_subscriptions_per_client`. - - [rpc] [\#3435](https://github.com/tendermint/tendermint/issues/3435) Default ReadTimeout and WriteTimeout changed to 10s. WriteTimeout can increased by setting `rpc.timeout_broadcast_tx_commit` in the config. - - [rpc/client] [\#3269](https://github.com/tendermint/tendermint/issues/3269) Update `EventsClient` interface to reflect new pubsub/eventBus API [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md). This includes `Subscribe`, `Unsubscribe`, and `UnsubscribeAll` methods. - -* Apps - - [abci] [\#3403](https://github.com/tendermint/tendermint/issues/3403) Remove `time_iota_ms` from BlockParams. This is a - ConsensusParam but need not be exposed to the app for now. - - [abci] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Rename `consensus_params.block_size` to `consensus_params.block` in ABCI ConsensusParams - -* Go API - - [libs/common] TrapSignal accepts logger as a first parameter and does not block anymore - * previously it was dumping "captured ..." msg to os.Stdout - * TrapSignal should not be responsible for blocking thread of execution - - [libs/db] [\#3397](https://github.com/tendermint/tendermint/pull/3397) Add possibility to `Close()` `Batch` to prevent memory leak when using ClevelDB. (@Stumble) - - [types] [\#3354](https://github.com/tendermint/tendermint/issues/3354) Remove RoundState from EventDataRoundState - - [rpc] [\#3435](https://github.com/tendermint/tendermint/issues/3435) `StartHTTPServer` / `StartHTTPAndTLSServer` now require a Config (use `rpcserver.DefaultConfig`) - -* Blockchain Protocol - -* P2P Protocol - -### FEATURES: -- [config] [\#3269](https://github.com/tendermint/tendermint/issues/2826) New configuration values for controlling RPC subscriptions: - - `rpc.max_subscription_clients` sets the maximum number of unique clients - with open subscriptions - - `rpc.max_subscriptions_per_client`sets the maximum number of unique - subscriptions from a given client - - `rpc.timeout_broadcast_tx_commit` sets the time to wait for a tx to be committed during `/broadcast_tx_commit` -- [types] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Add `time_iota_ms` to block's consensus parameters (not exposed to the application) -- [lite] [\#3269](https://github.com/tendermint/tendermint/issues/3269) Add `/unsubscribe_all` endpoint to unsubscribe from all events -- [mempool] [\#3079](https://github.com/tendermint/tendermint/issues/3079) Bound mempool memory usage via the `mempool.max_txs_bytes` configuration value. Set to 1GB by default. The mempool's current `txs_total_bytes` is exposed via `total_bytes` field in - `/num_unconfirmed_txs` and `/unconfirmed_txs` RPC endpoints. - -### IMPROVEMENTS: -- [all] [\#3385](https://github.com/tendermint/tendermint/issues/3385), [\#3386](https://github.com/tendermint/tendermint/issues/3386) Various linting improvements -- [crypto] [\#3371](https://github.com/tendermint/tendermint/issues/3371) Copy in secp256k1 package from go-ethereum instead of importing - go-ethereum (@silasdavis) -- [deps] [\#3382](https://github.com/tendermint/tendermint/issues/3382) Don't pin repos without releases -- [deps] [\#3357](https://github.com/tendermint/tendermint/issues/3357), [\#3389](https://github.com/tendermint/tendermint/issues/3389), [\#3392](https://github.com/tendermint/tendermint/issues/3392) Update gogo/protobuf, golang/protobuf, levigo, golang.org/x/crypto -- [libs/common] [\#3238](https://github.com/tendermint/tendermint/issues/3238) exit with zero (0) code upon receiving SIGTERM/SIGINT -- [libs/db] [\#3378](https://github.com/tendermint/tendermint/issues/3378) CLevelDB#Stats now returns the following properties: - - leveldb.num-files-at-level{n} - - leveldb.stats - - leveldb.sstables - - leveldb.blockpool - - leveldb.cachedblock - - leveldb.openedtables - - leveldb.alivesnaps - - leveldb.aliveiters -- [privval] [\#3351](https://github.com/tendermint/tendermint/pull/3351) First part of larger refactoring that clarifies and separates concerns in the privval package. - -### BUG FIXES: -- [blockchain] [\#3358](https://github.com/tendermint/tendermint/pull/3358) Fix timer leak in `BlockPool` (@guagualvcha) -- [cmd] [\#3408](https://github.com/tendermint/tendermint/issues/3408) Fix `testnet` command's panic when creating non-validator configs (using `--n` flag) (@srmo) -- [libs/db/remotedb/grpcdb] [\#3402](https://github.com/tendermint/tendermint/issues/3402) Close Iterator/ReverseIterator after use -- [libs/pubsub] [\#951](https://github.com/tendermint/tendermint/issues/951), [\#1880](https://github.com/tendermint/tendermint/issues/1880) Use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) -- [lite] [\#3364](https://github.com/tendermint/tendermint/issues/3364) Fix `/validators` and `/abci_query` proxy endpoints - (@guagualvcha) -- [p2p/conn] [\#3347](https://github.com/tendermint/tendermint/issues/3347) Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection -- [p2p] [\#3369](https://github.com/tendermint/tendermint/issues/3369) Do not panic when filter times out -- [p2p] [\#3359](https://github.com/tendermint/tendermint/pull/3359) Fix reconnecting report duplicate ID error due to race condition between adding peer to peerSet and starting it (@guagualvcha) - -## v0.30.2 - -*March 10th, 2019* - -This release fixes a CLevelDB memory leak. It was happening because we were not -closing the WriteBatch object after use. See [levigo's -godoc](https://godoc.org/github.com/jmhodges/levigo#WriteBatch.Close) for the -Close method. Special thanks goes to @Stumble who both reported an issue in -[cosmos-sdk](https://github.com/cosmos/cosmos-sdk/issues/3842) and provided a -fix here. - -### BREAKING CHANGES: - -* Go API - - [libs/db] [\#3842](https://github.com/cosmos/cosmos-sdk/issues/3842) Add Close() method to Batch interface (@Stumble) - -### BUG FIXES: -- [libs/db] [\#3842](https://github.com/cosmos/cosmos-sdk/issues/3842) Fix CLevelDB memory leak (@Stumble) - -## v0.30.1 - -*February 20th, 2019* - -This release fixes a consensus halt and a DataCorruptionError after restart -discovered in `game_of_stakes_6`. It also fixes a security issue in the p2p -handshake by authenticating the NetAddress.ID of the peer we're dialing. - -### IMPROVEMENTS: - -* [config] [\#3291](https://github.com/tendermint/tendermint/issues/3291) Make - config.ResetTestRootWithChainID() create concurrency-safe test directories. - -### BUG FIXES: - -* [consensus] [\#3295](https://github.com/tendermint/tendermint/issues/3295) - Flush WAL on stop to prevent data corruption during graceful shutdown. -* [consensus] [\#3302](https://github.com/tendermint/tendermint/issues/3302) - Fix possible halt by resetting TriggeredTimeoutPrecommit before starting next height. -* [rpc] [\#3251](https://github.com/tendermint/tendermint/issues/3251) Fix - `/net_info#peers#remote_ip` format. New format spec: - * dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address - * IPv6 ("2001:db8::1"), if ip is a valid IPv6 address -* [cmd] [\#3314](https://github.com/tendermint/tendermint/issues/3314) Return - an error on `show_validator` when the private validator file does not exist. -* [p2p] [\#3010](https://github.com/tendermint/tendermint/issues/3010#issuecomment-464287627) - Authenticate a peer against its NetAddress.ID when dialing. - -## v0.30.0 - -*February 8th, 2019* - -This release fixes yet another issue with the proposer selection algorithm. -We hope it's the last one, but we won't be surprised if it's not. -We plan to one day expose the selection algorithm more directly to -the application ([\#3285](https://github.com/tendermint/tendermint/issues/3285)), and even to support randomness ([\#763](https://github.com/tendermint/tendermint/issues/763)). -For more, see issues marked -[proposer-selection](https://github.com/tendermint/tendermint/labels/proposer-selection). - -This release also includes a fix to prevent Tendermint from including the same -piece of evidence in more than one block. This issue was reported by @chengwenxi in our -[bug bounty program](https://hackerone.com/tendermint). - -### BREAKING CHANGES: - -* Apps - - [state] [\#3222](https://github.com/tendermint/tendermint/issues/3222) - Duplicate updates for the same validator are forbidden. Apps must ensure - that a given `ResponseEndBlock.ValidatorUpdates` contains only one entry per pubkey. - -* Go API - - [types] [\#3222](https://github.com/tendermint/tendermint/issues/3222) - Remove `Add` and `Update` methods from `ValidatorSet` in favor of new - `UpdateWithChangeSet`. This allows updates to be applied as a set, instead of - one at a time. - -* Block Protocol - - [state] [\#3286](https://github.com/tendermint/tendermint/issues/3286) Blocks that include already committed evidence are invalid. - -* P2P Protocol - - [consensus] [\#3222](https://github.com/tendermint/tendermint/issues/3222) - Validator updates are applied as a set, instead of one at a time, thus - impacting the proposer priority calculation. This ensures that the proposer - selection algorithm does not depend on the order of updates in - `ResponseEndBlock.ValidatorUpdates`. - -### IMPROVEMENTS: -- [crypto] [\#3279](https://github.com/tendermint/tendermint/issues/3279) Use `btcec.S256().N` directly instead of hard coding a copy. - -### BUG FIXES: -- [state] [\#3222](https://github.com/tendermint/tendermint/issues/3222) Fix validator set updates so they are applied as a set, rather - than one at a time. This makes the proposer selection algorithm independent of - the order of updates in `ResponseEndBlock.ValidatorUpdates`. -- [evidence] [\#3286](https://github.com/tendermint/tendermint/issues/3286) Don't add committed evidence to evidence pool. - -## v0.29.2 - -*February 7th, 2019* - -Special thanks to external contributors on this release: -@ackratos, @rickyyangz - -**Note**: This release contains security sensitive patches in the `p2p` and -`crypto` packages: -- p2p: - - Partial fix for MITM attacks on the p2p connection. MITM conditions may - still exist. See [\#3010](https://github.com/tendermint/tendermint/issues/3010). -- crypto: - - Eliminate our fork of `btcd` and use the `btcd/btcec` library directly for - native secp256k1 signing. Note we still modify the signature encoding to - prevent malleability. - - Support the libsecp256k1 library via CGo through the `go-ethereum/crypto/secp256k1` package. - - Eliminate MixEntropy functions - -### BREAKING CHANGES: - -* Go API - - [crypto] [\#3278](https://github.com/tendermint/tendermint/issues/3278) Remove - MixEntropy functions - - [types] [\#3245](https://github.com/tendermint/tendermint/issues/3245) Commit uses `type CommitSig Vote` instead of `Vote` directly. - In preparation for removing redundant fields from the commit [\#1648](https://github.com/tendermint/tendermint/issues/1648) - -### IMPROVEMENTS: -- [consensus] [\#3246](https://github.com/tendermint/tendermint/issues/3246) Better logging and notes on recovery for corrupted WAL file -- [crypto] [\#3163](https://github.com/tendermint/tendermint/issues/3163) Use ethereum's libsecp256k1 go-wrapper for signatures when cgo is available -- [crypto] [\#3162](https://github.com/tendermint/tendermint/issues/3162) Wrap btcd instead of forking it to keep up with fixes (used if cgo is not available) -- [makefile] [\#3233](https://github.com/tendermint/tendermint/issues/3233) Use golangci-lint instead of go-metalinter -- [tools] [\#3218](https://github.com/tendermint/tendermint/issues/3218) Add go-deadlock tool to help detect deadlocks -- [tools] [\#3106](https://github.com/tendermint/tendermint/issues/3106) Add tm-signer-harness test harness for remote signers -- [tests] [\#3258](https://github.com/tendermint/tendermint/issues/3258) Fixed a bunch of non-deterministic test failures - -### BUG FIXES: -- [node] [\#3186](https://github.com/tendermint/tendermint/issues/3186) EventBus and indexerService should be started before first block (for replay last block on handshake) execution (@ackratos) -- [p2p] [\#3232](https://github.com/tendermint/tendermint/issues/3232) Fix infinite loop leading to addrbook deadlock for seed nodes -- [p2p] [\#3247](https://github.com/tendermint/tendermint/issues/3247) Fix panic in SeedMode when calling FlushStop and OnStop - concurrently -- [p2p] [\#3040](https://github.com/tendermint/tendermint/issues/3040) Fix MITM on secret connection by checking low-order points -- [privval] [\#3258](https://github.com/tendermint/tendermint/issues/3258) Fix race between sign requests and ping requests in socket that was causing messages to be corrupted - -## v0.29.1 - -*January 24, 2019* - -Special thanks to external contributors on this release: -@infinytum, @gauthamzz - -This release contains two important fixes: one for p2p layer where we sometimes -were not closing connections and one for consensus layer where consensus with -no empty blocks (`create_empty_blocks = false`) could halt. - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### IMPROVEMENTS: -- [pex] [\#3037](https://github.com/tendermint/tendermint/issues/3037) Only log "Reached max attempts to dial" once -- [rpc] [\#3159](https://github.com/tendermint/tendermint/issues/3159) Expose - `triggered_timeout_commit` in the `/dump_consensus_state` - -### BUG FIXES: -- [consensus] [\#3199](https://github.com/tendermint/tendermint/issues/3199) Fix consensus halt with no empty blocks from not resetting triggeredTimeoutCommit -- [p2p] [\#2967](https://github.com/tendermint/tendermint/issues/2967) Fix file descriptor leak - -## v0.29.0 - -*January 21, 2019* - -Special thanks to external contributors on this release: -@bradyjoestar, @kunaldhariwal, @gauthamzz, @hrharder - -This release is primarily about making some breaking changes to -the Block protocol version before Cosmos launch, and to fixing more issues -in the proposer selection algorithm discovered on Cosmos testnets. - -The Block protocol changes include using a standard Merkle tree format (RFC 6962), -fixing some inconsistencies between field orders in Vote and Proposal structs, -and constraining the hash of the ConsensusParams to include only a few fields. - -The proposer selection algorithm saw significant progress, -including a [formal proof by @cwgoes for the base-case in Idris](https://github.com/cwgoes/tm-proposer-idris) -and a [much more detailed specification (still in progress) by -@ancazamfir](https://github.com/tendermint/tendermint/pull/3140). - -Fixes to the proposer selection algorithm include normalizing the proposer -priorities to mitigate the effects of large changes to the validator set. -That said, we just discovered [another bug](https://github.com/tendermint/tendermint/issues/3181), -which will be fixed in the next breaking release. - -While we are trying to stabilize the Block protocol to preserve compatibility -with old chains, there may be some final changes yet to come before Cosmos -launch as we continue to audit and test the software. - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### BREAKING CHANGES: - -* CLI/RPC/Config - -* Apps - - [state] [\#3049](https://github.com/tendermint/tendermint/issues/3049) Total voting power of the validator set is upper bounded by - `MaxInt64 / 8`. Apps must ensure they do not return changes to the validator - set that cause this maximum to be exceeded. - -* Go API - - [node] [\#3082](https://github.com/tendermint/tendermint/issues/3082) MetricsProvider now requires you to pass a chain ID - - [types] [\#2713](https://github.com/tendermint/tendermint/issues/2713) Rename `TxProof.LeafHash` to `TxProof.Leaf` - - [crypto/merkle] [\#2713](https://github.com/tendermint/tendermint/issues/2713) `SimpleProof.Verify` takes a `leaf` instead of a - `leafHash` and performs the hashing itself - -* Blockchain Protocol - * [crypto/merkle] [\#2713](https://github.com/tendermint/tendermint/issues/2713) Merkle trees now match the RFC 6962 specification - * [types] [\#3078](https://github.com/tendermint/tendermint/issues/3078) Re-order Timestamp and BlockID in CanonicalVote so it's - consistent with CanonicalProposal (BlockID comes - first) - * [types] [\#3165](https://github.com/tendermint/tendermint/issues/3165) Hash of ConsensusParams only includes BlockSize.MaxBytes and - BlockSize.MaxGas - -* P2P Protocol - - [consensus] [\#3049](https://github.com/tendermint/tendermint/issues/3049) Normalize priorities to not exceed `2*TotalVotingPower` to mitigate unfair proposer selection - heavily preferring earlier joined validators in the case of an early bonded large validator unbonding - -### FEATURES: - -### IMPROVEMENTS: -- [rpc] [\#3065](https://github.com/tendermint/tendermint/issues/3065) Return maxPerPage (100), not defaultPerPage (30) if `per_page` is greater than the max 100. -- [instrumentation] [\#3082](https://github.com/tendermint/tendermint/issues/3082) Add `chain_id` label for all metrics - -### BUG FIXES: -- [crypto] [\#3164](https://github.com/tendermint/tendermint/issues/3164) Update `btcd` fork for rare signRFC6979 bug -- [lite] [\#3171](https://github.com/tendermint/tendermint/issues/3171) Fix verifying large validator set changes -- [log] [\#3125](https://github.com/tendermint/tendermint/issues/3125) Fix year format -- [mempool] [\#3168](https://github.com/tendermint/tendermint/issues/3168) Limit tx size to fit in the max reactor msg size -- [scripts] [\#3147](https://github.com/tendermint/tendermint/issues/3147) Fix json2wal for large block parts (@bradyjoestar) - -## v0.28.1 - -*January 18th, 2019* - -Special thanks to external contributors on this release: -@HaoyangLiu - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### BUG FIXES: -- [consensus] Fix consensus halt from proposing blocks with too much evidence - -## v0.28.0 - -*January 16th, 2019* - -Special thanks to external contributors on this release: -@fmauricios, @gianfelipe93, @husio, @needkane, @srmo, @yutianwu - -This release is primarily about upgrades to the `privval` system - -separating the `priv_validator.json` into distinct config and data files, and -refactoring the socket validator to support reconnections. - -**Note:** Please backup your existing `priv_validator.json` before using this -version. - -See [UPGRADING.md](UPGRADING.md) for more details. - -### BREAKING CHANGES: - -* CLI/RPC/Config - - [cli] Removed `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead. - - [cli] Renamed `--proxy_app=nilapp` to `--proxy_app=noop`. - - [config] [\#2992](https://github.com/tendermint/tendermint/issues/2992) `allow_duplicate_ip` is now set to false - - [privval] [\#1181](https://github.com/tendermint/tendermint/issues/1181) Split `priv_validator.json` into immutable (`config/priv_validator_key.json`) and mutable (`data/priv_validator_state.json`) parts (@yutianwu) - - [privval] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types - - [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Listen for unix socket connections instead of dialing them - -* Apps - -* Go API - - [types] [\#2981](https://github.com/tendermint/tendermint/issues/2981) Remove `PrivValidator.GetAddress()` - -* Blockchain Protocol - -* P2P Protocol - -### FEATURES: -- [rpc] [\#3052](https://github.com/tendermint/tendermint/issues/3052) Include peer's remote IP in `/net_info` - -### IMPROVEMENTS: -- [consensus] [\#3086](https://github.com/tendermint/tendermint/issues/3086) Log peerID on ignored votes (@srmo) -- [docs] [\#3061](https://github.com/tendermint/tendermint/issues/3061) Added specification for signing consensus msgs at - ./docs/spec/consensus/signing.md -- [privval] [\#2948](https://github.com/tendermint/tendermint/issues/2948) Memoize pubkey so it's only requested once on startup -- [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Retry RemoteSigner connections on error - -### BUG FIXES: - -- [build] [\#3085](https://github.com/tendermint/tendermint/issues/3085) Fix `Version` field in build scripts (@husio) -- [crypto/multisig] [\#3102](https://github.com/tendermint/tendermint/issues/3102) Fix multisig keys address length -- [crypto/encoding] [\#3101](https://github.com/tendermint/tendermint/issues/3101) Fix `PubKeyMultisigThreshold` unmarshalling into `crypto.PubKey` interface -- [p2p/conn] [\#3111](https://github.com/tendermint/tendermint/issues/3111) Make SecretConnection thread safe -- [rpc] [\#3053](https://github.com/tendermint/tendermint/issues/3053) Fix internal error in `/tx_search` when results are empty - (@gianfelipe93) -- [types] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Do not panic if retrieving the privval's public key fails - -## v0.27.4 - -*December 21st, 2018* - -### BUG FIXES: - -- [mempool] [\#3036](https://github.com/tendermint/tendermint/issues/3036) Fix - LRU cache by popping the least recently used item when the cache is full, - not the most recently used one! - -## v0.27.3 - -*December 16th, 2018* - -### BREAKING CHANGES: - -* Go API - - [dep] [\#3027](https://github.com/tendermint/tendermint/issues/3027) Revert to mainline Go crypto library, eliminating the modified - `bcrypt.GenerateFromPassword` - -## v0.27.2 - -*December 16th, 2018* - -### IMPROVEMENTS: - -- [node] [\#3025](https://github.com/tendermint/tendermint/issues/3025) Validate NodeInfo addresses on startup. - -### BUG FIXES: - -- [p2p] [\#3025](https://github.com/tendermint/tendermint/pull/3025) Revert to using defers in addrbook. Fixes deadlocks in pex and consensus upon invalid ExternalAddr/ListenAddr configuration. - -## v0.27.1 - -*December 15th, 2018* - -Special thanks to external contributors on this release: -@danil-lashin, @hleb-albau, @james-ray, @leo-xinwang - -### FEATURES: -- [rpc] [\#2964](https://github.com/tendermint/tendermint/issues/2964) Add `UnconfirmedTxs(limit)` and `NumUnconfirmedTxs()` methods to HTTP/Local clients (@danil-lashin) -- [docs] [\#3004](https://github.com/tendermint/tendermint/issues/3004) Enable full-text search on docs pages - -### IMPROVEMENTS: -- [consensus] [\#2971](https://github.com/tendermint/tendermint/issues/2971) Return error if ValidatorSet is empty after InitChain - (@leo-xinwang) -- [ci/cd] [\#3005](https://github.com/tendermint/tendermint/issues/3005) Updated CircleCI job to trigger website build when docs are updated -- [docs] Various updates - -### BUG FIXES: -- [cmd] [\#2983](https://github.com/tendermint/tendermint/issues/2983) `testnet` command always sets `addr_book_strict = false` -- [config] [\#2980](https://github.com/tendermint/tendermint/issues/2980) Fix CORS options formatting -- [kv indexer] [\#2912](https://github.com/tendermint/tendermint/issues/2912) Don't ignore key when executing CONTAINS -- [mempool] [\#2961](https://github.com/tendermint/tendermint/issues/2961) Call `notifyTxsAvailable` if there're txs left after committing a block, but recheck=false -- [mempool] [\#2994](https://github.com/tendermint/tendermint/issues/2994) Reject txs with negative GasWanted -- [p2p] [\#2990](https://github.com/tendermint/tendermint/issues/2990) Fix a bug where seeds don't disconnect from a peer after 3h -- [consensus] [\#3006](https://github.com/tendermint/tendermint/issues/3006) Save state after InitChain only when stateHeight is also 0 (@james-ray) - -## v0.27.0 - -*December 5th, 2018* - -Special thanks to external contributors on this release: -@danil-lashin, @srmo - -Special thanks to @dlguddus for discovering a [major -issue](https://github.com/tendermint/tendermint/issues/2718#issuecomment-440888677) -in the proposer selection algorithm. - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -This release is primarily about fixes to the proposer selection algorithm -in preparation for the [Cosmos Game of -Stakes](https://blog.cosmos.network/the-game-of-stakes-is-open-for-registration-83a404746ee6). -It also makes use of the `ConsensusParams.Validator.PubKeyTypes` to restrict the -key types that can be used by validators, and removes the `Heartbeat` consensus -message. - -### BREAKING CHANGES: - -* CLI/RPC/Config - - [rpc] [\#2932](https://github.com/tendermint/tendermint/issues/2932) Rename `accum` to `proposer_priority` - -* Go API - - [db] [\#2913](https://github.com/tendermint/tendermint/pull/2913) - ReverseIterator API change: start < end, and end is exclusive. - - [types] [\#2932](https://github.com/tendermint/tendermint/issues/2932) Rename `Validator.Accum` to `Validator.ProposerPriority` - -* Blockchain Protocol - - [state] [\#2714](https://github.com/tendermint/tendermint/issues/2714) Validators can now only use pubkeys allowed within - ConsensusParams.Validator.PubKeyTypes - -* P2P Protocol - - [consensus] [\#2871](https://github.com/tendermint/tendermint/issues/2871) - Remove *ProposalHeartbeat* message as it serves no real purpose (@srmo) - - [state] Fixes for proposer selection: - - [\#2785](https://github.com/tendermint/tendermint/issues/2785) Accum for new validators is `-1.125*totalVotingPower` instead of 0 - - [\#2941](https://github.com/tendermint/tendermint/issues/2941) val.Accum is preserved during ValidatorSet.Update to avoid being - reset to 0 - -### IMPROVEMENTS: - -- [state] [\#2929](https://github.com/tendermint/tendermint/issues/2929) Minor refactor of updateState logic (@danil-lashin) -- [node] [\#2959](https://github.com/tendermint/tendermint/issues/2959) Allow node to start even if software's BlockProtocol is - different from state's BlockProtocol -- [pex] [\#2959](https://github.com/tendermint/tendermint/issues/2959) Pex reactor logger uses `module=pex` - -### BUG FIXES: - -- [p2p] [\#2968](https://github.com/tendermint/tendermint/issues/2968) Panic on transport error rather than continuing to run but not - accept new connections -- [p2p] [\#2969](https://github.com/tendermint/tendermint/issues/2969) Fix mismatch in peer count between `/net_info` and the prometheus - metrics -- [rpc] [\#2408](https://github.com/tendermint/tendermint/issues/2408) `/broadcast_tx_commit`: Fix "interface conversion: interface {} in nil, not EventDataTx" panic (could happen if somebody sent a tx using `/broadcast_tx_commit` while Tendermint was being stopped) -- [state] [\#2785](https://github.com/tendermint/tendermint/issues/2785) Fix accum for new validators to be `-1.125*totalVotingPower` - instead of 0, forcing them to wait before becoming the proposer. Also: - - do not batch clip - - keep accums averaged near 0 -- [txindex/kv] [\#2925](https://github.com/tendermint/tendermint/issues/2925) Don't return false positives when range searching for a prefix of a tag value -- [types] [\#2938](https://github.com/tendermint/tendermint/issues/2938) Fix regression in v0.26.4 where we panic on empty - genDoc.Validators -- [types] [\#2941](https://github.com/tendermint/tendermint/issues/2941) Preserve val.Accum during ValidatorSet.Update to avoid it being - reset to 0 every time a validator is updated - -## v0.26.4 - -*November 27th, 2018* - -Special thanks to external contributors on this release: -@ackratos, @goolAdapter, @james-ray, @joe-bowman, @kostko, -@nagarajmanjunath, @tomtau - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### FEATURES: - -- [rpc] [\#2747](https://github.com/tendermint/tendermint/issues/2747) Enable subscription to tags emitted from `BeginBlock`/`EndBlock` (@kostko) -- [types] [\#2747](https://github.com/tendermint/tendermint/issues/2747) Add `ResultBeginBlock` and `ResultEndBlock` fields to `EventDataNewBlock` - and `EventDataNewBlockHeader` to support subscriptions (@kostko) -- [types] [\#2918](https://github.com/tendermint/tendermint/issues/2918) Add Marshal, MarshalTo, Unmarshal methods to various structs - to support Protobuf compatibility (@nagarajmanjunath) - -### IMPROVEMENTS: - -- [config] [\#2877](https://github.com/tendermint/tendermint/issues/2877) Add `blocktime_iota` to the config.toml (@ackratos) - - NOTE: this should be a ConsensusParam, not part of the config, and will be - removed from the config at a later date - ([\#2920](https://github.com/tendermint/tendermint/issues/2920). -- [mempool] [\#2882](https://github.com/tendermint/tendermint/issues/2882) Add txs from Update to cache -- [mempool] [\#2891](https://github.com/tendermint/tendermint/issues/2891) Remove local int64 counter from being stored in every tx -- [node] [\#2866](https://github.com/tendermint/tendermint/issues/2866) Add ability to instantiate IPCVal (@joe-bowman) - -### BUG FIXES: - -- [blockchain] [\#2731](https://github.com/tendermint/tendermint/issues/2731) Retry both blocks if either is bad to avoid getting stuck during fast sync (@goolAdapter) -- [consensus] [\#2893](https://github.com/tendermint/tendermint/issues/2893) Use genDoc.Validators instead of state.NextValidators on replay when appHeight==0 (@james-ray) -- [log] [\#2868](https://github.com/tendermint/tendermint/issues/2868) Fix `module=main` setting overriding all others - - NOTE: this changes the default logging behaviour to be much less verbose. - Set `log_level="info"` to restore the previous behaviour. -- [rpc] [\#2808](https://github.com/tendermint/tendermint/issues/2808) Fix `accum` field in `/validators` by calling `IncrementAccum` if necessary -- [rpc] [\#2811](https://github.com/tendermint/tendermint/issues/2811) Allow integer IDs in JSON-RPC requests (@tomtau) -- [txindex/kv] [\#2759](https://github.com/tendermint/tendermint/issues/2759) Fix tx.height range queries -- [txindex/kv] [\#2775](https://github.com/tendermint/tendermint/issues/2775) Order tx results by index if height is the same -- [txindex/kv] [\#2908](https://github.com/tendermint/tendermint/issues/2908) Don't return false positives when searching for a prefix of a tag value - -## v0.26.3 - -*November 17th, 2018* - -Special thanks to external contributors on this release: -@danil-lashin, @kevlubkcm, @krhubert, @srmo - -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). - -### BREAKING CHANGES: - -* Go API - - [rpc] [\#2791](https://github.com/tendermint/tendermint/issues/2791) Functions that start HTTP servers are now blocking: - - Impacts `StartHTTPServer`, `StartHTTPAndTLSServer`, and `StartGRPCServer` - - These functions now take a `net.Listener` instead of an address - - [rpc] [\#2767](https://github.com/tendermint/tendermint/issues/2767) Subscribing to events - `NewRound` and `CompleteProposal` return new types `EventDataNewRound` and - `EventDataCompleteProposal`, respectively, instead of the generic `EventDataRoundState`. (@kevlubkcm) - -### FEATURES: - -- [log] [\#2843](https://github.com/tendermint/tendermint/issues/2843) New `log_format` config option, which can be set to 'plain' for colored - text or 'json' for JSON output -- [types] [\#2767](https://github.com/tendermint/tendermint/issues/2767) New event types EventDataNewRound (with ProposerInfo) and EventDataCompleteProposal (with BlockID). (@kevlubkcm) - -### IMPROVEMENTS: - -- [dep] [\#2844](https://github.com/tendermint/tendermint/issues/2844) Dependencies are no longer pinned to an exact version in the - Gopkg.toml: - - Serialization libs are allowed to vary by patch release - - Other libs are allowed to vary by minor release -- [p2p] [\#2857](https://github.com/tendermint/tendermint/issues/2857) "Send failed" is logged at debug level instead of error. -- [rpc] [\#2780](https://github.com/tendermint/tendermint/issues/2780) Add read and write timeouts to HTTP servers -- [state] [\#2848](https://github.com/tendermint/tendermint/issues/2848) Make "Update to validators" msg value pretty (@danil-lashin) - -### BUG FIXES: -- [consensus] [\#2819](https://github.com/tendermint/tendermint/issues/2819) Don't send proposalHearbeat if not a validator -- [docs] [\#2859](https://github.com/tendermint/tendermint/issues/2859) Fix ConsensusParams details in spec -- [libs/autofile] [\#2760](https://github.com/tendermint/tendermint/issues/2760) Comment out autofile permissions check - should fix - running Tendermint on Windows -- [p2p] [\#2869](https://github.com/tendermint/tendermint/issues/2869) Set connection config properly instead of always using default -- [p2p/pex] [\#2802](https://github.com/tendermint/tendermint/issues/2802) Seed mode fixes: - - Only disconnect from inbound peers - - Use FlushStop instead of Sleep to ensure all messages are sent before - disconnecting - -## v0.26.2 - -*November 15th, 2018* - -Special thanks to external contributors on this release: @hleb-albau, @zhuzeyu - -Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). - -### FEATURES: - -- [rpc] [\#2582](https://github.com/tendermint/tendermint/issues/2582) Enable CORS on RPC API (@hleb-albau) - -### BUG FIXES: - -- [abci] [\#2748](https://github.com/tendermint/tendermint/issues/2748) Unlock mutex in localClient so even when app panics (e.g. during CheckTx), consensus continue working -- [abci] [\#2748](https://github.com/tendermint/tendermint/issues/2748) Fix DATA RACE in localClient -- [amino] [\#2822](https://github.com/tendermint/tendermint/issues/2822) Update to v0.14.1 to support compiling on 32-bit platforms -- [rpc] [\#2748](https://github.com/tendermint/tendermint/issues/2748) Drain channel before calling Unsubscribe(All) in `/broadcast_tx_commit` - -## v0.26.1 - -*November 11, 2018* - -Special thanks to external contributors on this release: @katakonst - -Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). - -### IMPROVEMENTS: - -- [consensus] [\#2704](https://github.com/tendermint/tendermint/issues/2704) Simplify valid POL round logic -- [docs] [\#2749](https://github.com/tendermint/tendermint/issues/2749) Deduplicate some ABCI docs -- [mempool] More detailed log messages - - [\#2724](https://github.com/tendermint/tendermint/issues/2724) - - [\#2762](https://github.com/tendermint/tendermint/issues/2762) - -### BUG FIXES: - -- [autofile] [\#2703](https://github.com/tendermint/tendermint/issues/2703) Do not panic when checking Head size -- [crypto/merkle] [\#2756](https://github.com/tendermint/tendermint/issues/2756) Fix crypto/merkle ProofOperators.Verify to check bounds on keypath parts. -- [mempool] fix a bug where we create a WAL despite `wal_dir` being empty -- [p2p] [\#2771](https://github.com/tendermint/tendermint/issues/2771) Fix `peer-id` label name to `peer_id` in prometheus metrics -- [p2p] [\#2797](https://github.com/tendermint/tendermint/pull/2797) Fix IDs in peer NodeInfo and require them for addresses - in AddressBook -- [p2p] [\#2797](https://github.com/tendermint/tendermint/pull/2797) Do not close conn immediately after sending pex addrs in seed mode. Partial fix for [\#2092](https://github.com/tendermint/tendermint/issues/2092). - -## v0.26.0 - -*November 2, 2018* - -Special thanks to external contributors on this release: -@bradyjoestar, @connorwstein, @goolAdapter, @HaoyangLiu, -@james-ray, @overbool, @phymbert, @Slamper, @Uzair1995, @yutianwu. - -Special thanks to @Slamper for a series of bug reports in our [bug bounty -program](https://hackerone.com/tendermint) which are fixed in this release. - -This release is primarily about adding Version fields to various data structures, -optimizing consensus messages for signing and verification in -restricted environments (like HSMs and the Ethereum Virtual Machine), and -aligning the consensus code with the [specification](https://arxiv.org/abs/1807.04938). -It also includes our first take at a generalized merkle proof system, and -changes the length of hashes used for hashing data structures from 20 to 32 -bytes. - -See the [UPGRADING.md](UPGRADING.md#v0.26.0) for details on upgrading to the new -version. - -Please note that we are still making breaking changes to the protocols. -While the new Version fields should help us to keep the software backwards compatible -even while upgrading the protocols, we cannot guarantee that new releases will -be compatible with old chains just yet. We expect there will be another breaking -release or two before the Cosmos Hub launch, but we will otherwise be paying -increasing attention to backwards compatibility. Thanks for bearing with us! - -### BREAKING CHANGES: - -* CLI/RPC/Config - * [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) Timeouts are now strings like "3s" and "100ms", not ints - * [config] [\#2505](https://github.com/tendermint/tendermint/issues/2505) Remove Mempool.RecheckEmpty (it was effectively useless anyways) - * [config] [\#2490](https://github.com/tendermint/tendermint/issues/2490) `mempool.wal` is disabled by default - * [privval] [\#2459](https://github.com/tendermint/tendermint/issues/2459) Split `SocketPVMsg`s implementations into Request and Response, where the Response may contain a error message (returned by the remote signer) - * [state] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Add Version field to State, breaking the format of State as - encoded on disk. - * [rpc] [\#2298](https://github.com/tendermint/tendermint/issues/2298) `/abci_query` takes `prove` argument instead of `trusted` and switches the default - behaviour to `prove=false` - * [rpc] [\#2654](https://github.com/tendermint/tendermint/issues/2654) Remove all `node_info.other.*_version` fields in `/status` and - `/net_info` - * [rpc] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Remove - `_params` suffix from fields in `consensus_params`. - -* Apps - * [abci] [\#2298](https://github.com/tendermint/tendermint/issues/2298) ResponseQuery.Proof is now a structured merkle.Proof, not just - arbitrary bytes - * [abci] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Add Version to Header and shift all fields by one - * [abci] [\#2662](https://github.com/tendermint/tendermint/issues/2662) Bump the field numbers for some `ResponseInfo` fields to make room for - `AppVersion` - * [abci] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Updates to ConsensusParams - * Remove `Params` suffix from field names - * Add `Params` suffix to message types - * Add new field and type, `Validator ValidatorParams`, to control what types of validator keys are allowed. - -* Go API - * [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) Timeouts are time.Duration, not ints - * [crypto/merkle & lite] [\#2298](https://github.com/tendermint/tendermint/issues/2298) Various changes to accomodate General Merkle trees - * [crypto/merkle] [\#2595](https://github.com/tendermint/tendermint/issues/2595) Remove all Hasher objects in favor of byte slices - * [crypto/merkle] [\#2635](https://github.com/tendermint/tendermint/issues/2635) merkle.SimpleHashFromTwoHashes is no longer exported - * [node] [\#2479](https://github.com/tendermint/tendermint/issues/2479) Remove node.RunForever - * [rpc/client] [\#2298](https://github.com/tendermint/tendermint/issues/2298) `ABCIQueryOptions.Trusted` -> `ABCIQueryOptions.Prove` - * [types] [\#2298](https://github.com/tendermint/tendermint/issues/2298) Remove `Index` and `Total` fields from `TxProof`. - * [types] [\#2598](https://github.com/tendermint/tendermint/issues/2598) - `VoteTypeXxx` are now of type `SignedMsgType byte` and named `XxxType`, eg. - `PrevoteType`, `PrecommitType`. - * [types] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Rename fields in ConsensusParams to remove `Params` suffixes - * [types] [\#2735](https://github.com/tendermint/tendermint/issues/2735) Simplify Proposal message to align with spec - -* Blockchain Protocol - * [crypto/tmhash] [\#2732](https://github.com/tendermint/tendermint/issues/2732) TMHASH is now full 32-byte SHA256 - * All hashes in the block header and Merkle trees are now 32-bytes - * PubKey Addresses are still only 20-bytes - * [state] [\#2587](https://github.com/tendermint/tendermint/issues/2587) Require block.Time of the fist block to be genesis time - * [state] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Require block.Version to match state.Version - * [types] Update SignBytes for `Vote`/`Proposal`/`Heartbeat`: - * [\#2459](https://github.com/tendermint/tendermint/issues/2459) Use amino encoding instead of JSON in `SignBytes`. - * [\#2598](https://github.com/tendermint/tendermint/issues/2598) Reorder fields and use fixed sized encoding. - * [\#2598](https://github.com/tendermint/tendermint/issues/2598) Change `Type` field from `string` to `byte` and use new - `SignedMsgType` to enumerate. - * [types] [\#2730](https://github.com/tendermint/tendermint/issues/2730) Use - same order for fields in `Vote` as in the SignBytes - * [types] [\#2732](https://github.com/tendermint/tendermint/issues/2732) Remove the address field from the validator hash - * [types] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Add Version struct to Header - * [types] [\#2609](https://github.com/tendermint/tendermint/issues/2609) ConsensusParams.Hash() is the hash of the amino encoded - struct instead of the Merkle tree of the fields - * [types] [\#2670](https://github.com/tendermint/tendermint/issues/2670) Header.Hash() builds Merkle tree out of fields in the same - order they appear in the header, instead of sorting by field name - * [types] [\#2682](https://github.com/tendermint/tendermint/issues/2682) Use proto3 `varint` encoding for ints that are usually unsigned (instead of zigzag encoding). - * [types] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Add Validator field to ConsensusParams - (Used to control which pubkey types validators can use, by abci type). - -* P2P Protocol - * [consensus] [\#2652](https://github.com/tendermint/tendermint/issues/2652) - Replace `CommitStepMessage` with `NewValidBlockMessage` - * [consensus] [\#2735](https://github.com/tendermint/tendermint/issues/2735) Simplify `Proposal` message to align with spec - * [consensus] [\#2730](https://github.com/tendermint/tendermint/issues/2730) - Add `Type` field to `Proposal` and use same order of fields as in the - SignBytes for both `Proposal` and `Vote` - * [p2p] [\#2654](https://github.com/tendermint/tendermint/issues/2654) Add `ProtocolVersion` struct with protocol versions to top of - DefaultNodeInfo and require `ProtocolVersion.Block` to match during peer handshake - - -### FEATURES: -- [abci] [\#2557](https://github.com/tendermint/tendermint/issues/2557) Add `Codespace` field to `Response{CheckTx, DeliverTx, Query}` -- [abci] [\#2662](https://github.com/tendermint/tendermint/issues/2662) Add `BlockVersion` and `P2PVersion` to `RequestInfo` -- [crypto/merkle] [\#2298](https://github.com/tendermint/tendermint/issues/2298) General Merkle Proof scheme for chaining various types of Merkle trees together -- [docs/architecture] [\#1181](https://github.com/tendermint/tendermint/issues/1181) S -plit immutable and mutable parts of priv_validator.json - -### IMPROVEMENTS: -- Additional Metrics - - [consensus] [\#2169](https://github.com/cosmos/cosmos-sdk/issues/2169) - - [p2p] [\#2169](https://github.com/cosmos/cosmos-sdk/issues/2169) -- [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) Added ValidateBasic method, which performs basic checks -- [crypto/ed25519] [\#2558](https://github.com/tendermint/tendermint/issues/2558) Switch to use latest `golang.org/x/crypto` through our fork at - github.com/tendermint/crypto -- [libs/log] [\#2707](https://github.com/tendermint/tendermint/issues/2707) Add year to log format (@yutianwu) -- [tools] [\#2238](https://github.com/tendermint/tendermint/issues/2238) Binary dependencies are now locked to a specific git commit - -### BUG FIXES: -- [\#2711](https://github.com/tendermint/tendermint/issues/2711) Validate all incoming reactor messages. Fixes various bugs due to negative ints. -- [autofile] [\#2428](https://github.com/tendermint/tendermint/issues/2428) Group.RotateFile need call Flush() before rename (@goolAdapter) -- [common] [\#2533](https://github.com/tendermint/tendermint/issues/2533) Fixed a bug in the `BitArray.Or` method -- [common] [\#2506](https://github.com/tendermint/tendermint/issues/2506) Fixed a bug in the `BitArray.Sub` method (@james-ray) -- [common] [\#2534](https://github.com/tendermint/tendermint/issues/2534) Fix `BitArray.PickRandom` to choose uniformly from true bits -- [consensus] [\#1690](https://github.com/tendermint/tendermint/issues/1690) Wait for - timeoutPrecommit before starting next round -- [consensus] [\#1745](https://github.com/tendermint/tendermint/issues/1745) Wait for - Proposal or timeoutProposal before entering prevote -- [consensus] [\#2642](https://github.com/tendermint/tendermint/issues/2642) Only propose ValidBlock, not LockedBlock -- [consensus] [\#2642](https://github.com/tendermint/tendermint/issues/2642) Initialized ValidRound and LockedRound to -1 -- [consensus] [\#1637](https://github.com/tendermint/tendermint/issues/1637) Limit the amount of evidence that can be included in a - block -- [consensus] [\#2652](https://github.com/tendermint/tendermint/issues/2652) Ensure valid block property with faulty proposer -- [evidence] [\#2515](https://github.com/tendermint/tendermint/issues/2515) Fix db iter leak (@goolAdapter) -- [libs/event] [\#2518](https://github.com/tendermint/tendermint/issues/2518) Fix event concurrency flaw (@goolAdapter) -- [node] [\#2434](https://github.com/tendermint/tendermint/issues/2434) Make node respond to signal interrupts while sleeping for genesis time -- [state] [\#2616](https://github.com/tendermint/tendermint/issues/2616) Pass nil to NewValidatorSet() when genesis file's Validators field is nil -- [p2p] [\#2555](https://github.com/tendermint/tendermint/issues/2555) Fix p2p switch FlushThrottle value (@goolAdapter) -- [p2p] [\#2668](https://github.com/tendermint/tendermint/issues/2668) Reconnect to originally dialed address (not self-reported address) for persistent peers - -## v0.25.0 - -*September 22, 2018* - -Special thanks to external contributors on this release: -@scriptionist, @bradyjoestar, @WALL-E - -This release is mostly about the ConsensusParams - removing fields and enforcing MaxGas. -It also addresses some issues found via security audit, removes various unused -functions from `libs/common`, and implements -[ADR-012](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-012-peer-transport.md). - -Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). - -BREAKING CHANGES: - -* CLI/RPC/Config - * [rpc] [\#2391](https://github.com/tendermint/tendermint/issues/2391) /status `result.node_info.other` became a map - * [types] [\#2364](https://github.com/tendermint/tendermint/issues/2364) Remove `TxSize` and `BlockGossip` from `ConsensusParams` - * Maximum tx size is now set implicitly via the `BlockSize.MaxBytes` - * The size of block parts in the consensus is now fixed to 64kB - -* Apps - * [mempool] [\#2360](https://github.com/tendermint/tendermint/issues/2360) Mempool tracks the `ResponseCheckTx.GasWanted` and - `ConsensusParams.BlockSize.MaxGas` and enforces: - - `GasWanted <= MaxGas` for every tx - - `(sum of GasWanted in block) <= MaxGas` for block proposal - -* Go API - * [libs/common] [\#2431](https://github.com/tendermint/tendermint/issues/2431) Remove Word256 due to lack of use - * [libs/common] [\#2452](https://github.com/tendermint/tendermint/issues/2452) Remove the following functions due to lack of use: - * byteslice.go: cmn.IsZeros, cmn.RightPadBytes, cmn.LeftPadBytes, cmn.PrefixEndBytes - * strings.go: cmn.IsHex, cmn.StripHex - * int.go: Uint64Slice, all put/get int64 methods - -FEATURES: -- [rpc] [\#2415](https://github.com/tendermint/tendermint/issues/2415) New `/consensus_params?height=X` endpoint to query the consensus - params at any height (@scriptonist) -- [types] [\#1714](https://github.com/tendermint/tendermint/issues/1714) Add Address to GenesisValidator -- [metrics] [\#2337](https://github.com/tendermint/tendermint/issues/2337) `consensus.block_interval_metrics` is now gauge, not histogram (you will be able to see spikes, if any) -- [libs] [\#2286](https://github.com/tendermint/tendermint/issues/2286) Panic if `autofile` or `db/fsdb` permissions change from 0600. - -IMPROVEMENTS: -- [libs/db] [\#2371](https://github.com/tendermint/tendermint/issues/2371) Output error instead of panic when the given `db_backend` is not initialised (@bradyjoestar) -- [mempool] [\#2399](https://github.com/tendermint/tendermint/issues/2399) Make mempool cache a proper LRU (@bradyjoestar) -- [p2p] [\#2126](https://github.com/tendermint/tendermint/issues/2126) Introduce PeerTransport interface to improve isolation of concerns -- [libs/common] [\#2326](https://github.com/tendermint/tendermint/issues/2326) Service returns ErrNotStarted - -BUG FIXES: -- [node] [\#2294](https://github.com/tendermint/tendermint/issues/2294) Delay starting node until Genesis time -- [consensus] [\#2048](https://github.com/tendermint/tendermint/issues/2048) Correct peer statistics for marking peer as good -- [rpc] [\#2460](https://github.com/tendermint/tendermint/issues/2460) StartHTTPAndTLSServer() now passes StartTLS() errors back to the caller rather than hanging forever. -- [p2p] [\#2047](https://github.com/tendermint/tendermint/issues/2047) Accept new connections asynchronously -- [tm-bench] [\#2410](https://github.com/tendermint/tendermint/issues/2410) Enforce minimum transaction size (@WALL-E) - -## 0.24.0 - -*September 6th, 2018* - -Special thanks to external contributors with PRs included in this release: ackratos, james-ray, bradyjoestar, -peerlink, Ahmah2009, bluele, b00f. - -This release includes breaking upgrades in the block header, -including the long awaited changes for delaying validator set updates by one -block to better support light clients. -It also fixes enforcement on the maximum size of blocks, and includes a BFT -timestamp in each block that can be safely used by applications. -There are also some minor breaking changes to the rpc, config, and ABCI. - -See the [UPGRADING.md](UPGRADING.md#v0.24.0) for details on upgrading to the new -version. - -From here on, breaking changes will be broken down to better reflect how users -are affected by a change. - -A few more breaking changes are in the works - each will come with a clear -Architecture Decision Record (ADR) explaining the change. You can review ADRs -[here](https://github.com/tendermint/tendermint/tree/develop/docs/architecture) -or in the [open Pull Requests](https://github.com/tendermint/tendermint/pulls). -You can also check in on the [issues marked as -breaking](https://github.com/tendermint/tendermint/issues?q=is%3Aopen+is%3Aissue+label%3Abreaking). - -BREAKING CHANGES: - -* CLI/RPC/Config - - [config] [\#2169](https://github.com/tendermint/tendermint/issues/2169) Replace MaxNumPeers with MaxNumInboundPeers and MaxNumOutboundPeers - - [config] [\#2300](https://github.com/tendermint/tendermint/issues/2300) Reduce default mempool size from 100k to 5k, until ABCI rechecking is implemented. - - [rpc] [\#1815](https://github.com/tendermint/tendermint/issues/1815) `/commit` returns a `signed_header` field instead of everything being top-level - -* Apps - - [abci] Added address of the original proposer of the block to Header - - [abci] Change ABCI Header to match Tendermint exactly - - [abci] [\#2159](https://github.com/tendermint/tendermint/issues/2159) Update use of `Validator` (see - [ADR-018](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-018-ABCI-Validators.md)): - - Remove PubKey from `Validator` (so it's just Address and Power) - - Introduce `ValidatorUpdate` (with just PubKey and Power) - - InitChain and EndBlock use ValidatorUpdate - - Update field names and types in BeginBlock - - [state] [\#1815](https://github.com/tendermint/tendermint/issues/1815) Validator set changes are now delayed by one block - - updates returned in ResponseEndBlock for block H will be included in RequestBeginBlock for block H+2 - -* Go API - - [lite] [\#1815](https://github.com/tendermint/tendermint/issues/1815) Complete refactor of the package - - [node] [\#2212](https://github.com/tendermint/tendermint/issues/2212) NewNode now accepts a `*p2p.NodeKey` (@bradyjoestar) - - [libs/common] [\#2199](https://github.com/tendermint/tendermint/issues/2199) Remove Fmt, in favor of fmt.Sprintf - - [libs/common] SplitAndTrim was deleted - - [libs/common] [\#2274](https://github.com/tendermint/tendermint/issues/2274) Remove unused Math functions like MaxInt, MaxInt64, - MinInt, MinInt64 (@Ahmah2009) - - [libs/clist] Panics if list extends beyond MaxLength - - [crypto] [\#2205](https://github.com/tendermint/tendermint/issues/2205) Rename AminoRoute variables to no longer be prefixed by signature type. - -* Blockchain Protocol - - [state] [\#1815](https://github.com/tendermint/tendermint/issues/1815) Validator set changes are now delayed by one block (!) - - Add NextValidatorSet to State, changes on-disk representation of state - - [state] [\#2184](https://github.com/tendermint/tendermint/issues/2184) Enforce ConsensusParams.BlockSize.MaxBytes (See - [ADR-020](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-020-block-size.md)). - - Remove ConsensusParams.BlockSize.MaxTxs - - Introduce maximum sizes for all components of a block, including ChainID - - [types] Updates to the block Header: - - [\#1815](https://github.com/tendermint/tendermint/issues/1815) NextValidatorsHash - hash of the validator set for the next block, - so the current validators actually sign over the hash for the new - validators - - [\#2106](https://github.com/tendermint/tendermint/issues/2106) ProposerAddress - address of the block's original proposer - - [consensus] [\#2203](https://github.com/tendermint/tendermint/issues/2203) Implement BFT time - - Timestamp in block must be monotonic and equal the median of timestamps in block's LastCommit - - [crypto] [\#2239](https://github.com/tendermint/tendermint/issues/2239) Secp256k1 signature changes (See - [ADR-014](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-014-secp-malleability.md)): - - format changed from DER to `r || s`, both little endian encoded as 32 bytes. - - malleability removed by requiring `s` to be in canonical form. - -* P2P Protocol - - [p2p] [\#2263](https://github.com/tendermint/tendermint/issues/2263) Update secret connection to use a little endian encoded nonce - - [blockchain] [\#2213](https://github.com/tendermint/tendermint/issues/2213) Fix Amino routes for blockchain reactor messages - (@peerlink) - - -FEATURES: -- [types] [\#2015](https://github.com/tendermint/tendermint/issues/2015) Allow genesis file to have 0 validators (@b00f) - - Initial validator set can be determined by the app in ResponseInitChain -- [rpc] [\#2161](https://github.com/tendermint/tendermint/issues/2161) New event `ValidatorSetUpdates` for when the validator set changes -- [crypto/multisig] [\#2164](https://github.com/tendermint/tendermint/issues/2164) Introduce multisig pubkey and signature format -- [libs/db] [\#2293](https://github.com/tendermint/tendermint/issues/2293) Allow passing options through when creating instances of leveldb dbs - -IMPROVEMENTS: -- [docs] Lint documentation with `write-good` and `stop-words`. -- [docs] [\#2249](https://github.com/tendermint/tendermint/issues/2249) Refactor, deduplicate, and improve the ABCI docs and spec (with thanks to @ttmc). -- [scripts] [\#2196](https://github.com/tendermint/tendermint/issues/2196) Added json2wal tool, which is supposed to help our users restore (@bradyjoestar) - corrupted WAL files and compose test WAL files (@bradyjoestar) -- [mempool] [\#2234](https://github.com/tendermint/tendermint/issues/2234) Now stores txs by hash inside of the cache, to mitigate memory leakage -- [mempool] [\#2166](https://github.com/tendermint/tendermint/issues/2166) Set explicit capacity for map when updating txs (@bluele) - -BUG FIXES: -- [config] [\#2284](https://github.com/tendermint/tendermint/issues/2284) Replace `db_path` with `db_dir` from automatically generated configuration files. -- [mempool] [\#2188](https://github.com/tendermint/tendermint/issues/2188) Fix OOM issue from cache map and list getting out of sync -- [state] [\#2051](https://github.com/tendermint/tendermint/issues/2051) KV store index supports searching by `tx.height` (@ackratos) -- [rpc] [\#2327](https://github.com/tendermint/tendermint/issues/2327) `/dial_peers` does not try to dial existing peers -- [node] [\#2323](https://github.com/tendermint/tendermint/issues/2323) Filter empty strings from config lists (@james-ray) -- [abci/client] [\#2236](https://github.com/tendermint/tendermint/issues/2236) Fix closing GRPC connection (@bradyjoestar) - -## 0.23.1 - -*August 22nd, 2018* - -BUG FIXES: -- [libs/autofile] [\#2261](https://github.com/tendermint/tendermint/issues/2261) Fix log rotation so it actually happens. - - Fixes issues with consensus WAL growing unbounded ala [\#2259](https://github.com/tendermint/tendermint/issues/2259) - -## 0.23.0 - -*August 5th, 2018* - -This release includes breaking upgrades in our P2P encryption, -some ABCI messages, and how we encode time and signatures. - -A few more changes are still coming to the Header, ABCI, -and validator set handling to better support light clients, BFT time, and -upgrades. Most notably, validator set changes will be delayed by one block (see -[#1815][i1815]). - -We also removed `make ensure_deps` in favour of `make get_vendor_deps`. - -BREAKING CHANGES: -- [abci] Changed time format from int64 to google.protobuf.Timestamp -- [abci] Changed Validators to LastCommitInfo in RequestBeginBlock -- [abci] Removed Fee from ResponseDeliverTx and ResponseCheckTx -- [crypto] Switch crypto.Signature from interface to []byte for space efficiency - [#2128](https://github.com/tendermint/tendermint/pull/2128) - - NOTE: this means signatures no longer have the prefix bytes in Amino - binary nor the `type` field in Amino JSON. They're just bytes. -- [p2p] Remove salsa and ripemd primitives, in favor of using chacha as a stream cipher, and hkdf [#2054](https://github.com/tendermint/tendermint/pull/2054) -- [tools] Removed `make ensure_deps` in favor of `make get_vendor_deps` -- [types] CanonicalTime uses nanoseconds instead of clipping to ms - - breaks serialization/signing of all messages with a timestamp - -FEATURES: -- [tools] Added `make check_dep` - - ensures gopkg.lock is synced with gopkg.toml - - ensures no branches are used in the gopkg.toml - -IMPROVEMENTS: -- [blockchain] Improve fast-sync logic - [#1805](https://github.com/tendermint/tendermint/pull/1805) - - tweak params - - only process one block at a time to avoid starving -- [common] bit array functions which take in another parameter are now thread safe -- [crypto] Switch hkdfchachapoly1305 to xchachapoly1305 -- [p2p] begin connecting to peers as soon a seed node provides them to you ([#2093](https://github.com/tendermint/tendermint/issues/2093)) - -BUG FIXES: -- [common] Safely handle cases where atomic write files already exist [#2109](https://github.com/tendermint/tendermint/issues/2109) -- [privval] fix a deadline for accepting new connections in socket private - validator. -- [p2p] Allow startup if a configured seed node's IP can't be resolved ([#1716](https://github.com/tendermint/tendermint/issues/1716)) -- [node] Fully exit when CTRL-C is pressed even if consensus state panics [#2072](https://github.com/tendermint/tendermint/issues/2072) - -[i1815]: https://github.com/tendermint/tendermint/pull/1815 - -## 0.22.8 - -*July 26th, 2018* - -BUG FIXES - -- [consensus, blockchain] Fix 0.22.7 below. - -## 0.22.7 +### Blockchain/store + +- Comment about panics + +### Blockchain/v0 + +- Stop tickers on poolRoutine exit (#5860) + +### Blockchain/v1 + +- Add noBlockResponse handling (#5401) +- Omit incoming message bytes from log + +### Blockchain/v2 + +- Allow setting nil switch, for CustomReactors() +- Don't broadcast base if height is 0 +- Fix excessive CPU usage due to spinning on closed channels (#4761) +- Respect fast_sync option (#4772) +- Integrate with state sync +- Correctly set block store base in status responses (#4971) +- Fix "panic: duplicate block enqueued by processor" (#5499) +- Fix panic: processed height X+1 but expected height X (#5530) +- Make the removal of an already removed peer a noop (#5553) +- Remove peers from the processor (#5607) +- Fix missing mutex unlock (#5862) + +### Blockchain[v1] + +- Increased timeout times for peer tests (#4871) + +### Blockpool + +- Fix removePeer bug + +### Blockstore + +- Allow initial SaveBlock() at any height +- Fix race conditions when loading data (#5382) + +### Build + +- Bump github.com/tendermint/tm-db from 0.1.1 to 0.2.0 (#4001) +- Bump github.com/gogo/protobuf from 1.3.0 to 1.3.1 (#4055) +- Bump github.com/spf13/viper from 1.4.0 to 1.5.0 (#4102) +- Bump github.com/spf13/viper from 1.5.0 to 1.6.1 (#4224) +- Bump github.com/pkg/errors from 0.9.0 to 0.9.1 (#4310) +- Bump google.golang.org/grpc from 1.26.0 to 1.27.0 (#4355) +- Bump github.com/stretchr/testify from 1.5.0 to 1.5.1 (#4441) +- Bump github.com/spf13/cobra from 0.0.3 to 0.0.6 (#4440) +- Bump github.com/golang/protobuf from 1.3.3 to 1.3.4 (#4485) +- Bump github.com/prometheus/client_golang (#4525) +- Bump github.com/Workiva/go-datastructures (#4545) +- Bump google.golang.org/grpc from 1.27.1 to 1.28.0 (#4551) +- Bump github.com/tendermint/tm-db from 0.4.1 to 0.5.0 (#4554) +- Bump github.com/golang/protobuf from 1.3.4 to 1.3.5 (#4563) +- Bump github.com/prometheus/client_golang (#4574) +- Bump github.com/gorilla/websocket from 1.4.1 to 1.4.2 (#4584) +- Bump github.com/spf13/cobra from 0.0.6 to 0.0.7 (#4612) +- Bump github.com/tendermint/tm-db from 0.5.0 to 0.5.1 (#4613) +- Bump google.golang.org/grpc from 1.28.0 to 1.28.1 (#4653) +- Bump github.com/spf13/viper from 1.6.2 to 1.6.3 (#4664) +- Bump @vuepress/plugin-google-analytics in /docs (#4692) +- Bump google.golang.org/grpc from 1.29.0 to 1.29.1 (#4735) +- Manually bump github.com/prometheus/client_golang from 1.5.1 to 1.6.0 (#4758) +- Bump github.com/golang/protobuf from 1.4.0 to 1.4.1 (#4794) +- Bump vuepress-theme-cosmos from 1.0.163 to 1.0.164 in /docs (#4815) +- Bump github.com/spf13/viper from 1.6.3 to 1.7.0 (#4814) +- Bump github.com/golang/protobuf from 1.4.1 to 1.4.2 (#4849) +- Bump vuepress-theme-cosmos from 1.0.164 to 1.0.165 in /docs +- Bump github.com/stretchr/testify from 1.5.1 to 1.6.0 +- Bump github.com/stretchr/testify from 1.6.0 to 1.6.1 +- Bump google.golang.org/grpc from 1.29.1 to 1.30.0 +- Bump github.com/prometheus/client_golang +- Bump vuepress-theme-cosmos from 1.0.168 to 1.0.169 in /docs +- Bump google.golang.org/grpc from 1.30.0 to 1.31.0 +- Bump github.com/spf13/viper from 1.7.0 to 1.7.1 +- Bump golangci/golangci-lint-action from v2.1.0 to v2.2.0 (#5245) +- Bump actions/cache from v1 to v2.1.0 (#5244) +- Bump codecov/codecov-action from v1.0.7 to v1.0.12 (#5247) +- Bump technote-space/get-diff-action from v1 to v3 (#5246) +- Bump gaurav-nelson/github-action-markdown-link-check from 0.6.0 to 1.0.5 (#5248) +- Bump codecov/codecov-action from v1.0.12 to v1.0.13 (#5258) +- Bump gaurav-nelson/github-action-markdown-link-check from 1.0.5 to 1.0.6 (#5265) +- Bump gaurav-nelson/github-action-markdown-link-check from 1.0.6 to 1.0.7 (#5269) +- Bump actions/cache from v2.1.0 to v2.1.1 (#5268) +- Bump github.com/tendermint/tm-db from 0.6.1 to 0.6.2 (#5296) +- Bump google.golang.org/grpc from 1.31.1 to 1.32.0 (#5346) +- Bump github.com/minio/highwayhash from 1.0.0 to 1.0.1 (#5370) +- Bump vuepress-theme-cosmos from 1.0.172 to 1.0.173 in /docs (#5390) +- Bump actions/cache from v2.1.1 to v2.1.2 (#5487) +- Bump golangci/golangci-lint-action from v2.2.0 to v2.2.1 (#5486) +- Bump technote-space/get-diff-action from v3 to v4 (#5485) +- Bump google.golang.org/grpc from 1.32.0 to 1.33.1 (#5544) +- Bump github.com/tendermint/tm-db from 0.6.2 to 0.6.3 +- Refactor BLS library/bindings integration (#9) +- BLS scripts - Improve build.sh, Fix install.sh (#10) +- Dashify some files (#11) +- Fix docker image and docker.yml workflow (#12) +- Fix coverage.yml, bump go version, install BLS, drop an invalid character (#19) +- Fix test.yml, bump go version, install BLS, fix job names (#18) +- Bump gaurav-nelson/github-action-markdown-link-check (#22) +- Bump codecov/codecov-action from v1.0.13 to v1.0.15 (#23) +- Bump golangci/golangci-lint-action from v2.2.1 to v2.3.0 (#24) +- Bump rtCamp/action-slack-notify from e9db0ef to 2.1.1 (#25) +- Bump google.golang.org/grpc from 1.33.2 to 1.34.0 (#26) +- Bump vuepress-theme-cosmos from 1.0.173 to 1.0.177 in /docs (#27) +- Bump actions/cache from v2.1.3 to v2.1.4 (#6055) +- Bump google.golang.org/grpc from 1.36.1 to 1.37.0 (bp #6330) (#6335) + +### Certifiers + +- Test uses WaitForHeight + +### Changelog + +- Add prehistory +- Add genesis amount->power +- More review fixes/release/v0.31.0 (#3427) +- Add summary & fix link & add external contributor (#3490) +- Add v0.31.9 and v0.31.8 updates (#4034) +- Fix typo (#4106) +- Explain breaking changes better +- GotVoteFromUnwantedRoundError -> ErrGotVoteFromUnwantedRound +- Add 0.32.9 changelog to master (#4305) +- Add entries from secruity releases +- Update 0.33.6 (#5075) +- Note breaking change in the 0.33.6 release (#5077) +- Reorgranize (#5065) +- Move entries from pending (#5172) +- Bump to 0.34.0-rc2 (#5176) +- Add v0.33.7 release (#5203) +- Add v0.32.13 release (#5204) +- Update for 0.34.0-rc3 (#5240) +- Add v0.33.8 from release (#5242) +- Minor tweaks (#5389) +- Add missing date to v0.33.5 release, fix indentation (#5454) (#5455) +- Prepare changelog for RC5 (#5494) +- Squash changelog from 0.34 RCs into one (#5687) +- Update changelog for v0.34.1 (#5872) +- Prepare 0.34.2 release (#5894) +- Update for 0.34.3 (#5926) +- Update for v0.34.4 (#6096) +- Improve with suggestions from @melekes (#6097) +- Update for 0.34.5 (#6129) +- Bump to v0.34.6 +- Fix changelog pending version numbering (#6149) +- Update for 0.34.8 (#6181) +- Prepare changelog for 0.34.9 release (#6333) +- Update for 0.34.10 (#6357) + +### Ci + +- Setup abci in dependency step +- Move over abci-cli tests +- Reduce log output in test_cover (#2105) +- Transition some ci to github actions +- Only run when applicable (#4752) +- Check git diff on each job (#4770) +- Checkout code before git diff check (#4779) +- Add paths +- Bump the timeout for test_coverage (#4864) +- Migrate localnet to github actions (#4878) +- Add timeouts (#4912) +- Migrate test_cover (#4869) +- Fix spacing of if statement (#5015) +- Try to fix codecov (#5095) +- Only run tests when go files are touched (#5097) +- Version linter fix (#5128) +- Freeze golangci action version (#5196) +- Fix net pipeline (#5272) +- Delay codecov notification (#5275) +- Fix net run (#5343) +- Docker remove circleci and add github action (#5551) +- Add goreleaser (#5527) +- Tests (#5577) +- Remove `add-path` (#5674) +- Build for 32 bit, libs: fix overflow (#5700) +- Install BLS library in lint.yml and bump its go version (#15) +- E2e fixes - docker image, e2e.yml BLS library, default KeyType (#21) + +### Ci/e2e + +- Avoid running job when no go files are touched (#5471) + +### Circle + +- Docker 1.10.0 +- Add metalinter to test +- Fix config.yml +- Add GOCACHE=off and -v to tests +- Save p2p logs as artifacts (#2566) + +### Circleci + +- Add a job to automatically update docs (#3005) +- Update go version (#3051) +- Removed complexity from docs deployment job (#3396) +- Run P2P IPv4 and IPv6 tests in parallel (#4459) +- Fix reproducible builds test (#4497) +- Remove Gitian reproducible_builds job (#5462) + +### Cleanup + +- Replace common.Exit with log.Crit or log.Fatal + +### Cli + +- Testnet cmd inits files for testnet +- ResetAll doesnt depend on cobra +- Support --root and --home +- More descriptive naming +- Viper.Set(HomeFlag, rootDir) +- Clean up error handling +- Use cobra's new ExactArgs() feature +- WriteDemoConfig -> WriteConfigVals +- Add option to not clear address book with unsafe reset (#3606) +- Add `--cs.create_empty_blocks_interval` flag (#4205) +- Add `--db_backend` and `--db_dir` flags to tendermint node cmd (#4235) +- Add optional `--genesis_hash` flag to check genesis hash upon startup (#4238) +- Debug sub-command (#4227) +- Add command to generate shell completion scripts (#4665) +- Light home dir should default to where the full node default is (#5392) + +### Client + +- ResultsCh chan json.RawMessage, ErrorsCh +- Wsc.String() +- Safe error handling +- Use vars for retry intervals + +### Clist + +- Reduce numTimes in test +- Speedup functions (#2208) +- Speedup Next by removing defers (#2511) + +### Cmap + +- Remove defers (#2210) + +### Cmd + +- Fixes for new config +- Query params are flags +- --consensus.no_empty_blocks +- Don't load config for version command. closes #620 +- Dont wait for genesis. closes #562 +- Make sure to have 'testnet' create the data directory for nonvals (#3409) +- Show useful error when tm not initialised (#4512) +- Fix debug kill and change debug dump archive filename format (#4517) +- Hyphen-case cli v0.34.1 (#5786) + +### Cmd/abci-cli + +- Use a single connection per session +- Implement batch + +### Cmd/debug + +- Execute p.Signal only when p is not nil (#4271) + +### Cmd/lite + +- Switch to new lite2 package (#4300) + +### Cmd/tendermint + +- Fix initialization file creation checks (#991) + +### Cmd/tendermint/commands + +- Update ParseConfig doc + +### Cmd/tendermint/commands/lite + +- Add tcp scheme to address URLs (#1297) + +### Cmn + +- Kill +- Fix race condition in prng +- Fix repeate timer test with manual ticker +- Fix race +- Fix HexBytes.MarshalJSON +- GetFreePort (#3255) + +### Commands + +- Run -> RunE + +### Common + +- ProtocolAndAddress +- Fingerprint comment +- WriteFileAtomic use tempfile in current dir +- Comments for Service +- No more relying on math/rand.DefaultSource +- Use names prng and mrand +- Use genius simplification of tests from @ebuchman +- Rand* warnings about cryptographic unsafety +- Fix BitArray.Update to avoid nil dereference +- BitArray: feedback from @adrianbrink to simplify tests +- IsHex should be able to handle 0X prefixed strings +- NewBitArray never crashes on negatives (#170) +- Remove {Left, Right}PadString (#168) +- NewBitArray never crashes on negatives (#170) +- Delete unused functions (#2452) +- CMap: slight optimization in Keys() and Values(). (#3567) + +### Common/BitArray + +- Reduce fragility with methods + +### Common/IsDirEmpty + +- Do not mask non-existance errors + +### Common/rand + +- Remove exponential distribution functions (#1979) + +### Config + +- Hardcode default genesis.json +- Block size, consensus timeouts, recheck tx +- Cswal_light, mempool_broadcast, mempool_reap +- Toggle authenticated encryption +- Disable_data_hash (for testing) +- All urls use tcp:// or unix:// prefix +- Filter_peers defaults to false +- Reduce timeouts during test +- Pex_reactor -> pex +- Write all default options to config file +- Lil fixes +- Unexpose chainID +- Fix addrbook path to go in config +- Fix private_peer_ids +- Rename skip_upnp to upnp (#1827) +- 10x default send/recv rate (#1978) +- Reduce default mempool size (#2300) +- Add ValidateBasic (#2485) +- Refactor ValidateBasic (#2503) +- Make possible to set absolute paths for TLS cert and key (#3765) +- Move max_msg_bytes into mempool section (#3869) +- Add rocksdb as a db backend option (#4239) +- Allow fastsync.version = v2 (#4639) +- Trust period consistency (#5297) +- Rename prof_laddr to pprof_laddr and move it to rpc (#5315) +- Set time_iota_ms to timeout_commit in test genesis (#5386) +- Add state sync discovery_time setting (#5399) +- Set statesync.rpc_servers when generating config file (#5433) (#5438) +- Fix mispellings (#5914) + +### Consensus + +- Broadcast evidence tx on ErrVoteConflictingSignature +- Check both vote orderings for dupeout txs +- Fix negative timeout; log levels +- Msg saving and replay +- Replay console +- Use replay log to avoid sign regression +- Don't wait for wal if conS not running +- Dont allow peer round states to decrease +- Cswal doesnt write any consensus msgs in light mode +- Fix more races in tests +- Fix race from OnStop accessing cs.Height +- T.Fatal -> panic +- Hvs.Reset(height, valSet) +- Increase mempool_test timeout +- Don't print shared vars in cs.String() +- Add note about replay test +- No sign err in replay; fix a race +- Hvs.StringIndented needed a lock. addresses #284 +- Test reactor +- Fix panic on POLRound=-1 +- Ensure dir for cswal on reactor tests +- Lock before loading commit +- Track index of privVal +- Test validator set change +- Wal.Flush() and cleanup replay tests +- TimeoutTicker, skip TimeoutCommit on HasAll +- Mv timeoutRoutine into TimeoutTicker +- No internal vars in reactor.String() +- Sync wal.writeHeight +- Remove crankTimeoutPropose from tests +- Be more explicit when we need to write height after handshake +- Let time.Timer handle non-positive durations +- Check HasAll when TwoThirdsMajority +- Nice error msg if ApplyBlock fails +- Handshake replay test using wal +- More handshake replay tests +- Some more informative logging +- Timeout on replayLastBlock +- Comment about test_data [ci skip] +- Fix tests +- Improve logging for conflicting votes +- Better logging +- More comments +- IsProposer func +- Remove rs from handleMsg +- Log ProposalHeartbeat msg +- Test proposal heartbeat +- Recover panics in receive routine +- Remove support for replay by #HEIGHT. closes #567 +- Use filepath for windows compatibility, closes #595 +- Kill process on app error +- Ensure prs.ProposalBlockParts is initialized. fixes #810 +- Fix for initializing block parts during catchup +- Make mempool_test deterministic +- Fix LastCommit log +- Crank timeout in timeoutWaitGroup +- Fix typo on ticker.go documentation +- Fix makeBlockchainFromWAL +- Remove log stmt. closes #987 +- Note about duplicate evidence +- Rename test funcs +- Minor cosmetic +- Fix SetLogger in tests +- Print go routines in failed test +- Return from go-routine in test +- Return from errors sooner in addVote +- Close pubsub channels. fixes #1372 +- Only fsync wal after internal msgs +- Link to spec from readme (#1609) +- Fixes #1754 +- Fix addProposalBlockPart +- Stop wal +- Wait on stop if not fastsync +- Include evidence in proposed block parts. fixes #2050 +- Fix test for blocks with evidence +- Failing test for ProposerAddress +- Wait timeout precommit before starting new round (#2493) +- Add ADR for first stage consensus refactor (#2462) +- Wait for proposal or timeout before prevote (#2540) +- Flush wal on stop (#3297) +- Reduce "Error attempting to add vote" message severity (Er… (#3871) +- Reduce log severity for ErrVoteNonDeterministicSignature (#4431) +- Add comment as to why use mocks during replay (#4785) +- Fix TestSimulateValidatorsChange +- Fix and rename TestStateLockPOLRelock (#4796) +- Bring back log.Error statement (#4899) +- Increase ensureTimeout (#4891) +- Fix startnextheightcorrectly test (#4938) +- Attempt to repair the WAL file on data corruption (#4682) +- Change logging and handling of height mismatch (#4954) +- Stricter on LastCommitRound check (#4970) +- Proto migration (#4984) +- Do not allow signatures for a wrong block in commits +- Msg testvectors (#5076) +- Added byzantine test, modified previous test (#5150) +- Only call privValidator.GetPubKey once per block (#5143) +- Don't check InitChain app hash vs genesis app hash, replace it (#5237) +- Double-sign risk reduction (ADR-51) (#5147) +- Fix wrong proposer schedule for `InitChain` validators (#5329) +- Check block parts don't exceed maximum block bytes (#5436) +- Open target WAL as read/write during autorepair (#5536) (#5547) +- Groom Logs (#5917) +- Remove privValidator from log call (#6128) +- More log grooming (bp #6140) (#6143) + +### Consensus/WAL + +- Benchmark WALDecode across data sizes + +### Consensus/replay + +- Remove timeout + +### Consensus/types + +- Fix BenchmarkRoundStateDeepCopy panics (#4244) + +### Consensus/wal + +- #HEIGHT -> #ENDHEIGHT + +### Console + +- Fix output, closes #93 +- Fix tests + +### Contributing + +- Use full version from site +- Add steps for adding and removing rc branches (#5223) + +### Core + +- Apply megacheck vet tool (unused, gosimple, staticcheck) + +### Counter + +- Fix tx buffer overflow + +### Cov + +- Ignore autogen file (#5033) + +### Crypto + +- Rename last traces of go-crypto (#1786) +- Abstract pubkey / signature size when known to constants (#1808) +- Refactor to move files out of the top level directory +- Remove Ed25519 and Secp256k1 suffix on GenPrivKey +- Fix package imports from the refactor +- Add benchmarking code for signature schemes (#2061) +- Switch hkdfchacha back to xchacha (#2058) +- Add compact bit array for intended usage in the multisig +- Remove interface from crypto.Signature +- Add compact bit array for intended usage in the multisig +- Threshold multisig implementation +- Add compact bit array for intended usage in the multisig (#2140) +- Remove unnecessary prefixes from amino route variable names (#2205) +- Add a way to go from pubkey to route (#2574) +- Use stdlib crypto/rand. ref #2099 (#2669) +- Revert to mainline Go crypto lib (#3027) +- Delete unused code (#3426) +- Proof of Concept for iterative version of SimpleHashFromByteSlices (#2611) (#3530) +- Add sr25519 signature scheme (#4190) +- Fix sr25519 from raw import (#4272) +- Remove SimpleHashFromMap() and SimpleProofsFromMap() +- Remove key suffixes (#4941) +- Removal of multisig (#4988) +- Consistent api across keys (#5214) +- API modifications (#5236) +- Remove secp256k1 (#5280) +- Remove proto privatekey (#5301) +- Reword readme (#5349) +- Add in secp256k1 support (#5500) +- Fix infinite recursion in Secp256k1 string formatting (#5707) (#5709) -*July 26th, 2018* +### Crypto/amino -BUG FIXES +- Address anton's comment on PubkeyAminoRoute (#2592) +- Add function to modify key codec (#4112) -- [consensus, blockchain] Register the Evidence interface so it can be - marshalled/unmarshalled by the blockchain and consensus reactors +### Crypto/ed25519 -## 0.22.6 +- Update the godocs (#2002) +- Remove privkey.Generate method (#2022) -*July 24th, 2018* +### Crypto/hkdfchachapoly -BUG FIXES +- Add testing seal to the test vector -- [rpc] Fix `/blockchain` endpoint - - (#2049) Fix OOM attack by returning error on negative input - - Fix result length to have max 20 (instead of 21) block metas -- [rpc] Validate height is non-negative in `/abci_query` -- [consensus] (#2050) Include evidence in proposal block parts (previously evidence was - not being included in blocks!) -- [p2p] (#2046) Close rejected inbound connections so file descriptor doesn't - leak -- [Gopkg] (#2053) Fix versions in the toml +### Crypto/merkle -## 0.22.5 +- Remove byter in favor of plain byte slices (#2595) +- Remove simple prefix (#4989) -*July 23th, 2018* +### Crypto/random -BREAKING CHANGES: -- [crypto] Refactor `tendermint/crypto` into many subpackages -- [libs/common] remove exponentially distributed random numbers +- Use chacha20, add forward secrecy (#2562) -IMPROVEMENTS: -- [abci, libs/common] Generated gogoproto static marshaller methods -- [config] Increase default send/recv rates to 5 mB/s -- [p2p] reject addresses coming from private peers -- [p2p] allow persistent peers to be private +### Crypto/secp256k1 -BUG FIXES: -- [mempool] fixed a race condition when `create_empty_blocks=false` where a - transaction is published at an old height. -- [p2p] dial external IP setup by `persistent_peers`, not internal NAT IP -- [rpc] make `/status` RPC endpoint resistant to consensus halt +- Add godocs, remove indirection in privkeys (#2017) +- Fix signature malleability, adopt more efficient en… (#2239) -## 0.22.4 +### Crypto|p2p|state|types -*July 14th, 2018* +- Rename Pub/PrivKey's TypeIdentifier() and Type() -BREAKING CHANGES: -- [genesis] removed deprecated `app_options` field. -- [types] Genesis.AppStateJSON -> Genesis.AppState - -FEATURES: -- [tools] Merged in from github.com/tendermint/tools - -BUG FIXES: -- [tools/tm-bench] Various fixes -- [consensus] Wait for WAL to stop on shutdown -- [abci] Fix #1891, pending requests cannot hang when abci server dies. - Previously a crash in BeginBlock could leave tendermint in broken state. +### Cs -## 0.22.3 +- Prettify logging of ignored votes (#3086) +- Reset triggered timeout precommit (#3310) +- Sync WAL more frequently (#3300) +- Update wal comments (#3334) +- Comment out log.Error to avoid TestReactorValidatorSetChanges timing out (#3401) +- Fix nondeterministic tests (#3582) +- Exit if SwitchToConsensus fails (#3706) +- Check for SkipTimeoutCommit or wait timeout in handleTxsAvailable (#3928) +- Don't panic when block is not found in store (#4163) +- Clarify where 24 comes from in maxMsgSizeBytes (wal.go) +- Set missing_validators(_power) metrics to 0 for 1st block (#4194) +- Check if cs.privValidator is nil (#4295) -*July 10th, 2018* +### Cs/replay -IMPROVEMENTS -- Update dependencies - * pin all values in Gopkg.toml to version or commit - * update golang/protobuf to v1.1.0 +- Check appHash for each block (#3579) +- ExecCommitBlock should not read from state.lastValidators (#3067) -## 0.22.2 +### Cs/wal -*July 10th, 2018* +- Refuse to encode msg that is bigger than maxMsgSizeBytes (#3303) -IMPROVEMENTS -- More cleanup post repo merge! -- [docs] Include `ecosystem.json` and `tendermint-bft.md` from deprecated `aib-data` repository. -- [config] Add `instrumentation.max_open_connections`, which limits the number - of requests in flight to Prometheus server (if enabled). Default: 3. +### Cswal +- Write #HEIGHT:1 for empty wal -BUG FIXES -- [rpc] Allow unquoted integers in requests - - NOTE: this is only for URI requests. JSONRPC requests and all responses - will use quoted integers (the proto3 JSON standard). -- [consensus] Fix halt on shutdown +### Daemon -## 0.22.1 +- Refactor out of cmd into own package -*July 5th, 2018* +### Db -IMPROVEMENTS +- Add Close() to db interface. closes #31 +- Fix memdb iterator +- Fix MemDB.Close +- Sort keys for memdb iterator +- Some comments in types.go +- Test panic on nil key +- Some test cleanup +- Fixes to fsdb and clevledb +- Memdb iterator +- Goleveldb iterator +- Cleveldb iterator +- Fsdb iterator +- Fix c and go iterators +- Simplify exists check, fix IsKeyInDomain signature, Iterator Close +- Add support for badgerdb (#5233) -* Cleanup post repo-merge. -* [docs] Various improvements. +### Dep -BUG FIXES +- Pin all deps to version or commit +- Revert updates +- Update tm-db to 0.4.0 (#4289) +- Bump gokit dep (#4424) +- Maunally bump dep (#4436) +- Bump protobuf, cobra, btcutil & std lib deps (#4676) -* [state] Return error when EndBlock returns a 0-power validator that isn't - already in the validator set. -* [consensus] Shut down WAL properly. +### Deps +- Update gogo/protobuf from 1.1.1 to 1.2.1 and golang/protobuf from 1.1.0 to 1.3.0 (#3357) +- Update gogo/protobuf version from v1.2.1 to v1.3.0 (#3947) +- Bump github.com/magiconair/properties from 1.8.0 to 1.8.1 (#3937) +- Bump github.com/rs/cors from 1.6.0 to 1.7.0 (#3939) +- Bump github.com/fortytw2/leaktest from 1.2.0 to 1.3.0 (#3943) +- Bump github.com/libp2p/go-buffer-pool from 0.0.1 to 0.0.2 (#3948) +- Bump google.golang.org/grpc from 1.22.0 to 1.23.0 (#3942) +- Bump github.com/gorilla/websocket from 1.2.0 to 1.4.1 (#3945) +- Bump viper to 1.4.0 and logfmt to 0.4.0 (#3950) +- Bump github.com/stretchr/testify from 1.3.0 to 1.4.0 (#3951) +- Bump github.com/go-kit/kit from 0.6.0 to 0.9.0 (#3952) +- Bump google.golang.org/grpc from 1.23.0 to 1.23.1 (#3982) +- Bump google.golang.org/grpc from 1.23.1 to 1.24.0 (#4021) +- Bump google.golang.org/grpc from 1.25.0 to 1.25.1 (#4127) +- Bump google.golang.org/grpc from 1.25.1 to 1.26.0 (#4264) +- Bump github.com/go-logfmt/logfmt from 0.4.0 to 0.5.0 (#4282) +- Bump github.com/pkg/errors from 0.8.1 to 0.9.0 (#4301) +- Bump github.com/spf13/viper from 1.6.1 to 1.6.2 (#4318) +- Bump github.com/golang/protobuf from 1.3.2 to 1.3.3 (#4359) +- Bump google.golang.org/grpc from 1.27.0 to 1.27.1 (#4372) +- Bump github.com/stretchr/testify from 1.4.0 to 1.5.0 (#4435) +- Bump github.com/tendermint/tm-db from 0.4.0 to 0.4.1 (#4476) +- Bump github.com/Workiva/go-datastructures (#4519) +- Bump deps that bot cant (#4555) +- Run go mod tidy (#4587) +- Bump tm-db to 0.6.0 (#5058) + +### Dist + +- Dont mkdir in container +- Dont mkdir in container + +### Distribution + +- Lock binary dependencies to specific commits (#2550) + +### Dummy + +- Valset changes and tests +- Verify pubkey is go-crypto encoded in DeliverTx. closes #51 +- Include app.key tag + +### E2e + +- Use ed25519 for secretConn (remote signer) (#5678) +- Releases nightly (#5906) +- Add benchmarking functionality (bp #6210) (#6216) +- Integrate light clients (bp #6196) +- Tx load to use broadcast sync instead of commit (backport #6347) (#6352) +- Relax timeouts (#6356) + +### Ebuchman + +- Added some demos on how to parse unknown types + +### Ed25519 + +- Use golang/x/crypto fork (#2558) + +### Encoding + +- Remove codecs (#4996) + +### Errcheck + +- PR comment fixes + +### Events + +- Integrate event switch into services via Eventable interface + +### Evidence + +- More funcs in store.go +- Store tests and fixes +- Pool test +- Reactor test +- Reactor test +- Dont send evidence to unsynced peers +- Check peerstate exists; dont send old evidence +- Give each peer a go-routine +- Enforce ordering in DuplicateVoteEvidence (#4151) +- Introduce time.Duration to evidence params (#4254) +- Both MaxAgeDuration and MaxAgeNumBlocks need to be surpassed (#4667) +- Handling evidence from light client(s) (#4532) +- Remove unused param (#4726) +- Remove pubkey from duplicate vote evidence +- Add doc.go +- Protect valToLastHeight w/ mtx +- Check evidence is pending before validating evidence +- Refactor evidence mocks throughout packages (#4787) +- Cap evidence to an absolute number (#4780) +- Create proof of lock change and implement it in evidence store (#4746) +- Prevent proposer from proposing duplicate pieces of evidence (#4839) +- Remove header from phantom evidence (#4892) +- Retrieve header at height of evidence for validation (#4870) +- Json tags for DuplicateVoteEvidence (#4959) +- Migrate reactor to proto (#4949) +- Adr56 form amnesia evidence (#4821) +- Improve amnesia evidence handling (#5003) +- Replace mock evidence with mocked duplicate vote evidence (#5036) +- Fix data race in Pool.updateValToLastHeight() (#5100) +- Check lunatic vote matches header (#5093) +- New evidence event subscription (#5108) +- Minor correction to potential amnesia ev validate basic (#5151) +- Remove phantom validator evidence (#5181) +- Don't stop evidence verification if an evidence fails (#5189) +- Fix usage of time field in abci evidence (#5201) +- Change evidence time to block time (#5219) +- Remove validator index verification (#5225) +- Modularise evidence by moving verification function into evidence package (#5234) +- Remove ConflictingHeaders type (#5317) +- Remove lunatic (#5318) +- Remove amnesia & POLC (#5319) +- Introduction of LightClientAttackEvidence and refactor of evidence lifecycle (#5361) +- Use bytes instead of quantity to limit size (#5449)(#5476) +- Don't gossip consensus evidence too soon (#5528) +- Don't send committed evidence and ignore inbound evidence that is already committed (#5574) +- Structs can independently form abci evidence (#5610) +- Omit bytes field (#5745) +- Buffer evidence from consensus (#5890) +- Terminate broadcastEvidenceRoutine when peer is stopped (#6068) +- Fix bug with hashes (backport #6375) (#6381) -## 0.22.0 +### Example -*July 2nd, 2018* +- Fix func suffix -BREAKING CHANGES: -- [config] - * Remove `max_block_size_txs` and `max_block_size_bytes` in favor of - consensus params from the genesis file. - * Rename `skip_upnp` to `upnp`, and turn it off by default. - * Change `max_packet_msg_size` back to `max_packet_msg_payload_size` -- [rpc] - * All integers are encoded as strings (part of the update for Amino v0.10.1) - * `syncing` is now called `catching_up` -- [types] Update Amino to v0.10.1 - * Amino is now fully proto3 compatible for the basic types - * JSON-encoded types now use the type name instead of the prefix bytes - * Integers are encoded as strings -- [crypto] Update go-crypto to v0.10.0 and merge into `crypto` - * privKey.Sign returns error. - * ed25519 address changed to the first 20-bytes of the SHA256 of the raw pubkey bytes - * `tmlibs/merkle` -> `crypto/merkle`. Uses SHA256 instead of RIPEMD160 -- [tmlibs] Update to v0.9.0 and merge into `libs` - * remove `merkle` package (moved to `crypto/merkle`) +### Example/dummy -FEATURES -- [cmd] Added metrics (served under `/metrics` using a Prometheus client; - disabled by default). See the new `instrumentation` section in the config and - [metrics](https://tendermint.readthedocs.io/projects/tools/en/develop/metrics.html) - guide. -- [p2p] Add IPv6 support to peering. -- [p2p] Add `external_address` to config to allow specifying the address for - peers to dial +- Remove iavl dep - just use raw db -IMPROVEMENT -- [rpc/client] Supports https and wss now. -- [crypto] Make public key size into public constants -- [mempool] Log tx hash, not entire tx -- [abci] Merged in github.com/tendermint/abci -- [crypto] Merged in github.com/tendermint/go-crypto -- [libs] Merged in github.com/tendermint/tmlibs -- [docs] Move from .rst to .md +### Example/kvstore -BUG FIXES: -- [rpc] Limit maximum number of HTTP/WebSocket connections - (`rpc.max_open_connections`) and gRPC connections - (`rpc.grpc_max_open_connections`). Check out "Running In Production" guide if - you want to increase them. -- [rpc] Limit maximum request body size to 1MB (header is limited to 1MB). -- [consensus] Fix a halting bug where `create_empty_blocks=false` -- [p2p] Fix panic in seed mode +- Return ABCI query height (#4509) -## 0.21.0 +### Fmt -*June 21th, 2018* +- Run 'make fmt' -BREAKING CHANGES +### Format -- [config] Change default ports from 4665X to 2665X. Ports over 32768 are - ephemeral and reserved for use by the kernel. -- [cmd] `unsafe_reset_all` removes the addrbook.json +- Add format cmd & goimport repo (#4586) -IMPROVEMENT +### Genesis -- [pubsub] Set default capacity to 0 -- [docs] Various improvements +- Add support for arbitrary initial height (#5191) -BUG FIXES +### Github -- [consensus] Fix an issue where we don't make blocks after `fast_sync` when `create_empty_blocks=false` -- [mempool] Fix #1761 where we don't process txs if `cache_size=0` -- [rpc] Fix memory leak in Websocket (when using `/subscribe` method) -- [config] Escape paths in config - fixes config paths on Windows +- Update PR template to indicate changing pending changelog. (#2059) +- Edit templates for use in issues and pull requests (#4483) +- Add nightly E2E testnet action (#5480) +- Rename e2e jobs (#5502) +- Only notify nightly E2E failures once (#5559) -## 0.20.0 +### Gitian -*June 6th, 2018* +- Update reproducible builds to build with Go 1.12.8 (#3902) -This is the first in a series of breaking releases coming to Tendermint after -soliciting developer feedback and conducting security audits. +### Gitignore -This release does not break any blockchain data structures or -protocols other than the ABCI messages between Tendermint and the application. +- Add .vendor-new (#3566) -Applications that upgrade for ABCI v0.11.0 should be able to continue running Tendermint -v0.20.0 on blockchains created with v0.19.X +### Glide -BREAKING CHANGES +- Update go-common +- Update lock and add util scripts +- Update go-common +- Update go-wire +- Use versions where applicable +- Update for autofile fix +- More external deps locked to versions +- Update grpc version -- [abci] Upgrade to - [v0.11.0](https://github.com/tendermint/abci/blob/master/CHANGELOG.md#0110) -- [abci] Change Query path for filtering peers by node ID from - `p2p/filter/pubkey/` to `p2p/filter/id/` +### Go.mod -## 0.19.9 +- Upgrade iavl and deps (#5657) -*June 5th, 2018* +### Goreleaser -BREAKING CHANGES +- Downcase archive and binary names (#6029) +- Reintroduce arm64 build instructions -- [types/priv_validator] Moved to top level `privval` package +### Grpcdb -FEATURES +- Better readability for docs and constructor names +- Close Iterator/ReverseIterator after use (#3424) -- [config] Collapse PeerConfig into P2PConfig -- [docs] Add quick-install script -- [docs/spec] Add table of Amino prefixes +### Hd -BUG FIXES +- Optimize ReverseBytes + add tests +- Comments and some cleanup -- [rpc] Return 404 for unknown endpoints -- [consensus] Flush WAL on stop -- [evidence] Don't send evidence to peers that are behind -- [p2p] Fix memory leak on peer disconnects -- [rpc] Fix panic when `per_page=0` +### Header -## 0.19.8 +- Check block protocol (#5340) -*June 4th, 2018* +### Http -BREAKING: +- Http-utils added after extraction -- [p2p] Remove `auth_enc` config option, peer connections are always auth - encrypted. Technically a breaking change but seems no one was using it and - arguably a bug fix :) +### Https -BUG FIXES +- //github.com/tendermint/tendermint/pull/1128#discussion_r162799294 -- [mempool] Fix deadlock under high load when `skip_timeout_commit=true` and - `create_empty_blocks=false` +### Indexer -## 0.19.7 +- Allow indexing an event at runtime (#4466) +- Remove index filtering (#5006) +- Remove info log (#6194) -*May 31st, 2018* +### Ints -BREAKING: +- Stricter numbers (#4939) -- [libs/pubsub] TagMap#Get returns a string value -- [libs/pubsub] NewTagMap accepts a map of strings +### Json -FEATURES +- Add Amino-compatible encoder/decoder (#4955) -- [rpc] the RPC documentation is now published to https://tendermint.github.io/slate -- [p2p] AllowDuplicateIP config option to refuse connections from same IP. - - true by default for now, false by default in next breaking release -- [docs] Add docs for query, tx indexing, events, pubsub -- [docs] Add some notes about running Tendermint in production +### Json2wal -IMPROVEMENTS: +- Increase reader's buffer size (#3147) -- [consensus] Consensus reactor now receives events from a separate synchronous event bus, - which is not dependant on external RPC load -- [consensus/wal] do not look for height in older files if we've seen height - 1 -- [docs] Various cleanup and link fixes +### Jsonrpc -## 0.19.6 +- Change log to debug (#5131) -*May 29th, 2018* +### Keys -BUG FIXES +- Transactions.go -> types.go +- Change to []bytes (#4950) -- [blockchain] Fix fast-sync deadlock during high peer turnover +### Keys/keybase.go -BUG FIX: +- Comments and fixes -- [evidence] Dont send peers evidence from heights they haven't synced to yet -- [p2p] Refuse connections to more than one peer with the same IP -- [docs] Various fixes +### Libs -## 0.19.5 +- Update BitArray go docs (#2079) +- Make bitarray functions lock parameters that aren't the caller (#2081) +- Remove usage of custom Fmt, in favor of fmt.Sprintf (#2199) +- Handle SIGHUP explicitly inside autofile (#2480) +- Call Flush() before rename #2428 (#2439) +- Fix event concurrency flaw (#2519) +- Refactor & document events code (#2576) +- Let prefixIterator implements Iterator correctly (#2581) +- Test deadlock from listener removal inside callback (#2588) +- Remove useless code in group (#3504) +- Remove commented and unneeded code (#3757) +- Minor cleanup (#3794) +- Remove db from tendermint in favor of tendermint/tm-cmn (#3811) +- Remove bech32 +- Remove kv (#4874) +- Wrap mutexes for build flag with godeadlock (#5126) -*May 20th, 2018* +### Libs/autofile -BREAKING CHANGES +- Bring back loops (#2261) -- [rpc/client] TxSearch and UnconfirmedTxs have new arguments (see below) -- [rpc/client] TxSearch returns ResultTxSearch -- [version] Breaking changes to Go APIs will not be reflected in breaking - version change, but will be included in changelog. +### Libs/autofile/group_test -FEATURES +- Remove unnecessary logging (#2100) -- [rpc] `/tx_search` takes `page` (starts at 1) and `per_page` (max 100, default 30) args to paginate results -- [rpc] `/unconfirmed_txs` takes `limit` (max 100, default 30) arg to limit the output -- [config] `mempool.size` and `mempool.cache_size` options +### Libs/bits -IMPROVEMENTS +- Inline defer and change order of mutexes (#5187) -- [docs] Lots of updates -- [consensus] Only Fsync() the WAL before executing msgs from ourselves +### Libs/cmn -BUG FIXES +- Remove Tempfile, Tempdir, switch to ioutil variants (#2114) -- [mempool] Enforce upper bound on number of transactions +### Libs/cmn/writefileatomic -## 0.19.4 (May 17th, 2018) +- Handle file already exists gracefully (#2113) -IMPROVEMENTS +### Libs/common -- [state] Improve tx indexing by using batches -- [consensus, state] Improve logging (more consensus logs, fewer tx logs) -- [spec] Moved to `docs/spec` (TODO cleanup the rest of the docs ...) +- Refactor tempfile code into its own file +- Remove deprecated PanicXXX functions (#3595) +- Remove heap.go (#3780) +- Remove unused functions (#3784) +- Refactor libs/common 01 (#4230) +- Refactor libs/common 2 (#4231) +- Refactor libs common 3 (#4232) +- Refactor libs/common 4 (#4237) +- Refactor libs/common 5 (#4240) -BUG FIXES +### Libs/common/rand -- [consensus] Fix issue #1575 where a late proposer can get stuck +- Update godocs -## 0.19.3 (May 14th, 2018) +### Libs/db -FEATURES +- Add cleveldb.Stats() (#3379) +- Close batch (#3397) +- Close batch (#3397) +- Bbolt (etcd's fork of bolt) (#3610) +- Close boltDBIterator (#3627) +- Fix boltdb batching +- Conditional compilation (#3628) +- Boltdb: use slice instead of sync.Map (#3633) +- Remove deprecated `LevelDBBackend` const (#3632) +- Fix the BoltDB Batch.Delete +- Fix the BoltDB Get and Iterator -- [rpc] New `/consensus_state` returns just the votes seen at the current height +### Libs/fail -IMPROVEMENTS +- Clean up `fail.go` (#3785) -- [rpc] Add stringified votes and fraction of power voted to `/dump_consensus_state` -- [rpc] Add PeerStateStats to `/dump_consensus_state` +### Libs/kv -BUG FIXES +- Remove unused type KI64Pair (#4542) -- [cmd] Set GenesisTime during `tendermint init` -- [consensus] fix ValidBlock rules +### Libs/log -## 0.19.2 (April 30th, 2018) +- Format []byte as hexidecimal string (uppercased) (#5960) +- [JSON format] include timestamp (bp #6174) (#6179) -FEATURES: +### Libs/os -- [p2p] Allow peers with different Minor versions to connect -- [rpc] `/net_info` includes `n_peers` +- EnsureDir now returns IO errors and checks file type (#5852) +- Avoid CopyFile truncating destination before checking if regular file (backport: #6428) (#6436) -IMPROVEMENTS: +### Libs/pubsub -- [p2p] Various code comments, cleanup, error types -- [p2p] Change some Error logs to Debug +- Relax tx querying (#4070) -BUG FIXES: +### Libs/pubsub/query -- [p2p] Fix reconnect to persistent peer when first dial fails -- [p2p] Validate NodeInfo.ListenAddr -- [p2p] Only allow (MaxNumPeers - MaxNumOutboundPeers) inbound peers -- [p2p/pex] Limit max msg size to 64kB -- [p2p] Fix panic when pex=false -- [p2p] Allow multiple IPs per ID in AddrBook -- [p2p] Fix before/after bugs in addrbook isBad() +- Add EXISTS operator (#4077) -## 0.19.1 (April 27th, 2018) +### Libs/rand -Note this release includes some small breaking changes in the RPC and one in the -config that are really bug fixes. v0.19.1 will work with existing chains, and make Tendermint -easier to use and debug. With <3 +- Fix "out-of-memory" error on unexpected argument (#5215) -BREAKING (MINOR) +### Light -- [config] Removed `wal_light` setting. If you really needed this, let us know +- Rename lite2 to light & remove lite (#4946) +- Implement validate basic (#4916) +- Migrate to proto (#4964) +- Added more tests for pruning, initialization and bisection (#4978) +- Fix rpc calls: /block_results & /validators (#5104) +- Use bisection (not VerifyCommitTrusting) when verifying a head… (#5119) +- Return if target header is invalid (#5124) +- Update ADR 47 with light traces (#5250) +- Implement light block (#5298) +- Move dropout handling and invalid data to the provider (#5308) +- Cross-check the very first header (#5429) +- Run detector for sequentially validating light client (#5538) (#5601) +- Make fraction parts uint64, ensuring that it is always positive (#5655) +- Fix panic with RPC calls to commit and validator when height is nil (#6040) +- Remove witnesses in order of decreasing index (#6065) +- Handle too high errors correctly (backport #6346) (#6351) -FEATURES: +### Light/evidence -- [networks] moved in tooling from devops repo: terraform and ansible scripts for deploying testnets ! -- [cmd] Added `gen_node_key` command +- Handle FLA backport (#6331) -BUG FIXES +### Light/provider/http + +- Fix Validators (#6024) + +### Light/rpc + +- Fix ABCIQuery (#5375) + +### Lint + +- Remove dot import (go-common) +- S/common.Fmt/fmt.Sprintf +- S/+=1/++, remove else clauses +- Couple more fixes +- Apply deadcode/unused +- Golint issue fixes (#4258) +- Add review dog (#4652) +- Enable nolintlinter, disable on tests +- Various fixes +- Errcheck (#5091) +- Add markdown linter (#5254) +- Add errchecks (#5316) +- Enable errcheck (#5336) +- Run gofmt and goimports (#13) + +### Linter + +- Couple fixes +- Add metalinter to Makefile & apply some fixes +- Last fixes & add to circle +- Address deadcode, implement incremental lint testing +- Sort through each kind and address small fixes +- Enable in CI & make deterministic +- (1/2) enable errcheck (#5064) +- Fix some bls-related linter issues (#14) + +### Linters + +- Enable scopelint (#3963) +- Modify code to pass maligned and interfacer (#3959) +- Enable stylecheck (#4153) + +### Linting + +- Cover the basics +- Catch some errors +- Add to Makefile & do some fixes +- Next round of fixes +- Fixup some stuffs +- Little more fixes +- A few fixes +- Replace megacheck with metalinter +- Apply 'gofmt -s -w' throughout +- Apply misspell +- Apply errcheck part1 +- Apply errcheck part2 +- Moar fixes +- Few more fixes +- Remove unused variable + +### Lite + +- MemStoreProvider GetHeightBinarySearch method + fix ValKeys.signHeaders +- < len(v) in for loop check, as per @melekes' recommendation +- TestCacheGetsBestHeight with GetByHeight and GetByHeightBinarySearch +- Comment out iavl code - TODO #1183 +- Add synchronization in lite verify (#2396) +- Follow up from #3989 (#4209) +- Modified bisection to loop (#4400) +- Add helper functions for initiating the light client (#4486) +- Fix HTTP provider error handling + +### Lite/proxy + +- Validation* tests and hardening for nil dereferences +- Consolidate some common test headers into a variable + +### Lite2 + +- Light client with weak subjectivity (#3989) +- Move AutoClient into Client (#4326) +- Improve auto update (#4334) +- Add Start, TrustedValidatorSet funcs (#4337) +- Rename alternative providers to witnesses (#4344) +- Refactor cleanup() (#4343) +- Batch save & delete operations in DB store (#4345) +- Panic if witness is on another chain (#4356) +- Make witnesses mandatory (#4358) +- Replace primary provider with alternative when unavailable (#4354) +- Fetch missing headers (#4362) +- Validate TrustOptions, add NewClientFromTrustedStore (#4374) +- Return if there are no headers in RemoveNoLongerTrustedHeaders (#4378) +- Manage witness dropout (#4380) +- Improve string output of all existing providers (#4387) +- Modified sequence method to match bisection (#4403) +- Disconnect from bad nodes (#4388) +- Divide verify functions (#4412) +- Return already verified headers and verify earlier headers (#4428) +- Don't save intermediate headers (#4452) +- Store current validator set (#4472) +- Cross-check first header and update tests (#4471) +- Remove expiration checks on functions that don't require them (#4477) +- Prune-headers (#4478) +- Return height as 2nd return param in TrustedValidatorSet (#4479) +- Actually run example tests + clock drift (#4487) +- Fix tendermint lite sub command (#4505) +- Remove auto update (#4535) +- Indicate success/failure of Update (#4536) +- Replace primary when providing invalid header (#4523) +- Add benchmarking tests (#4514) +- Cache headers in bisection (#4562) +- Use bisection for some of backward verification (#4575) +- Make maxClockDrift an option (#4616) +- Prevent falsely returned double voting error (#4620) +- Default to http scheme in provider.New (#4649) +- Verify ConsensusHash in rpc client +- Fix pivot height during bisection (#4850) +- Correctly return the results of the "latest" block (#4931) +- Allow bigger requests to LC proxy (#4930) +- Check header w/ witnesses only when doing bisection (#4929) +- Compare header with witnesses in parallel (#4935) + +### Lite2/http + +- Fix provider test by increasing the block retention value (#4890) + +### Lite2/rpc + +- Verify block results and validators (#4703) + +### Localnet + +- Fix $LOG variable (#3423) + +### Log + +- Move some Info to Debug +- Tm -> TM + +### Logging + +- Print string instead of callback (#6178) +- Shorten precommit log message (#6270) (#6274) + +### Logs + +- Cleanup (#6198) + +### Make + +- Dont use -v on go test +- Update protoc_abci use of awk +- Add back tools cmd (#4281) +- Remove sentry setup cmds (#4383) + +### Makefile + +- Remove megacheck +- Fix protoc_libs +- Add `make check_dep` and remove `make ensure_deps` (#2055) +- Lint flags +- Fix build-docker-localnode target (#3122) +- Minor cleanup (#3994) +- Place phony markers after targets (#4408) +- Add options for other DBs (#5357) +- Remove call to tools (#6104) + +### Markdownlint + +- Ignore .github directory (#5351) + +### Maverick + +- Reduce some duplication (#6052) + +### Mempool + +- Add GetState() +- Remove bad txs from cacheMap +- Don't remove committed txs from cache +- Comments +- Reactor test +- Implement Mempool.CloseWAL +- Return error on cached txs +- Assert -> require in test +- Remove Peer interface. use p2p.Peer +- Cfg.CacheSize and expose InitWAL +- Fix cache_size==0. closes #1761 +- Log hashes, not whole tx +- Chan bool -> chan struct{} +- Keep cache hashmap and linked list in sync (#2188) +- Store txs by hash inside of cache (#2234) +- Filter new txs if they have insufficient gas (#2385) +- ErrPreCheck and more log info (#2724) +- Print postCheck error (#2762) +- Add txs from Update to cache +- Add a comment and missing changelog entry (#2996) +- NotifyTxsAvailable if there're txs left, but recheck=false (#2991) +- Move tx to back, not front (#3036) +- Move tx to back, not front (#3036) +- Enforce maxMsgSize limit in CheckTx (#3168) +- Correct args order in the log msg (#3221) +- Fix broadcastTxRoutine leak (#3478) +- Add a safety check, write tests for mempoolIDs (#3487) +- Move interface into mempool package (#3524) +- Remove only valid (Code==0) txs on Update (#3625) +- Make max_msg_bytes configurable (#3826) +- Make `max_tx_bytes` configurable instead of `max_msg_bytes` (#3877) +- Fix memory loading error on 32-bit machines (#3969) +- Moved TxInfo parameter into Mempool.CheckTx() (#4083) +- Reserve IDs in InitPeer instead of AddPeer +- Move mock into mempool directory +- Allow ReapX and CheckTx functions to run in parallel +- Do not launch broadcastTxRoutine if Broadcast is off +- Make it clear overwriting of pre/postCheck filters is intent… (#5054) +- Use oneof (#5063) +- Add RemoveTxByKey function (#5066) +- Return an error when WAL fails (#5292) +- Batch txs per peer in broadcastTxRoutine (#5321) +- Fix nil pointer dereference (#5412) +- Introduce KeepInvalidTxsInCache config option (#5813) +- Disable MaxBatchBytes (#5800) -Some of these are breaking in the RPC response, but they're really bugs! +### Mempool/reactor -- [spec] Document address format and pubkey encoding pre and post Amino -- [rpc] Lower case JSON field names -- [rpc] Fix missing entries, improve, and lower case the fields in `/dump_consensus_state` -- [rpc] Fix NodeInfo.Channels format to hex -- [rpc] Add Validator address to `/status` -- [rpc] Fix `prove` in ABCIQuery -- [cmd] MarshalJSONIndent on init +- Fix reactor broadcast test (#5362) + +### Mempool/rpc + +- Log grooming (bp #6201) (#6203) + +### Mergify + +- Remove unnecessary conditions (#4501) +- Use strict merges (#4502) +- Use PR title and body for squash merge commit (#4669) + +### Merkle + +- Go-common -> tmlibs +- Remove go-wire dep by copying EncodeByteSlice +- Remove unused funcs. unexport simplemap. improv docs +- Use amino for byteslice encoding +- Return hashes for empty merkle trees (#5193) + +### Metalinter + +- Add linter to Makefile like tendermint + +### Metrics + +- Add additional metrics to p2p and consensus (#2425) +- Only increase last_signed_height if commitSig for block (#4283) +- Switch from gauge to histogram (#5326) + +### Mocks + +- Update with 2.2.1 (#5294) + +### Mod + +- Go mod tidy + +### Nano + +- Update comments + +### Networks + +- Update readmes + +### Networks/remote + +- Turn on GO111MODULE and use git clone instead of go get (#4203) + +### Node + +- ConfigFromViper +- NewNode takes DBProvider and GenDocProvider +- Clean makeNodeInfo +- Remove dup code from rebase +- Remove commented out trustMetric +- Respond always to OS interrupts (#2479) +- Refactor privValidator ext client code & tests (#2895) +- Refactor node.NewNode (#3456) +- Fix a bug where `nil` is recorded as node's address (#3740) +- Run whole func in goroutine, not just logger.Error fn (#3743) +- Allow registration of custom reactors while creating node (#3771) +- Use GRPCMaxOpenConnections when creating the gRPC server (#4349) +- Don't attempt fast sync when InitChain sets self as only validator (#5211) +- Fix genesis state propagation to state sync (#5302) + +### Note + +- Add nondeterministic note to events (#6220) (#6225) + +### Os + +- Simplify EnsureDir() (#5871) + +### P2p + +- Push handshake containing chainId for early disconnect. Closes #12 +- Fix switch_test to account for handshake +- Broadcast spawns goroutine to Send on each peer and times out after 10 seconds. Closes #7 +- Fix switch test for Broadcast returning success channel +- Use cmn instead of . +- Fix race by peer.Start() before peers.Add() +- Fix test +- Sw.peers.List() is empty in sw.OnStart +- Put maxMsgPacketPayloadSize, recvRate, sendRate in config +- Test fix +- Fully test PeerSet, more docs, parallelize PeerSet tests +- Minor comment fixes +- Delete unused and untested *IPRangeCount functions +- Sw.AddPeer -> sw.addPeer +- Allow listener with no external connection +- Update readme, some minor things +- Some fixes re @odeke-em issues #813,#816,#817 +- Comment on the wg.Add before go saveRoutine() +- Peer should respect errors from SetDeadline +- Use fake net.Pipe since only >=Go1.10 implements SetDeadline +- NetPipe for peer.ID +- Add ID to NetAddress and use for AddrBook +- Support addr format ID@IP:PORT +- Authenticate peer ID +- Remove deprecated Dockerfile +- Seed mode fixes from rebase and review +- Seed disconnects after sending addrs +- Add back lost func +- Use sub dirs +- Tmconn->conn and types->p2p +- Use conn.Close when peer is nil +- Notes about ListenAddr +- AddrBook.Save() on DialPeersAsync +- Add Channels to NodeInfo and don't send for unknown channels +- Fix tests for required channels +- Fix break in double loop +- Introduce peerConn to simplify peer creation (#1226) +- Keep reference to connections in test peer +- Persistent - redial if first dial fails +- Switch - reconnect only if persistent +- Don't use dial funcn in peerconfig +- NodeInfo.Channels is HexBytes +- Dont require minor versions to match in handshake +- Explicit netaddress errors +- Some comments and a log line +- MinNumOutboundPeers. Closes #1501 +- Change some logs from Error to Debug. #1476 +- Small lint +- Prevent connections from same ip +- External address +- Reject addrs coming from private peers (#2032) +- Fix conn leak. part of #2046 +- Connect to peers from a seed node immediately (#2115) +- Add test vectors for deriving secrets (#2120) +- Integrate new Transport +- Add RPCAddress to NodeInfoOther.String() (#2442) +- NodeInfo is an interface; General cleanup (#2556) +- Restore OriginalAddr (#2668) +- Peer-id -> peer_id (#2771) +- AddressBook requires addresses to have IDs; Do not close conn immediately after sending pex addrs in seed mode (#2797) +- Re-check after sleeps (#2664) +- Log 'Send failed' on Debug (#2857) +- NewMultiplexTransport takes an MConnConfig (#2869) +- Panic on transport error (#2968) +- Fix peer count mismatch #2332 (#2969) +- Set MConnection#created during init (#2990) +- File descriptor leaks (#3150) +- Fix infinite loop in addrbook (#3232) +- Check secret conn id matches dialed id (#3321) +- Fix comment in secret connection (#3348) +- Do not panic when filter times out (#3384) +- Refactor GetSelectionWithBias for addressbook (#3475) +- Seed mode refactoring (#3011) +- Do not log err if peer is private (#3474) +- (seed mode) limit the number of attempts to connect to a peer (#3573) +- Session should terminate on nonce wrapping (#3531) (#3609) +- Make persistent prop independent of conn direction (#3593) +- PeerBehaviour implementation (#3539) (#3552) +- Peer state init too late and pex message too soon (#3634) +- Per channel metrics (#3666) (#3677) +- Remove NewNetAddressStringWithOptionalID (#3711) +- Peerbehaviour follow up (#3653) (#3663) +- Refactor Switch#OnStop (#3729) +- Dial addrs which came from seed instead of calling ensurePeers (#3762) +- Extract ID validation into a separate func (#3754) +- Fix error logging for connection stop (#3824) +- Do not write 'Couldn't connect to any seeds' if there are no seeds (#3834) +- Only allow ed25519 pubkeys when connecting +- Log as debug msg when address dialing is already connected (#4082) +- Make SecretConnection non-malleable (#3668) +- Add `unconditional_peer_ids` and `persistent_peers_max_dial_period` (#4176) +- Extract maxBackoffDurationForPeer func and remove 1 test (#4218) +- Use curve25519.X25519() instead of ScalarMult() (#4449) +- PEX message abuse should ban as well as disconnect (#4621) +- Limit the number of incoming connections +- Set RecvMessageCapacity to maxMsgSize in all reactors +- Return err on `signChallenge` (#4795) +- Return masked IP (not the actual IP) in addrbook#groupKey +- TestTransportMultiplexAcceptNonBlocking and TestTransportMultiplexConnFilterTimeout (#4868) +- Remove nil guard (#4901) +- Expose SaveAs on NodeKey (#4981) +- Proto leftover (#4995) +- Remove data race bug in netaddr stringer (#5048) +- Ensure peers can't change IP of known nodes (#5136) +- Reduce log severity (#5338) +- Fix MConnection inbound traffic statistics and rate limiting (#5868) (#5870) +- Fix "Unknown Channel" bug on CustomReactors (#6297) +- Fix using custom channels (#6339) + +### P2p/addrbook + +- Comments +- AddrNew/Old -> bucketsNew/Old +- Simplify PickAddress +- AddAddress returns error. more defensive PickAddress +- Add non-terminating test +- Fix addToOldBucket +- Some comments + +### P2p/conn + +- Better handling for some stop conditions +- FlushStop. Use in pex. Closes #2092 (#2802) +- Don't hold stopMtx while waiting (#3254) +- Add Bufferpool (#3664) +- Simplify secret connection handshake malleability fix with merlin (#4185) +- Add a test for MakeSecretConnection (#4829) +- Migrate to Protobuf (#4990) +- Check for channel id overflow before processing receive msg (backport #6522) (#6528) + +### P2p/connetion + +- Remove panics, test error cases + +### P2p/pex + +- Simplify ensurePeers +- Wait to connect to all peers in reactor test +- Minor cleanup and comments +- Some addrbook fixes +- Allow configured seed nodes to not be resolvable over DNS (#2129) +- Fix mismatch between dialseeds and checkseeds. (#2151) +- Consult seeds in crawlPeersRoutine (#3647) +- Fix DATA RACE +- Migrate to Protobuf (#4973) + +### P2p/secret_connection + +- Switch salsa usage to hkdf + chacha + +### P2p/test + +- Wait for listener to get ready (#4881) +- Fix Switch test race condition (#4893) + +### P2p/trust + +- Split into multiple files and improve function order +- Lock on Copy() +- Remove extra channels +- Fix nil pointer error on TrustMetric Copy() (#1819) + +### P2p/trustmetric + +- Non-deterministic test + +### Pex + +- Dial seeds when address book needs more addresses (#3603) +- Various follow-ups (#3605) +- Use highwayhash for pex bucket + +### Premerge2 + +- Rpc -> rpc/tendermint + +### Priv-val + +- Fix timestamp for signing things that only differ by timestamp + +### PrivVal + +- Improve SocketClient network code (#1315) + +### Privval -## 0.19.0 (April 13th, 2018) +- Switch to amino encoding in SignBytes (#2459) +- Set deadline in readMsg (#2548) +- Add IPCPV and fix SocketPV (#2568) +- Fixes from review (#3126) +- Improve Remote Signer implementation (#3351) +- Increase timeout to mitigate non-deterministic test failure (#3580) +- Remove misplaced debug statement (#4103) +- Add `SignerDialerEndpointRetryWaitInterval` option (#4115) +- Return error on getpubkey (#4534) +- Remove deprecated `OldFilePV` +- Retry GetPubKey/SignVote/SignProposal N times before +- Migrate to protobuf (#4985) +- If remote signer errors, don't retry (#5140) +- Add chainID to requests (#5239) +- Allow passing options to NewSignerDialerEndpoint (#5434) (#5437) +- Fix ping message encoding (#5442) +- Make response values non nullable (#5583) +- Increase read/write timeout to 5s and calculate ping interva… (#5666) +- Reset pingTimer to avoid sending unnecessary pings (#5642) (#5668) -BREAKING: -- [cmd] improved `testnet` command; now it can fill in `persistent_peers` for you in the config file and much more (see `tendermint testnet --help` for details) -- [cmd] `show_node_id` now returns an error if there is no node key -- [rpc]: changed the output format for the `/status` endpoint (see https://godoc.org/github.com/tendermint/tendermint/rpc/core#Status) +### Prometheus/metrics -Upgrade from go-wire to go-amino. This is a sweeping change that breaks everything that is -serialized to disk or over the network. +- Three new metrics for consensus (#4263) -See github.com/tendermint/go-amino for details on the new format. +### Proto -See `scripts/wire2amino.go` for a tool to upgrade -genesis/priv_validator/node_key JSON files. +- Add buf and protogen script (#4369) +- Minor linting to proto files (#4386) +- Use docker to generate stubs (#4615) +- Bring over proto types & msgs (#4718) +- Regenerate proto (#4730) +- Remove test files +- Add proto files for ibc unblock (#4853) +- Add more to/from (#4956) +- Change to use gogofaster (#4957) +- Remove amino proto tests (#4982) +- Move keys to oneof (#4983) +- Leftover amino (#4986) +- Move all proto dirs to /proto (#5012) +- Folder structure adhere to buf (#5025) +- Increase lint level to basic and fix lint warnings (#5096) +- Improve enums (#5099) +- Reorganize Protobuf schemas (#5102) +- Minor cleanups (#5105) +- Change type + a cleanup (#5107) +- Add a comment for Validator#Address (#5144) +- Bump gogoproto (1.3.2) (#5886) +- Docker deployment (#5931) -FEATURES +### Proto/tendermint/abci -- [test] docker-compose for local testnet setup (thanks Greg!) +- Fix Request oneof numbers (#5116) -## 0.18.0 (April 6th, 2018) +### Protoc -BREAKING: +- "//nolint: gas" directive after pb generation (#164) -- [types] Merkle tree uses different encoding for varints (see tmlibs v0.8.0) -- [types] ValidtorSet.GetByAddress returns -1 if no validator found -- [p2p] require all addresses come with an ID no matter what -- [rpc] Listening address must contain tcp:// or unix:// prefix +### Proxy -FEATURES: +- Typed app conns +- NewAppConns takes a NewTMSPClient func +- Wrap NewTMSPClient in ClientCreator +- Nil -> nilapp +- Remove Handshaker from proxy pkg (#2437) +- Improve ABCI app connection handling (#5078) + +### Pubsub + +- Comments +- Fixes after Ethan's review (#3212) + +### Reactors + +- Omit incoming message bytes from reactor logs (#5743) + +### Readme + +- Js-tmsp -> js-abci +- Update install instruction (#100) +- Re-organize & update docs links +- Fix link to original paper (#4391) +- Add discord to readme (#4533) +- Add badge for git tests (#4732) +- Add source graph badge (#4980) + +### Relase_notes + +- Add release notes for v0.34.0 + +### Release + +- Minor release 0.33.1 (#4401) + +### Remotedb + +- A client package implementing the db.DB interface + +### Removal + +- Remove build folder (#4565) + +### Repeat_timer + +- Drain channel in Stop; done -> wg + +### Replay + +- Larger read buffer +- More tests +- Ensure cs.height and wal.height match + +### Rfc + +- Add end-to-end testing RFC (#5337) + +### Rpc + +- Add status and net info +- Return tx hash, creates contract, contract addr in broadcast (required some helper functions). Closes #30 +- Give each call a dedicated Response struct, add basic test +- Separate out golang API into rpc/core +- Generalized rpc using reflection on funcs and params +- Fixes for better type handlings, explicit error field in response, more tests +- Cleanup, more tests, working http and jsonrpc +- Fix tests to count mempool; copy responses to avoid data races +- Return (*Response, error) for all functions +- GetStorage and Call methods. Tests. +- Decrement mempool count after block mined +- GetStorage and Call methods. Tests. +- Decrement mempool count after block mined +- Auto generated client methods using rpc-gen +- Myriad little fixes +- Cleanup, use client for tests, rpc-gen fixes +- Websockets +- Tests cleanup, use client lib for JSONRPC testing too +- Test CallCode and Call +- Fix memcount error in tests +- Use gorilla websockets +- First successful websocket event subscription +- Websocket events testing +- Use NewBlock event in rpc tests +- Cleanup tests and test contract calls +- Genesis route +- Remove unecessary response wrappers +- Add app_hash to /status +- TMResult and TMEventData +- Test cleanup +- Unsafe_set_config +- Num_unconfirmed_txs (avoid sending txs back) +- Start/stop cpu profiler +- Unsafe_write_heap_profile +- Broadcast tests. closes #219 +- Unsafe_flush_mempool. closes #190 +- Use interfaces for pipe +- Remove restriction on DialSeeds +- /commit +- Fix SeenCommit condition +- Dial_seeds msg. addresses #403 +- Better arg validation for /tx +- /tx allows height+hash +- Use HTTP error codes +- Repsonse types use data.Bytes +- Response types use Result instead of pb Response +- Fix tests +- Decode args without wire +- Cleanup some comments [ci skip] +- Fix tests +- SetWriteDeadline for ws ping. fixes #553 +- Move grpc_test from test/ to grpc/ +- Typo fixes +- Comments +- Historical validators +- Block and Commit take pointers; return latest on nil +- Fix client websocket timeout (#687) +- Subscribe on reconnection (#689) +- Use /iavl repo in test (#713) +- Wait for rpc servers to be available in tests +- Fix tests +- Make time human readable. closes #926 +- GetHeight helper function +- Fix getHeight +- Lower_case peer_round_states, use a list, add the node_address +- Docs/comments +- Add n_peers to /net_info +- Add voting power totals to vote bitarrays +- /consensus_state for simplified output +- Break up long lines +- Test Validator retrevial timeout +- Fix /blockchain OOM #2049 +- Validate height in abci_query +- Log error when we timeout getting validators from consensus (#2045) +- Improve slate for Jenkins (#2070) +- Transform /status result.node_info.other into map (#2417) +- Add /consensus_params endpoint (#2415) +- Fix tx.height range queries (#2899) +- Include peer's remote IP in `/net_info` (#3052) +- Client disable compression (#3430) +- Support tls rpc (#3469) +- Fix response time grow over time (#3537) +- Add support for batched requests/responses (#3534) +- /dial_peers: only mark peers as persistent if flag is on (#3620) +- Use Wrap instead of Errorf error (#3686) +- Make max_body_bytes and max_header_bytes configurable (#3818) +- /broadcast_evidence (#3481) +- Return err if page is incorrect (less than 0 or greater than tot… (#3825) +- Protect subscription access from race condition (#3910) +- Allow using a custom http client in rpc client (#3779) +- Remove godoc comments in favor of swagger docs (#4126) +- /block_results fix docs + write test + restructure response (#3615) +- Remove duplication of data in `ResultBlock ` (#3856) +- Add pagination to /validators (#3993) +- Update swagger docs to openapi 3.0 (#4223) +- Added proposer in consensus_state (#4250) +- Pass `outCapacity` to `eventBus#Subscribe` when subscribing using a l… (#4279) +- Add method block_by_hash (#4257) +- Modify New* functions to return error (#4274) +- Check nil blockmeta (#4320) +- PR#4320 follow up (#4323) +- Add sort_order option to tx_search (#4342) +- Fix issue with multiple subscriptions (#4406) +- Fix tx_search pagination with ordered results (#4437) +- Fix txsearch tests (#4438) +- Fix TxSearch test nits (#4446) +- Stop txSearch result processing if context is done (#4418) +- Keep the original subscription "id" field when new RPCs come in (#4493) +- Remove BlockStoreRPC in favor of BlockStore (#4510) +- Create buffered subscriptions on /subscribe (#4521) +- Fix panic when `Subscribe` is called (#4570) +- Add codespace to ResultBroadcastTx (#4611) +- Handle panics during panic handling +- Use a struct to wrap all the global objects +- Refactor lib folder (#4836) +- Increase waitForEventTimeout to 8 seconds (#4917) +- Add BlockByHash to Client (#4923) +- Replace Amino with new JSON encoder (#4968) +- Support EXISTS operator in /tx_search query (#4979) +- Add /check_tx endpoint (#5017) +- Move docs from doc.go to swagger.yaml (#5044) +- /broadcast_evidence nil evidence check (#5109) +- Make gasWanted/Used snake_case (#5137) +- Add private & unconditional to /dial_peer (#5293) +- Fix openapi spec syntax error (#5358) +- Fix test data races (#5363) +- Revert JSON-RPC/WebSocket response batching (#5378) +- Fix content-type header +- Index block events to support block event queries (bp #6226) (#6261) + +### Rpc/client + +- Use compile time assertions instead of methods +- Include NetworkClient interface into Client interface (#3473) +- Add basic authentication (#4291) +- Split out client packages (#4628) +- Take context as first param (#5347) + +### Rpc/client/http + +- Log error (#5182) + +### Rpc/core + +- Ints are strings in responses, closes #1896 +- Do not lock ConsensusState mutex +- Return an error if `page=0` (#4947) + +### Rpc/core/types + +- UintX -> int + +### Rpc/jsonrpc + +- Unmarshal RPCRequest correctly (bp #6191) (#6193) + +### Rpc/jsonrpc/server -- [rpc] StartHTTPAndTLSServer (not used yet) -- [rpc] Include validator's voting power in `/status` -- [rpc] `/tx` and `/tx_search` responses now include the transaction hash -- [rpc] Include peer NodeIDs in `/net_info` +- Merge WriteRPCResponseHTTP and WriteRPCResponseAr (#5141) +- Ws server optimizations (#5312) +- Return an error in WriteRPCResponseHTTP(Error) (bp #6204) (#6230) -IMPROVEMENTS: -- [config] trim whitespace from elements of lists (like `persistent_peers`) -- [rpc] `/tx_search` results are sorted by height -- [p2p] do not try to connect to ourselves (ok, maybe only once) -- [p2p] seeds respond with a bias towards good peers +### Rpc/lib -BUG FIXES: -- [rpc] fix subscribing using an abci.ResponseDeliverTx tag -- [rpc] fix tx_indexers matchRange -- [rpc] fix unsubscribing (see tmlibs v0.8.0) +- No Result wrapper +- Test tcp and unix +- Set logger on ws conn +- Remove dead files, closes #710 +- Write a test for TLS server (#3703) +- Fix RPC client, which was previously resolving https protocol to http (#4131) -## 0.17.1 (March 27th, 2018) +### Rpc/lib/client -BUG FIXES: -- [types] Actually support `app_state` in genesis as `AppStateJSON` +- Add jitter for exponential backoff of WSClient +- Jitter test updates and only to-be run on releases -## 0.17.0 (March 27th, 2018) - -BREAKING: -- [types] WriteSignBytes -> SignBytes - -IMPROVEMENTS: -- [all] renamed `dummy` (`persistent_dummy`) to `kvstore` (`persistent_kvstore`) (name "dummy" is deprecated and will not work in the next breaking release) -- [docs] note on determinism (docs/determinism.rst) -- [genesis] `app_options` field is deprecated. please rename it to `app_state` in your genesis file(s). `app_options` will not work in the next breaking release -- [p2p] dial seeds directly without potential peers -- [p2p] exponential backoff for addrs in the address book -- [p2p] mark peer as good if it contributed enough votes or block parts -- [p2p] stop peer if it sends incorrect data, msg to unknown channel, msg we did not expect -- [p2p] when `auth_enc` is true, all dialed peers must have a node ID in their address -- [spec] various improvements -- switched from glide to dep internally for package management -- [wire] prep work for upgrading to new go-wire (which is now called go-amino) - -FEATURES: -- [config] exposed `auth_enc` flag to enable/disable encryption -- [config] added the `--p2p.private_peer_ids` flag and `PrivatePeerIDs` config variable (see config for description) -- [rpc] added `/health` endpoint, which returns empty result for now -- [types/priv_validator] new format and socket client, allowing for remote signing - -BUG FIXES: -- [consensus] fix liveness bug by introducing ValidBlock mechanism - -## 0.16.0 (February 20th, 2018) - -BREAKING CHANGES: -- [config] use $TMHOME/config for all config and json files -- [p2p] old `--p2p.seeds` is now `--p2p.persistent_peers` (persistent peers to which TM will always connect to) -- [p2p] now `--p2p.seeds` only used for getting addresses (if addrbook is empty; not persistent) -- [p2p] NodeInfo: remove RemoteAddr and add Channels - - we must have at least one overlapping channel with peer - - we only send msgs for channels the peer advertised -- [p2p/conn] pong timeout -- [lite] comment out IAVL related code - -FEATURES: -- [p2p] added new `/dial_peers&persistent=_` **unsafe** endpoint -- [p2p] persistent node key in `$THMHOME/config/node_key.json` -- [p2p] introduce peer ID and authenticate peers by ID using addresses like `ID@IP:PORT` -- [p2p/pex] new seed mode crawls the network and serves as a seed. -- [config] MempoolConfig.CacheSize -- [config] P2P.SeedMode (`--p2p.seed_mode`) - -IMPROVEMENT: -- [p2p/pex] stricter rules in the PEX reactor for better handling of abuse -- [p2p] various improvements to code structure including subpackages for `pex` and `conn` -- [docs] new spec! -- [all] speed up the tests! - -BUG FIX: -- [blockchain] StopPeerForError on timeout -- [consensus] StopPeerForError on a bad Maj23 message -- [state] flush mempool conn before calling commit -- [types] fix priv val signing things that only differ by timestamp -- [mempool] fix memory leak causing zombie peers -- [p2p/conn] fix potential deadlock - -## 0.15.0 (December 29, 2017) - -BREAKING CHANGES: -- [p2p] enable the Peer Exchange reactor by default -- [types] add Timestamp field to Proposal/Vote -- [types] add new fields to Header: TotalTxs, ConsensusParamsHash, LastResultsHash, EvidenceHash -- [types] add Evidence to Block -- [types] simplify ValidateBasic -- [state] updates to support changes to the header -- [state] Enforce <1/3 of validator set can change at a time - -FEATURES: -- [state] Send indices of absent validators and addresses of byzantine validators in BeginBlock -- [state] Historical ConsensusParams and ABCIResponses -- [docs] Specification for the base Tendermint data structures. -- [evidence] New evidence reactor for gossiping and managing evidence -- [rpc] `/block_results?height=X` returns the DeliverTx results for a given height. - -IMPROVEMENTS: -- [consensus] Better handling of corrupt WAL file - -BUG FIXES: -- [lite] fix race -- [state] validate block.Header.ValidatorsHash -- [p2p] allow seed addresses to be prefixed with eg. `tcp://` -- [p2p] use consistent key to refer to peers so we dont try to connect to existing peers -- [cmd] fix `tendermint init` to ignore files that are there and generate files that aren't. - -## 0.14.0 (December 11, 2017) - -BREAKING CHANGES: -- consensus/wal: removed separator -- rpc/client: changed Subscribe/Unsubscribe/UnsubscribeAll funcs signatures to be identical to event bus. - -FEATURES: -- new `tendermint lite` command (and `lite/proxy` pkg) for running a light-client RPC proxy. - NOTE it is currently insecure and its APIs are not yet covered by semver - -IMPROVEMENTS: -- rpc/client: can act as event bus subscriber (See https://github.com/tendermint/tendermint/issues/945). -- p2p: use exponential backoff from seconds to hours when attempting to reconnect to persistent peer -- config: moniker defaults to the machine's hostname instead of "anonymous" - -BUG FIXES: -- p2p: no longer exit if one of the seed addresses is incorrect - -## 0.13.0 (December 6, 2017) - -BREAKING CHANGES: -- abci: update to v0.8 using gogo/protobuf; includes tx tags, vote info in RequestBeginBlock, data.Bytes everywhere, use int64, etc. -- types: block heights are now `int64` everywhere -- types & node: EventSwitch and EventCache have been replaced by EventBus and EventBuffer; event types have been overhauled -- node: EventSwitch methods now refer to EventBus -- rpc/lib/types: RPCResponse is no longer a pointer; WSRPCConnection interface has been modified -- rpc/client: WaitForOneEvent takes an EventsClient instead of types.EventSwitch -- rpc/client: Add/RemoveListenerForEvent are now Subscribe/Unsubscribe -- rpc/core/types: ResultABCIQuery wraps an abci.ResponseQuery -- rpc: `/subscribe` and `/unsubscribe` take `query` arg instead of `event` -- rpc: `/status` returns the LatestBlockTime in human readable form instead of in nanoseconds -- mempool: cached transactions return an error instead of an ABCI response with BadNonce - -FEATURES: -- rpc: new `/unsubscribe_all` WebSocket RPC endpoint -- rpc: new `/tx_search` endpoint for filtering transactions by more complex queries -- p2p/trust: new trust metric for tracking peers. See ADR-006 -- config: TxIndexConfig allows to set what DeliverTx tags to index - -IMPROVEMENTS: -- New asynchronous events system using `tmlibs/pubsub` -- logging: Various small improvements -- consensus: Graceful shutdown when app crashes -- tests: Fix various non-deterministic errors -- p2p: more defensive programming - -BUG FIXES: -- consensus: fix panic where prs.ProposalBlockParts is not initialized -- p2p: fix panic on bad channel - -## 0.12.1 (November 27, 2017) - -BUG FIXES: -- upgrade tmlibs dependency to enable Windows builds for Tendermint - -## 0.12.0 (October 27, 2017) - -BREAKING CHANGES: - - rpc/client: websocket ResultsCh and ErrorsCh unified in ResponsesCh. - - rpc/client: ABCIQuery no longer takes `prove` - - state: remove GenesisDoc from state. - - consensus: new binary WAL format provides efficiency and uses checksums to detect corruption - - use scripts/wal2json to convert to json for debugging - -FEATURES: - - new `Verifiers` pkg contains the tendermint light-client library (name subject to change)! - - rpc: `/genesis` includes the `app_options` . - - rpc: `/abci_query` takes an additional `height` parameter to support historical queries. - - rpc/client: new ABCIQueryWithOptions supports options like `trusted` (set false to get a proof) and `height` to query a historical height. - -IMPROVEMENTS: - - rpc: `/genesis` result includes `app_options` - - rpc/lib/client: add jitter to reconnects. - - rpc/lib/types: `RPCError` satisfies the `error` interface. - -BUG FIXES: - - rpc/client: fix ws deadlock after stopping - - blockchain: fix panic on AddBlock when peer is nil - - mempool: fix sending on TxsAvailable when a tx has been invalidated - - consensus: dont run WAL catchup if we fast synced - -## 0.11.1 (October 10, 2017) - -IMPROVEMENTS: - - blockchain/reactor: respondWithNoResponseMessage for missing height - -BUG FIXES: - - rpc: fixed client WebSocket timeout - - rpc: client now resubscribes on reconnection - - rpc: fix panics on missing params - - rpc: fix `/dump_consensus_state` to have normal json output (NOTE: technically breaking, but worth a bug fix label) - - types: fixed out of range error in VoteSet.addVote - - consensus: fix wal autofile via https://github.com/tendermint/tmlibs/blob/master/CHANGELOG.md#032-october-2-2017 - -## 0.11.0 (September 22, 2017) - -BREAKING: - - genesis file: validator `amount` is now `power` - - abci: Info, BeginBlock, InitChain all take structs - - rpc: various changes to match JSONRPC spec (http://www.jsonrpc.org/specification), including breaking ones: - - requests that previously returned HTTP code 4XX now return 200 with an error code in the JSONRPC. - - `rpctypes.RPCResponse` uses new `RPCError` type instead of `string`. - - - cmd: if there is no genesis, exit immediately instead of waiting around for one to show. - - types: `Signer.Sign` returns an error. - - state: every validator set change is persisted to disk, which required some changes to the `State` structure. - - p2p: new `p2p.Peer` interface used for all reactor methods (instead of `*p2p.Peer` struct). - -FEATURES: - - rpc: `/validators?height=X` allows querying of validators at previous heights. - - rpc: Leaving the `height` param empty for `/block`, `/validators`, and `/commit` will return the value for the latest height. - -IMPROVEMENTS: - - docs: Moved all docs from the website and tools repo in, converted to `.rst`, and cleaned up for presentation on `tendermint.readthedocs.io` - -BUG FIXES: - - fix WAL openning issue on Windows - -## 0.10.4 (September 5, 2017) - -IMPROVEMENTS: -- docs: Added Slate docs to each rpc function (see rpc/core) -- docs: Ported all website docs to Read The Docs -- config: expose some p2p params to tweak performance: RecvRate, SendRate, and MaxMsgPacketPayloadSize -- rpc: Upgrade the websocket client and server, including improved auto reconnect, and proper ping/pong - -BUG FIXES: -- consensus: fix panic on getVoteBitArray -- consensus: hang instead of panicking on byzantine consensus failures -- cmd: dont load config for version command - -## 0.10.3 (August 10, 2017) - -FEATURES: -- control over empty block production: - - new flag, `--consensus.create_empty_blocks`; when set to false, blocks are only created when there are txs or when the AppHash changes. - - new config option, `consensus.create_empty_blocks_interval`; an empty block is created after this many seconds. - - in normal operation, `create_empty_blocks = true` and `create_empty_blocks_interval = 0`, so blocks are being created all the time (as in all previous versions of tendermint). The number of empty blocks can be reduced by increasing `create_empty_blocks_interval` or by setting `create_empty_blocks = false`. - - new `TxsAvailable()` method added to Mempool that returns a channel which fires when txs are available. - - new heartbeat message added to consensus reactor to notify peers that a node is waiting for txs before entering propose step. -- rpc: Add `syncing` field to response returned by `/status`. Is `true` while in fast-sync mode. - -IMPROVEMENTS: -- various improvements to documentation and code comments - -BUG FIXES: -- mempool: pass height into constructor so it doesn't always start at 0 - -## 0.10.2 (July 10, 2017) - -FEATURES: -- Enable lower latency block commits by adding consensus reactor sleep durations and p2p flush throttle timeout to the config - -IMPROVEMENTS: -- More detailed logging in the consensus reactor and state machine -- More in-code documentation for many exposed functions, especially in consensus/reactor.go and p2p/switch.go -- Improved readability for some function definitions and code blocks with long lines - -## 0.10.1 (June 28, 2017) - -FEATURES: -- Use `--trace` to get stack traces for logged errors -- types: GenesisDoc.ValidatorHash returns the hash of the genesis validator set -- types: GenesisDocFromFile parses a GenesiDoc from a JSON file - -IMPROVEMENTS: -- Add a Code of Conduct -- Variety of improvements as suggested by `megacheck` tool -- rpc: deduplicate tests between rpc/client and rpc/tests -- rpc: addresses without a protocol prefix default to `tcp://`. `http://` is also accepted as an alias for `tcp://` -- cmd: commands are more easily reuseable from other tools -- DOCKER: automate build/push - -BUG FIXES: -- Fix log statements using keys with spaces (logger does not currently support spaces) -- rpc: set logger on websocket connection -- rpc: fix ws connection stability by setting write deadline on pings - -## 0.10.0 (June 2, 2017) - -Includes major updates to configuration, logging, and json serialization. -Also includes the Grand Repo-Merge of 2017. - -BREAKING CHANGES: - -- Config and Flags: - - The `config` map is replaced with a [`Config` struct](https://github.com/tendermint/tendermint/blob/master/config/config.go#L11), -containing substructs: `BaseConfig`, `P2PConfig`, `MempoolConfig`, `ConsensusConfig`, `RPCConfig` - - This affects the following flags: - - `--seeds` is now `--p2p.seeds` - - `--node_laddr` is now `--p2p.laddr` - - `--pex` is now `--p2p.pex` - - `--skip_upnp` is now `--p2p.skip_upnp` - - `--rpc_laddr` is now `--rpc.laddr` - - `--grpc_laddr` is now `--rpc.grpc_laddr` - - Any configuration option now within a substract must come under that heading in the `config.toml`, for instance: - ``` - [p2p] - laddr="tcp://1.2.3.4:46656" - - [consensus] - timeout_propose=1000 - ``` - - Use viper and `DefaultConfig() / TestConfig()` functions to handle defaults, and remove `config/tendermint` and `config/tendermint_test` - - Change some function and method signatures to - - 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`. -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 - -- JSON serialization: - - Replace `[TypeByte, Xxx]` with `{"type": "some-type", "data": Xxx}` in RPC and all `.json` files by using `go-wire/data`. For instance, a public key is now: - ``` - "pub_key": { - "type": "ed25519", - "data": "83DDF8775937A4A12A2704269E2729FCFCD491B933C4B0A7FFE37FE41D7760D0" - } - ``` - - Remove type information about RPC responses, so `[TypeByte, {"jsonrpc": "2.0", ... }]` is now just `{"jsonrpc": "2.0", ... }` - - Change `[]byte` to `data.Bytes` in all serialized types (for hex encoding) - - Lowercase the JSON tags in `ValidatorSet` fields - - Introduce `EventDataInner` for serializing events - -- Other: - - Send InitChain message in handshake if `appBlockHeight == 0` - - Do not include the `Accum` field when computing the validator hash. This makes the ValidatorSetHash unique for a given validator set, rather than changing with every block (as the Accum changes) - - Unsafe RPC calls are not enabled by default. This includes `/dial_seeds`, and all calls prefixed with `unsafe`. Use the `--rpc.unsafe` flag to enable. - - -FEATURES: - -- Per-module log levels. For instance, the new default is `state:info,*:error`, which means the `state` package logs at `info` level, and everything else logs at `error` level -- Log if a node is validator or not in every consensus round -- Use ldflags to set git hash as part of the version -- Ignore `address` and `pub_key` fields in `priv_validator.json` and overwrite them with the values derrived from the `priv_key` - -IMPROVEMENTS: - -- Merge `tendermint/go-p2p -> tendermint/tendermint/p2p` and `tendermint/go-rpc -> tendermint/tendermint/rpc/lib` -- Update paths for grand repo merge: - - `go-common -> tmlibs/common` - - `go-data -> go-wire/data` - - All other `go-` libs, except `go-crypto` and `go-wire`, are merged under `tmlibs` -- No global loggers (loggers are passed into constructors, or preferably set with a SetLogger method) -- Return HTTP status codes with errors for RPC responses -- Limit `/blockchain_info` call to return a maximum of 20 blocks -- Use `.Wrap()` and `.Unwrap()` instead of eg. `PubKeyS` for `go-crypto` types -- RPC JSON responses use pretty printing (via `json.MarshalIndent`) -- Color code different instances of the consensus for tests -- Isolate viper to `cmd/tendermint/commands` and do not read config from file for tests - - -## 0.9.2 (April 26, 2017) - -BUG FIXES: - -- Fix bug in `ResetPrivValidator` where we were using the global config and log (causing external consumers, eg. basecoin, to fail). - -## 0.9.1 (April 21, 2017) - -FEATURES: - -- Transaction indexing - txs are indexed by their hash using a simple key-value store; easily extended to more advanced indexers -- New `/tx?hash=X` endpoint to query for transactions and their DeliverTx result by hash. Optionally returns a proof of the tx's inclusion in the block -- `tendermint testnet` command initializes files for a testnet - -IMPROVEMENTS: - -- CLI now uses Cobra framework -- TMROOT is now TMHOME (TMROOT will stop working in 0.10.0) -- `/broadcast_tx_XXX` also returns the Hash (can be used to query for the tx) -- `/broadcast_tx_commit` also returns the height the block was committed in -- ABCIResponses struct persisted to disk before calling Commit; makes handshake replay much cleaner -- WAL uses #ENDHEIGHT instead of #HEIGHT (#HEIGHT will stop working in 0.10.0) -- Peers included via `--seeds`, under `seeds` in the config, or in `/dial_seeds` are now persistent, and will be reconnected to if the connection breaks - -BUG FIXES: - -- Fix bug in fast-sync where we stop syncing after a peer is removed, even if they're re-added later -- Fix handshake replay to handle validator set changes and results of DeliverTx when we crash after app.Commit but before state.Save() - -## 0.9.0 (March 6, 2017) - -BREAKING CHANGES: - -- Update ABCI to v0.4.0, where Query is now `Query(RequestQuery) ResponseQuery`, enabling precise proofs at particular heights: - -``` -message RequestQuery{ - bytes data = 1; - string path = 2; - uint64 height = 3; - bool prove = 4; -} - -message ResponseQuery{ - CodeType code = 1; - int64 index = 2; - bytes key = 3; - bytes value = 4; - bytes proof = 5; - uint64 height = 6; - string log = 7; -} -``` +### Rpc/lib/server +- Add handlers tests +- Update with @melekes and @ebuchman feedback +- Separate out Notifications test +- Minor changes to test +- Add test for int parsing -- `BlockMeta` data type unifies its Hash and PartSetHash under a `BlockID`: +### Rpc/lib/types -``` -type BlockMeta struct { - BlockID BlockID `json:"block_id"` // the block hash and partsethash - Header *Header `json:"header"` // The block's Header -} -``` +- RPCResponse.Result is not a pointer -- `ValidatorSet.Proposer` is exposed as a field and persisted with the `State`. Use `GetProposer()` to initialize or update after validator-set changes. +### Rpc/libs/doc -- `tendermint gen_validator` command output is now pure JSON +- Formatting for godoc, closes #2420 -FEATURES: +### Rpc/net_info -- New RPC endpoint `/commit?height=X` returns header and commit for block at height `X` -- Client API for each endpoint, including mocks for testing +- Change RemoteIP type from net.IP to String (#3309) -IMPROVEMENTS: +### Rpc/swagger -- `Node` is now a `BaseService` -- Simplified starting Tendermint in-process from another application -- Better organized Makefile -- Scripts for auto-building binaries across platforms -- Docker image improved, slimmed down (using Alpine), and changed from tendermint/tmbase to tendermint/tendermint -- New repo files: `CONTRIBUTING.md`, Github `ISSUE_TEMPLATE`, `CHANGELOG.md` -- Improvements on CircleCI for managing build/test artifacts -- Handshake replay is doen through the consensus package, possibly using a mockApp -- Graceful shutdown of RPC listeners -- Tests for the PEX reactor and DialSeeds +- Add numtxs to blockmeta (#4139) -BUG FIXES: +### Rpc/test -- Check peer.Send for failure before updating PeerState in consensus -- Fix panic in `/dial_seeds` with invalid addresses -- Fix proposer selection logic in ValidatorSet by taking the address into account in the `accumComparable` -- Fix inconcistencies with `ValidatorSet.Proposer` across restarts by persisting it in the `State` +- /tx +- Restore txindexer after setting null +- Fix test race in TestAppCalls (#4894) +- Wait for mempool CheckTx callback (#4908) +- Wait for subscription in TestTxEventsSentWithBroadcastTxAsync (#4907) +### Rpc/tests -## 0.8.0 (January 13, 2017) +- Panic dont t.Fatal. use random txs for broadcast -BREAKING CHANGES: +### Rpc/wsevents -- New data type `BlockID` to represent blocks: +- Small cleanup -``` -type BlockID struct { - Hash []byte `json:"hash"` - PartsHeader PartSetHeader `json:"parts"` -} -``` +### Rtd -- `Vote` data type now includes validator address and index: +- Build fixes -``` -type Vote struct { - ValidatorAddress []byte `json:"validator_address"` - ValidatorIndex int `json:"validator_index"` - Height int `json:"height"` - Round int `json:"round"` - Type byte `json:"type"` - BlockID BlockID `json:"block_id"` // zero if vote is nil. - Signature crypto.Signature `json:"signature"` -} -``` +### Scripts -- Update TMSP to v0.3.0, where it is now called ABCI and AppendTx is DeliverTx -- Hex strings in the RPC are now "0x" prefixed +- Quickest/easiest fresh install +- Remove install scripts (#4242) +### Scripts/txs -FEATURES: +- Add 0x and randomness -- New message type on the ConsensusReactor, `Maj23Msg`, for peers to alert others they've seen a Maj23, -in order to track and handle conflicting votes intelligently to prevent Byzantine faults from causing halts: +### Secp256k1 -``` -type VoteSetMaj23Message struct { - Height int - Round int - Type byte - BlockID types.BlockID -} -``` - -- Configurable block part set size -- Validator set changes -- Optionally skip TimeoutCommit if we have all the votes -- Handshake between Tendermint and App on startup to sync latest state and ensure consistent recovery from crashes -- GRPC server for BroadcastTx endpoint +- Use compressed pubkey, bitcoin-style address +- Change build tags (#3277) -IMPROVEMENTS: +### Server -- Less verbose logging -- Better test coverage (37% -> 49%) -- Canonical SignBytes for signable types -- Write-Ahead Log for Mempool and Consensus via tmlibs/autofile -- Better in-process testing for the consensus reactor and byzantine faults -- Better crash/restart testing for individual nodes at preset failure points, and of networks at arbitrary points -- Better abstraction over timeout mechanics +- Allow multiple connections +- Return result with error +- Use cmn.ProtocolAndAddress +- Minor refactor -BUG FIXES: +### Service -- Fix memory leak in mempool peer -- Fix panic on POLRound=-1 -- Actually set the CommitTime -- Actually send BeginBlock message -- Fix a liveness issues caused by Byzantine proposals/votes. Uses the new `Maj23Msg`. +- Start/stop logs are info, ignored are debug +- Reset() for restarts +### Shame -## 0.7.4 (December 14, 2016) +- Forgot a file +- Version bump 0.7.4 +- Forgot to add new code pkg -FEATURES: +### SocketClient -- Enable the Peer Exchange reactor with the `--pex` flag for more resilient gossip network (feature still in development, beware dragons) +- Fix and test for StopForError deadlock -IMPROVEMENTS: +### Spec -- Remove restrictions on RPC endpoint `/dial_seeds` to enable manual network configuration +- Fixes from review +- Convert to rst +- Typos & other fixes +- Remove notes, see #1152 +- More fixes +- Minor fixes +- Update encoding.md +- Note on byte arrays, clean up bitarrays and more, add merkle proof, add crypto.go script +- Add Address spec. notes about Query +- Pex update +- Abci notes. closes #1257 +- Move to final location (#1576) +- Add missing field to NodeInfoOther (#2426) -## 0.7.3 (October 20, 2016) +### State -IMPROVEMENTS: +- ExecTx bug fixes for create contract +- Fix debug logs +- Fix CreateAddress to use Address not Word +- Fixes for creating a contract and msging it in the same block +- Fix GetStorage on blockcache with unknown account +- FireEvents flag on ExecTx and fixes for GetAccount +- ApplyBlock +- AppHashIsStale -> IntermediateState +- Remove StateIntermediate +- ABCIResponses, s.Save() in ApplyBlock +- Comments; use wire.BinaryBytes +- Persist validators +- Minor comment fixes +- Return to-be-used function +- TestValidateBlock +- Move methods to funcs +- BlockExecutor +- Re-order funcs. fix tests +- Send byzantine validators in BeginBlock +- Builds +- Fix txResult issue with UnmarshalBinary into ptr +- S -> state +- B -> block +- Err if 0 power validator is added to the validator set +- Format panics +- Require block.Time of the fist block to be genesis time (#2594) +- Use last height changed if validator set is empty (#3560) +- Add more tests for block validation (#3674) +- Txindex/kv: fsync data to disk immediately after receiving it (#4104) +- Txindex/kv: return an error if there's one (#4095) +- Export InitStateVersion +- Proto migration (#4951) +- Proto migration (#4972) +- Revert event hashing (#5159) +- Don't save genesis state in database when loaded (#5231) +- Define interface for state store (#5348) +- Fix block event indexing reserved key check (#6314) (#6315) -- Type safe FireEvent -- More WAL/replay tests -- Cleanup some docs +### State/store -BUG FIXES: +- Remove extra `if` statement (#3774) -- Fix deadlock in mempool for synchronous apps -- Replay handles non-empty blocks -- Fix race condition in HeightVoteSet +### Statesync -## 0.7.2 (September 11, 2016) +- Use Protobuf instead of Amino for p2p traffic (#4943) +- Fix valset off-by-one causing consensus failures (#5311) +- Broadcast snapshot request to all peers on startup (#5320) +- Fix the validator set heights (again) (#5330) +- Check all necessary heights when adding snapshot to pool (#5516) (#5518) +- Improve e2e test outcomes (backport #6378) (#6380) -BUG FIXES: +### Store -- Set mustConnect=false so tendermint will retry connecting to the app +- Register block amino, not just crypto (#3894) +- Proto migration (#4974) -## 0.7.1 (September 10, 2016) +### Swagger -FEATURES: +- Update swagger port (#4498) +- Remove duplicate blockID +- Define version (#4952) +- Update (#5257) -- New TMSP connection for Query/Info -- New RPC endpoints: - - `tmsp_query` - - `tmsp_info` -- Allow application to filter peers through Query (off by default) +### Template -IMPROVEMENTS: +- Add labels to pr template -- TMSP connection type enforced at compile time -- All listen/client urls use a "tcp://" or "unix://" prefix +### Throttle_timer -BUG FIXES: +- Fix race, use mtx instead of atomic -- Save LastSignature/LastSignBytes to `priv_validator.json` for recovery -- Fix event unsubscribe -- Fix fastsync/blockchain reactor +### Tm-bench -## 0.7.0 (August 7, 2016) +- Improve code shape +- Update dependencies, add total metrics +- Add deprecation warning (#3992) -BREAKING CHANGES: +### Tm-monitor -- Strict SemVer starting now! -- Update to ABCI v0.2.0 -- Validation types now called Commit -- NewBlock event only returns the block header +- Update health after we added / removed node (#2694) +- Update build-docker Makefile target (#3790) +- Add Context to RPC handlers (#3792) + +### Tmbench + +- Fix iterating through the blocks, update readme +- Make tx size configurable +- Update dependencies to use tendermint's master +- Make sendloop act in one second segments (#110) +- Make it more resilient to WSConn breaking (#111) + +### Tmhash + +- Add Sum function + +### Tmsp + +- ResponseInfo and ResponseEndBlock + +### Tmtime + +- Canonical, some comments (#2312) + +### Toml + +- Make sections standout (#4993) + +### Tool + +- Add Mergify (#4490) + +### Tooling + +- Remove tools/Makefile (bp #6102) (#6106) + +### Tools + +- Remove redundant grep -v vendors/ (#1996) +- Clean up Makefile and remove LICENSE file (#2042) +- Refactor tm-bench (#2570) +- Remove need to install buf (#4605) +- Update gogoproto get cmd (#5007) + +### Tools.mk + +- Use tags instead of revisions where possible +- Install protoc + +### Tools/build + +- Delete stale tools (#4558) + +### Tools/tm-bench + +- Don't count the first block if its empty +- Remove testing flags from help (#1949) +- Don't count the first block if its empty (#1948) +- Bounds check for txSize and improving test cases (#2410) +- Remove tm-bench in favor of tm-load-test (#4169) + +### Tools/tm-signer-harness + +- Fix listener leak in newTestHarnessListener() (#5850) + +### Tools/tmbench + +- Fix the end time being used for statistics calculation +- Improve accuracy with large tx sizes. +- Move statistics to a seperate file + +### Txindexer + +- Refactor Tx Search Aggregation (#3851) + +### Types + +- PrivVal.LastSignature. closes #247 +- Pretty print validators +- Update LastBlockInfo and ConfigInfo +- Copy vote set bit array +- Copy commit bit array +- Benchmark WriteSignBytes +- Canonical_json.go +- SignatureEd25519 -> Signature +- Use mtx on PartSet.String() +- ValSet LastProposer->Proposer and Proposer()->GetProposer() +- []byte -> data.Bytes +- Result and Validator use data.Bytes +- Methods convert pb types to use data.Bytes +- Block comments +- Remove redundant version file +- PrivVal.Sign returns an error +- More . -> cmn +- Comments +- ConsensusParams test + document the ranges/limits +- ConsensusParams: add feedback from @ebuchman and @melekes +- Unexpose valset.To/FromBytes +- Add gas and fee fields to CheckTx +- Use data.Bytes directly in type.proto via gogo/protobuf. wow +- Consolidate some file +- Add note about ReadMessage having no cap +- RequestBeginBlock includes absent and byzantine validators +- Drop uint64 from protobuf.go +- IsOK() +- Int32 with gogo int +- Fix for broken customtype int in gogo +- Add MarshalJSON funcs for Response types with a Code +- Add UnmarshalJSON funcs for Response types +- Compile type assertions to avoid sneaky runtime surprises +- Check ResponseCheckTx too +- Update String() test to assert Prevote type +- Rename exampleVote to examplePrecommit on vote_test +- Add test for IsVoteTypeValid +- Params.Update() +- Comments; compiles; evidence test +- Evidences for merkle hashing; Evidence.String() +- Tx.go comments +- Evidence cleanup +- Better error messages for votes +- Check bufio.Reader +- TxEventBuffer.Flush now uses capacity preserving slice clearing idiom +- RequestInitChain.AppStateBytes +- Update for new go-wire. WriteSignBytes -> SignBytes +- Remove dep on p2p +- Tests build +- Builds +- Revert to old wire. builds +- Working on tests... +- P2pID -> P2PID +- Fix validator_set_test issue with UnmarshalBinary into ptr +- Bring back json.Marshal/Unmarshal for genesis/priv_val +- TestValidatorSetVerifyCommit +- Uncomment some tests +- Hash invoked for nil Data and Header should not panic +- Revert CheckTx/DeliverTx changes. make them the same +- Fix genesis.AppStateJSON +- Lock block on MakePartSet +- Fix formatting when printing signatures +- Allow genesis file to have 0 validators (#2148) +- Remove pubkey from validator hash (#2512) +- Cap evidence in block validation (#2560) +- Remove Version from CanonicalXxx (#2666) +- Dont use SimpleHashFromMap for header. closes #1841 (#2670) +- First field in Canonical structs is Type (#2675) +- Emit tags from BeginBlock/EndBlock (#2747) +- NewValidatorSet doesn't panic on empty valz list (#2938) +- ValidatorSet.Update preserves Accum (#2941) +- Comments on user vs internal events +- Validator set update tests (#3284) +- Followup after validator set changes (#3301) +- Remove check for priority order of existing validators (#3407) +- Refactor PB2TM.ConsensusParams to take BlockTimeIota as an arg (#3442) +- CommitVotes struct as last step towards #1648 (#3298) +- Do not ignore errors returned by PublishWithEvents (#3722) +- Move MakeVote / MakeBlock functions (#3819) +- Add test for block commits with votes for the wrong blockID (#3936) +- Prevent temporary power overflows on validator updates (#4165) +- Change number_txs to num_txs json tag in BlockMeta +- Remove dots from errors in SignedHeader#ValidateBasic +- Change `Commit` to consist of just signatures (#4146) +- Prevent spurious validator power overflow warnings when changing the validator set (#4183) +- VerifyCommitX return when +2/3 sigs are verified (#4445) +- Implement Header#ValidateBasic (#4638) +- Return an error if voting power overflows +- Sort validators by voting power +- Simplify VerifyCommitTrusting +- Remove extra validation in VerifyCommit +- Assert specific error in TestValSetUpdateOverflowRelated +- Remove unnecessary sort call (#4876) +- Create ValidateBasic() funcs for validator and validator set (#4905) +- Remove VerifyFutureCommit (#4961) +- Migrate params to protobuf (#4962) +- Remove duplicated validation in VerifyCommit (#4991) +- Add tests for blockmeta (#5013) +- Remove pubkey options (#5016) +- More test cases for TestValidatorSet_VerifyCommit (#5018) +- Rename partsheader to partsetheader (#5029) +- Fix evidence timestamp calculation (#5032) +- Add AppVersion to ConsensusParams (#5031) +- Reject blocks w/ ConflictingHeadersEvidence (#5041) +- Simplify safeMul (#5061) +- Verify commit fully +- Validatebasic on from proto (#5152) +- Check if nil or empty valset (#5167) +- Comment on need for length prefixing (#5283) + +### Types/heartbeat + +- Test all Heartbeat functions + +### Types/params + +- Introduce EvidenceParams + +### Types/priv_validator + +- Fixes for latest p2p and cmn + +### Types/test + +- Remove slow test cases in TestValSetUpdatePriorityOrderTests (#4903) + +### Types/time + +- Add note about stripping monotonic part + +### Types/validator_set_test + +- Move funcs around + +### Upgrading + +- Add note on rpc/client subpackages (#4636) +- State store change (#5364) +- Update 0.34 instructions with updates since RC4 (#5686) + +### Upnp + +- Keep a link + +### Ux + +- Use docker to format proto files (#5384) + +### Version + +- Bump 0.7.3 +- Add and bump abci version +- Types +- Bump version numbers (#5173) + +### Vm + +- Check errors early to avoid infinite loop +- Fix Pad functions, state: add debug log for create new account +- Fix endianess by flipping on subslic +- Flip sha3 result +- Fix errors not being returned +- Eventable and flip fix on CALL address +- Catch stack underflow on Peek() +### Wal -FEATURES: +- Gr.Close() -- TMSP and RPC support TCP and UNIX sockets -- Addition config options including block size and consensus parameters -- New WAL mode `cswal_light`; logs only the validator's own votes -- New RPC endpoints: - - for starting/stopping profilers, and for updating config - - `/broadcast_tx_commit`, returns when tx is included in a block, else an error - - `/unsafe_flush_mempool`, empties the mempool +### Wip +- Tendermint specification +- Priv val via sockets +- Comment types +- Fix code block in ADR +- Fix nil pointer deference +- Avoid underscore in var name +- Check error of wire read -IMPROVEMENTS: +### Wire -- Various optimizations -- Remove bad or invalidated transactions from the mempool cache (allows later duplicates) -- More elaborate testing using CircleCI including benchmarking throughput on 4 digitalocean droplets +- No codec yet -BUG FIXES: +### Ws -- Various fixes to WAL and replay logic -- Various race conditions +- Small comment -## PreHistory +### WsConnection -Strict versioning only began with the release of v0.7.0, in late summer 2016. -The project itself began in early summer 2014 and was workable decentralized cryptocurrency software by the end of that year. -Through the course of 2015, in collaboration with Eris Industries (now Monax Industries), -many additional features were integrated, including an implementation from scratch of the Ethereum Virtual Machine. -That implementation now forms the heart of [Burrow](https://github.com/hyperledger/burrow). -In the later half of 2015, the consensus algorithm was upgraded with a more asynchronous design and a more deterministic and robust implementation. +- Call onDisconnect -By late 2015, frustration with the difficulty of forking a large monolithic stack to create alternative cryptocurrency designs led to the -invention of the Application Blockchain Interface (ABCI), then called the Tendermint Socket Protocol (TMSP). -The Ethereum Virtual Machine and various other transaction features were removed, and Tendermint was whittled down to a core consensus engine -driving an application running in another process. -The ABCI interface and implementation were iterated on and improved over the course of 2016, -until versioned history kicked in with v0.7.0. + diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index d0f1a4500e..b4cb26ad6e 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -42,6 +42,13 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [node] \#6059 Validate and complete genesis doc before saving to state store (@silasdavis) - [state] \#6067 Batch save state data (@githubsands & @cmwaters) - [libs/log] \#6174 Include timestamp (`ts` field; `time.RFC3339Nano` format) in JSON logger output (@melekes) +- [rpc] [\#6717](https://github.com/tendermint/tendermint/pull/6717) introduce + `/genesis_chunked` rpc endpoint for handling large genesis files by chunking them ### BUG FIXES +- [light] [\#6685](https://github.com/tendermint/tendermint/pull/6685) fix bug + with incorrectly handling contexts that would occasionally freeze state sync. (@cmwaters) +- [statesync] #6881 improvements to stateprovider logic (@cmwaters) +- [ABCI] #6873 change client to use multi-reader mutexes (@tychoish) +- [indexing] #6906 enable the PostgreSQL indexer sink (@creachadair) diff --git a/Makefile b/Makefile index f0776bbd83..000cf6c53e 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,9 @@ CGO_ENABLED ?= 1 ifeq (,$(findstring nostrip,$(TENDERMINT_BUILD_OPTIONS))) BUILD_FLAGS += -trimpath LD_FLAGS += -s -w +else + BUILD_FLAGS += -gcflags=all="-N -l" + export GOTRACEBACK = crash endif # handle race diff --git a/abci/client/client.go b/abci/client/client.go index c5c1ab219a..0bfe7d4970 100644 --- a/abci/client/client.go +++ b/abci/client/client.go @@ -81,7 +81,7 @@ type ReqRes struct { *sync.WaitGroup *types.Response // Not set atomically, so be sure to use WaitGroup. - mtx tmsync.Mutex + mtx tmsync.RWMutex done bool // Gets set to true once *after* WaitGroup.Done(). cb func(*types.Response) // A single callback that may be set. } @@ -131,16 +131,16 @@ func (r *ReqRes) InvokeCallback() { // // ref: https://github.com/tendermint/tendermint/issues/5439 func (r *ReqRes) GetCallback() func(*types.Response) { - r.mtx.Lock() - defer r.mtx.Unlock() + r.mtx.RLock() + defer r.mtx.RUnlock() return r.cb } // SetDone marks the ReqRes object as done. func (r *ReqRes) SetDone() { r.mtx.Lock() + defer r.mtx.Unlock() r.done = true - r.mtx.Unlock() } func waitGroup1() (wg *sync.WaitGroup) { diff --git a/abci/client/grpc_client.go b/abci/client/grpc_client.go index b375c6cc9c..71f460d2bd 100644 --- a/abci/client/grpc_client.go +++ b/abci/client/grpc_client.go @@ -27,7 +27,7 @@ type grpcClient struct { conn *grpc.ClientConn chReqRes chan *ReqRes // dispatches "async" responses to callbacks *in order*, needed by mempool - mtx tmsync.Mutex + mtx tmsync.RWMutex addr string err error resCb func(*types.Request, *types.Response) // listens to all callbacks @@ -146,8 +146,8 @@ func (cli *grpcClient) StopForError(err error) { } func (cli *grpcClient) Error() error { - cli.mtx.Lock() - defer cli.mtx.Unlock() + cli.mtx.RLock() + defer cli.mtx.RUnlock() return cli.err } @@ -155,8 +155,8 @@ func (cli *grpcClient) Error() error { // NOTE: callback may get internally generated flush responses. func (cli *grpcClient) SetResponseCallback(resCb Callback) { cli.mtx.Lock() + defer cli.mtx.Unlock() cli.resCb = resCb - cli.mtx.Unlock() } //---------------------------------------- diff --git a/abci/client/local_client.go b/abci/client/local_client.go index b3bdc451dc..09e6446410 100644 --- a/abci/client/local_client.go +++ b/abci/client/local_client.go @@ -15,27 +15,35 @@ var _ Client = (*localClient)(nil) type localClient struct { service.BaseService - mtx *tmsync.Mutex + mtx *tmsync.RWMutex types.Application Callback } -func NewLocalClient(mtx *tmsync.Mutex, app types.Application) Client { +var _ Client = (*localClient)(nil) + +// NewLocalClient creates a local client, which will be directly calling the +// methods of the given app. +// +// Both Async and Sync methods ignore the given context.Context parameter. +func NewLocalClient(mtx *tmsync.RWMutex, app types.Application) Client { if mtx == nil { - mtx = new(tmsync.Mutex) + mtx = &tmsync.RWMutex{} } + cli := &localClient{ mtx: mtx, Application: app, } + cli.BaseService = *service.NewBaseService(nil, "localClient", cli) return cli } func (app *localClient) SetResponseCallback(cb Callback) { app.mtx.Lock() + defer app.mtx.Unlock() app.Callback = cb - app.mtx.Unlock() } // TODO: change types.Application to include Error()? @@ -59,8 +67,8 @@ func (app *localClient) EchoAsync(msg string) *ReqRes { } func (app *localClient) InfoAsync(req types.RequestInfo) *ReqRes { - app.mtx.Lock() - defer app.mtx.Unlock() + app.mtx.RLock() + defer app.mtx.RUnlock() res := app.Application.Info(req) return app.callback( @@ -103,8 +111,8 @@ func (app *localClient) CheckTxAsync(req types.RequestCheckTx) *ReqRes { } func (app *localClient) QueryAsync(req types.RequestQuery) *ReqRes { - app.mtx.Lock() - defer app.mtx.Unlock() + app.mtx.RLock() + defer app.mtx.RUnlock() res := app.Application.Query(req) return app.callback( @@ -212,8 +220,8 @@ func (app *localClient) EchoSync(msg string) (*types.ResponseEcho, error) { } func (app *localClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) { - app.mtx.Lock() - defer app.mtx.Unlock() + app.mtx.RLock() + defer app.mtx.RUnlock() res := app.Application.Info(req) return &res, nil @@ -244,8 +252,8 @@ func (app *localClient) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCh } func (app *localClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) { - app.mtx.Lock() - defer app.mtx.Unlock() + app.mtx.RLock() + defer app.mtx.RUnlock() res := app.Application.Query(req) return &res, nil diff --git a/abci/client/socket_client.go b/abci/client/socket_client.go index a369f878c7..fd32315a60 100644 --- a/abci/client/socket_client.go +++ b/abci/client/socket_client.go @@ -34,7 +34,7 @@ type socketClient struct { reqQueue chan *ReqRes flushTimer *timer.ThrottleTimer - mtx tmsync.Mutex + mtx tmsync.RWMutex err error reqSent *list.List // list of requests sent, waiting for response resCb func(*types.Request, *types.Response) // called on all requests, if set. @@ -99,8 +99,8 @@ func (cli *socketClient) OnStop() { // Error returns an error if the client was stopped abruptly. func (cli *socketClient) Error() error { - cli.mtx.Lock() - defer cli.mtx.Unlock() + cli.mtx.RLock() + defer cli.mtx.RUnlock() return cli.err } @@ -110,8 +110,8 @@ func (cli *socketClient) Error() error { // NOTE: callback may get internally generated flush responses. func (cli *socketClient) SetResponseCallback(resCb Callback) { cli.mtx.Lock() + defer cli.mtx.Unlock() cli.resCb = resCb - cli.mtx.Unlock() } //---------------------------------------- diff --git a/abci/example/kvstore/helpers.go b/abci/example/kvstore/helpers.go index 5217f7f663..068e8ffa62 100644 --- a/abci/example/kvstore/helpers.go +++ b/abci/example/kvstore/helpers.go @@ -5,10 +5,12 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/bls12381" cryptoenc "github.com/tendermint/tendermint/crypto/encoding" + dashtypes "github.com/tendermint/tendermint/dash/types" ) -func ValUpdate(pubKey crypto.PubKey, proTxHash crypto.ProTxHash) types.ValidatorUpdate { - return types.UpdateValidator(proTxHash, pubKey.Bytes(), 100) +func ValUpdate( + pubKey crypto.PubKey, proTxHash crypto.ProTxHash, address dashtypes.ValidatorAddress) types.ValidatorUpdate { + return types.UpdateValidator(proTxHash, pubKey.Bytes(), 100, address.String()) } // RandValidatorSetUpdate returns a list of cnt validators for initializing @@ -19,7 +21,7 @@ func RandValidatorSetUpdate(cnt int) types.ValidatorSetUpdate { privKeys, proTxHashes, thresholdPublicKey := bls12381.CreatePrivLLMQDataDefaultThreshold(cnt) for i := 0; i < cnt; i++ { - res[i] = ValUpdate(privKeys[i].PubKey(), proTxHashes[i]) + res[i] = ValUpdate(privKeys[i].PubKey(), proTxHashes[i], dashtypes.RandValidatorAddress()) } thresholdPublicKeyABCI, err := cryptoenc.PubKeyToProto(thresholdPublicKey) if err != nil { diff --git a/abci/example/kvstore/kvstore_test.go b/abci/example/kvstore/kvstore_test.go index d02a8a84db..c513f2752b 100644 --- a/abci/example/kvstore/kvstore_test.go +++ b/abci/example/kvstore/kvstore_test.go @@ -180,9 +180,9 @@ func makeApplyBlock( } kvstore.BeginBlock(types.RequestBeginBlock{Hash: hash, Header: header}) - for _, tx := range txs { + for i, tx := range txs { if r := kvstore.DeliverTx(types.RequestDeliverTx{Tx: tx}); r.IsErr() { - t.Fatal(r) + t.Fatal(fmt.Sprintf("i=%d, tx=%s, err=%s", i, tx, r.String())) } } resEndBlock := kvstore.EndBlock(types.RequestEndBlock{Height: header.Height}) diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index 9e259d2d0d..ce33fa0b64 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -56,6 +56,10 @@ func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication } } +func (app *PersistentKVStoreApplication) Close() error { + return app.app.state.db.Close() +} + func (app *PersistentKVStoreApplication) SetLogger(l log.Logger) { app.logger = l } @@ -108,7 +112,7 @@ func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) (res return resQuery case "/val": - key := []byte("val:" + string(reqQuery.Data)) + key := []byte(valSetTxKey(string(reqQuery.Data))) value, err := app.app.state.db.Get(key) if err != nil { panic(err) @@ -233,6 +237,12 @@ func (app *PersistentKVStoreApplication) ValidatorSet() (validatorSet types.Vali return validatorSet } +// valSetTxKey generates a key for validator set change transaction, like: +// `val:BASE64ENCODINGOFPROTXHASH` +func valSetTxKey(proTxHashBase64 string) string { + return ValidatorSetChangePrefix + proTxHashBase64 +} + func MakeValSetChangeTx(proTxHash []byte, pubkey *pc.PublicKey, power int64) []byte { pubStr := "" if pubkey != nil { @@ -243,12 +253,11 @@ func MakeValSetChangeTx(proTxHash []byte, pubkey *pc.PublicKey, power int64) []b pubStr = base64.StdEncoding.EncodeToString(pk.Bytes()) } proTxHashStr := base64.StdEncoding.EncodeToString(proTxHash) - return []byte(fmt.Sprintf("val:%s!%s!%d", proTxHashStr, pubStr, power)) + return []byte(valSetTxKey(proTxHashStr) + "!" + pubStr + "!" + strconv.FormatInt(power, 10)) } func MakeValSetRemovalTx(proTxHash []byte) []byte { - proTxHashStr := base64.StdEncoding.EncodeToString(proTxHash) - return []byte(fmt.Sprintf("val:%s!!%d", proTxHashStr, 0)) + return MakeValSetChangeTx(proTxHash, nil, 0) } func MakeThresholdPublicKeyChangeTx(thresholdPublicKey pc.PublicKey) []byte { @@ -317,8 +326,7 @@ func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.Respon Code: code.CodeTypeEncodingError, Log: fmt.Sprintf("Power (%s) is not an int", powerS)} } - - return app.updateValidatorSet(types.UpdateValidator(proTxHash, pubkey, power)) + return app.updateValidatorSet(types.UpdateValidator(proTxHash, pubkey, power, "")) } // format is "tpk:pubkey" @@ -365,7 +373,7 @@ func (app *PersistentKVStoreApplication) updateValidatorSet(v types.ValidatorUpd if v.ProTxHash == nil { panic(fmt.Errorf("proTxHash can not be nil")) } - key := []byte("val:" + string(v.ProTxHash)) + key := []byte(valSetTxKey(string(v.ProTxHash))) if v.Power == 0 { // remove validator diff --git a/abci/tests/server/client.go b/abci/tests/server/client.go index eb8cba6399..d44bf54593 100644 --- a/abci/tests/server/client.go +++ b/abci/tests/server/client.go @@ -17,10 +17,10 @@ func InitChain(client abcicli.Client) error { vals := make([]types.ValidatorUpdate, total) privKeys, proTxHashes, thresholdPublicKey := bls12381.CreatePrivLLMQDataDefaultThreshold(total) for i := 0; i < total; i++ { - pubkey := privKeys[i].PubKey().Bytes() + pubkey := privKeys[i].PubKey() proTxHash := proTxHashes[i] power := 100 - vals[i] = types.UpdateValidator(proTxHash, pubkey, int64(power)) + vals[i] = types.UpdateValidator(proTxHash, pubkey.Bytes(), int64(power), "") } abciThresholdPublicKey, err := cryptoenc.PubKeyToProto(thresholdPublicKey) if err != nil { diff --git a/abci/types/pubkey.go b/abci/types/pubkey.go index e810eaa371..3ddbdea5b7 100644 --- a/abci/types/pubkey.go +++ b/abci/types/pubkey.go @@ -7,13 +7,19 @@ import ( crypto2 "github.com/tendermint/tendermint/proto/tendermint/crypto" ) -func UpdateValidator(proTxHash []byte, pk []byte, power int64) ValidatorUpdate { +func UpdateValidator(proTxHash crypto.ProTxHash, + pubkeyBytes []byte, + power int64, + nodeAddress string, +) ValidatorUpdate { valUpdate := ValidatorUpdate{ - Power: power, - ProTxHash: proTxHash, + Power: power, + ProTxHash: proTxHash, + NodeAddress: nodeAddress, } - if len(pk) > 0 { - pke := bls12381.PubKey(pk) + + if len(pubkeyBytes) > 0 { + pke := bls12381.PubKey(pubkeyBytes) pkp, err := cryptoenc.PubKeyToProto(pke) if err != nil { panic(err) diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index 92eebcedc7..abb0f6fc98 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -1616,12 +1616,11 @@ func (m *ResponseFlush) XXX_DiscardUnknown() { var xxx_messageInfo_ResponseFlush proto.InternalMessageInfo type ResponseInfo struct { - Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` - Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` - AppVersion uint64 `protobuf:"varint,3,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"` - LastBlockHeight int64 `protobuf:"varint,4,opt,name=last_block_height,json=lastBlockHeight,proto3" json:"last_block_height,omitempty"` - LastBlockAppHash []byte `protobuf:"bytes,5,opt,name=last_block_app_hash,json=lastBlockAppHash,proto3" json:"last_block_app_hash,omitempty"` - LastCoreChainLockedHeight uint32 `protobuf:"varint,100,opt,name=last_core_chain_locked_height,json=lastCoreChainLockedHeight,proto3" json:"last_core_chain_locked_height,omitempty"` + Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + AppVersion uint64 `protobuf:"varint,3,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"` + LastBlockHeight int64 `protobuf:"varint,4,opt,name=last_block_height,json=lastBlockHeight,proto3" json:"last_block_height,omitempty"` + LastBlockAppHash []byte `protobuf:"bytes,5,opt,name=last_block_app_hash,json=lastBlockAppHash,proto3" json:"last_block_app_hash,omitempty"` } func (m *ResponseInfo) Reset() { *m = ResponseInfo{} } @@ -1692,13 +1691,6 @@ func (m *ResponseInfo) GetLastBlockAppHash() []byte { return nil } -func (m *ResponseInfo) GetLastCoreChainLockedHeight() uint32 { - if m != nil { - return m.LastCoreChainLockedHeight - } - return 0 -} - // nondeterministic type ResponseSetOption struct { Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` @@ -1767,6 +1759,7 @@ type ResponseInitChain struct { AppHash []byte `protobuf:"bytes,3,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` ValidatorSetUpdate ValidatorSetUpdate `protobuf:"bytes,100,opt,name=validator_set_update,json=validatorSetUpdate,proto3" json:"validator_set_update"` NextCoreChainLockUpdate *types1.CoreChainLock `protobuf:"bytes,101,opt,name=next_core_chain_lock_update,json=nextCoreChainLockUpdate,proto3" json:"next_core_chain_lock_update,omitempty"` + InitialCoreHeight uint32 `protobuf:"varint,102,opt,name=initial_core_height,json=initialCoreHeight,proto3" json:"initial_core_height,omitempty"` } func (m *ResponseInitChain) Reset() { *m = ResponseInitChain{} } @@ -1830,6 +1823,13 @@ func (m *ResponseInitChain) GetNextCoreChainLockUpdate() *types1.CoreChainLock { return nil } +func (m *ResponseInitChain) GetInitialCoreHeight() uint32 { + if m != nil { + return m.InitialCoreHeight + } + return 0 +} + type ResponseQuery struct { Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` // bytes data = 2; // use "value" instead. @@ -2934,9 +2934,10 @@ func (m *Validator) GetProTxHash() []byte { // ValidatorUpdate type ValidatorUpdate struct { - PubKey *crypto.PublicKey `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` - Power int64 `protobuf:"varint,2,opt,name=power,proto3" json:"power,omitempty"` - ProTxHash []byte `protobuf:"bytes,3,opt,name=pro_tx_hash,json=proTxHash,proto3" json:"pro_tx_hash,omitempty"` + PubKey *crypto.PublicKey `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` + Power int64 `protobuf:"varint,2,opt,name=power,proto3" json:"power,omitempty"` + ProTxHash []byte `protobuf:"bytes,3,opt,name=pro_tx_hash,json=proTxHash,proto3" json:"pro_tx_hash,omitempty"` + NodeAddress string `protobuf:"bytes,4,opt,name=node_address,json=nodeAddress,proto3" json:"node_address,omitempty"` } func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} } @@ -2993,6 +2994,13 @@ func (m *ValidatorUpdate) GetProTxHash() []byte { return nil } +func (m *ValidatorUpdate) GetNodeAddress() string { + if m != nil { + return m.NodeAddress + } + return "" +} + type ValidatorSetUpdate struct { ValidatorUpdates []ValidatorUpdate `protobuf:"bytes,1,rep,name=validator_updates,json=validatorUpdates,proto3" json:"validator_updates"` ThresholdPublicKey crypto.PublicKey `protobuf:"bytes,2,opt,name=threshold_public_key,json=thresholdPublicKey,proto3" json:"threshold_public_key"` @@ -3417,195 +3425,197 @@ func init() { func init() { proto.RegisterFile("tendermint/abci/types.proto", fileDescriptor_252557cfdd89a31a) } var fileDescriptor_252557cfdd89a31a = []byte{ - // 3005 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xcd, 0x77, 0x23, 0xc5, - 0xb5, 0x57, 0x4b, 0xb2, 0x3e, 0xae, 0xac, 0x0f, 0xd7, 0x98, 0x19, 0x8d, 0x66, 0xc6, 0x1e, 0x9a, - 0x03, 0x0c, 0x03, 0xd8, 0x0f, 0xcf, 0xe1, 0xeb, 0x3d, 0xde, 0x03, 0x4b, 0x68, 0x90, 0x19, 0x63, - 0x9b, 0xb2, 0x66, 0x78, 0x09, 0x61, 0x9a, 0x96, 0x54, 0x96, 0x9a, 0x91, 0xba, 0x9b, 0xee, 0x92, - 0xb1, 0xd9, 0x42, 0x36, 0xac, 0xc8, 0x2e, 0x59, 0xf0, 0x77, 0x64, 0x91, 0x73, 0xb2, 0x66, 0xc9, - 0x32, 0x2b, 0xc2, 0x81, 0x93, 0x4d, 0x96, 0xd9, 0xe4, 0x9c, 0x9c, 0x93, 0x93, 0x9c, 0xfa, 0xe8, - 0x2f, 0x49, 0x2d, 0xc9, 0xcc, 0x32, 0xbb, 0xae, 0x5b, 0xf7, 0xde, 0xae, 0xaa, 0xae, 0xfa, 0xdd, - 0xdf, 0xbd, 0x5d, 0x70, 0x8d, 0x12, 0xb3, 0x47, 0x9c, 0x91, 0x61, 0xd2, 0x6d, 0xbd, 0xd3, 0x35, - 0xb6, 0xe9, 0xb9, 0x4d, 0xdc, 0x2d, 0xdb, 0xb1, 0xa8, 0x85, 0xca, 0x41, 0xe7, 0x16, 0xeb, 0xac, - 0xdd, 0x08, 0x69, 0x77, 0x9d, 0x73, 0x9b, 0x5a, 0xdb, 0xb6, 0x63, 0x59, 0x27, 0x42, 0xbf, 0x76, - 0x3d, 0xd4, 0xcd, 0xfd, 0x84, 0xbd, 0x45, 0x7a, 0xa5, 0xf1, 0x23, 0x72, 0xee, 0xf5, 0xde, 0x98, - 0xb2, 0xb5, 0x75, 0x47, 0x1f, 0x79, 0xdd, 0x9b, 0x7d, 0xcb, 0xea, 0x0f, 0xc9, 0x36, 0x6f, 0x75, - 0xc6, 0x27, 0xdb, 0xd4, 0x18, 0x11, 0x97, 0xea, 0x23, 0x5b, 0x2a, 0xac, 0xf7, 0xad, 0xbe, 0xc5, - 0x1f, 0xb7, 0xd9, 0x93, 0x90, 0xaa, 0xbf, 0xc9, 0x41, 0x16, 0x93, 0x4f, 0xc7, 0xc4, 0xa5, 0x68, - 0x07, 0xd2, 0xa4, 0x3b, 0xb0, 0xaa, 0xca, 0x4d, 0xe5, 0x56, 0x61, 0xe7, 0xfa, 0xd6, 0xc4, 0xe4, - 0xb6, 0xa4, 0x5e, 0xb3, 0x3b, 0xb0, 0x5a, 0x09, 0xcc, 0x75, 0xd1, 0xcb, 0xb0, 0x72, 0x32, 0x1c, - 0xbb, 0x83, 0x6a, 0x92, 0x1b, 0xdd, 0x88, 0x33, 0xba, 0xcb, 0x94, 0x5a, 0x09, 0x2c, 0xb4, 0xd9, - 0xab, 0x0c, 0xf3, 0xc4, 0xaa, 0xa6, 0xe6, 0xbf, 0x6a, 0xcf, 0x3c, 0xe1, 0xaf, 0x62, 0xba, 0xa8, - 0x0e, 0xe0, 0x12, 0xaa, 0x59, 0x36, 0x35, 0x2c, 0xb3, 0x9a, 0xe6, 0x96, 0x4f, 0xc6, 0x59, 0x1e, - 0x13, 0x7a, 0xc8, 0x15, 0x5b, 0x09, 0x9c, 0x77, 0xbd, 0x06, 0xf3, 0x61, 0x98, 0x06, 0xd5, 0xba, - 0x03, 0xdd, 0x30, 0xab, 0x2b, 0xf3, 0x7d, 0xec, 0x99, 0x06, 0x6d, 0x30, 0x45, 0xe6, 0xc3, 0xf0, - 0x1a, 0x6c, 0xca, 0x9f, 0x8e, 0x89, 0x73, 0x5e, 0xcd, 0xcc, 0x9f, 0xf2, 0xfb, 0x4c, 0x89, 0x4d, - 0x99, 0x6b, 0xa3, 0x26, 0x14, 0x3a, 0xa4, 0x6f, 0x98, 0x5a, 0x67, 0x68, 0x75, 0x1f, 0x55, 0xb3, - 0xdc, 0x58, 0x8d, 0x33, 0xae, 0x33, 0xd5, 0x3a, 0xd3, 0x6c, 0x25, 0x30, 0x74, 0xfc, 0x16, 0x7a, - 0x03, 0x72, 0xdd, 0x01, 0xe9, 0x3e, 0xd2, 0xe8, 0x59, 0x35, 0xc7, 0x7d, 0x6c, 0xc6, 0xf9, 0x68, - 0x30, 0xbd, 0xf6, 0x59, 0x2b, 0x81, 0xb3, 0x5d, 0xf1, 0xc8, 0xe6, 0xdf, 0x23, 0x43, 0xe3, 0x94, - 0x38, 0xcc, 0x3e, 0x3f, 0x7f, 0xfe, 0x6f, 0x0b, 0x4d, 0xee, 0x21, 0xdf, 0xf3, 0x1a, 0xe8, 0x4d, - 0xc8, 0x13, 0xb3, 0x27, 0xa7, 0x01, 0xdc, 0xc5, 0xcd, 0xd8, 0xbd, 0x62, 0xf6, 0xbc, 0x49, 0xe4, - 0x88, 0x7c, 0x46, 0xaf, 0x41, 0xa6, 0x6b, 0x8d, 0x46, 0x06, 0xad, 0x16, 0xb8, 0xf5, 0x46, 0xec, - 0x04, 0xb8, 0x56, 0x2b, 0x81, 0xa5, 0x3e, 0x3a, 0x80, 0xd2, 0xd0, 0x70, 0xa9, 0xe6, 0x9a, 0xba, - 0xed, 0x0e, 0x2c, 0xea, 0x56, 0x57, 0xb9, 0x87, 0xa7, 0xe3, 0x3c, 0xec, 0x1b, 0x2e, 0x3d, 0xf6, - 0x94, 0x5b, 0x09, 0x5c, 0x1c, 0x86, 0x05, 0xcc, 0x9f, 0x75, 0x72, 0x42, 0x1c, 0xdf, 0x61, 0xb5, - 0x38, 0xdf, 0xdf, 0x21, 0xd3, 0xf6, 0xec, 0x99, 0x3f, 0x2b, 0x2c, 0x40, 0x1f, 0xc2, 0xa5, 0xa1, - 0xa5, 0xf7, 0x7c, 0x77, 0x5a, 0x77, 0x30, 0x36, 0x1f, 0x55, 0x4b, 0xdc, 0xe9, 0x73, 0xb1, 0x83, - 0xb4, 0xf4, 0x9e, 0xe7, 0xa2, 0xc1, 0x0c, 0x5a, 0x09, 0xbc, 0x36, 0x9c, 0x14, 0xa2, 0x87, 0xb0, - 0xae, 0xdb, 0xf6, 0xf0, 0x7c, 0xd2, 0x7b, 0x99, 0x7b, 0xbf, 0x1d, 0xe7, 0x7d, 0x97, 0xd9, 0x4c, - 0xba, 0x47, 0xfa, 0x94, 0xb4, 0x9e, 0x85, 0x95, 0x53, 0x7d, 0x38, 0x26, 0xea, 0xb3, 0x50, 0x08, - 0x1d, 0x75, 0x54, 0x85, 0xec, 0x88, 0xb8, 0xae, 0xde, 0x27, 0x1c, 0x19, 0xf2, 0xd8, 0x6b, 0xaa, - 0x25, 0x58, 0x0d, 0x1f, 0x6f, 0x75, 0xe4, 0x1b, 0xb2, 0x83, 0xcb, 0x0c, 0x4f, 0x89, 0xe3, 0xb2, - 0xd3, 0x2a, 0x0d, 0x65, 0x13, 0x3d, 0x05, 0x45, 0xbe, 0x7d, 0x34, 0xaf, 0x9f, 0xa1, 0x47, 0x1a, - 0xaf, 0x72, 0xe1, 0x03, 0xa9, 0xb4, 0x09, 0x05, 0x7b, 0xc7, 0xf6, 0x55, 0x52, 0x5c, 0x05, 0xec, - 0x1d, 0x5b, 0x2a, 0xa8, 0xff, 0x0d, 0x95, 0xc9, 0xd3, 0x8e, 0x2a, 0x90, 0x7a, 0x44, 0xce, 0xe5, - 0xfb, 0xd8, 0x23, 0x5a, 0x97, 0xd3, 0xe2, 0xef, 0xc8, 0x63, 0x39, 0xc7, 0x2f, 0x52, 0xbe, 0xb1, - 0x7f, 0xcc, 0xd1, 0x6b, 0x90, 0x66, 0xa8, 0x29, 0x01, 0xb0, 0xb6, 0x25, 0x20, 0x75, 0xcb, 0x83, - 0xd4, 0xad, 0xb6, 0x07, 0xa9, 0xf5, 0xdc, 0xb7, 0xdf, 0x6f, 0x26, 0xbe, 0xfe, 0xf3, 0xa6, 0x82, - 0xb9, 0x05, 0xba, 0xca, 0x4e, 0xa5, 0x6e, 0x98, 0x9a, 0xd1, 0x93, 0xef, 0xc9, 0xf2, 0xf6, 0x5e, - 0x0f, 0xdd, 0x83, 0x4a, 0xd7, 0x32, 0x5d, 0x62, 0xba, 0x63, 0x57, 0x13, 0x90, 0x2d, 0x61, 0x6f, - 0xfa, 0xd4, 0x34, 0x3c, 0xc5, 0x23, 0xae, 0x87, 0xcb, 0xdd, 0xa8, 0x00, 0x1d, 0x40, 0xf1, 0x54, - 0x1f, 0x1a, 0x3d, 0x9d, 0x5a, 0x8e, 0xe6, 0x12, 0x2a, 0x61, 0xf0, 0xa9, 0x29, 0x4f, 0x0f, 0x3c, - 0xad, 0x63, 0x42, 0xef, 0xdb, 0x3d, 0x9d, 0x92, 0x7a, 0xfa, 0xdb, 0xef, 0x37, 0x15, 0xbc, 0x7a, - 0x1a, 0xea, 0x41, 0xcf, 0x40, 0x59, 0xb7, 0x6d, 0xcd, 0xa5, 0x3a, 0x25, 0x5a, 0xe7, 0x9c, 0x12, - 0x97, 0x83, 0xe2, 0x2a, 0x2e, 0xea, 0xb6, 0x7d, 0xcc, 0xa4, 0x75, 0x26, 0x44, 0x4f, 0x43, 0x89, - 0x01, 0xa0, 0xa1, 0x0f, 0xb5, 0x01, 0x31, 0xfa, 0x03, 0xca, 0xc1, 0x2f, 0x85, 0x8b, 0x52, 0xda, - 0xe2, 0x42, 0xb4, 0x05, 0x97, 0x3c, 0xb5, 0xae, 0xe5, 0x10, 0x4f, 0x97, 0x61, 0x5d, 0x11, 0xaf, - 0xc9, 0xae, 0x86, 0xe5, 0x10, 0xa1, 0xaf, 0xf6, 0xfc, 0x0d, 0xc4, 0xc1, 0x12, 0x21, 0x48, 0xf7, - 0x74, 0xaa, 0xf3, 0x0f, 0xb0, 0x8a, 0xf9, 0x33, 0x93, 0xd9, 0x3a, 0x1d, 0xc8, 0x65, 0xe5, 0xcf, - 0xe8, 0x32, 0x64, 0xa4, 0xeb, 0x14, 0x1f, 0x86, 0x6c, 0xb1, 0x6f, 0x6d, 0x3b, 0xd6, 0x29, 0xe1, - 0xcb, 0x92, 0xc3, 0xa2, 0xa1, 0x7e, 0x99, 0x84, 0xb5, 0x29, 0x58, 0x65, 0x7e, 0x07, 0xba, 0x3b, - 0xf0, 0xde, 0xc5, 0x9e, 0xd1, 0x2b, 0xcc, 0xaf, 0xde, 0x23, 0x8e, 0x0c, 0x67, 0xd5, 0xf0, 0xba, - 0x8a, 0x50, 0xdd, 0xe2, 0xfd, 0x7c, 0x31, 0x13, 0x58, 0x6a, 0xa3, 0x43, 0xa8, 0x0c, 0x75, 0x97, - 0x6a, 0x02, 0xa6, 0xb4, 0x50, 0x68, 0x9b, 0x06, 0xe7, 0x7d, 0xdd, 0x03, 0x36, 0x76, 0x48, 0xa4, - 0xa3, 0xd2, 0x30, 0x22, 0x45, 0x18, 0xd6, 0x3b, 0xe7, 0x9f, 0xeb, 0x26, 0x35, 0x4c, 0xa2, 0xf9, - 0x5f, 0xcc, 0xad, 0xa6, 0x6f, 0xa6, 0x6e, 0x15, 0x76, 0xae, 0x4e, 0x39, 0x6d, 0x9e, 0x1a, 0x3d, - 0x62, 0x76, 0x89, 0x74, 0x77, 0xc9, 0x37, 0xf6, 0xf7, 0x81, 0xab, 0x62, 0x28, 0x45, 0x03, 0x03, - 0x2a, 0x41, 0x92, 0x9e, 0xc9, 0x05, 0x48, 0xd2, 0x33, 0xf4, 0x5f, 0x90, 0x66, 0x93, 0xe4, 0x93, - 0x2f, 0xcd, 0x88, 0xca, 0xd2, 0xae, 0x7d, 0x6e, 0x13, 0xcc, 0x35, 0x55, 0xd5, 0x3f, 0x45, 0x7e, - 0xb0, 0x98, 0xf4, 0xaa, 0x3e, 0x07, 0xe5, 0x89, 0x68, 0x10, 0xfa, 0x7e, 0x4a, 0xf8, 0xfb, 0xa9, - 0x65, 0x28, 0x46, 0xa0, 0x5f, 0xbd, 0x0c, 0xeb, 0xb3, 0x90, 0x5c, 0x1d, 0xf8, 0xf2, 0x08, 0x22, - 0xa3, 0x97, 0x21, 0xe7, 0x43, 0xb9, 0x38, 0xc5, 0xd3, 0x6b, 0xe5, 0x29, 0x63, 0x5f, 0x95, 0x1d, - 0x5f, 0x76, 0x0c, 0xf8, 0x7e, 0x48, 0xf2, 0x81, 0x67, 0x75, 0xdb, 0x6e, 0xe9, 0xee, 0x40, 0xfd, - 0x18, 0xaa, 0x71, 0x30, 0x3d, 0x31, 0x8d, 0xb4, 0xbf, 0x0d, 0x2f, 0x43, 0xe6, 0xc4, 0x72, 0x46, - 0x3a, 0xe5, 0xce, 0x8a, 0x58, 0xb6, 0xd8, 0xf6, 0x14, 0x90, 0x9d, 0xe2, 0x62, 0xd1, 0x50, 0x35, - 0xb8, 0x1a, 0x0b, 0xd5, 0xcc, 0xc4, 0x30, 0x7b, 0x44, 0xac, 0x67, 0x11, 0x8b, 0x46, 0xe0, 0x48, - 0x0c, 0x56, 0x34, 0xd8, 0x6b, 0x5d, 0x3e, 0x57, 0xee, 0x3f, 0x8f, 0x65, 0x4b, 0xfd, 0x4b, 0x0e, - 0x72, 0x98, 0xb8, 0x36, 0xc3, 0x12, 0x54, 0x87, 0x3c, 0x39, 0xeb, 0x12, 0x41, 0xa2, 0x94, 0x58, - 0x12, 0x22, 0xb4, 0x9b, 0x9e, 0x26, 0x63, 0x00, 0xbe, 0x19, 0xba, 0x23, 0x89, 0x62, 0x3c, 0xe7, - 0x93, 0xe6, 0x61, 0xa6, 0xf8, 0x8a, 0xc7, 0x14, 0x53, 0xb1, 0x41, 0x5f, 0x58, 0x4d, 0x50, 0xc5, - 0x3b, 0x92, 0x2a, 0xa6, 0x17, 0xbc, 0x2c, 0xc2, 0x15, 0x1b, 0x11, 0xae, 0xb8, 0xb2, 0x60, 0x9a, - 0x31, 0x64, 0xb1, 0x11, 0x21, 0x8b, 0x99, 0x05, 0x4e, 0x62, 0xd8, 0xe2, 0x2b, 0x1e, 0x5b, 0xcc, - 0x2e, 0x98, 0xf6, 0x04, 0x5d, 0xbc, 0x1b, 0xa5, 0x8b, 0xb9, 0x18, 0x9c, 0xf7, 0xac, 0x63, 0xf9, - 0xe2, 0xff, 0x86, 0xf8, 0x62, 0x3e, 0x96, 0xac, 0x09, 0x27, 0x33, 0x08, 0x63, 0x23, 0x42, 0x18, - 0x61, 0xc1, 0x1a, 0xc4, 0x30, 0xc6, 0xb7, 0xc2, 0x8c, 0xb1, 0x10, 0x4b, 0x3a, 0xe5, 0xa6, 0x99, - 0x45, 0x19, 0x5f, 0xf7, 0x29, 0xe3, 0x6a, 0x2c, 0xe7, 0x95, 0x73, 0x98, 0xe4, 0x8c, 0x87, 0x53, - 0x9c, 0x51, 0x70, 0xbc, 0x67, 0x62, 0x5d, 0x2c, 0x20, 0x8d, 0x87, 0x53, 0xa4, 0xb1, 0xb4, 0xc0, - 0xe1, 0x02, 0xd6, 0xf8, 0xab, 0xd9, 0xac, 0x31, 0x9e, 0xd7, 0xc9, 0x61, 0x2e, 0x47, 0x1b, 0xb5, - 0x18, 0xda, 0x58, 0xe1, 0xee, 0x9f, 0x8f, 0x75, 0x7f, 0x71, 0xde, 0xf8, 0x1c, 0x0b, 0xb3, 0x13, - 0xc0, 0xc1, 0xa0, 0x8a, 0x38, 0x8e, 0xe5, 0x48, 0x4a, 0x26, 0x1a, 0xea, 0x2d, 0x16, 0xf8, 0x03, - 0x90, 0x98, 0xc3, 0x31, 0x79, 0x48, 0x08, 0x01, 0x83, 0xfa, 0x0f, 0x25, 0xb0, 0xe5, 0xb1, 0x32, - 0x4c, 0x1a, 0xf2, 0x92, 0x34, 0x84, 0xa8, 0x67, 0x32, 0x4a, 0x3d, 0x37, 0xa1, 0xc0, 0xa0, 0x7e, - 0x82, 0x55, 0xea, 0xb6, 0xc7, 0x2a, 0xd1, 0x6d, 0x58, 0xe3, 0xb1, 0x5c, 0x10, 0x54, 0x89, 0xef, - 0x69, 0x1e, 0xa6, 0xca, 0xac, 0x43, 0x6c, 0x4e, 0x01, 0xf4, 0x2f, 0xc2, 0xa5, 0x90, 0xae, 0x1f, - 0x42, 0x04, 0x85, 0xaa, 0xf8, 0xda, 0xbb, 0x22, 0x96, 0xa0, 0xb7, 0xe0, 0x86, 0xa4, 0x09, 0x0e, - 0x11, 0xa8, 0xa2, 0xb1, 0x6e, 0xd2, 0xf3, 0x5e, 0xd3, 0xe3, 0x20, 0x7f, 0x55, 0x90, 0x01, 0x87, - 0x70, 0x04, 0xd9, 0xe7, 0x1a, 0x92, 0x30, 0xbd, 0x17, 0x2c, 0x71, 0xc0, 0x79, 0x11, 0xa4, 0xbb, - 0x56, 0x8f, 0xc8, 0x10, 0xc1, 0x9f, 0x19, 0x0f, 0x1e, 0x5a, 0x7d, 0x19, 0x08, 0xd8, 0x23, 0xd3, - 0xf2, 0x71, 0x34, 0x2f, 0x60, 0x52, 0xfd, 0x7d, 0x32, 0xf0, 0x17, 0xd0, 0xe0, 0x59, 0x8c, 0x55, - 0xf9, 0xb9, 0x8c, 0x35, 0x1c, 0x5a, 0x53, 0x91, 0xd0, 0x8a, 0x3e, 0x84, 0xf5, 0x08, 0x99, 0xd5, - 0xc6, 0x9c, 0xa8, 0xf2, 0x55, 0xb8, 0x00, 0xa7, 0x4d, 0x60, 0x74, 0x3a, 0xd5, 0x83, 0x3e, 0x82, - 0x6b, 0x26, 0x39, 0x9b, 0x5a, 0x6b, 0xef, 0x1d, 0x64, 0x1a, 0x46, 0x04, 0xbf, 0x8b, 0xac, 0x3b, - 0xbe, 0xc2, 0x7c, 0x44, 0x44, 0xc2, 0xbd, 0xfa, 0x77, 0x25, 0xd8, 0x97, 0x3e, 0x77, 0xfd, 0x79, - 0x5f, 0x21, 0x88, 0xf1, 0x2b, 0x7c, 0x97, 0xc9, 0x18, 0x2f, 0x33, 0x99, 0x0c, 0x5f, 0xb3, 0x68, - 0x26, 0x93, 0x15, 0x51, 0x9f, 0x37, 0xd0, 0x6b, 0x90, 0xe7, 0x25, 0x26, 0xcd, 0xb2, 0x5d, 0x19, - 0x26, 0xae, 0x85, 0xa7, 0x25, 0x2a, 0x49, 0x5b, 0x47, 0x4c, 0xe7, 0xd0, 0x76, 0x71, 0xce, 0x96, - 0x4f, 0x21, 0xfa, 0x92, 0x8f, 0xb0, 0xe8, 0xeb, 0x90, 0x67, 0xa3, 0x77, 0x6d, 0xbd, 0x4b, 0x38, - 0xe4, 0xe7, 0x71, 0x20, 0x50, 0x1f, 0x02, 0x9a, 0x0e, 0x3a, 0xa8, 0x05, 0x19, 0x72, 0x4a, 0x4c, - 0xca, 0x76, 0x0a, 0xa3, 0xa8, 0x97, 0x67, 0x50, 0x54, 0x62, 0xd2, 0x7a, 0x95, 0x7d, 0xb0, 0xbf, - 0x7e, 0xbf, 0x59, 0x11, 0xda, 0x2f, 0x58, 0x23, 0x83, 0x92, 0x91, 0x4d, 0xcf, 0xb1, 0xb4, 0x57, - 0xbf, 0x48, 0x32, 0xbe, 0x18, 0x09, 0x48, 0x33, 0xd7, 0xd6, 0x3b, 0xf6, 0xc9, 0x50, 0xae, 0xb0, - 0xdc, 0x7a, 0x6f, 0x00, 0xf4, 0x75, 0x57, 0xfb, 0x4c, 0x37, 0x29, 0xe9, 0xc9, 0x45, 0x0f, 0x49, - 0x50, 0x0d, 0x72, 0xac, 0x35, 0x76, 0x49, 0x4f, 0xa6, 0x39, 0x7e, 0x3b, 0x34, 0xcf, 0xec, 0xe3, - 0xcd, 0x33, 0xba, 0xca, 0xb9, 0xc9, 0x55, 0xfe, 0x75, 0xe8, 0x64, 0x06, 0xd4, 0xfa, 0x3f, 0x6f, - 0x1d, 0xfe, 0x96, 0x64, 0x19, 0x46, 0x94, 0x19, 0xa0, 0xff, 0x87, 0x2b, 0x13, 0x00, 0x25, 0x8f, - 0xb5, 0x2b, 0x29, 0xe9, 0x62, 0x9c, 0x7a, 0x22, 0x8a, 0x53, 0xe2, 0x54, 0xbb, 0xa1, 0x69, 0xa5, - 0x1e, 0x73, 0x5a, 0x0b, 0xf0, 0xa7, 0xf7, 0x78, 0xf8, 0x13, 0x8b, 0x9d, 0xe4, 0xa2, 0xf5, 0x80, - 0x19, 0xd8, 0xa9, 0xee, 0xb1, 0x4c, 0x31, 0x4c, 0xa7, 0x66, 0x6e, 0xb2, 0xa7, 0xa0, 0xe8, 0x10, - 0xca, 0x26, 0x16, 0xc9, 0xc5, 0x57, 0x85, 0x50, 0x06, 0xac, 0x23, 0x78, 0x62, 0x26, 0xad, 0x42, - 0xaf, 0x42, 0x3e, 0x60, 0x64, 0x4a, 0x4c, 0x5a, 0xeb, 0xa7, 0x6a, 0x81, 0xae, 0xfa, 0x47, 0x25, - 0x70, 0x19, 0x4d, 0xfe, 0x9a, 0x90, 0x71, 0x88, 0x3b, 0x1e, 0x8a, 0x74, 0xac, 0xb4, 0xf3, 0xe2, - 0x72, 0x84, 0x8c, 0x49, 0xc7, 0x43, 0x8a, 0xa5, 0xb1, 0xfa, 0x10, 0x32, 0x42, 0x82, 0x0a, 0x90, - 0xbd, 0x7f, 0x70, 0xef, 0xe0, 0xf0, 0x83, 0x83, 0x4a, 0x02, 0x01, 0x64, 0x76, 0x1b, 0x8d, 0xe6, - 0x51, 0xbb, 0xa2, 0xa0, 0x3c, 0xac, 0xec, 0xd6, 0x0f, 0x71, 0xbb, 0x92, 0x64, 0x62, 0xdc, 0x7c, - 0xb7, 0xd9, 0x68, 0x57, 0x52, 0x68, 0x0d, 0x8a, 0xe2, 0x59, 0xbb, 0x7b, 0x88, 0xdf, 0xdb, 0x6d, - 0x57, 0xd2, 0x21, 0xd1, 0x71, 0xf3, 0xe0, 0xed, 0x26, 0xae, 0xac, 0xa8, 0x2f, 0xb1, 0x7c, 0x2f, - 0x86, 0xc2, 0x05, 0x99, 0x9d, 0x12, 0xca, 0xec, 0xd4, 0xdf, 0x26, 0xa1, 0x16, 0xcf, 0xcb, 0xd0, - 0xbb, 0x13, 0x13, 0xdf, 0xb9, 0x00, 0xa9, 0x9b, 0x98, 0x3d, 0x7a, 0x1a, 0x4a, 0x0e, 0x39, 0x21, - 0xb4, 0x3b, 0x10, 0x3c, 0x91, 0x1d, 0xa9, 0xd4, 0xad, 0x22, 0x2e, 0x4a, 0x29, 0x37, 0x72, 0x85, - 0xda, 0x27, 0xa4, 0x4b, 0x35, 0x91, 0x64, 0x8a, 0x03, 0x93, 0x67, 0x6a, 0x4c, 0x7a, 0x2c, 0x84, - 0xea, 0xc7, 0x17, 0x5a, 0xcb, 0x3c, 0xac, 0xe0, 0x66, 0x1b, 0xff, 0xa2, 0x92, 0x42, 0x08, 0x4a, - 0xfc, 0x51, 0x3b, 0x3e, 0xd8, 0x3d, 0x3a, 0x6e, 0x1d, 0xb2, 0xb5, 0xbc, 0x04, 0x65, 0x6f, 0x2d, - 0x3d, 0xe1, 0x8a, 0xfa, 0x2f, 0x05, 0xca, 0x13, 0x87, 0x1b, 0xed, 0xc0, 0x8a, 0xc8, 0x35, 0xe2, - 0xfe, 0x64, 0x70, 0x18, 0x91, 0x48, 0x20, 0x54, 0xd1, 0x1b, 0x90, 0x23, 0xb2, 0x88, 0x32, 0x0b, - 0x44, 0xc4, 0xe1, 0xf4, 0xca, 0x2c, 0xd2, 0xd4, 0xb7, 0x40, 0x6f, 0x42, 0xde, 0x3f, 0x47, 0x32, - 0xc1, 0x7d, 0x72, 0xda, 0xdc, 0x3f, 0x84, 0xd2, 0x3e, 0xb0, 0x41, 0xaf, 0x07, 0x84, 0x35, 0x1d, - 0x07, 0x0d, 0x92, 0xa1, 0x4a, 0x63, 0x4f, 0x5f, 0x6d, 0x40, 0x21, 0x34, 0x1f, 0x74, 0x0d, 0xf2, - 0x23, 0xfd, 0x4c, 0x16, 0xf3, 0x44, 0x79, 0x25, 0x37, 0xd2, 0xcf, 0x44, 0x1d, 0xef, 0x0a, 0x64, - 0x59, 0x67, 0x5f, 0x17, 0x48, 0x99, 0xc2, 0x99, 0x91, 0x7e, 0xf6, 0x8e, 0xee, 0xaa, 0xbf, 0x53, - 0xa0, 0x14, 0xad, 0x4c, 0xb1, 0xad, 0xe8, 0x58, 0x63, 0xb3, 0xc7, 0x9d, 0xac, 0x60, 0xd1, 0x60, - 0xfc, 0xf9, 0xd3, 0xb1, 0xe5, 0x8c, 0x47, 0x61, 0x4a, 0x07, 0x42, 0xc4, 0x59, 0xdd, 0xb3, 0x50, - 0x16, 0x74, 0xd8, 0x35, 0xfa, 0xa6, 0x4e, 0xc7, 0x8e, 0xa8, 0xc6, 0xad, 0xe2, 0x12, 0x17, 0x1f, - 0x7b, 0x52, 0xa6, 0x28, 0xea, 0x8e, 0x81, 0xa2, 0x20, 0xce, 0x25, 0x2e, 0xf6, 0x15, 0xd5, 0xcf, - 0x61, 0x85, 0xa3, 0x2e, 0x43, 0x21, 0x5e, 0x9f, 0x92, 0x4c, 0x9f, 0x3d, 0xa3, 0x8f, 0x00, 0x74, - 0x4a, 0x1d, 0xa3, 0x33, 0x16, 0xf0, 0x9f, 0x9a, 0x99, 0x1d, 0x72, 0xfb, 0x5d, 0x4f, 0xaf, 0x7e, - 0x5d, 0xc2, 0xf7, 0x7a, 0x60, 0x1a, 0x82, 0xf0, 0x90, 0x43, 0xf5, 0x00, 0x4a, 0x51, 0xdb, 0x70, - 0x85, 0x79, 0x75, 0x46, 0x85, 0xd9, 0xe7, 0x65, 0x3e, 0xab, 0x4b, 0x89, 0x5a, 0x24, 0x6f, 0xa8, - 0x5f, 0x29, 0x90, 0x6b, 0x9f, 0xc9, 0x33, 0x11, 0x53, 0x06, 0x0b, 0x4c, 0x93, 0xe1, 0xa2, 0x8f, - 0xa8, 0xab, 0xa5, 0xfc, 0x6a, 0xdd, 0x5b, 0xfe, 0xa9, 0x4f, 0x2f, 0x9b, 0x96, 0x7b, 0x65, 0x4b, - 0x89, 0x74, 0xbb, 0x90, 0xf7, 0xb7, 0x24, 0xaf, 0x9d, 0x5a, 0x9f, 0xc9, 0xe2, 0x51, 0x0a, 0x8b, - 0x06, 0xda, 0x80, 0x82, 0xed, 0x58, 0x1a, 0x3d, 0x13, 0x9f, 0x5b, 0x7c, 0x49, 0x46, 0x38, 0xdb, - 0x67, 0xbc, 0x3c, 0xf6, 0xa5, 0x02, 0x65, 0xdf, 0x87, 0x8c, 0x4d, 0xff, 0x03, 0x59, 0x7b, 0xdc, - 0xd1, 0xbc, 0x55, 0x9a, 0x38, 0x80, 0x1e, 0x1f, 0x1d, 0x77, 0x86, 0x46, 0xf7, 0x1e, 0x39, 0x97, - 0x71, 0x28, 0x63, 0x8f, 0x3b, 0xf7, 0xc4, 0x62, 0x8a, 0x61, 0x24, 0xe7, 0x0c, 0x23, 0x35, 0x39, - 0x8c, 0x1f, 0x14, 0x40, 0xd3, 0x21, 0x0e, 0x1d, 0xc3, 0x5a, 0x10, 0x25, 0x3d, 0x8a, 0x20, 0x82, - 0xcd, 0xcd, 0xf8, 0x10, 0x19, 0xc9, 0x2d, 0x2a, 0xa7, 0x51, 0xb1, 0x8b, 0xda, 0xb0, 0x4e, 0x07, - 0x0e, 0x71, 0x07, 0xd6, 0xb0, 0xa7, 0xd9, 0x7c, 0x1a, 0x7c, 0xae, 0xc9, 0x25, 0xe7, 0x9a, 0xc0, - 0xc8, 0xb7, 0xf7, 0x7b, 0x16, 0x9e, 0x2b, 0xd5, 0x86, 0x6a, 0x7b, 0xca, 0x4c, 0xce, 0x33, 0x6e, - 0x48, 0xca, 0xe3, 0x0c, 0x49, 0xbd, 0x03, 0x95, 0xf7, 0xfd, 0xf7, 0xcb, 0x37, 0x4d, 0x0c, 0x53, - 0x99, 0x1a, 0xe6, 0x29, 0xe4, 0x1e, 0x58, 0x54, 0x64, 0xe6, 0xff, 0x17, 0x46, 0x45, 0xef, 0xa7, - 0x4a, 0xec, 0xb2, 0xcb, 0x91, 0x84, 0x40, 0xf1, 0x36, 0xac, 0x31, 0x6c, 0x20, 0x3d, 0x2d, 0xc8, - 0xb2, 0xf9, 0x32, 0xe7, 0x70, 0x59, 0x74, 0xec, 0x7b, 0x29, 0xb6, 0xfa, 0x4f, 0x05, 0x72, 0x1e, - 0x3c, 0xa3, 0x97, 0x42, 0x40, 0x51, 0x9a, 0x51, 0x33, 0xf4, 0x14, 0x83, 0x4a, 0x76, 0x74, 0xac, - 0xc9, 0x8b, 0x8f, 0x35, 0xee, 0x97, 0x84, 0xf7, 0x4f, 0x29, 0x7d, 0xe1, 0x7f, 0x4a, 0x2f, 0x00, - 0xa2, 0x16, 0xd5, 0x87, 0xda, 0xa9, 0x45, 0x0d, 0xb3, 0xaf, 0x89, 0x63, 0x21, 0x68, 0x7a, 0x85, - 0xf7, 0x3c, 0xe0, 0x1d, 0x47, 0x4c, 0xae, 0xfe, 0x41, 0x81, 0x9c, 0xcf, 0x84, 0x2e, 0x5a, 0x98, - 0xbe, 0x0c, 0x19, 0x19, 0xec, 0x45, 0x65, 0x5a, 0xb6, 0xfc, 0x7f, 0x24, 0xe9, 0xd0, 0x3f, 0x92, - 0x1a, 0xe4, 0x46, 0x84, 0xea, 0x9c, 0x0e, 0x0a, 0xbc, 0xf6, 0xdb, 0xe8, 0x55, 0xa8, 0x2e, 0xa8, - 0x6d, 0x3c, 0xd1, 0x9d, 0x55, 0xd7, 0xb8, 0xfd, 0x3a, 0x14, 0x42, 0x3f, 0x17, 0x18, 0xc6, 0x1e, - 0x34, 0x3f, 0xa8, 0x24, 0x6a, 0xd9, 0xaf, 0xbe, 0xb9, 0x99, 0x3a, 0x20, 0x9f, 0xa1, 0x2a, 0x64, - 0x71, 0xb3, 0xd1, 0x6a, 0x36, 0xee, 0x55, 0x94, 0x5a, 0xe1, 0xab, 0x6f, 0x6e, 0x66, 0x31, 0xe1, - 0x35, 0xca, 0xdb, 0x2d, 0x58, 0x0d, 0x7f, 0xce, 0x28, 0xd1, 0x40, 0x50, 0x7a, 0xfb, 0xfe, 0xd1, - 0xfe, 0x5e, 0x63, 0xb7, 0xdd, 0xd4, 0x1e, 0x1c, 0xb6, 0x9b, 0x15, 0x05, 0x5d, 0x81, 0x4b, 0xfb, - 0x7b, 0xef, 0xb4, 0xda, 0x5a, 0x63, 0x7f, 0xaf, 0x79, 0xd0, 0xd6, 0x76, 0xdb, 0xed, 0xdd, 0xc6, - 0xbd, 0x4a, 0x72, 0xe7, 0x0b, 0x80, 0xf2, 0x6e, 0xbd, 0xb1, 0xc7, 0x48, 0x92, 0xd1, 0xd5, 0x65, - 0x0d, 0x38, 0xcd, 0x0b, 0x54, 0x73, 0x6f, 0x43, 0xd4, 0xe6, 0x97, 0xc0, 0xd1, 0x5d, 0x58, 0xe1, - 0xb5, 0x2b, 0x34, 0xff, 0x7a, 0x44, 0x6d, 0x41, 0x4d, 0x9c, 0x0d, 0x86, 0x9f, 0xab, 0xb9, 0xf7, - 0x25, 0x6a, 0xf3, 0x4b, 0xe4, 0x08, 0x43, 0x3e, 0x28, 0x1d, 0x2d, 0xbe, 0x3f, 0x51, 0x5b, 0xa2, - 0x6c, 0xce, 0x7c, 0x06, 0x49, 0xea, 0xe2, 0xfb, 0x04, 0xb5, 0x25, 0x42, 0x15, 0xda, 0x87, 0xac, - 0x97, 0xfe, 0x2f, 0xba, 0xe1, 0x50, 0x5b, 0x58, 0xd2, 0x66, 0x9f, 0x40, 0x94, 0x69, 0xe6, 0x5f, - 0xd7, 0xa8, 0x2d, 0xa8, 0xcf, 0xa3, 0x3d, 0xc8, 0xc8, 0x94, 0x68, 0xc1, 0xad, 0x85, 0xda, 0xa2, - 0x12, 0x35, 0x5b, 0xb4, 0xa0, 0xe6, 0xb6, 0xf8, 0x12, 0x4a, 0x6d, 0x89, 0x5f, 0x0f, 0xe8, 0x3e, - 0x40, 0xa8, 0x28, 0xb3, 0xc4, 0xed, 0x92, 0xda, 0x32, 0xbf, 0x14, 0xd0, 0x21, 0xe4, 0xfc, 0xe4, - 0x7b, 0xe1, 0x5d, 0x8f, 0xda, 0xe2, 0xda, 0x3e, 0x7a, 0x08, 0xc5, 0x68, 0x3a, 0xb8, 0xdc, 0x0d, - 0x8e, 0xda, 0x92, 0x45, 0x7b, 0xe6, 0x3f, 0x9a, 0x1b, 0x2e, 0x77, 0xa3, 0xa3, 0xb6, 0x64, 0x0d, - 0x1f, 0x7d, 0x02, 0x6b, 0xd3, 0xb9, 0xdb, 0xf2, 0x17, 0x3c, 0x6a, 0x17, 0xa8, 0xea, 0xa3, 0x11, - 0xa0, 0x19, 0x39, 0xdf, 0x05, 0xee, 0x7b, 0xd4, 0x2e, 0x52, 0xe4, 0xaf, 0x37, 0xbf, 0xfd, 0x71, - 0x43, 0xf9, 0xee, 0xc7, 0x0d, 0xe5, 0x87, 0x1f, 0x37, 0x94, 0xaf, 0x7f, 0xda, 0x48, 0x7c, 0xf7, - 0xd3, 0x46, 0xe2, 0x4f, 0x3f, 0x6d, 0x24, 0x7e, 0xf9, 0x7c, 0xdf, 0xa0, 0x83, 0x71, 0x67, 0xab, - 0x6b, 0x8d, 0xb6, 0xc3, 0x97, 0xd1, 0x66, 0x5d, 0x90, 0xeb, 0x64, 0x78, 0x84, 0xbb, 0xf3, 0xef, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x7b, 0x07, 0x1f, 0x0d, 0x40, 0x27, 0x00, 0x00, + // 3034 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xcf, 0x73, 0x23, 0xc5, + 0xf5, 0xd7, 0x48, 0xb2, 0x7e, 0x3c, 0x59, 0xb2, 0xdc, 0x6b, 0x76, 0x85, 0x76, 0xd7, 0x5e, 0x86, + 0x02, 0x96, 0x05, 0xec, 0x2f, 0xde, 0xe2, 0xd7, 0x37, 0x24, 0x60, 0x0b, 0x2d, 0x32, 0x6b, 0x6c, + 0xd3, 0xd6, 0x2e, 0x49, 0x08, 0x3b, 0x8c, 0x34, 0x6d, 0x69, 0x58, 0x69, 0x66, 0x98, 0x69, 0x19, + 0x9b, 0x2b, 0xc9, 0x85, 0x13, 0xb9, 0x25, 0x07, 0xfe, 0x84, 0xfc, 0x05, 0xa9, 0xca, 0x99, 0x23, + 0xc7, 0x5c, 0x42, 0x28, 0xa8, 0x54, 0xaa, 0x72, 0xcc, 0x25, 0xa7, 0x54, 0x52, 0xfd, 0x63, 0x7e, + 0x49, 0x1a, 0x49, 0x66, 0x8f, 0xb9, 0x4d, 0xbf, 0x7e, 0xef, 0x4d, 0x77, 0x4f, 0xf7, 0xe7, 0x7d, + 0xde, 0x9b, 0x86, 0xab, 0x94, 0x58, 0x06, 0x71, 0x87, 0xa6, 0x45, 0xb7, 0xf4, 0x4e, 0xd7, 0xdc, + 0xa2, 0xe7, 0x0e, 0xf1, 0x36, 0x1d, 0xd7, 0xa6, 0x36, 0x5a, 0x09, 0x3b, 0x37, 0x59, 0x67, 0xfd, + 0x7a, 0x44, 0xbb, 0xeb, 0x9e, 0x3b, 0xd4, 0xde, 0x72, 0x5c, 0xdb, 0x3e, 0x11, 0xfa, 0xf5, 0x6b, + 0x91, 0x6e, 0xee, 0x27, 0xea, 0x2d, 0xd6, 0x2b, 0x8d, 0x1f, 0x92, 0x73, 0xbf, 0xf7, 0xfa, 0x84, + 0xad, 0xa3, 0xbb, 0xfa, 0xd0, 0xef, 0xde, 0xe8, 0xd9, 0x76, 0x6f, 0x40, 0xb6, 0x78, 0xab, 0x33, + 0x3a, 0xd9, 0xa2, 0xe6, 0x90, 0x78, 0x54, 0x1f, 0x3a, 0x52, 0x61, 0xad, 0x67, 0xf7, 0x6c, 0xfe, + 0xb8, 0xc5, 0x9e, 0x84, 0x54, 0xfd, 0x6d, 0x01, 0xf2, 0x98, 0x7c, 0x32, 0x22, 0x1e, 0x45, 0xdb, + 0x90, 0x25, 0xdd, 0xbe, 0x5d, 0x53, 0x6e, 0x28, 0x37, 0x4b, 0xdb, 0xd7, 0x36, 0xc7, 0x26, 0xb7, + 0x29, 0xf5, 0x9a, 0xdd, 0xbe, 0xdd, 0x4a, 0x61, 0xae, 0x8b, 0x5e, 0x82, 0xa5, 0x93, 0xc1, 0xc8, + 0xeb, 0xd7, 0xd2, 0xdc, 0xe8, 0x7a, 0x92, 0xd1, 0x1d, 0xa6, 0xd4, 0x4a, 0x61, 0xa1, 0xcd, 0x5e, + 0x65, 0x5a, 0x27, 0x76, 0x2d, 0x33, 0xfb, 0x55, 0x7b, 0xd6, 0x09, 0x7f, 0x15, 0xd3, 0x45, 0xbb, + 0x00, 0x1e, 0xa1, 0x9a, 0xed, 0x50, 0xd3, 0xb6, 0x6a, 0x59, 0x6e, 0xf9, 0x44, 0x92, 0xe5, 0x31, + 0xa1, 0x87, 0x5c, 0xb1, 0x95, 0xc2, 0x45, 0xcf, 0x6f, 0x30, 0x1f, 0xa6, 0x65, 0x52, 0xad, 0xdb, + 0xd7, 0x4d, 0xab, 0xb6, 0x34, 0xdb, 0xc7, 0x9e, 0x65, 0xd2, 0x06, 0x53, 0x64, 0x3e, 0x4c, 0xbf, + 0xc1, 0xa6, 0xfc, 0xc9, 0x88, 0xb8, 0xe7, 0xb5, 0xdc, 0xec, 0x29, 0xbf, 0xc7, 0x94, 0xd8, 0x94, + 0xb9, 0x36, 0x6a, 0x42, 0xa9, 0x43, 0x7a, 0xa6, 0xa5, 0x75, 0x06, 0x76, 0xf7, 0x61, 0x2d, 0xcf, + 0x8d, 0xd5, 0x24, 0xe3, 0x5d, 0xa6, 0xba, 0xcb, 0x34, 0x5b, 0x29, 0x0c, 0x9d, 0xa0, 0x85, 0x5e, + 0x87, 0x42, 0xb7, 0x4f, 0xba, 0x0f, 0x35, 0x7a, 0x56, 0x2b, 0x70, 0x1f, 0x1b, 0x49, 0x3e, 0x1a, + 0x4c, 0xaf, 0x7d, 0xd6, 0x4a, 0xe1, 0x7c, 0x57, 0x3c, 0xb2, 0xf9, 0x1b, 0x64, 0x60, 0x9e, 0x12, + 0x97, 0xd9, 0x17, 0x67, 0xcf, 0xff, 0x2d, 0xa1, 0xc9, 0x3d, 0x14, 0x0d, 0xbf, 0x81, 0xde, 0x80, + 0x22, 0xb1, 0x0c, 0x39, 0x0d, 0xe0, 0x2e, 0x6e, 0x24, 0xee, 0x15, 0xcb, 0xf0, 0x27, 0x51, 0x20, + 0xf2, 0x19, 0xbd, 0x0a, 0xb9, 0xae, 0x3d, 0x1c, 0x9a, 0xb4, 0x56, 0xe2, 0xd6, 0xeb, 0x89, 0x13, + 0xe0, 0x5a, 0xad, 0x14, 0x96, 0xfa, 0xe8, 0x00, 0x2a, 0x03, 0xd3, 0xa3, 0x9a, 0x67, 0xe9, 0x8e, + 0xd7, 0xb7, 0xa9, 0x57, 0x5b, 0xe6, 0x1e, 0x9e, 0x4a, 0xf2, 0xb0, 0x6f, 0x7a, 0xf4, 0xd8, 0x57, + 0x6e, 0xa5, 0x70, 0x79, 0x10, 0x15, 0x30, 0x7f, 0xf6, 0xc9, 0x09, 0x71, 0x03, 0x87, 0xb5, 0xf2, + 0x6c, 0x7f, 0x87, 0x4c, 0xdb, 0xb7, 0x67, 0xfe, 0xec, 0xa8, 0x00, 0x7d, 0x00, 0x97, 0x06, 0xb6, + 0x6e, 0x04, 0xee, 0xb4, 0x6e, 0x7f, 0x64, 0x3d, 0xac, 0x55, 0xb8, 0xd3, 0x67, 0x13, 0x07, 0x69, + 0xeb, 0x86, 0xef, 0xa2, 0xc1, 0x0c, 0x5a, 0x29, 0xbc, 0x3a, 0x18, 0x17, 0xa2, 0x07, 0xb0, 0xa6, + 0x3b, 0xce, 0xe0, 0x7c, 0xdc, 0xfb, 0x0a, 0xf7, 0x7e, 0x2b, 0xc9, 0xfb, 0x0e, 0xb3, 0x19, 0x77, + 0x8f, 0xf4, 0x09, 0xe9, 0x6e, 0x1e, 0x96, 0x4e, 0xf5, 0xc1, 0x88, 0xa8, 0xcf, 0x40, 0x29, 0x72, + 0xd4, 0x51, 0x0d, 0xf2, 0x43, 0xe2, 0x79, 0x7a, 0x8f, 0x70, 0x64, 0x28, 0x62, 0xbf, 0xa9, 0x56, + 0x60, 0x39, 0x7a, 0xbc, 0xd5, 0x61, 0x60, 0xc8, 0x0e, 0x2e, 0x33, 0x3c, 0x25, 0xae, 0xc7, 0x4e, + 0xab, 0x34, 0x94, 0x4d, 0xf4, 0x24, 0x94, 0xf9, 0xf6, 0xd1, 0xfc, 0x7e, 0x86, 0x1e, 0x59, 0xbc, + 0xcc, 0x85, 0xf7, 0xa5, 0xd2, 0x06, 0x94, 0x9c, 0x6d, 0x27, 0x50, 0xc9, 0x70, 0x15, 0x70, 0xb6, + 0x1d, 0xa9, 0xa0, 0xfe, 0x3f, 0x54, 0xc7, 0x4f, 0x3b, 0xaa, 0x42, 0xe6, 0x21, 0x39, 0x97, 0xef, + 0x63, 0x8f, 0x68, 0x4d, 0x4e, 0x8b, 0xbf, 0xa3, 0x88, 0xe5, 0x1c, 0x3f, 0xcf, 0x04, 0xc6, 0xc1, + 0x31, 0x47, 0xaf, 0x42, 0x96, 0xa1, 0xa6, 0x04, 0xc0, 0xfa, 0xa6, 0x80, 0xd4, 0x4d, 0x1f, 0x52, + 0x37, 0xdb, 0x3e, 0xa4, 0xee, 0x16, 0xbe, 0xfe, 0x76, 0x23, 0xf5, 0xe5, 0x5f, 0x37, 0x14, 0xcc, + 0x2d, 0xd0, 0xe3, 0xec, 0x54, 0xea, 0xa6, 0xa5, 0x99, 0x86, 0x7c, 0x4f, 0x9e, 0xb7, 0xf7, 0x0c, + 0x74, 0x17, 0xaa, 0x5d, 0xdb, 0xf2, 0x88, 0xe5, 0x8d, 0x3c, 0x4d, 0x40, 0xb6, 0x84, 0xbd, 0xc9, + 0x53, 0xd3, 0xf0, 0x15, 0x8f, 0xb8, 0x1e, 0x5e, 0xe9, 0xc6, 0x05, 0xe8, 0x00, 0xca, 0xa7, 0xfa, + 0xc0, 0x34, 0x74, 0x6a, 0xbb, 0x9a, 0x47, 0xa8, 0x84, 0xc1, 0x27, 0x27, 0x3c, 0xdd, 0xf7, 0xb5, + 0x8e, 0x09, 0xbd, 0xe7, 0x18, 0x3a, 0x25, 0xbb, 0xd9, 0xaf, 0xbf, 0xdd, 0x50, 0xf0, 0xf2, 0x69, + 0xa4, 0x07, 0x3d, 0x0d, 0x2b, 0xba, 0xe3, 0x68, 0x1e, 0xd5, 0x29, 0xd1, 0x3a, 0xe7, 0x94, 0x78, + 0x1c, 0x14, 0x97, 0x71, 0x59, 0x77, 0x9c, 0x63, 0x26, 0xdd, 0x65, 0x42, 0xf4, 0x14, 0x54, 0x18, + 0x00, 0x9a, 0xfa, 0x40, 0xeb, 0x13, 0xb3, 0xd7, 0xa7, 0x1c, 0xfc, 0x32, 0xb8, 0x2c, 0xa5, 0x2d, + 0x2e, 0x44, 0x9b, 0x70, 0xc9, 0x57, 0xeb, 0xda, 0x2e, 0xf1, 0x75, 0x19, 0xd6, 0x95, 0xf1, 0xaa, + 0xec, 0x6a, 0xd8, 0x2e, 0x11, 0xfa, 0xaa, 0x11, 0x6c, 0x20, 0x0e, 0x96, 0x08, 0x41, 0xd6, 0xd0, + 0xa9, 0xce, 0x3f, 0xc0, 0x32, 0xe6, 0xcf, 0x4c, 0xe6, 0xe8, 0xb4, 0x2f, 0x97, 0x95, 0x3f, 0xa3, + 0xcb, 0x90, 0x93, 0xae, 0x33, 0x7c, 0x18, 0xb2, 0xc5, 0xbe, 0xb5, 0xe3, 0xda, 0xa7, 0x84, 0x2f, + 0x4b, 0x01, 0x8b, 0x86, 0xfa, 0xeb, 0x34, 0xac, 0x4e, 0xc0, 0x2a, 0xf3, 0xdb, 0xd7, 0xbd, 0xbe, + 0xff, 0x2e, 0xf6, 0x8c, 0x5e, 0x66, 0x7e, 0x75, 0x83, 0xb8, 0x32, 0x9c, 0xd5, 0xa2, 0xeb, 0x2a, + 0x42, 0x75, 0x8b, 0xf7, 0xf3, 0xc5, 0x4c, 0x61, 0xa9, 0x8d, 0x0e, 0xa1, 0x3a, 0xd0, 0x3d, 0xaa, + 0x09, 0x98, 0xd2, 0x22, 0xa1, 0x6d, 0x12, 0x9c, 0xf7, 0x75, 0x1f, 0xd8, 0xd8, 0x21, 0x91, 0x8e, + 0x2a, 0x83, 0x98, 0x14, 0x61, 0x58, 0xeb, 0x9c, 0x7f, 0xa6, 0x5b, 0xd4, 0xb4, 0x88, 0x16, 0x7c, + 0x31, 0xaf, 0x96, 0xbd, 0x91, 0xb9, 0x59, 0xda, 0x7e, 0x7c, 0xc2, 0x69, 0xf3, 0xd4, 0x34, 0x88, + 0xd5, 0x25, 0xd2, 0xdd, 0xa5, 0xc0, 0x38, 0xd8, 0x07, 0x9e, 0x8a, 0xa1, 0x12, 0x0f, 0x0c, 0xa8, + 0x02, 0x69, 0x7a, 0x26, 0x17, 0x20, 0x4d, 0xcf, 0xd0, 0xff, 0x41, 0x96, 0x4d, 0x92, 0x4f, 0xbe, + 0x32, 0x25, 0x2a, 0x4b, 0xbb, 0xf6, 0xb9, 0x43, 0x30, 0xd7, 0x54, 0xd5, 0xe0, 0x14, 0x05, 0xc1, + 0x62, 0xdc, 0xab, 0xfa, 0x2c, 0xac, 0x8c, 0x45, 0x83, 0xc8, 0xf7, 0x53, 0xa2, 0xdf, 0x4f, 0x5d, + 0x81, 0x72, 0x0c, 0xfa, 0xd5, 0xcb, 0xb0, 0x36, 0x0d, 0xc9, 0xd5, 0x7e, 0x20, 0x8f, 0x21, 0x32, + 0x7a, 0x09, 0x0a, 0x01, 0x94, 0x8b, 0x53, 0x3c, 0xb9, 0x56, 0xbe, 0x32, 0x0e, 0x54, 0xd9, 0xf1, + 0x65, 0xc7, 0x80, 0xef, 0x87, 0x34, 0x1f, 0x78, 0x5e, 0x77, 0x9c, 0x96, 0xee, 0xf5, 0xd5, 0x8f, + 0xa0, 0x96, 0x04, 0xd3, 0x63, 0xd3, 0xc8, 0x06, 0xdb, 0xf0, 0x32, 0xe4, 0x4e, 0x6c, 0x77, 0xa8, + 0x53, 0xee, 0xac, 0x8c, 0x65, 0x8b, 0x6d, 0x4f, 0x01, 0xd9, 0x19, 0x2e, 0x16, 0x0d, 0x55, 0x83, + 0xc7, 0x13, 0xa1, 0x9a, 0x99, 0x98, 0x96, 0x41, 0xc4, 0x7a, 0x96, 0xb1, 0x68, 0x84, 0x8e, 0xc4, + 0x60, 0x45, 0x83, 0xbd, 0xd6, 0xe3, 0x73, 0xe5, 0xfe, 0x8b, 0x58, 0xb6, 0xd4, 0xbf, 0x15, 0xa0, + 0x80, 0x89, 0xe7, 0x30, 0x2c, 0x41, 0xbb, 0x50, 0x24, 0x67, 0x5d, 0x22, 0x48, 0x94, 0x92, 0x48, + 0x42, 0x84, 0x76, 0xd3, 0xd7, 0x64, 0x0c, 0x20, 0x30, 0x43, 0xb7, 0x25, 0x51, 0x4c, 0xe6, 0x7c, + 0xd2, 0x3c, 0xca, 0x14, 0x5f, 0xf6, 0x99, 0x62, 0x26, 0x31, 0xe8, 0x0b, 0xab, 0x31, 0xaa, 0x78, + 0x5b, 0x52, 0xc5, 0xec, 0x9c, 0x97, 0xc5, 0xb8, 0x62, 0x23, 0xc6, 0x15, 0x97, 0xe6, 0x4c, 0x33, + 0x81, 0x2c, 0x36, 0x62, 0x64, 0x31, 0x37, 0xc7, 0x49, 0x02, 0x5b, 0x7c, 0xd9, 0x67, 0x8b, 0xf9, + 0x39, 0xd3, 0x1e, 0xa3, 0x8b, 0x77, 0xe2, 0x74, 0xb1, 0x90, 0x80, 0xf3, 0xbe, 0x75, 0x22, 0x5f, + 0xfc, 0x69, 0x84, 0x2f, 0x16, 0x13, 0xc9, 0x9a, 0x70, 0x32, 0x85, 0x30, 0x36, 0x62, 0x84, 0x11, + 0xe6, 0xac, 0x41, 0x02, 0x63, 0x7c, 0x33, 0xca, 0x18, 0x4b, 0x89, 0xa4, 0x53, 0x6e, 0x9a, 0x69, + 0x94, 0xf1, 0xb5, 0x80, 0x32, 0x2e, 0x27, 0x72, 0x5e, 0x39, 0x87, 0x71, 0xce, 0x78, 0x38, 0xc1, + 0x19, 0x05, 0xc7, 0x7b, 0x3a, 0xd1, 0xc5, 0x1c, 0xd2, 0x78, 0x38, 0x41, 0x1a, 0x2b, 0x73, 0x1c, + 0xce, 0x61, 0x8d, 0xbf, 0x9a, 0xce, 0x1a, 0x93, 0x79, 0x9d, 0x1c, 0xe6, 0x62, 0xb4, 0x51, 0x4b, + 0xa0, 0x8d, 0x55, 0xee, 0xfe, 0xb9, 0x44, 0xf7, 0x17, 0xe7, 0x8d, 0xcf, 0xb2, 0x30, 0x3b, 0x06, + 0x1c, 0x0c, 0xaa, 0x88, 0xeb, 0xda, 0xae, 0xa4, 0x64, 0xa2, 0xa1, 0xde, 0x64, 0x81, 0x3f, 0x04, + 0x89, 0x19, 0x1c, 0x93, 0x87, 0x84, 0x08, 0x30, 0xa8, 0x7f, 0x51, 0x42, 0x5b, 0x1e, 0x2b, 0xa3, + 0xa4, 0xa1, 0x28, 0x49, 0x43, 0x84, 0x7a, 0xa6, 0xe3, 0xd4, 0x73, 0x03, 0x4a, 0x0c, 0xea, 0xc7, + 0x58, 0xa5, 0xee, 0xf8, 0xac, 0x12, 0xdd, 0x82, 0x55, 0x1e, 0xcb, 0x05, 0x41, 0x95, 0xf8, 0x9e, + 0xe5, 0x61, 0x6a, 0x85, 0x75, 0x88, 0xcd, 0x29, 0x80, 0xfe, 0x05, 0xb8, 0x14, 0xd1, 0x0d, 0x42, + 0x88, 0xa0, 0x50, 0xd5, 0x40, 0x7b, 0x47, 0xc4, 0x92, 0x77, 0xb2, 0x05, 0xa3, 0x4a, 0xf0, 0x75, + 0x49, 0x15, 0x5c, 0x22, 0x90, 0x45, 0x63, 0x2a, 0xc4, 0x90, 0xaf, 0x52, 0xdf, 0x0d, 0x57, 0x31, + 0xa4, 0xb5, 0x08, 0xb2, 0x5d, 0xdb, 0x20, 0x32, 0x0a, 0xf0, 0x67, 0x46, 0x75, 0x07, 0x76, 0x4f, + 0x62, 0x3d, 0x7b, 0x64, 0x5a, 0x01, 0x54, 0x16, 0x05, 0x12, 0xaa, 0x7f, 0x4f, 0x87, 0xfe, 0x42, + 0xa6, 0x3b, 0x8d, 0x94, 0x2a, 0x3f, 0x96, 0x94, 0x46, 0xa3, 0x67, 0x26, 0x16, 0x3d, 0xd1, 0x07, + 0xb0, 0x16, 0xe3, 0xab, 0xda, 0x88, 0x73, 0xd1, 0x9a, 0x71, 0x31, 0xda, 0x9a, 0xc2, 0xe8, 0x74, + 0xa2, 0x07, 0x7d, 0x08, 0x57, 0x2d, 0x72, 0x36, 0xb1, 0x94, 0xfe, 0x3b, 0xc8, 0x24, 0x52, 0x08, + 0x0a, 0xc7, 0x08, 0x28, 0x5f, 0x86, 0x7d, 0xbb, 0xfb, 0x10, 0x5f, 0x61, 0x3e, 0x62, 0x22, 0xe9, + 0x3e, 0x81, 0xcc, 0x9e, 0x24, 0x91, 0xd9, 0x7f, 0x29, 0xe1, 0x56, 0x0d, 0xe8, 0xec, 0x8f, 0xfb, + 0x6a, 0x61, 0xd8, 0x5f, 0xe2, 0x1b, 0x4f, 0x86, 0x7d, 0x99, 0xdc, 0xe4, 0xf8, 0x1a, 0xc7, 0x93, + 0x9b, 0xbc, 0x20, 0x02, 0xbc, 0x81, 0x5e, 0x85, 0x22, 0xaf, 0x3a, 0x69, 0xb6, 0xe3, 0xc9, 0xc8, + 0x71, 0x35, 0xba, 0x0c, 0xa2, 0xb8, 0xb4, 0x79, 0xc4, 0x74, 0x0e, 0x1d, 0x0f, 0x17, 0x1c, 0xf9, + 0x14, 0x61, 0x34, 0xc5, 0x18, 0xb1, 0xbe, 0x06, 0x45, 0x36, 0x7a, 0xcf, 0xd1, 0xbb, 0x84, 0x47, + 0x81, 0x22, 0x0e, 0x05, 0xea, 0x03, 0x40, 0x93, 0x71, 0x08, 0xb5, 0x20, 0x47, 0x4e, 0x89, 0x45, + 0xd9, 0xce, 0x62, 0xac, 0xf5, 0xf2, 0x14, 0xd6, 0x4a, 0x2c, 0xba, 0x5b, 0x63, 0x1f, 0xf8, 0x1f, + 0xdf, 0x6e, 0x54, 0x85, 0xf6, 0xf3, 0xf6, 0xd0, 0xa4, 0x64, 0xe8, 0xd0, 0x73, 0x2c, 0xed, 0xd5, + 0xcf, 0xd3, 0x8c, 0x42, 0xc6, 0x62, 0xd4, 0xd4, 0xb5, 0xf5, 0x91, 0x20, 0x1d, 0x49, 0x1f, 0x16, + 0x5b, 0xef, 0x75, 0x80, 0x9e, 0xee, 0x69, 0x9f, 0xea, 0x16, 0x25, 0x86, 0x5c, 0xf4, 0x88, 0x04, + 0xd5, 0xa1, 0xc0, 0x5a, 0x23, 0x8f, 0x18, 0x32, 0xf3, 0x09, 0xda, 0x91, 0x79, 0xe6, 0x1f, 0x6d, + 0x9e, 0xf1, 0x55, 0x2e, 0x8c, 0xaf, 0xf2, 0x6f, 0x22, 0x27, 0x39, 0x64, 0xdb, 0xff, 0x7b, 0xeb, + 0xf0, 0xcf, 0x34, 0x4b, 0x3a, 0xe2, 0x64, 0x01, 0xfd, 0x1c, 0xae, 0x8c, 0x01, 0x9a, 0x84, 0x01, + 0x4f, 0xb2, 0xd4, 0xf9, 0xb8, 0xf6, 0x58, 0x1c, 0xd7, 0x04, 0x0a, 0x78, 0x91, 0x69, 0x65, 0x1e, + 0x71, 0x5a, 0x73, 0xf0, 0xca, 0x78, 0x44, 0xbc, 0x4a, 0xc2, 0x5a, 0x72, 0xd1, 0x12, 0xc1, 0x14, + 0xac, 0x55, 0xf7, 0x58, 0xf2, 0x18, 0x65, 0x58, 0x53, 0x37, 0xd9, 0x93, 0x50, 0x76, 0x09, 0x65, + 0x13, 0x8b, 0xa5, 0xe7, 0xcb, 0x42, 0x28, 0x71, 0xf2, 0x08, 0x1e, 0x9b, 0xca, 0xb4, 0xd0, 0x2b, + 0x50, 0x0c, 0x49, 0x9a, 0x92, 0x90, 0xe9, 0x06, 0xd9, 0x5b, 0xa8, 0xab, 0xfe, 0x49, 0x09, 0x5d, + 0xc6, 0xf3, 0xc1, 0x26, 0xe4, 0x5c, 0xe2, 0x8d, 0x06, 0x22, 0x43, 0xab, 0x6c, 0xbf, 0xb0, 0x18, + 0x47, 0x63, 0xd2, 0xd1, 0x80, 0x62, 0x69, 0xac, 0x3e, 0x80, 0x9c, 0x90, 0xa0, 0x12, 0xe4, 0xef, + 0x1d, 0xdc, 0x3d, 0x38, 0x7c, 0xff, 0xa0, 0x9a, 0x42, 0x00, 0xb9, 0x9d, 0x46, 0xa3, 0x79, 0xd4, + 0xae, 0x2a, 0xa8, 0x08, 0x4b, 0x3b, 0xbb, 0x87, 0xb8, 0x5d, 0x4d, 0x33, 0x31, 0x6e, 0xbe, 0xd3, + 0x6c, 0xb4, 0xab, 0x19, 0xb4, 0x0a, 0x65, 0xf1, 0xac, 0xdd, 0x39, 0xc4, 0xef, 0xee, 0xb4, 0xab, + 0xd9, 0x88, 0xe8, 0xb8, 0x79, 0xf0, 0x56, 0x13, 0x57, 0x97, 0xd4, 0x17, 0x59, 0x0a, 0x98, 0xc0, + 0xea, 0xc2, 0x64, 0x4f, 0x89, 0x24, 0x7b, 0xea, 0xef, 0xd2, 0x50, 0x4f, 0xa6, 0x6a, 0xe8, 0x9d, + 0xb1, 0x89, 0x6f, 0x5f, 0x80, 0xe7, 0x8d, 0xcd, 0x1e, 0x3d, 0x05, 0x15, 0x97, 0x9c, 0x10, 0xda, + 0xed, 0x0b, 0xea, 0xc8, 0x8e, 0x54, 0xe6, 0x66, 0x19, 0x97, 0xa5, 0x94, 0x1b, 0x79, 0x42, 0xed, + 0x63, 0xd2, 0xa5, 0x9a, 0xc8, 0x3b, 0xc5, 0x81, 0x29, 0x32, 0x35, 0x26, 0x3d, 0x16, 0x42, 0xf5, + 0xa3, 0x0b, 0xad, 0x65, 0x11, 0x96, 0x70, 0xb3, 0x8d, 0x7f, 0x51, 0xcd, 0x20, 0x04, 0x15, 0xfe, + 0xa8, 0x1d, 0x1f, 0xec, 0x1c, 0x1d, 0xb7, 0x0e, 0xd9, 0x5a, 0x5e, 0x82, 0x15, 0x7f, 0x2d, 0x7d, + 0xe1, 0x92, 0xfa, 0x1f, 0x05, 0x56, 0xc6, 0x0e, 0x37, 0xda, 0x86, 0x25, 0x91, 0x7e, 0x24, 0xfd, + 0xdc, 0xe0, 0x30, 0x22, 0x91, 0x40, 0xa8, 0xa2, 0xd7, 0xa1, 0x40, 0x64, 0x5d, 0x65, 0x1a, 0x88, + 0x88, 0xc3, 0xe9, 0x57, 0x5e, 0xa4, 0x69, 0x60, 0x81, 0xde, 0x80, 0x62, 0x70, 0x8e, 0x64, 0xce, + 0xfb, 0xc4, 0xa4, 0x79, 0x70, 0x08, 0xa5, 0x7d, 0x68, 0x83, 0x5e, 0x0b, 0x39, 0x6c, 0x36, 0x09, + 0x1a, 0x24, 0x69, 0x95, 0xc6, 0xbe, 0xbe, 0xda, 0x80, 0x52, 0x64, 0x3e, 0xe8, 0x2a, 0x14, 0x87, + 0xfa, 0x99, 0xac, 0xef, 0x89, 0x8a, 0x4b, 0x61, 0xa8, 0x9f, 0x89, 0xd2, 0xde, 0x15, 0xc8, 0xb3, + 0xce, 0x9e, 0x2e, 0x90, 0x32, 0x83, 0x73, 0x43, 0xfd, 0xec, 0x6d, 0xdd, 0x53, 0x7f, 0xaf, 0x40, + 0x25, 0x5e, 0xac, 0x62, 0x5b, 0xd1, 0xb5, 0x47, 0x96, 0xc1, 0x9d, 0x2c, 0x61, 0xd1, 0x60, 0x94, + 0xfa, 0x93, 0x91, 0xed, 0x8e, 0x86, 0x51, 0x0a, 0x08, 0x42, 0xc4, 0x59, 0xe0, 0x33, 0xb0, 0x22, + 0x18, 0xb2, 0x67, 0xf6, 0x2c, 0x9d, 0x8e, 0x5c, 0x51, 0xa0, 0x5b, 0xc6, 0x15, 0x2e, 0x3e, 0xf6, + 0xa5, 0x4c, 0x51, 0x94, 0x22, 0x43, 0x45, 0xc1, 0xa5, 0x2b, 0x5c, 0x1c, 0x28, 0xaa, 0x9f, 0xc1, + 0x12, 0x47, 0x5d, 0x86, 0x42, 0xbc, 0x64, 0x25, 0xc9, 0x3f, 0x7b, 0x46, 0x1f, 0x02, 0xe8, 0x94, + 0xba, 0x66, 0x67, 0x24, 0xe0, 0x3f, 0x33, 0x35, 0x61, 0xe4, 0xf6, 0x3b, 0xbe, 0xde, 0xee, 0x35, + 0x09, 0xdf, 0x6b, 0xa1, 0x69, 0x04, 0xc2, 0x23, 0x0e, 0xd5, 0x03, 0xa8, 0xc4, 0x6d, 0xa3, 0x45, + 0xe7, 0xe5, 0x29, 0x45, 0xe7, 0x80, 0x97, 0x05, 0xac, 0x2e, 0x23, 0xca, 0x93, 0xbc, 0xa1, 0x7e, + 0xa1, 0x40, 0xa1, 0x7d, 0x26, 0xcf, 0x44, 0x42, 0x65, 0x2c, 0x34, 0x4d, 0x47, 0xeb, 0x40, 0xa2, + 0xd4, 0x96, 0x09, 0x0a, 0x78, 0x6f, 0x06, 0xa7, 0x3e, 0xbb, 0x68, 0xa6, 0xee, 0x57, 0x32, 0x25, + 0xd2, 0xed, 0x40, 0x31, 0xd8, 0x92, 0xbc, 0x9c, 0x6a, 0x7f, 0x2a, 0xeb, 0x49, 0x19, 0x2c, 0x1a, + 0x68, 0x1d, 0x4a, 0x8e, 0x6b, 0x6b, 0xf4, 0x4c, 0x7c, 0x6e, 0xf1, 0x25, 0x19, 0xe1, 0x6c, 0x9f, + 0xf1, 0x8a, 0xd9, 0x1f, 0x14, 0x58, 0x09, 0x7c, 0xc8, 0xd8, 0xf4, 0x13, 0xc8, 0x3b, 0xa3, 0x8e, + 0xe6, 0xaf, 0xd2, 0xd8, 0x01, 0xf4, 0xf9, 0xe8, 0xa8, 0x33, 0x30, 0xbb, 0x77, 0xc9, 0xb9, 0x8c, + 0x43, 0x39, 0x67, 0xd4, 0xb9, 0x2b, 0x16, 0x53, 0x0c, 0x23, 0x3d, 0x63, 0x18, 0x99, 0xb1, 0x61, + 0xa0, 0x67, 0x60, 0xd9, 0xb2, 0x0d, 0xa2, 0xe9, 0x86, 0xe1, 0x12, 0xcf, 0x13, 0x34, 0x47, 0x7a, + 0x2e, 0xb1, 0x9e, 0x1d, 0xd1, 0xa1, 0x7e, 0xa7, 0x00, 0x9a, 0x8c, 0x85, 0xe8, 0x18, 0x56, 0xc3, + 0x70, 0xea, 0x73, 0x09, 0x11, 0x95, 0x6e, 0x24, 0xc7, 0xd2, 0x58, 0xd2, 0x52, 0x3d, 0x8d, 0x8b, + 0x3d, 0xd4, 0x86, 0x35, 0xda, 0x77, 0x89, 0xd7, 0xb7, 0x07, 0x86, 0xe6, 0xf0, 0xf9, 0xf2, 0x45, + 0x49, 0x2f, 0xb8, 0x28, 0x29, 0x8c, 0x02, 0xfb, 0xa0, 0x67, 0xee, 0x01, 0x54, 0x1d, 0xa8, 0xb5, + 0x27, 0xcc, 0xe4, 0x3c, 0x93, 0x86, 0xa4, 0x3c, 0xca, 0x90, 0xd4, 0xdb, 0x50, 0x7d, 0x2f, 0x78, + 0xbf, 0x7c, 0xd3, 0xd8, 0x30, 0x95, 0x89, 0x61, 0x9e, 0x42, 0xe1, 0xbe, 0x4d, 0x45, 0x56, 0xff, + 0xb3, 0x28, 0x7c, 0xfa, 0x3f, 0x64, 0x12, 0x97, 0x5d, 0x8e, 0x24, 0x82, 0x9e, 0xb7, 0x60, 0x95, + 0x81, 0x08, 0x31, 0xb4, 0x30, 0x43, 0xe7, 0xcb, 0x5c, 0xc0, 0x2b, 0xa2, 0x63, 0xdf, 0x4f, 0xcf, + 0xd5, 0x7f, 0x2b, 0x50, 0xf0, 0x71, 0x1c, 0xbd, 0x18, 0x41, 0x94, 0xca, 0x94, 0x7a, 0xa3, 0xaf, + 0x18, 0x56, 0xc1, 0xe3, 0x63, 0x4d, 0x5f, 0x7c, 0xac, 0x49, 0xbf, 0x33, 0xfc, 0xff, 0x51, 0xd9, + 0x0b, 0xff, 0x8f, 0x7a, 0x1e, 0x10, 0xb5, 0xa9, 0x3e, 0xd0, 0x4e, 0x6d, 0x6a, 0x5a, 0x3d, 0x4d, + 0x9c, 0x1f, 0xc1, 0xe7, 0xab, 0xbc, 0xe7, 0x3e, 0xef, 0x38, 0x62, 0x72, 0xf5, 0x8f, 0x0a, 0x14, + 0x02, 0xca, 0x74, 0xd1, 0xa2, 0xf6, 0x65, 0xc8, 0x49, 0x56, 0x20, 0xaa, 0xda, 0xb2, 0x15, 0xfc, + 0x5f, 0xc9, 0x46, 0xfe, 0xaf, 0xd4, 0xa1, 0x30, 0x24, 0x54, 0xe7, 0xbc, 0x51, 0x00, 0x7b, 0xd0, + 0x46, 0xaf, 0x40, 0x2d, 0xa9, 0x26, 0xc2, 0xa9, 0x71, 0x99, 0x11, 0xf4, 0x08, 0xeb, 0x25, 0x86, + 0xe0, 0x93, 0xb7, 0x5e, 0x83, 0x52, 0xe4, 0xc7, 0x04, 0x03, 0xe3, 0x83, 0xe6, 0xfb, 0xd5, 0x54, + 0x3d, 0xff, 0xc5, 0x57, 0x37, 0x32, 0x07, 0xe4, 0x53, 0x54, 0x83, 0x3c, 0x6e, 0x36, 0x5a, 0xcd, + 0xc6, 0xdd, 0xaa, 0x52, 0x2f, 0x7d, 0xf1, 0xd5, 0x8d, 0x3c, 0x26, 0xbc, 0xbe, 0x79, 0xab, 0x05, + 0xcb, 0xd1, 0xcf, 0x19, 0x67, 0x24, 0x08, 0x2a, 0x6f, 0xdd, 0x3b, 0xda, 0xdf, 0x6b, 0xec, 0xb4, + 0x9b, 0xda, 0xfd, 0xc3, 0x76, 0xb3, 0xaa, 0xa0, 0x2b, 0x70, 0x69, 0x7f, 0xef, 0xed, 0x56, 0x5b, + 0x6b, 0xec, 0xef, 0x35, 0x0f, 0xda, 0xda, 0x4e, 0xbb, 0xbd, 0xd3, 0xb8, 0x5b, 0x4d, 0x6f, 0x7f, + 0x0e, 0xb0, 0xb2, 0xb3, 0xdb, 0xd8, 0x63, 0x6c, 0xca, 0xec, 0xea, 0xb2, 0x7e, 0x9c, 0xe5, 0xc5, + 0xad, 0x99, 0x37, 0x29, 0xea, 0xb3, 0xcb, 0xe7, 0xe8, 0x0e, 0x2c, 0xf1, 0xba, 0x17, 0x9a, 0x7d, + 0xb5, 0xa2, 0x3e, 0xa7, 0x9e, 0xce, 0x06, 0xc3, 0xcf, 0xd5, 0xcc, 0xbb, 0x16, 0xf5, 0xd9, 0xe5, + 0x75, 0x84, 0xa1, 0x18, 0xd6, 0xa4, 0xe6, 0xdf, 0xbd, 0xa8, 0x2f, 0x50, 0x72, 0x67, 0x3e, 0xc3, + 0x6c, 0x76, 0xfe, 0x5d, 0x84, 0xfa, 0x02, 0x31, 0x0d, 0xed, 0x43, 0xde, 0xaf, 0x13, 0xcc, 0xbb, + 0x1d, 0x51, 0x9f, 0x5b, 0x0e, 0x67, 0x9f, 0x40, 0xd4, 0x73, 0x66, 0x5f, 0xf5, 0xa8, 0xcf, 0xa9, + 0xed, 0xa3, 0x3d, 0xc8, 0xc9, 0xdc, 0x69, 0xce, 0x8d, 0x87, 0xfa, 0xbc, 0xf2, 0x36, 0x5b, 0xb4, + 0xb0, 0x98, 0x37, 0xff, 0x02, 0x4b, 0x7d, 0x81, 0xdf, 0x16, 0xe8, 0x1e, 0x40, 0xa4, 0x7a, 0xb3, + 0xc0, 0xcd, 0x94, 0xfa, 0x22, 0xbf, 0x23, 0xd0, 0x21, 0x14, 0x82, 0x2c, 0x7d, 0xee, 0x3d, 0x91, + 0xfa, 0xfc, 0xff, 0x02, 0xe8, 0x01, 0x94, 0xe3, 0x79, 0xe3, 0x62, 0xb7, 0x3f, 0xea, 0x0b, 0x16, + 0xfc, 0x99, 0xff, 0x78, 0x12, 0xb9, 0xd8, 0x6d, 0x90, 0xfa, 0x82, 0xf5, 0x7f, 0xf4, 0x31, 0xac, + 0x4e, 0x26, 0x79, 0x8b, 0x5f, 0x0e, 0xa9, 0x5f, 0xe0, 0x8f, 0x00, 0x1a, 0x02, 0x9a, 0x92, 0x1c, + 0x5e, 0xe0, 0xae, 0x48, 0xfd, 0x22, 0x3f, 0x08, 0x76, 0x9b, 0x5f, 0x7f, 0xbf, 0xae, 0x7c, 0xf3, + 0xfd, 0xba, 0xf2, 0xdd, 0xf7, 0xeb, 0xca, 0x97, 0x3f, 0xac, 0xa7, 0xbe, 0xf9, 0x61, 0x3d, 0xf5, + 0xe7, 0x1f, 0xd6, 0x53, 0xbf, 0x7c, 0xae, 0x67, 0xd2, 0xfe, 0xa8, 0xb3, 0xd9, 0xb5, 0x87, 0x5b, + 0xd1, 0x8b, 0x6c, 0xd3, 0x2e, 0xd7, 0x75, 0x72, 0x3c, 0xc2, 0xdd, 0xfe, 0x6f, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x9d, 0x0c, 0xad, 0x23, 0x7c, 0x27, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -5598,13 +5608,6 @@ func (m *ResponseInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.LastCoreChainLockedHeight != 0 { - i = encodeVarintTypes(dAtA, i, uint64(m.LastCoreChainLockedHeight)) - i-- - dAtA[i] = 0x6 - i-- - dAtA[i] = 0xa0 - } if len(m.LastBlockAppHash) > 0 { i -= len(m.LastBlockAppHash) copy(dAtA[i:], m.LastBlockAppHash) @@ -5701,6 +5704,13 @@ func (m *ResponseInitChain) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.InitialCoreHeight != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.InitialCoreHeight)) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xb0 + } if m.NextCoreChainLockUpdate != nil { { size, err := m.NextCoreChainLockUpdate.MarshalToSizedBuffer(dAtA[:i]) @@ -6642,6 +6652,13 @@ func (m *ValidatorUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.NodeAddress) > 0 { + i -= len(m.NodeAddress) + copy(dAtA[i:], m.NodeAddress) + i = encodeVarintTypes(dAtA, i, uint64(len(m.NodeAddress))) + i-- + dAtA[i] = 0x22 + } if len(m.ProTxHash) > 0 { i -= len(m.ProTxHash) copy(dAtA[i:], m.ProTxHash) @@ -7661,9 +7678,6 @@ func (m *ResponseInfo) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } - if m.LastCoreChainLockedHeight != 0 { - n += 2 + sovTypes(uint64(m.LastCoreChainLockedHeight)) - } return n } @@ -7707,6 +7721,9 @@ func (m *ResponseInitChain) Size() (n int) { l = m.NextCoreChainLockUpdate.Size() n += 2 + l + sovTypes(uint64(l)) } + if m.InitialCoreHeight != 0 { + n += 2 + sovTypes(uint64(m.InitialCoreHeight)) + } return n } @@ -8112,6 +8129,10 @@ func (m *ValidatorUpdate) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + l = len(m.NodeAddress) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -11477,25 +11498,6 @@ func (m *ResponseInfo) Unmarshal(dAtA []byte) error { m.LastBlockAppHash = []byte{} } iNdEx = postIndex - case 100: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field LastCoreChainLockedHeight", wireType) - } - m.LastCoreChainLockedHeight = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.LastCoreChainLockedHeight |= uint32(b&0x7F) << shift - if b < 0x80 { - break - } - } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -11818,6 +11820,25 @@ func (m *ResponseInitChain) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 102: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InitialCoreHeight", wireType) + } + m.InitialCoreHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InitialCoreHeight |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -14564,6 +14585,38 @@ func (m *ValidatorUpdate) Unmarshal(dAtA []byte) error { m.ProTxHash = []byte{} } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + 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 ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NodeAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/blockchain/v0/reactor.go b/blockchain/v0/reactor.go index 9f5bfbe070..cda41d4b8c 100644 --- a/blockchain/v0/reactor.go +++ b/blockchain/v0/reactor.go @@ -368,7 +368,14 @@ FOR_LOOP: firstParts := first.MakePartSet(types.BlockPartSizeBytes) firstPartSetHeader := firstParts.Header() firstID := types.BlockID{Hash: first.Hash(), PartSetHeader: firstPartSetHeader} - firstStateID := types.StateID{LastAppHash: first.Header.AppHash} + + firstStateID := types.StateID{Height: first.Height - 1, LastAppHash: first.AppHash} + if !firstStateID.Equals(state.StateID()) { + // Not sure if this is should ever happen, needs investigation if it does + bcR.Logger.Debug("stateID generated from first block doesn't match current state, please investigate", + "first.stateID", firstStateID, + "state.StateID()", state.StateID()) + } // Finally, verify the first block using the second's commit // NOTE: we can probably make this more efficient, but note that calling diff --git a/blockchain/v0/reactor_test.go b/blockchain/v0/reactor_test.go index 3ea37240f3..c2780d44c1 100644 --- a/blockchain/v0/reactor_test.go +++ b/blockchain/v0/reactor_test.go @@ -105,7 +105,7 @@ func newBlockchainReactor( blockHeight-1, 0, types.BlockID{}, - types.StateID{LastAppHash: nil}, + state.LastStateID, // For height-1, we use previous state ID nil, nil, nil, @@ -117,7 +117,7 @@ func newBlockchainReactor( vote, err := types.MakeVote( lastBlock.Header.Height, lastBlockMeta.BlockID, - lastBlockMeta.StateID, + state.LastStateID, // For height-1, we use previous state ID state.Validators, privVals[0], lastBlock.Header.ChainID, @@ -127,7 +127,7 @@ func newBlockchainReactor( } // since there is only 1 vote, use it as threshold lastCommit = types.NewCommit(vote.Height, vote.Round, - lastBlockMeta.BlockID, lastBlockMeta.StateID, nil, vote.BlockSignature, + lastBlockMeta.BlockID, state.LastStateID, nil, vote.BlockSignature, vote.StateSignature) } diff --git a/blockchain/v1/reactor.go b/blockchain/v1/reactor.go index ebda804e3a..4e54b5b50b 100644 --- a/blockchain/v1/reactor.go +++ b/blockchain/v1/reactor.go @@ -473,7 +473,15 @@ func (bcR *BlockchainReactor) processBlock() error { firstParts := first.MakePartSet(types.BlockPartSizeBytes) firstPartSetHeader := firstParts.Header() firstID := types.BlockID{Hash: first.Hash(), PartSetHeader: firstPartSetHeader} - firstStateID := types.StateID{LastAppHash: first.Header.AppHash} + + firstStateID := types.StateID{Height: first.Height - 1, LastAppHash: first.AppHash} + if !firstStateID.Equals(bcR.state.StateID()) { + // Not sure if this is should ever happen, needs investigation if it does + bcR.Logger.Debug("stateID generated from first block doesn't match current state, please investigate", + "first.stateID", firstStateID, + "state.StateID()", bcR.state.StateID()) + } + // Finally, verify the first block using the second's commit // NOTE: we can probably make this more efficient, but note that calling // first.Hash() doesn't verify the tx contents, so MakePartSet() is diff --git a/blockchain/v1/reactor_test.go b/blockchain/v1/reactor_test.go index f919c19b34..212882a403 100644 --- a/blockchain/v1/reactor_test.go +++ b/blockchain/v1/reactor_test.go @@ -1,6 +1,7 @@ package v1 import ( + "encoding/binary" "fmt" "os" "sync" @@ -37,6 +38,7 @@ func randGenesisDoc(numValidators int) (*types.GenesisDoc, []types.PrivValidator Validators: validators, ThresholdPublicKey: thresholdPublicKey, QuorumHash: quorumHash, + AppHash: make([]byte, crypto.DefaultAppHashSize), }, privValidators } @@ -59,12 +61,11 @@ func makeVote( Round: 1, Type: tmproto.PrecommitType, BlockID: blockID, - StateID: stateID, } vpb := vote.ToProto() - _ = privVal.SignVote(header.ChainID, valset.QuorumType, valset.QuorumHash, vpb, nil) + _ = privVal.SignVote(header.ChainID, valset.QuorumType, valset.QuorumHash, vpb, stateID, nil) vote.BlockSignature = vpb.BlockSignature vote.StateSignature = vpb.StateSignature @@ -131,15 +132,27 @@ func newBlockchainReactor( // let's add some blocks in for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { - lastCommit := types.NewCommit(blockHeight-1, 1, types.BlockID{}, types.StateID{}, nil, nil, nil) + lastCommit := types.NewCommit(blockHeight-1, 1, types.BlockID{}, state.LastStateID, nil, nil, nil) if blockHeight > 1 { lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) lastBlock := blockStore.LoadBlock(blockHeight - 1) - vote := makeVote(t, &lastBlock.Header, lastBlockMeta.BlockID, lastBlockMeta.StateID, state.Validators, privVals[0]) + vote := makeVote( + t, + &lastBlock.Header, + lastBlockMeta.BlockID, + state.LastStateID, // For height-1, we use previous state ID + state.Validators, + privVals[0]) // since there is only 1 vote, use it as threshold - lastCommit = types.NewCommit(vote.Height, vote.Round, lastBlockMeta.BlockID, lastBlockMeta.StateID, - state.Validators.QuorumHash, vote.BlockSignature, vote.StateSignature) + lastCommit = types.NewCommit( + vote.Height, + vote.Round, + lastBlockMeta.BlockID, + state.LastStateID, // For height-1, we use previous state ID + state.Validators.QuorumHash, + vote.BlockSignature, + vote.StateSignature) } thisBlock := makeBlock(blockHeight, nil, state, lastCommit) @@ -396,4 +409,21 @@ func makeBlock(height int64, coreChainLock *types.CoreChainLock, state sm.State, type testApp struct { abci.BaseApplication + + lastHeight int64 +} + +func (app *testApp) Commit() abci.ResponseCommit { + // Change AppHash + appHash := make([]byte, crypto.DefaultAppHashSize) + binary.LittleEndian.PutUint64(appHash, uint64(app.lastHeight)) + + return abci.ResponseCommit{ + Data: appHash, + } +} + +func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { + app.lastHeight = req.Header.Height + return abci.ResponseBeginBlock{} } diff --git a/blockchain/v2/processor.go b/blockchain/v2/processor.go index d1a3dcca90..8a2b796f04 100644 --- a/blockchain/v2/processor.go +++ b/blockchain/v2/processor.go @@ -162,7 +162,7 @@ func (state *pcState) handle(event Event) (Event, error) { first, second = firstItem.block, secondItem.block firstParts = first.MakePartSet(types.BlockPartSizeBytes) firstID = types.BlockID{Hash: first.Hash(), PartSetHeader: firstParts.Header()} - firstStateID = types.StateID{LastAppHash: first.Header.AppHash} + firstStateID = types.StateID{Height: first.Height - 1, LastAppHash: first.AppHash} ) // verify if +second+ last commit "confirms" +first+ block diff --git a/blockchain/v2/reactor_test.go b/blockchain/v2/reactor_test.go index a498476624..f9ac5f9e48 100644 --- a/blockchain/v2/reactor_test.go +++ b/blockchain/v2/reactor_test.go @@ -1,6 +1,7 @@ package v2 import ( + "encoding/binary" "fmt" "net" "os" @@ -157,8 +158,8 @@ type testReactorParams struct { mockA bool } -func newTestReactor(p testReactorParams) *BlockchainReactor { - store, state, _ := newReactorStore(p.genDoc, p.privVals, p.startHeight) +func newTestReactor(p testReactorParams, t *testing.T) *BlockchainReactor { + store, state, _ := newReactorStore(p.genDoc, p.privVals, p.startHeight, t) reporter := behaviour.NewMockReporter() nodeProTxHash, err := p.privVals[0].GetProTxHash() @@ -422,7 +423,7 @@ func TestReactorHelperMode(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - reactor := newTestReactor(params) + reactor := newTestReactor(params, t) mockSwitch := &mockSwitchIo{switchedToConsensus: false} reactor.io = mockSwitch err := reactor.Start() @@ -469,7 +470,7 @@ func TestReactorSetSwitchNil(t *testing.T) { logger: log.TestingLogger(), genDoc: genDoc, privVals: privVals, - }) + }, t) reactor.SetSwitch(nil) assert.Nil(t, reactor.Switch) @@ -503,6 +504,22 @@ func makeBlock(height int64, coreChainLock *types.CoreChainLock, state sm.State, type testApp struct { abci.BaseApplication + lastHeight int64 +} + +func (app *testApp) Commit() abci.ResponseCommit { + // Change AppHash + appHash := make([]byte, crypto.DefaultAppHashSize) + binary.LittleEndian.PutUint64(appHash, uint64(app.lastHeight)) + + return abci.ResponseCommit{ + Data: appHash, + } +} + +func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { + app.lastHeight = req.Header.Height + return abci.ResponseBeginBlock{} } func randGenesisDoc(chainID string, numValidators int) ( @@ -516,6 +533,7 @@ func randGenesisDoc(chainID string, numValidators int) ( Validators: validators, ThresholdPublicKey: thresholdPublicKey, QuorumHash: quorumHash, + AppHash: make([]byte, crypto.DefaultAppHashSize), }, privValidators } @@ -524,7 +542,8 @@ func randGenesisDoc(chainID string, numValidators int) ( func newReactorStore( genDoc *types.GenesisDoc, privVals []types.PrivValidator, - maxBlockHeight int64) (*store.BlockStore, sm.State, *sm.BlockExecutor) { + maxBlockHeight int64, + t *testing.T) (*store.BlockStore, sm.State, *sm.BlockExecutor) { if len(privVals) != 1 { panic("only support one validator") } @@ -565,9 +584,11 @@ func newReactorStore( panic(err) } + var prevBlock *types.Block // add blocks in for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { - lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, types.StateID{}, nil, + + lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, state.LastStateID, nil, nil, nil) if blockHeight > 1 { lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) @@ -575,7 +596,7 @@ func newReactorStore( vote, err := types.MakeVote( lastBlock.Header.Height, lastBlockMeta.BlockID, - lastBlockMeta.StateID, + state.LastStateID, // For height-1, we use previous state ID state.Validators, privVals[0], lastBlock.Header.ChainID, @@ -585,7 +606,7 @@ func newReactorStore( } // since there is only 1 vote, use it as threshold lastCommit = types.NewCommit(vote.Height, vote.Round, - lastBlockMeta.BlockID, lastBlockMeta.StateID, nil, + lastBlockMeta.BlockID, state.LastStateID, nil, vote.BlockSignature, vote.StateSignature) } @@ -599,7 +620,28 @@ func newReactorStore( panic(fmt.Errorf("error apply block: %w", err)) } + // Assert state + require.GreaterOrEqual(t, len(state.LastStateID.LastAppHash), crypto.SmallAppHashSize) + assert.EqualValues(t, blockHeight, state.LastBlockHeight) + assert.EqualValues(t, blockHeight, binary.LittleEndian.Uint64(state.AppHash)) + + // Assert some StateID params + assert.EqualValues(t, blockHeight-1, state.LastStateID.Height) + assert.EqualValues(t, blockHeight-1, binary.LittleEndian.Uint64(state.LastStateID.LastAppHash)) + + // Assert last commit + assert.EqualValues(t, blockHeight-1, lastCommit.Height) + if blockHeight > state.InitialHeight { + // verify StateID of commit for block N-1 (which is in thisBlock) + assert.EqualValues(t, thisBlock.Height-2, thisBlock.LastCommit.StateID.Height) + assert.EqualValues(t, prevBlock.Header.AppHash, thisBlock.LastCommit.StateID.LastAppHash) + } else { + assert.EqualValues(t, 0, lastCommit.StateID.Height) + assert.EqualValues(t, 0, binary.LittleEndian.Uint64(lastCommit.StateID.LastAppHash)) + } + blockStore.SaveBlock(thisBlock, thisParts, lastCommit) + prevBlock = thisBlock } return blockStore, state, blockExec } diff --git a/config/config.go b/config/config.go index 7644986444..8715330985 100644 --- a/config/config.go +++ b/config/config.go @@ -919,6 +919,9 @@ type ConsensusConfig struct { // EmptyBlocks mode and possible interval between empty blocks CreateEmptyBlocks bool `mapstructure:"create_empty_blocks"` CreateEmptyBlocksInterval time.Duration `mapstructure:"create_empty_blocks_interval"` + // CreateProofBlockRange determines how many past blocks are inspected in order to determine if we need to create + // additional proof block. + CreateProofBlockRange int64 `mapstructure:"create_proof_block_range"` // Reactor sleep duration parameters PeerGossipSleepDuration time.Duration `mapstructure:"peer_gossip_sleep_duration"` @@ -947,10 +950,11 @@ func DefaultConsensusConfig() *ConsensusConfig { DontAutoPropose: false, CreateEmptyBlocks: true, CreateEmptyBlocksInterval: 0 * time.Second, + CreateProofBlockRange: 1, PeerGossipSleepDuration: 100 * time.Millisecond, PeerQueryMaj23SleepDuration: 2000 * time.Millisecond, DoubleSignCheckHeight: int64(0), - AppHashSize: crypto.DefaultHashSize, + AppHashSize: crypto.DefaultAppHashSize, QuorumType: btcjson.LLMQType_5_60, } } @@ -1050,6 +1054,9 @@ func (cfg *ConsensusConfig) ValidateBasic() error { if cfg.CreateEmptyBlocksInterval < 0 { return errors.New("create_empty_blocks_interval can't be negative") } + if cfg.CreateProofBlockRange < 1 { + return errors.New("create_proof_block_range must be greater or equal to 1") + } if cfg.PeerGossipSleepDuration < 0 { return errors.New("peer_gossip_sleep_duration can't be negative") } @@ -1080,7 +1087,12 @@ type TxIndexConfig struct { // 1) "null" // 2) "kv" (default) - the simplest possible indexer, // backed by key-value storage (defaults to levelDB; see DBBackend). + // 3) "psql" - the indexer services backed by PostgreSQL. Indexer string `mapstructure:"indexer"` + + // The PostgreSQL connection configuration, the connection format: + // postgresql://:@:/? + PsqlConn string `mapstructure:"psql-conn"` } // DefaultTxIndexConfig returns a default configuration for the transaction indexer. diff --git a/config/toml.go b/config/toml.go index b1147a14a1..3925a4e408 100644 --- a/config/toml.go +++ b/config/toml.go @@ -260,7 +260,8 @@ laddr = "{{ .P2P.ListenAddress }}" # 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. +# to figure out the address. ip and port are required +# example: 159.89.10.97:26656 external_address = "{{ .P2P.ExternalAddress }}" # Comma separated list of seed nodes to connect to @@ -441,6 +442,9 @@ skip_timeout_commit = {{ .Consensus.SkipTimeoutCommit }} create_empty_blocks = {{ .Consensus.CreateEmptyBlocks }} create_empty_blocks_interval = "{{ .Consensus.CreateEmptyBlocksInterval }}" +# How many blocks are inspected in order to determine if we need to create additional proof block. +create_proof_block_range = "{{ .Consensus.CreateProofBlockRange }}" + # Reactor sleep duration parameters peer_gossip_sleep_duration = "{{ .Consensus.PeerGossipSleepDuration }}" peer_query_maj23_sleep_duration = "{{ .Consensus.PeerQueryMaj23SleepDuration }}" diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 6513b04dc7..8837f2e08a 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -37,13 +37,14 @@ import ( func TestByzantinePrevoteEquivocation(t *testing.T) { const nValidators = 4 const byzantineNode = 0 - const prevoteHeight = int64(2) + testName := "consensus_byzantine_test" tickerFunc := newMockTickerFunc(true) appFunc := newCounter - genDoc, privVals := randGenesisDoc(nValidators, false, 30) + genDoc, privVals := randGenesisDoc(nValidators, false, 30, 1) css := make([]*State, nValidators) + prevoteHeight := genDoc.InitialHeight + 1 for i := 0; i < nValidators; i++ { logger := consensusLogger().With("test", "byzantine", "validator", i) @@ -61,7 +62,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { blockStore := store.NewBlockStore(blockDB) // one for mempool, one for consensus - mtx := new(tmsync.Mutex) + mtx := new(tmsync.RWMutex) proxyAppConnMem := abcicli.NewLocalClient(mtx, app) proxyAppConnCon := abcicli.NewLocalClient(mtx, app) proxyAppConnQry := abcicli.NewLocalClient(mtx, app) @@ -324,7 +325,7 @@ func TestByzantineConflictingProposalsWithPartition(t *testing.T) { N := 4 logger := consensusLogger().With("test", "byzantine") app := newCounter - css, cleanup := randConsensusNet(N, "consensus_byzantine_test", newMockTickerFunc(false), app) + css, cleanup := randConsensusNet(N, 1, "consensus_byzantine_test", newMockTickerFunc(false), app) defer cleanup() // give the byzantine validator a normal ticker diff --git a/consensus/common_test.go b/consensus/common_test.go index 000f1dc066..a337e174c8 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -18,7 +19,7 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/bls12381" - "github.com/go-kit/kit/log/term" + "github.com/go-kit/log/term" "github.com/stretchr/testify/require" "path" @@ -48,6 +49,7 @@ import ( const ( testSubscriber = "test-client" + ensureTimeout = time.Millisecond * 800 ) // A cleanupFunc cleans up any config / test files created for a particular @@ -58,7 +60,12 @@ type cleanupFunc func() var ( config *cfg.Config // NOTE: must be reset for each _test.go file consensusReplayConfig *cfg.Config - ensureTimeout = time.Millisecond * 800 + genesisAppHash = tmbytes.HexBytes{ // read-only, do not modify + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, + } ) func ensureDir(dir string, mode os.FileMode) { @@ -84,11 +91,12 @@ type validatorStub struct { var testMinPower int64 = types.DefaultDashVotingPower -func newValidatorStub(privValidator types.PrivValidator, valIndex int32) *validatorStub { +func newValidatorStub(privValidator types.PrivValidator, valIndex int32, initialHeight int64) *validatorStub { return &validatorStub{ Index: valIndex, PrivValidator: privValidator, VotingPower: testMinPower, + Height: initialHeight, } } @@ -112,10 +120,16 @@ func (vs *validatorStub) signVote( Round: vs.Round, Type: voteType, BlockID: types.BlockID{Hash: hash, PartSetHeader: header}, - StateID: types.StateID{LastAppHash: lastAppHash}, + } + + stateID := types.StateID{ + Height: vote.Height - 1, + LastAppHash: lastAppHash, } v := vote.ToProto() - err = vs.PrivValidator.SignVote(config.ChainID(), quorumType, quorumHash, v, nil) + + err = vs.PrivValidator.SignVote(config.ChainID(), quorumType, quorumHash, v, stateID, nil) + vote.BlockSignature = v.BlockSignature vote.StateSignature = v.StateSignature @@ -397,7 +411,7 @@ func newStateWithConfigAndBlockStore( blockStore := store.NewBlockStore(blockDB) // one for mempool, one for consensus, one for signature validation - mtx := new(tmsync.Mutex) + mtx := new(tmsync.RWMutex) proxyAppConnMem := abcicli.NewLocalClient(mtx, app) proxyAppConnCon := abcicli.NewLocalClient(mtx, app) proxyAppConnQry := abcicli.NewLocalClient(mtx, app) @@ -453,10 +467,8 @@ func randState(nValidators int) (*State, []*validatorStub) { cs := newState(state, privVals[0], counter.NewApplication(true)) for i := 0; i < nValidators; i++ { - vss[i] = newValidatorStub(privVals[i], int32(i)) + vss[i] = newValidatorStub(privVals[i], int32(i), cs.state.InitialHeight) } - // since cs1 starts at 1 - incrementHeight(vss[1:]...) return cs, vss } @@ -697,12 +709,15 @@ func consensusLogger() log.Logger { }, "debug").With("module", "consensus") } -func randConsensusNet(nValidators int, testName string, tickerFunc func() TimeoutTicker, +func randConsensusNet(nValidators int, initialHeight int64, testName string, tickerFunc func() TimeoutTicker, appFunc func() abci.Application, configOpts ...func(*cfg.Config)) ([]*State, cleanupFunc) { - genDoc, privVals := randGenesisDoc(nValidators, false, 30) + genDoc, privVals := randGenesisDoc(nValidators, false, 30, initialHeight) css := make([]*State, nValidators) logger := consensusLogger() + + closeFuncs := make([]func() error, 0, nValidators) configRootDirs := make([]string, 0, nValidators) + for i := 0; i < nValidators; i++ { stateDB := dbm.NewMemDB() // each state needs its own db stateStore := sm.NewStore(stateDB) @@ -714,6 +729,11 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou } ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal app := appFunc() + + if appCloser, ok := app.(io.Closer); ok { + closeFuncs = append(closeFuncs, appCloser.Close) + } + vals := types.TM2PB.ValidatorUpdates(state.Validators) app.InitChain(abci.RequestInitChain{ValidatorSet: &vals}) @@ -722,6 +742,9 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou css[i].SetLogger(logger.With("validator", i, "module", "consensus")) } return css, func() { + for _, closer := range closeFuncs { + _ = closer() + } for _, dir := range configRootDirs { os.RemoveAll(dir) } @@ -906,10 +929,11 @@ func randConsensusNetWithPeers( tickerFunc func() TimeoutTicker, appFunc func(string) abci.Application, ) ([]*State, *types.GenesisDoc, *cfg.Config, cleanupFunc) { - genDoc, privVals := randGenesisDoc(nValidators, false, testMinPower) + genDoc, privVals := randGenesisDoc(nValidators, false, testMinPower, 1) css := make([]*State, nPeers) logger := consensusLogger() var peer0Config *cfg.Config + closeFuncs := make([]func() error, 0, nValidators) configRootDirs := make([]string, 0, nPeers) for i := 0; i < nPeers; i++ { stateDB := dbm.NewMemDB() // each state needs its own db @@ -942,6 +966,9 @@ func randConsensusNetWithPeers( } app := appFunc(path.Join(config.DBDir(), fmt.Sprintf("%s_%d", testName, i))) + if appCloser, ok := app.(io.Closer); ok { + closeFuncs = append(closeFuncs, appCloser.Close) + } vals := types.TM2PB.ValidatorUpdates(state.Validators) if _, ok := app.(*kvstore.PersistentKVStoreApplication); ok { // simulate handshake, receive app version. If don't do this, replay test will fail @@ -956,6 +983,9 @@ func randConsensusNetWithPeers( css[i].SetLogger(logger.With("validator", i, "proTxHash", proTxHash.ShortString(), "module", "consensus")) } return css, genDoc, peer0Config, func() { + for _, closer := range closeFuncs { + _ = closer() + } for _, dir := range configRootDirs { os.RemoveAll(dir) } @@ -974,7 +1004,7 @@ func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int { //------------------------------------------------------------------------------- // genesis -func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) { +func randGenesisDoc(numValidators int, randPower bool, minPower int64, initialHeight int64) (*types.GenesisDoc, []types.PrivValidator) { validators := make([]types.GenesisValidator, numValidators) privValidators := make([]types.PrivValidator, numValidators) @@ -987,8 +1017,8 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G Power: val.VotingPower, ProTxHash: val.ProTxHash, } - privValidators[i] = types.NewMockPVWithParams(privateKeys[i], proTxHashes[i], quorumHash, thresholdPublicKey, - false, false) + privValidators[i] = types.NewMockPVWithParams(privateKeys[i], proTxHashes[i], quorumHash, + thresholdPublicKey, false, false) } sort.Sort(types.PrivValidatorsByProTxHash(privValidators)) @@ -996,18 +1026,19 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G return &types.GenesisDoc{ GenesisTime: tmtime.Now(), - InitialHeight: 1, + InitialHeight: initialHeight, ChainID: config.ChainID(), Validators: validators, InitialCoreChainLockedHeight: 1, InitialProposalCoreChainLock: coreChainLock.ToProto(), ThresholdPublicKey: thresholdPublicKey, QuorumHash: quorumHash, + AppHash: genesisAppHash, }, privValidators } func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) { - genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower) + genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower, 1) s0, _ := sm.MakeGenesisState(genDoc) return s0, privValidators } diff --git a/consensus/core_chainlock_test.go b/consensus/core_chainlock_test.go index 736033db88..255b0dfd1a 100644 --- a/consensus/core_chainlock_test.go +++ b/consensus/core_chainlock_test.go @@ -29,7 +29,8 @@ func newCounterWithBackwardsCoreChainLocks() abci.Application { func TestValidProposalChainLocks(t *testing.T) { N := 4 - css, cleanup := randConsensusNet(N, "consensus_chainlocks_test", newMockTickerFunc(true), newCounterWithCoreChainLocks) + css, cleanup := randConsensusNet(N, 1, "consensus_chainlocks_test", + newMockTickerFunc(true), newCounterWithCoreChainLocks) defer cleanup() for i := 0; i < 4; i++ { @@ -47,7 +48,9 @@ func TestValidProposalChainLocks(t *testing.T) { msg := <-blocksSubs[j].Out() block := msg.Data().(types.EventDataNewBlock).Block // this is true just because of this test where each new height has a new chain lock that is incremented by 1 - assert.EqualValues(t, block.Header.Height, block.Header.CoreChainLockedHeight) + state := css[0].GetState() + assert.EqualValues(t, i+1, block.Header.CoreChainLockedHeight) //nolint:scopelint + assert.EqualValues(t, state.InitialHeight+int64(i), block.Header.Height) //nolint:scopelint }, css) } } @@ -55,7 +58,8 @@ func TestValidProposalChainLocks(t *testing.T) { // one byz val sends a proposal for a height 1 less than it should, but then sends the correct block after it func TestReactorInvalidProposalHeightForChainLocks(t *testing.T) { N := 4 - css, cleanup := randConsensusNet(N, "consensus_chainlocks_test", newMockTickerFunc(true), newCounterWithCoreChainLocks) + css, cleanup := randConsensusNet(N, 1, "consensus_chainlocks_test", + newMockTickerFunc(true), newCounterWithCoreChainLocks) defer cleanup() for i := 0; i < 4; i++ { @@ -87,7 +91,9 @@ func TestReactorInvalidProposalHeightForChainLocks(t *testing.T) { msg := <-blocksSubs[j].Out() block := msg.Data().(types.EventDataNewBlock).Block // this is true just because of this test where each new height has a new chain lock that is incremented by 1 - assert.EqualValues(t, block.Header.Height, block.Header.CoreChainLockedHeight) + state := css[0].GetState() + assert.EqualValues(t, i+1, block.Header.CoreChainLockedHeight) //nolint:scopelint + assert.EqualValues(t, state.InitialHeight+int64(i), block.Header.Height) //nolint:scopelint }, css) } } @@ -145,7 +151,7 @@ func invalidProposeCoreChainLockFunc(t *testing.T, height int64, round int32, cs func TestReactorInvalidBlockChainLock(t *testing.T) { // TODO: Leads to race, explore N := 4 - css, cleanup := randConsensusNet(N, "consensus_chainlocks_test", + css, cleanup := randConsensusNet(N, 1, "consensus_chainlocks_test", newMockTickerFunc(true), newCounterWithBackwardsCoreChainLocks) defer cleanup() @@ -164,7 +170,8 @@ func TestReactorInvalidBlockChainLock(t *testing.T) { msg := <-blocksSubs[j].Out() block := msg.Data().(types.EventDataNewBlock).Block // this is true just because of this test where each new height has a new chain lock that is incremented by 1 - if block.Header.Height == 1 { + state := css[0].GetState() + if block.Header.Height == state.InitialHeight { assert.EqualValues(t, 1, block.Header.CoreChainLockedHeight) } else { // We started at 1 then 99, then try 98, 97, 96... diff --git a/consensus/invalid_test.go b/consensus/invalid_test.go index bd1fdc30ba..43e43e6c23 100644 --- a/consensus/invalid_test.go +++ b/consensus/invalid_test.go @@ -18,7 +18,7 @@ import ( // Ensure a testnet makes blocks func TestReactorInvalidPrecommit(t *testing.T) { N := 4 - css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + css, cleanup := randConsensusNet(N, 1, "consensus_reactor_test", newMockTickerFunc(true), newCounter) defer cleanup() for i := 0; i < 4; i++ { @@ -68,12 +68,13 @@ func invalidDoPrevoteFunc(t *testing.T, height int64, round int32, cs *State, sw } valIndex, _ := cs.Validators.GetByProTxHash(proTxHash) + stateID := cs.state.StateID() + // precommit a random block blockHash := bytes.HexBytes(tmrand.Bytes(32)) - lastAppHash := bytes.HexBytes(tmrand.Bytes(32)) // we want to see both errors, so send the correct state id half the time if tmrand.Bool() == true { - lastAppHash = cs.state.AppHash + stateID.LastAppHash = cs.state.AppHash } precommit := &types.Vote{ ValidatorProTxHash: proTxHash, @@ -84,12 +85,10 @@ func invalidDoPrevoteFunc(t *testing.T, height int64, round int32, cs *State, sw BlockID: types.BlockID{ Hash: blockHash, PartSetHeader: types.PartSetHeader{Total: 1, Hash: tmrand.Bytes(32)}}, - StateID: types.StateID{ - LastAppHash: lastAppHash, - }, } p := precommit.ToProto() - err = cs.privValidator.SignVote(cs.state.ChainID, cs.Validators.QuorumType, cs.Validators.QuorumHash, p, nil) + err = cs.privValidator.SignVote(cs.state.ChainID, cs.Validators.QuorumType, cs.Validators.QuorumHash, + p, stateID, nil) if err != nil { t.Error(err) } diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index 1823774b57..33edf21621 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -25,22 +25,31 @@ func assertMempool(txn txNotifier) mempl.Mempool { } func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) { - config := ResetConfig("consensus_mempool_txs_available_test") - defer os.RemoveAll(config.RootDir) - config.Consensus.CreateEmptyBlocks = false - state, privVals := randGenesisState(1, false, 100) - cs := newStateWithConfig(config, state, privVals[0], NewCounterApplication()) - assertMempool(cs.txNotifier).EnableTxsAvailable() - height, round := cs.Height, cs.Round - newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock) - startTestRound(cs, height, round) - - ensureNewEventOnChannel(newBlockCh) // first block gets committed - ensureNoNewEventOnChannel(newBlockCh) - deliverTxsRange(cs, 0, 1) - ensureNewEventOnChannel(newBlockCh) // commit txs - ensureNewEventOnChannel(newBlockCh) // commit updated app hash - ensureNoNewEventOnChannel(newBlockCh) + for proofBlockRange := int64(1); proofBlockRange <= 3; proofBlockRange++ { + t.Logf("Checking proof block range %d", proofBlockRange) + + config := ResetConfig("consensus_mempool_txs_available_test") + defer os.RemoveAll(config.RootDir) + config.Consensus.CreateEmptyBlocks = false + config.Consensus.CreateProofBlockRange = proofBlockRange + + state, privVals := randGenesisState(1, false, types.DefaultDashVotingPower) + + cs := newStateWithConfig(config, state, privVals[0], NewCounterApplication()) + assertMempool(cs.txNotifier).EnableTxsAvailable() + height, round := cs.Height, cs.Round + newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock) + startTestRound(cs, height, round) + + ensureNewEventOnChannel(newBlockCh) // first block gets committed + ensureNoNewEventOnChannel(newBlockCh) + deliverTxsRange(cs, 0, 1) + ensureNewEventOnChannel(newBlockCh) // commit txs + for i := int64(0); i < proofBlockRange; i++ { + ensureNewEventOnChannel(newBlockCh) // commit updated app hash + } + ensureNoNewEventOnChannel(newBlockCh) + } } func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) { @@ -48,7 +57,9 @@ func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) { defer os.RemoveAll(config.RootDir) config.Consensus.CreateEmptyBlocksInterval = ensureTimeout + state, privVals := randGenesisState(1, false, 10) + cs := newStateWithConfig(config, state, privVals[0], NewCounterApplication()) assertMempool(cs.txNotifier).EnableTxsAvailable() @@ -73,7 +84,7 @@ func TestMempoolProgressInHigherRound(t *testing.T) { newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound) timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose) cs.setProposal = func(proposal *types.Proposal) error { - if cs.Height == 2 && cs.Round == 0 { + if cs.Height == cs.state.InitialHeight+1 && cs.Round == 0 { // dont set the proposal in round 0 so we timeout and // go to next round cs.Logger.Info("Ignoring set proposal at height 2, round 0") @@ -250,8 +261,11 @@ func txAsUint64(tx []byte) uint64 { func (app *CounterApplication) Commit() abci.ResponseCommit { app.mempoolTxCount = app.txCount if app.txCount == 0 { - return abci.ResponseCommit{} + return abci.ResponseCommit{ + Data: genesisAppHash, + } } + hash := make([]byte, 32) binary.BigEndian.PutUint64(hash, uint64(app.txCount)) return abci.ResponseCommit{Data: hash} diff --git a/consensus/msgs_test.go b/consensus/msgs_test.go index 0b0fd707e6..33aa38d2bc 100644 --- a/consensus/msgs_test.go +++ b/consensus/msgs_test.go @@ -333,10 +333,6 @@ func TestConsMsgsVectors(t *testing.T) { PartSetHeader: psh, } - si := types.StateID{ - LastAppHash: make([]byte, 32), - } - pbBi := bi.ToProto() bits := bits.NewBitArray(1) pbBits := bits.ToProto() @@ -373,7 +369,6 @@ func TestConsMsgsVectors(t *testing.T) { Round: 0, Type: tmproto.PrecommitType, BlockID: bi, - StateID: si, } vpb := v.ToProto() @@ -410,7 +405,7 @@ func TestConsMsgsVectors(t *testing.T) { "2a36080110011a3008011204746573741a26080110011a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d"}, {"Vote", &tmcons.Message{Sum: &tmcons.Message_Vote{ Vote: &tmcons.Vote{Vote: vpb}}}, - "328d010a8a010802100122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d32146164645f6d6f72655f6578636c616d6174696f6e38014a220a200000000000000000000000000000000000000000000000000000000000000000"}, + "32680a660802100122480a206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d1224080112206164645f6d6f72655f6578636c616d6174696f6e5f6d61726b735f636f64652d32146164645f6d6f72655f6578636c616d6174696f6e3801"}, {"HasVote", &tmcons.Message{Sum: &tmcons.Message_HasVote{ HasVote: &tmcons.HasVote{Height: 1, Round: 1, Type: tmproto.PrevoteType, Index: 1}}}, "3a080801100118012001"}, diff --git a/consensus/reactor.go b/consensus/reactor.go index 24cebfd6f5..fb4a10a0a3 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -1151,7 +1151,15 @@ func (ps *PeerState) SendCommit(commit *types.Commit) bool { func (ps *PeerState) PickSendVote(votes types.VoteSetReader) bool { if vote, ok := ps.PickVoteToSend(votes); ok { msg := &VoteMessage{vote} - ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote) + ps.logger.Debug( + "Sending vote message", + "ps", ps, + "peer_proTxHash", ps.peer.NodeInfo().GetProTxHash().ShortString(), + "vote", vote, + "val_proTxHash", vote.ValidatorProTxHash.ShortString(), + "height", vote.Height, + "round", vote.Round, + ) if ps.peer.Send(VoteChannel, MustEncode(msg)) { ps.SetHasVote(vote, nil) return true @@ -1344,11 +1352,13 @@ func (ps *PeerState) SetHasVote(vote *types.Vote, cs *State) { logger := log.Logger(nil) if cs != nil { roundState := cs.GetRoundState() - logger = ps.logger.With("peer", peerProTxHash.ShortString(), - "peerHR", - fmt.Sprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), - "HR", - fmt.Sprintf("%d/%d", roundState.Height, roundState.Round)) + logger = ps.logger.With( + "peer", peerProTxHash.ShortString(), + "height", roundState.Height, + "round", roundState.Round, + "peer_height", ps.PRS.Height, + "peer_round", ps.PRS.Round, + ) } ps.setHasVote(vote.Height, vote.Round, vote.Type, vote.ValidatorIndex, logger) @@ -1359,11 +1369,14 @@ func (ps *PeerState) setHasVote( if logger == nil { peerProTxHash := ps.peer.NodeInfo().GetProTxHash() - logger = ps.logger.With("peer", peerProTxHash.ShortString(), - "peerHR", - fmt.Sprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), - "HR", - fmt.Sprintf("%d/%d", height, round)) + logger = ps.logger.With( + "peer", peerProTxHash.ShortString(), + "height", height, + "round", round, + "peer_height", ps.PRS.Height, + "peer_round", ps.PRS.Round, + ) + } // NOTE: some may be nil BitArrays -> no side effects. @@ -1373,8 +1386,13 @@ func (ps *PeerState) setHasVote( psVotes.SetIndex(int(index), true) countVotes = psVotes.CountTrueBits() } - logger.Debug("peerState setHasVote", "type", voteType, "index", index, "peerVotes", psVotes, - "peerVoteCount", countVotes) + logger.Debug( + "peerState setHasVote", + "type", voteType, + "index", index, + "peerVotes", psVotes, + "peerVoteCount", countVotes, + ) } // SetHasCommit sets the given vote as known by the peer @@ -1383,10 +1401,11 @@ func (ps *PeerState) SetHasCommit(commit *types.Commit) { defer ps.mtx.Unlock() logger := ps.logger.With( - "peerHR", - fmt.Sprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), - "HR", - fmt.Sprintf("%d/%d", commit.Height, commit.Round)) + "height", commit.Height, + "round", commit.Round, + "peer_height", ps.PRS.Height, + "peer_round", ps.PRS.Round, + ) logger.Debug("setHasCommit") ps.setHasCommit(commit.Height, commit.Round) @@ -1401,10 +1420,11 @@ func (ps *PeerState) SetHasCommit(commit *types.Commit) { func (ps *PeerState) setHasCommit(height int64, round int32) { logger := ps.logger.With( - "peerHR", - fmt.Sprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), - "HR", - fmt.Sprintf("%d/%d", height, round)) + "height", height, + "round", round, + "peer_height", ps.PRS.Height, + "peer_round", ps.PRS.Round, + ) logger.Debug("setHasCommit") if ps.PRS.Height < height || (ps.PRS.Height == height && ps.PRS.Round <= round) { @@ -1587,6 +1607,7 @@ func init() { tmjson.RegisterType(&HasCommitMessage{}, "tendermint/HasCommit") tmjson.RegisterType(&VoteSetMaj23Message{}, "tendermint/VoteSetMaj23") tmjson.RegisterType(&VoteSetBitsMessage{}, "tendermint/VoteSetBits") + tmjson.RegisterType(&CommitMessage{}, "tendermint/CommitMessage") } func decodeMsg(bz []byte) (msg Message, err error) { diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index cb7f9e562d..93b265e2bb 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -1,4 +1,3 @@ -//nolint:lll package consensus import ( @@ -116,152 +115,191 @@ func stopConsensusNet(logger log.Logger, reactors []*Reactor, eventBuses []*type // Ensure a testnet makes blocks func TestReactorBasic(t *testing.T) { - N := 4 - css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) - defer cleanup() - reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N) - defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) - // wait till everyone makes the first new block - timeoutWaitGroup(t, N, func(j int) { - <-blocksSubs[j].Out() - }, css) -} -// Ensure a testnet doesn't make blocks if only 4 out of 7 nodes in a quorum are online -func TestReactorNoThreshold(t *testing.T) { - N := 7 - T := 4 - css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) - for i := 0; i < N; i++ { - css[i].config.SkipTimeoutCommit = false + testCases := []struct { + name string + nValidators int + initialHeight int64 + }{ + {"initialHeight 1", 4, 1}, + {"initialHeight 10000", 6, 10000}, } - defer cleanup() - reactors, blocksSubs, eventBuses := startConsensusNet(t, css, T) - voteCh := subscribe(css[0].eventBus, types.EventQueryVote) - // map of active validators - activeVals := make(map[string]struct{}) - for i := 0; i < T; i++ { - proTxHash, err := css[i].privValidator.GetProTxHash() - require.NoError(t, err) - activeVals[string(proTxHash)] = struct{}{} + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + nValidators := tc.nValidators // nolint:scopelint + initialHeight := tc.initialHeight // nolint:scopelint + css, cleanup := randConsensusNet(nValidators, initialHeight, "consensus_reactor_test", + newMockTickerFunc(true), newCounter) + defer cleanup() + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, nValidators) + defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + // wait till everyone makes the first new block + timeoutWaitGroup(t, nValidators, func(j int) { + <-blocksSubs[j].Out() + }, css) + }) } +} - defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) +// Ensure the testnet makes blocks only if there are enough nodes +// Note that each negative test can take up to ~ 30 seconds here - see timeout in timeoutWaitNoBlockGroup() +// Run with at least -test.timeout 90s +func TestReactorThreshold(t *testing.T) { + testCases := []struct { + activeValidators int + allValidators int + initialHeight int64 + skipTimeoutCommit bool + shouldGenerateBlock bool + }{ + {4, 7, 1, false, false}, // doesn't make blocks if only 4 out of 7 nodes in a quorum are online + {4, 7, 10000, false, false}, // doesn't make blocks if only 4 out of 7 nodes in a quorum are online + {5, 7, 1, false, true}, // testnet makes blocks if only 5 out of 7 nodes in a quorum are online + {7, 7, 10000, false, true}, // testnet makes blocks if only 5 out of 7 nodes in a quorum are online + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("%d of %d,h=%d", tc.activeValidators, tc.allValidators, tc.initialHeight), func(t *testing.T) { + allValidators := tc.allValidators // nolint:scopelint + activeValidators := tc.activeValidators // nolint:scopelint + initialHeight := tc.initialHeight // nolint:scopelint + shouldGenerate := tc.shouldGenerateBlock // nolint:scopelint + + logger := log.TestingLogger() + + css, cleanup := randConsensusNet(allValidators, initialHeight, "consensus_reactor_test", + newMockTickerFunc(true), newCounter) + for i := 0; i < allValidators; i++ { + // TODO document why we need this + css[i].config.SkipTimeoutCommit = tc.skipTimeoutCommit // nolint:scopelint + } + defer cleanup() - ensurePrevote(voteCh, 1, 0) - waitForAndValidateNoBlock(t, T, blocksSubs, css) -} + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, activeValidators) -// Ensure a testnet makes blocks if only 5 out of 7 nodes in a quorum are online -func TestReactorAtThreshold(t *testing.T) { - N := 7 - T := 5 - css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) - defer cleanup() - reactors, blocksSubs, eventBuses := startConsensusNet(t, css, T) + voteCh := subscribe(css[0].eventBus, types.EventQueryVote) + // map of active validators + activeVals := make(map[string]struct{}) + for i := 0; i < activeValidators; i++ { + proTxHash, err := css[i].privValidator.GetProTxHash() + require.NoError(t, err) + activeVals[string(proTxHash)] = struct{}{} + } - logger := log.TestingLogger() + defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) - // map of active validators - activeVals := make(map[string]struct{}) - for i := 0; i < T; i++ { - proTxHash, err := css[i].privValidator.GetProTxHash() - require.NoError(t, err) - activeVals[string(proTxHash)] = struct{}{} + if shouldGenerate { + waitForAndValidateBlock(t, activeValidators, activeVals, blocksSubs, css) + logger.Info("->Passed block 2") + } else { + ensurePrevote(voteCh, css[0].state.InitialHeight, 0) + waitForAndValidateNoBlock(t, activeValidators, blocksSubs, css) + } + }) } - - defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) - // wait till everyone makes the first new block - waitForAndValidateBlock(t, T, activeVals, blocksSubs, css) - logger.Info("->Passed block 2") } // Ensure we can process blocks with evidence func TestReactorWithEvidence(t *testing.T) { - nValidators := 4 - testName := "consensus_reactor_test" - tickerFunc := newMockTickerFunc(true) - appFunc := newCounter - - // heed the advice from https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction - // to unroll unwieldy abstractions. Here we duplicate the code from: - // css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) - - genDoc, privVals := randGenesisDoc(nValidators, false, 30) - css := make([]*State, nValidators) - logger := consensusLogger() - for i := 0; i < nValidators; i++ { - stateDB := dbm.NewMemDB() // each state needs its own db - stateStore := sm.NewStore(stateDB) - state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc) - thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) - defer os.RemoveAll(thisConfig.RootDir) - ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal - app := appFunc() - vals := types.TM2PB.ValidatorUpdates(state.Validators) - app.InitChain(abci.RequestInitChain{ValidatorSet: &vals}) - - pv := privVals[i] - // duplicate code from: - // css[i] = newStateWithConfig(thisConfig, state, privVals[i], app) - - blockDB := dbm.NewMemDB() - blockStore := store.NewBlockStore(blockDB) - - // one for mempool, one for consensus - mtx := new(tmsync.Mutex) - proxyAppConnMem := abcicli.NewLocalClient(mtx, app) - proxyAppConnCon := abcicli.NewLocalClient(mtx, app) - proxyAppConnQry := abcicli.NewLocalClient(mtx, app) - - // Make Mempool - mempool := mempl.NewCListMempool(thisConfig.Mempool, proxyAppConnMem, 0) - mempool.SetLogger(log.TestingLogger().With("module", "mempool")) - if thisConfig.Consensus.WaitForTxs() { - mempool.EnableTxsAvailable() - } + testCases := []struct { + name string + nValidators int + initialHeight int64 + }{ + {"initialHeight 1", 4, 1}, + {"initialHeight 10000", 6, 10000}, + } - // mock the evidence pool - // everyone includes evidence of another double signing - vIdx := (i + 1) % nValidators - ev := types.NewMockDuplicateVoteEvidenceWithValidator(1, defaultTestTime, privVals[vIdx], config.ChainID(), - btcjson.LLMQType_5_60, state.Validators.QuorumHash) - evpool := &statemocks.EvidencePool{} - evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil) - evpool.On("PendingEvidence", mock.AnythingOfType("int64")).Return([]types.Evidence{ - ev}, int64(len(ev.Bytes()))) - evpool.On("Update", mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return() - - evpool2 := sm.EmptyEvidencePool{} - - // Make State - blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyAppConnCon, proxyAppConnQry, mempool, evpool, nil) - cs := NewState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool2) - cs.SetLogger(log.TestingLogger().With("module", "consensus")) - cs.SetPrivValidator(pv) - - eventBus := types.NewEventBus() - eventBus.SetLogger(log.TestingLogger().With("module", "events")) - err := eventBus.Start() - require.NoError(t, err) - cs.SetEventBus(eventBus) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + nValidators := tc.nValidators // nolint:scopelint + initialHeight := tc.initialHeight // nolint:scopelint + testName := "consensus_reactor_test" + tickerFunc := newMockTickerFunc(true) + appFunc := newCounter + + // heed the advice from https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction + // to unroll unwieldy abstractions. Here we duplicate the code from: + // css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + + genDoc, privVals := randGenesisDoc(nValidators, false, 30, initialHeight) + css := make([]*State, nValidators) + logger := consensusLogger() + for i := 0; i < nValidators; i++ { + stateDB := dbm.NewMemDB() // each state needs its own db + stateStore := sm.NewStore(stateDB) + state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc) + thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) + defer os.RemoveAll(thisConfig.RootDir) + ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal + app := appFunc() + vals := types.TM2PB.ValidatorUpdates(state.Validators) + app.InitChain(abci.RequestInitChain{ValidatorSet: &vals}) + + pv := privVals[i] + // duplicate code from: + // css[i] = newStateWithConfig(thisConfig, state, privVals[i], app) + + blockDB := dbm.NewMemDB() + blockStore := store.NewBlockStore(blockDB) + + // one for mempool, one for consensus + mtx := new(tmsync.RWMutex) + proxyAppConnMem := abcicli.NewLocalClient(mtx, app) + proxyAppConnCon := abcicli.NewLocalClient(mtx, app) + proxyAppConnQry := abcicli.NewLocalClient(mtx, app) + + // Make Mempool + mempool := mempl.NewCListMempool(thisConfig.Mempool, proxyAppConnMem, 0) + mempool.SetLogger(log.TestingLogger().With("module", "mempool")) + if thisConfig.Consensus.WaitForTxs() { + mempool.EnableTxsAvailable() + } + + // mock the evidence pool + // everyone includes evidence of another double signing + vIdx := (i + 1) % nValidators + ev := types.NewMockDuplicateVoteEvidenceWithValidator(1, defaultTestTime, privVals[vIdx], config.ChainID(), + btcjson.LLMQType_5_60, state.Validators.QuorumHash) + evpool := &statemocks.EvidencePool{} + evpool.On("CheckEvidence", mock.AnythingOfType("types.EvidenceList")).Return(nil) + evpool.On("PendingEvidence", mock.AnythingOfType("int64")).Return([]types.Evidence{ + ev}, int64(len(ev.Bytes()))) + evpool.On("Update", mock.AnythingOfType("state.State"), mock.AnythingOfType("types.EvidenceList")).Return() + + evpool2 := sm.EmptyEvidencePool{} + + // Make State + blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), + proxyAppConnCon, proxyAppConnQry, mempool, evpool, nil) + cs := NewState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool2) + cs.SetLogger(log.TestingLogger().With("module", "consensus")) + cs.SetPrivValidator(pv) + + eventBus := types.NewEventBus() + eventBus.SetLogger(log.TestingLogger().With("module", "events")) + err := eventBus.Start() + require.NoError(t, err) + cs.SetEventBus(eventBus) - cs.SetTimeoutTicker(tickerFunc()) - cs.SetLogger(logger.With("validator", i, "module", "consensus")) + cs.SetTimeoutTicker(tickerFunc()) + cs.SetLogger(logger.With("validator", i, "module", "consensus")) - css[i] = cs - } + css[i] = cs + } - reactors, blocksSubs, eventBuses := startConsensusNet(t, css, nValidators) - defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, nValidators) + defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) - // we expect for each validator that is the proposer to propose one piece of evidence. - for i := 0; i < nValidators; i++ { - timeoutWaitGroup(t, nValidators, func(j int) { - msg := <-blocksSubs[j].Out() - block := msg.Data().(types.EventDataNewBlock).Block - assert.Len(t, block.Evidence.Evidence, 1) - }, css) + // we expect for each validator that is the proposer to propose one piece of evidence. + for i := 0; i < nValidators; i++ { + timeoutWaitGroup(t, nValidators, func(j int) { + msg := <-blocksSubs[j].Out() + block := msg.Data().(types.EventDataNewBlock).Block + assert.Len(t, block.Evidence.Evidence, 1) + }, css) + } + }) } } @@ -269,29 +307,45 @@ func TestReactorWithEvidence(t *testing.T) { // Ensure a testnet makes blocks when there are txs func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) { - N := 4 - css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter, - func(c *cfg.Config) { - c.Consensus.CreateEmptyBlocks = false - }) - defer cleanup() - reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N) - defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) - // send a tx - if err := assertMempool(css[3].txNotifier).CheckTx([]byte{1, 2, 3}, nil, mempl.TxInfo{}); err != nil { - t.Error(err) + testCases := []struct { + nValidators int + initialHeight int64 + }{ + {nValidators: 4, initialHeight: 1}, + {nValidators: 5, initialHeight: 10000}, } - // wait till everyone makes the first new block - timeoutWaitGroup(t, N, func(j int) { - <-blocksSubs[j].Out() - }, css) + for _, tc := range testCases { + t.Run(fmt.Sprintf("N:%d H:%d", tc.nValidators, tc.initialHeight), func(t *testing.T) { + nValidators := tc.nValidators // nolint:scopelint + initialHeight := tc.initialHeight // nolint:scopelint + + css, cleanup := randConsensusNet(nValidators, initialHeight, "consensus_reactor_test", + newMockTickerFunc(true), newCounter, + func(c *cfg.Config) { + c.Consensus.CreateEmptyBlocks = false + }) + defer cleanup() + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, nValidators) + defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + + // send a tx + if err := assertMempool(css[3].txNotifier).CheckTx([]byte{1, 2, 3}, nil, mempl.TxInfo{}); err != nil { + t.Error(err) + } + + // wait till everyone makes the first new block + timeoutWaitGroup(t, nValidators, func(j int) { + <-blocksSubs[j].Out() + }, css) + }) + } } func TestReactorReceiveDoesNotPanicIfAddPeerHasntBeenCalledYet(t *testing.T) { N := 1 - css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + css, cleanup := randConsensusNet(N, 1, "consensus_reactor_test", newMockTickerFunc(true), newCounter) defer cleanup() reactors, _, eventBuses := startConsensusNet(t, css, N) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) @@ -299,7 +353,7 @@ func TestReactorReceiveDoesNotPanicIfAddPeerHasntBeenCalledYet(t *testing.T) { var ( reactor = reactors[0] peer = p2pmock.NewPeer(nil) - msg = MustEncode(&HasVoteMessage{Height: 1, + msg = MustEncode(&HasVoteMessage{Height: css[0].state.InitialHeight, Round: 1, Index: 1, Type: tmproto.PrevoteType}) ) @@ -314,7 +368,7 @@ func TestReactorReceiveDoesNotPanicIfAddPeerHasntBeenCalledYet(t *testing.T) { func TestReactorReceivePanicsIfInitPeerHasntBeenCalledYet(t *testing.T) { N := 1 - css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + css, cleanup := randConsensusNet(N, 1, "consensus_reactor_test", newMockTickerFunc(true), newCounter) defer cleanup() reactors, _, eventBuses := startConsensusNet(t, css, N) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) @@ -322,7 +376,7 @@ func TestReactorReceivePanicsIfInitPeerHasntBeenCalledYet(t *testing.T) { var ( reactor = reactors[0] peer = p2pmock.NewPeer(nil) - msg = MustEncode(&HasVoteMessage{Height: 1, + msg = MustEncode(&HasVoteMessage{Height: css[0].state.InitialHeight, Round: 1, Index: 1, Type: tmproto.PrevoteType}) ) @@ -337,7 +391,7 @@ func TestReactorReceivePanicsIfInitPeerHasntBeenCalledYet(t *testing.T) { // Test we record stats about votes and block parts from other peers. func TestReactorRecordsVotesAndBlockParts(t *testing.T) { N := 4 - css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + css, cleanup := randConsensusNet(N, 1, "consensus_reactor_test", newMockTickerFunc(true), newCounter) defer cleanup() reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) @@ -390,7 +444,8 @@ func TestReactorValidatorSetChanges(t *testing.T) { // css contains all peers, the first 4 here are validators, so we will take the 5th peer and add it as a validator - updatedValidators, newValidatorProTxHashes, newThresholdPublicKey, quorumHash := updateConsensusNetAddNewValidators(css, 2, 1, true) + updatedValidators, newValidatorProTxHashes, newThresholdPublicKey, quorumHash := + updateConsensusNetAddNewValidators(css, 2, 1, true) updateTransactions := make([][]byte, len(updatedValidators)+2) for i := 0; i < len(updatedValidators); i++ { @@ -432,7 +487,8 @@ func TestReactorValidatorSetChanges(t *testing.T) { //--------------------------------------------------------------------------- logger.Info("---------------------------- Testing adding two validators at once") - updatedValidators, newValidatorProTxHashes, newThresholdPublicKey, quorumHash = updateConsensusNetAddNewValidators(css, 7, 2, true) + updatedValidators, newValidatorProTxHashes, newThresholdPublicKey, quorumHash = + updateConsensusNetAddNewValidators(css, 7, 2, true) updateTransactions2 := make([][]byte, len(updatedValidators)+2) for i := 0; i < len(updatedValidators); i++ { @@ -466,7 +522,8 @@ func TestReactorValidatorSetChanges(t *testing.T) { // since the quorum hash is changing, we do not need to set the removed validators - updatedValidators, removedValidators, newThresholdPublicKey, newQuorumHash := updateConsensusNetRemoveValidators(css, 12, 2, true) + updatedValidators, removedValidators, newThresholdPublicKey, newQuorumHash := + updateConsensusNetRemoveValidators(css, 12, 2, true) updateTransactions3 := make([][]byte, len(updatedValidators)+2) for i := 0; i < len(updatedValidators); i++ { @@ -499,7 +556,8 @@ func TestReactorValidatorSetChanges(t *testing.T) { // Check we can make blocks with skip_timeout_commit=false func TestReactorWithTimeoutCommit(t *testing.T) { N := 4 - css, cleanup := randConsensusNet(N, "consensus_reactor_with_timeout_commit_test", newMockTickerFunc(false), newCounter) + css, cleanup := randConsensusNet(N, 1, "consensus_reactor_with_timeout_commit_test", + newMockTickerFunc(false), newCounter) defer cleanup() // override default SkipTimeoutCommit == true for tests for i := 0; i < N; i++ { diff --git a/consensus/replay.go b/consensus/replay.go index 14fab87cfe..5653f8aef5 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -279,11 +279,9 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) (uint64, error) { return 0, fmt.Errorf("got a negative last block height (%d) from the app", blockHeight) } appHash := res.LastBlockAppHash - coreChainLockedHeight := res.LastCoreChainLockedHeight h.logger.Info("ABCI Handshake App Info", "height", blockHeight, - "core height", coreChainLockedHeight, "hash", appHash, "software-version", res.Version, "protocol-version", res.AppVersion, @@ -300,9 +298,6 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) (uint64, error) { return 0, fmt.Errorf("error on replay: %v", err) } - // Set the initial state last core chain locked block height - h.initialState.LastCoreChainLockedBlockHeight = coreChainLockedHeight - h.logger.Info("Completed ABCI Handshake - Tendermint and App are synced", "appHeight", blockHeight, "appHash", appHash) @@ -434,6 +429,13 @@ func (h *Handshaker) ReplayBlocks( ) state.Version.Consensus.App = state.ConsensusParams.Version.AppVersion } + + // If we received non-zero initial core height, we set it here + if res.InitialCoreHeight > 0 && res.InitialCoreHeight != req.InitialCoreHeight { + state.LastCoreChainLockedBlockHeight = res.InitialCoreHeight + h.initialState.LastCoreChainLockedBlockHeight = res.InitialCoreHeight + } + // We update the last results hash with the empty hash, to conform with RFC-6962. state.LastResultsHash = merkle.HashFromByteSlices(nil) if err := h.stateStore.Save(state); err != nil { diff --git a/consensus/replay_file.go b/consensus/replay_file.go index da71a00b0b..0dbb4df008 100644 --- a/consensus/replay_file.go +++ b/consensus/replay_file.go @@ -318,7 +318,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo } // Create proxyAppConn connection (consensus, mempool, query) - clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) + clientCreator, _ := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) proxyApp := proxy.NewAppConns(clientCreator) err = proxyApp.Start() if err != nil { diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 9e2f668783..7fe7657ef9 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -3,6 +3,7 @@ package consensus import ( "bytes" "context" + "encoding/binary" "fmt" "io" "io/ioutil" @@ -359,13 +360,13 @@ func TestSimulateValidatorsChange(t *testing.T) { vss := make([]*validatorStub, nPeers) for i := 0; i < nPeers; i++ { - vss[i] = newValidatorStub(css[i].privValidator, int32(i)) + vss[i] = newValidatorStub(css[i].privValidator, int32(i), genDoc.InitialHeight) } height, round := css[0].Height, css[0].Round - // start the machine + // start the machine; note height should be equal to InitialHeight here, + // so we don't need to increment it startTestRound(css[0], height, round) - incrementHeight(vss...) ensureNewRound(newRoundCh, height, 0) ensureNewProposal(proposalCh, height, round) @@ -1119,7 +1120,7 @@ func testHandshakeReplay( t.Error(err) } }) - chain, commits, err = makeBlockchainFromWAL(wal) + chain, commits, err = makeBlockchainFromWAL(wal, gdoc) require.NoError(t, err) pubKey, err := privVal.GetPubKey(gdoc.QuorumHash) require.NoError(t, err) @@ -1155,6 +1156,7 @@ func testHandshakeReplay( // make a new client creator kvstoreApp := kvstore.NewPersistentKVStoreApplication( filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_a", nBlocks, mode))) + t.Cleanup(func() { require.NoError(t, kvstoreApp.Close()) }) clientCreator2 := proxy.NewLocalClientCreator(kvstoreApp) if nBlocks > 0 { @@ -1327,9 +1329,11 @@ func buildTMStateFromChain( nBlocks int, mode uint) sm.State { // run the whole chain against this client to build up the tendermint state - clientCreator := proxy.NewLocalClientCreator( - kvstore.NewPersistentKVStoreApplication( - filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_t", nBlocks, mode)))) + kvstoreApp := kvstore.NewPersistentKVStoreApplication( + filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_t", nBlocks, mode))) + defer kvstoreApp.Close() + clientCreator := proxy.NewLocalClientCreator(kvstoreApp) + proxyApp := proxy.NewAppConns(clientCreator) if err := proxyApp.Start(); err != nil { panic(err) @@ -1390,7 +1394,8 @@ func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) { genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) state.LastValidators = state.Validators.Copy() // mode = 0 for committing all the blocks - blocks := makeBlocks(3, &state, privVal) + blocks, err := makeBlocks(3, &state, privVal) + assert.NoError(t, err) store.chain = blocks // 2. Tendermint must panic if app returns wrong hash for the first block @@ -1456,7 +1461,7 @@ func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) { } } -func makeBlocks(n int, state *sm.State, privVal types.PrivValidator) []*types.Block { +func makeBlocks(n int, state *sm.State, privVal types.PrivValidator) ([]*types.Block, error) { blocks := make([]*types.Block, 0) var ( @@ -1464,78 +1469,63 @@ func makeBlocks(n int, state *sm.State, privVal types.PrivValidator) []*types.Bl prevBlockMeta *types.BlockMeta ) - appHeight := byte(0x01) - for i := 0; i < n; i++ { - height := int64(i + 1) + for height := state.InitialHeight; height < state.InitialHeight+int64(n); height++ { - block, parts := makeBlock(*state, prevBlock, prevBlockMeta, privVal, height) + block, parts, err := makeBlock(*state, prevBlock, prevBlockMeta, privVal, height) + if err != nil { + return nil, err + } blocks = append(blocks, block) prevBlock = block prevBlockMeta = types.NewBlockMeta(block, parts) // update state - state.AppHash = []byte{ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - appHeight, - } - appHeight++ + state.LastStateID = state.StateID() + state.AppHash = make([]byte, crypto.DefaultAppHashSize) + binary.BigEndian.PutUint64(state.AppHash, uint64(height)) + state.LastBlockHeight = height } - return blocks + return blocks, nil } func makeBlock(state sm.State, lastBlock *types.Block, lastBlockMeta *types.BlockMeta, - privVal types.PrivValidator, height int64) (*types.Block, *types.PartSet) { + privVal types.PrivValidator, height int64) (*types.Block, *types.PartSet, error) { + + var lastCommit *types.Commit - lastCommit := types.NewCommit(height-1, 0, types.BlockID{}, types.StateID{}, nil, - nil, nil) - if height > 1 { - vote, _ := types.MakeVote( + if state.LastBlockHeight != (height - 1) { + return nil, nil, fmt.Errorf("requested height %d should be 1 more than last block height %d", + height, state.LastBlockHeight) + } + + if lastBlock != nil && state.LastBlockHeight != lastBlock.Height { + return nil, nil, fmt.Errorf("last block height mismatch: %d while state has %d", + lastBlock.Height, state.LastBlockHeight) + } + if height > state.InitialHeight { + vote, err := types.MakeVote( lastBlock.Header.Height, lastBlockMeta.BlockID, - lastBlockMeta.StateID, + state.LastStateID, state.Validators, privVal, lastBlock.Header.ChainID) + if err != nil { + return nil, nil, fmt.Errorf("error when creating vote at height %d: %s", height, err) + } // since there is only 1 vote, use it as threshold lastCommit = types.NewCommit(vote.Height, vote.Round, - lastBlockMeta.BlockID, lastBlockMeta.StateID, crypto.RandQuorumHash(), + lastBlockMeta.BlockID, state.StateID(), crypto.RandQuorumHash(), vote.BlockSignature, vote.StateSignature) + } else { + lastCommit = types.NewCommit(height-1, 0, types.BlockID{}, state.StateID(), nil, + nil, nil) } - return state.MakeBlock( + block, partset := state.MakeBlock( height, nil, []types.Tx{}, @@ -1544,6 +1534,7 @@ func makeBlock(state sm.State, lastBlock *types.Block, lastBlockMeta *types.Bloc state.Validators.GetProposer().ProTxHash, 0, ) + return block, partset, nil } type badApp struct { @@ -1574,7 +1565,7 @@ func (app *badApp) Commit() abci.ResponseCommit { //-------------------------- // utils for making blocks -func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { +func makeBlockchainFromWAL(wal WAL, genDoc *types.GenesisDoc) ([]*types.Block, []*types.Commit, error) { var height int64 // Search for height marker @@ -1648,8 +1639,17 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { } case *types.Vote: if p.Type == tmproto.PrecommitType { + // previous block, needed to detemine StateID + var stateID types.StateID + if len(blocks) >= 1 { + prevBlock := blocks[len(blocks)-1] + stateID = types.StateID{Height: prevBlock.Height, LastAppHash: prevBlock.AppHash} + } else { + stateID = types.StateID{Height: genDoc.InitialHeight, LastAppHash: genDoc.AppHash} + } + thisBlockCommit = types.NewCommit(p.Height, p.Round, - p.BlockID, p.StateID, crypto.RandQuorumHash(), p.BlockSignature, p.StateSignature) + p.BlockID, stateID, crypto.RandQuorumHash(), p.BlockSignature, p.StateSignature) } } } @@ -1848,14 +1848,70 @@ func TestHandshakeUpdatesValidators(t *testing.T) { assert.Equal(t, newValProTxHash, expectValProTxHash) } +func TestHandshakeInitialCoreLockHeight(t *testing.T) { + const InitialCoreHeight uint32 = 12345 + config := ResetConfig("handshake_test_initial_core_lock_height") + defer os.RemoveAll(config.RootDir) + privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) + + randQuorumHash, err := privVal.GetFirstQuorumHash() + require.NoError(t, err) + + app := &initChainApp{initialCoreHeight: InitialCoreHeight} + clientCreator := proxy.NewLocalClientCreator(app) + + pubKey, err := privVal.GetPubKey(randQuorumHash) + require.NoError(t, err) + proTxHash, err := privVal.GetProTxHash() + require.NoError(t, err) + stateDB, state, store := stateAndStore(config, pubKey, 0x0) + stateStore := sm.NewStore(stateDB) + + // now start the app using the handshake - it should sync + genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) + handshaker := NewHandshaker( + stateStore, + state, + store, + genDoc, + &proTxHash, + config.Consensus.AppHashSize, + ) + proxyApp := proxy.NewAppConns(clientCreator) + if err := proxyApp.Start(); err != nil { + t.Fatalf("Error starting proxy app connections: %v", err) + } + t.Cleanup(func() { + if err := proxyApp.Stop(); err != nil { + t.Error(err) + } + }) + if _, err := handshaker.Handshake(proxyApp); err != nil { + t.Fatalf("Error on abci handshake: %v", err) + } + + // reload the state, check the validator set was updated + state, err = stateStore.Load() + require.NoError(t, err) + assert.Equal(t, InitialCoreHeight, state.LastCoreChainLockedBlockHeight) + assert.Equal(t, InitialCoreHeight, handshaker.initialState.LastCoreChainLockedBlockHeight) +} + // returns the vals on InitChain type initChainApp struct { abci.BaseApplication - vals *abci.ValidatorSetUpdate + vals *abci.ValidatorSetUpdate + initialCoreHeight uint32 } func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { - return abci.ResponseInitChain{ - ValidatorSetUpdate: *ica.vals, + resp := abci.ResponseInitChain{ + InitialCoreHeight: ica.initialCoreHeight, } + + if ica.vals != nil { + resp.ValidatorSetUpdate = *ica.vals + } + + return resp } diff --git a/consensus/state.go b/consensus/state.go index 3a265c7c22..cd54749259 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -704,6 +704,9 @@ func (cs *State) updateToState(state sm.State, commit *types.Commit, logger log. "to", validators.BasicInfoString()) } } + + stateID := state.StateID() + cs.Validators = validators cs.Proposal = nil cs.ProposalBlock = nil @@ -715,7 +718,7 @@ func (cs *State) updateToState(state sm.State, commit *types.Commit, logger log. cs.ValidBlock = nil cs.ValidBlockParts = nil cs.Commit = nil - cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators) + cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, stateID, validators) cs.CommitRound = -1 cs.LastValidators = state.LastValidators cs.TriggeredTimeoutPrecommit = false @@ -919,8 +922,8 @@ func (cs *State) handleMsg(mi msgInfo, fromReplay bool) { "height", cs.Height, "round", cs.Round, "peer", peerID, + "msg_type", fmt.Sprintf("%T", msg), "err", err, - "msg", msg, ) } } @@ -1076,19 +1079,36 @@ func (cs *State) enterNewRound(height int64, round int32) { } } -// needProofBlock returns true on the first height (so the genesis app hash is signed right away) -// and where the last block (height-1) caused the app hash to change +// needProofBlock returns true if additional proof block needs to be created. +// It happens on the first height (so the genesis app hash is signed right away) +// and where the last block (height-1) or the previous one (height - 2) caused +// the app hash to change. func (cs *State) needProofBlock(height int64) bool { if height == cs.state.InitialHeight { return true } - lastBlockMeta := cs.blockStore.LoadBlockMeta(height - 1) - if lastBlockMeta == nil { - panic(fmt.Sprintf("needProofBlock: last block meta for height %d not found", height-1)) + proofBlockRange := cs.config.CreateProofBlockRange + + for blockHeight := height - 1; blockHeight >= height-proofBlockRange; blockHeight-- { + if blockHeight >= cs.state.InitialHeight { + blockMeta := cs.blockStore.LoadBlockMeta(blockHeight) + if blockMeta == nil { + panic(fmt.Sprintf("needProofBlock (height=%d): last block meta for height %d not found", height, blockHeight)) + } + if !bytes.Equal(cs.state.AppHash, blockMeta.Header.AppHash) { + cs.Logger.Debug( + "needProofBlock: proof block needed", + "height", height, + "modified_height", blockHeight, + "range", proofBlockRange, + ) + return true + } + } } - return !bytes.Equal(cs.state.AppHash, lastBlockMeta.Header.AppHash) + return false } // Enter (CreateEmptyBlocks): from enterNewRound(height,round) @@ -1277,7 +1297,7 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa case cs.Height == cs.state.InitialHeight: // We're creating a proposal for the first block. // The commit is empty, but not nil. - commit = types.NewCommit(0, 0, types.BlockID{}, types.StateID{}, nil, nil, nil) + commit = types.NewCommit(0, 0, types.BlockID{}, cs.state.StateID(), nil, nil, nil) case cs.LastCommit != nil: // Make the commit from LastPrecommits commit = cs.LastCommit @@ -1815,7 +1835,7 @@ func (cs *State) verifyCommit( return false, err } - stateID := types.StateID{LastAppHash: cs.state.AppHash} + stateID := cs.state.StateID() if rs.Proposal == nil || ignoreProposalBlock { if ignoreProposalBlock { @@ -2373,19 +2393,11 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error return } - if vote.BlockID.Hash != nil && !bytes.Equal(vote.StateID.LastAppHash, cs.state.AppHash) { - added = false - err = errors.New("vote state last app hash does not match the known state app hash") - cs.Logger.Debug("vote ignored because sending wrong app hash", "voteHeight", vote.Height, - "csHeight", cs.Height, "peerID", peerID) - return false, err - } - cs.Logger.Debug( "adding vote", - "vote_height", vote.Height, - "vote_round", vote.Round, - "vote_type", vote.Type, + "height", vote.Height, + "round", vote.Round, + "type", vote.Type, "val_proTxHash", vote.ValidatorProTxHash.ShortString(), "vote_block_key", vote.BlockID.Key(), "vote_block_signature", vote.BlockSignature, @@ -2557,7 +2569,7 @@ func (cs *State) signVote( // Since the block has already been validated the block.lastAppHash must be the state.AppHash - var lastAppHash = cs.state.AppHash + stateID := cs.state.StateID() vote := &types.Vote{ ValidatorProTxHash: proTxHash, @@ -2566,21 +2578,16 @@ func (cs *State) signVote( Round: cs.Round, Type: msgType, BlockID: types.BlockID{Hash: hash, PartSetHeader: header}, - StateID: types.StateID{LastAppHash: lastAppHash}, } - // if hash is nil no need to send the state id - if hash == nil { - vote.StateID.LastAppHash = nil - } - // fmt.Printf("##state signing vote %v\n", vote) + protoVote := vote.ToProto() - v := vote.ToProto() // fmt.Printf("validators for signing vote are %v\n", cs.state.Validators) err := cs.privValidator.SignVote( - cs.state.ChainID, cs.state.Validators.QuorumType, cs.state.Validators.QuorumHash, v, cs.Logger) - vote.BlockSignature = v.BlockSignature - vote.StateSignature = v.StateSignature + cs.state.ChainID, cs.state.Validators.QuorumType, cs.state.Validators.QuorumHash, + protoVote, stateID, cs.Logger) + vote.BlockSignature = protoVote.BlockSignature + vote.StateSignature = protoVote.StateSignature return vote, err } diff --git a/consensus/state_test.go b/consensus/state_test.go index 3889248abf..6dc0943ddb 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -13,6 +13,7 @@ import ( "github.com/tendermint/tendermint/abci/example/counter" cstypes "github.com/tendermint/tendermint/consensus/types" "github.com/tendermint/tendermint/crypto/tmhash" + tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" tmrand "github.com/tendermint/tendermint/libs/rand" @@ -194,11 +195,8 @@ func TestStateBadProposal(t *testing.T) { round++ incrementRound(vss[1:]...) - // make the block bad by tampering with statehash - stateHash := propBlock.AppHash - if len(stateHash) == 0 { - stateHash = make([]byte, 32) - } + stateHash := make(tmbytes.HexBytes, len(propBlock.AppHash)) + copy(stateHash, propBlock.AppHash) stateHash[0] = (stateHash[0] + 1) % 255 propBlock.AppHash = stateHash propBlockParts := propBlock.MakePartSet(partSize) @@ -1806,7 +1804,7 @@ func TestStateOutputsBlockPartsStats(t *testing.T) { // 1) new block part parts := types.NewPartSetFromData(tmrand.Bytes(100), 10) msg := &BlockPartMessage{ - Height: 1, + Height: cs.state.InitialHeight, Round: 0, Part: parts.GetPart(0), } diff --git a/consensus/types/height_vote_set.go b/consensus/types/height_vote_set.go index 40ab68037d..37a143a30c 100644 --- a/consensus/types/height_vote_set.go +++ b/consensus/types/height_vote_set.go @@ -42,6 +42,7 @@ One for their LastPrecommits round, and another for the official commit round. type HeightVoteSet struct { chainID string height int64 + stateID types.StateID // State ID describing current state (eg. previous height and previous app hash) valSet *types.ValidatorSet mtx sync.Mutex @@ -50,9 +51,14 @@ type HeightVoteSet struct { peerCatchupRounds map[p2p.ID][]int32 // keys: peer.ID; values: at most 2 rounds } -func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet { +func NewHeightVoteSet( + chainID string, + height int64, + stateID types.StateID, + valSet *types.ValidatorSet) *HeightVoteSet { hvs := &HeightVoteSet{ chainID: chainID, + stateID: stateID, } hvs.Reset(height, valSet) return hvs @@ -106,8 +112,8 @@ func (hvs *HeightVoteSet) addRound(round int32) { } // log.Debug("addRound(round)", "round", round) if hvs.valSet.HasPublicKeys { - prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrevoteType, hvs.valSet) - precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet) + prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrevoteType, hvs.valSet, hvs.stateID) + precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, tmproto.PrecommitType, hvs.valSet, hvs.stateID) hvs.roundVoteSets[round] = RoundVoteSet{ Prevotes: prevotes, Precommits: precommits, diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index f841eb74d1..dd8498b8f7 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -28,21 +28,23 @@ func TestMain(m *testing.M) { func TestPeerCatchupRounds(t *testing.T) { valSet, privVals := types.GenerateValidatorSet(10) - hvs := NewHeightVoteSet(config.ChainID(), 1, valSet) + stateID := types.StateID{} - vote999_0 := makeVoteHR(t, 1, 0, 999, privVals, valSet.QuorumType, valSet.QuorumHash) + hvs := NewHeightVoteSet(config.ChainID(), 1, stateID, valSet) + + vote999_0 := makeVoteHR(t, 1, 0, 999, privVals, valSet.QuorumType, valSet.QuorumHash, stateID) added, err := hvs.AddVote(vote999_0, "peer1") if !added || err != nil { t.Error("Expected to successfully add vote from peer", added, err) } - vote1000_0 := makeVoteHR(t, 1, 0, 1000, privVals, valSet.QuorumType, valSet.QuorumHash) + vote1000_0 := makeVoteHR(t, 1, 0, 1000, privVals, valSet.QuorumType, valSet.QuorumHash, stateID) added, err = hvs.AddVote(vote1000_0, "peer1") if !added || err != nil { t.Error("Expected to successfully add vote from peer", added, err) } - vote1001_0 := makeVoteHR(t, 1, 0, 1001, privVals, valSet.QuorumType, valSet.QuorumHash) + vote1001_0 := makeVoteHR(t, 1, 0, 1001, privVals, valSet.QuorumType, valSet.QuorumHash, stateID) added, err = hvs.AddVote(vote1001_0, "peer1") if err != ErrGotVoteFromUnwantedRound { t.Errorf("expected GotVoteFromUnwantedRoundError, but got %v", err) @@ -59,7 +61,7 @@ func TestPeerCatchupRounds(t *testing.T) { } func makeVoteHR(t *testing.T, height int64, valIndex, round int32, privVals []types.PrivValidator, - quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash) *types.Vote { + quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, stateID types.StateID) *types.Vote { privVal := privVals[valIndex] proTxHash, err := privVal.GetProTxHash() if err != nil { @@ -67,7 +69,6 @@ func makeVoteHR(t *testing.T, height int64, valIndex, round int32, privVals []ty } randBytes1 := tmrand.Bytes(tmhash.Size) - randBytes2 := tmrand.Bytes(tmhash.Size) vote := &types.Vote{ ValidatorProTxHash: proTxHash, @@ -76,12 +77,11 @@ func makeVoteHR(t *testing.T, height int64, valIndex, round int32, privVals []ty Round: round, Type: tmproto.PrecommitType, BlockID: types.BlockID{Hash: randBytes1, PartSetHeader: types.PartSetHeader{}}, - StateID: types.StateID{LastAppHash: randBytes2}, } chainID := config.ChainID() v := vote.ToProto() - err = privVal.SignVote(chainID, quorumType, quorumHash, v, nil) + err = privVal.SignVote(chainID, quorumType, quorumHash, v, stateID, nil) if err != nil { panic(fmt.Sprintf("Error signing vote: %v", err)) } diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index 5568ba7963..1369430083 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" db "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/abci/example/kvstore" @@ -31,6 +32,7 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { config := getConfig(t) app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator")) + t.Cleanup(func() { require.NoError(t, app.Close()) }) logger := log.TestingLogger().With("wal_generator", "wal_generator") logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks) diff --git a/consensus/wal_test.go b/consensus/wal_test.go index 4ee8136091..233d4d2976 100644 --- a/consensus/wal_test.go +++ b/consensus/wal_test.go @@ -15,7 +15,9 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/consensus/types" + tmcrypto "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/merkle" + "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/libs/autofile" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" @@ -150,6 +152,69 @@ func TestWALWrite(t *testing.T) { } } +func TestWALWriteCommit(t *testing.T) { + walDir, err := ioutil.TempDir("", "wal") + require.NoError(t, err) + defer os.RemoveAll(walDir) + walFile := filepath.Join(walDir, "wal") + + wal, err := NewWAL(walFile) + require.NoError(t, err) + err = wal.Start() + require.NoError(t, err) + defer func() { + if err := wal.Stop(); err != nil { + t.Error(err) + } + // wait for the wal to finish shutting down so we + // can safely remove the directory + wal.Wait() + }() + + // Prepare and write commit msg + stateID := tmtypes.RandStateID() + blockID := tmtypes.BlockID{ + Hash: tmcrypto.CRandBytes(tmhash.Size), + PartSetHeader: tmtypes.PartSetHeader{ + Total: 0, + Hash: tmcrypto.CRandBytes(tmhash.Size)}, + } + msg := &CommitMessage{ + Commit: &tmtypes.Commit{ + Height: stateID.Height + 1, + StateID: stateID, + BlockID: blockID, + ThresholdBlockSignature: tmcrypto.CRandBytes(96), + ThresholdStateSignature: tmcrypto.CRandBytes(96), + }, + } + err = wal.Write(msgInfo{ + Msg: msg, + }) + assert.NoError(t, err) + assert.NoError(t, wal.FlushAndSync()) + + // open WAL for reading + gr, err := wal.Group().NewReader(0) + require.NoError(t, err) + defer gr.Close() + + // starting msg which we ignore + dec := NewWALDecoder(gr) + _, err = dec.Decode() + assert.NoError(t, err, "expected to decode a message") + + readMsg, err := dec.Decode() + assert.NoError(t, err, "expected to decode a message") + assert.NotNil(t, readMsg) + + msgInfo, ok := readMsg.Msg.(msgInfo) + require.True(t, ok, "expected message of type msgInfo, got %T", readMsg.Msg) + commitMsg, ok := msgInfo.Msg.(*CommitMessage) + require.True(t, ok, "expected message of type *CommitMessage, got %T", msgInfo.Msg) + assert.EqualValues(t, stateID.Height, commitMsg.Commit.StateID.Height) +} + func TestWALSearchForEndHeight(t *testing.T) { walBody, err := WALWithNBlocks(t, 6) if err != nil { diff --git a/crypto/quorum.go b/crypto/quorum.go index 6e4c6dd62c..54d34a4d7e 100644 --- a/crypto/quorum.go +++ b/crypto/quorum.go @@ -5,6 +5,8 @@ import ( bls "github.com/dashpay/bls-signatures/go-bindings" ) +// SignID returns signing session data that will be signed to get threshold signature share. +// See DIP-0007 func SignID(llmqType btcjson.LLMQType, quorumHash QuorumHash, requestID []byte, messageHash []byte) []byte { var blsQuorumHash bls.Hash copy(blsQuorumHash[:], quorumHash.Bytes()) diff --git a/crypto/secp256k1/secp256k1_nocgo.go b/crypto/secp256k1/secp256k1_nocgo.go index 99b012d430..2137cc567d 100644 --- a/crypto/secp256k1/secp256k1_nocgo.go +++ b/crypto/secp256k1/secp256k1_nocgo.go @@ -1,3 +1,4 @@ +//go:build !libsecp256k1 // +build !libsecp256k1 package secp256k1 diff --git a/dash/quorum/mock/mock_switch.go b/dash/quorum/mock/mock_switch.go new file mode 100644 index 0000000000..ffb123e332 --- /dev/null +++ b/dash/quorum/mock/mock_switch.go @@ -0,0 +1,129 @@ +package mock + +import ( + "fmt" + "strings" + "time" + + dashtypes "github.com/tendermint/tendermint/dash/types" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/mocks" +) + +// MOCK SWITCH // +const ( + OpDialMany = "dialMany" + OpStopOne = "stopOne" +) + +// SwitchHistoryEvent is a log of dial and stop operations executed by the MockSwitch +type SwitchHistoryEvent struct { + Timestamp time.Time + Operation string // OpDialMany, OpStopOne + Params []string + Comment string +} + +// Switch implements `dash.Switch`. It sends event about DialPeersAsync() and StopPeerGracefully() calls +// to HistoryChan and stores them in History +type Switch struct { + PeerSet *p2p.PeerSet + PersistentPeers map[string]bool + History []SwitchHistoryEvent + HistoryChan chan SwitchHistoryEvent + AddressBook p2p.AddrBook +} + +// NewMockSwitch creates a new mock Switch +func NewMockSwitch() *Switch { + isw := &Switch{ + PeerSet: p2p.NewPeerSet(), + PersistentPeers: map[string]bool{}, + History: []SwitchHistoryEvent{}, + HistoryChan: make(chan SwitchHistoryEvent, 1000), + AddressBook: &p2p.AddrBookMock{}, + } + return isw +} + +// AddBook returns mock address book to use in this mock switch +func (sw *Switch) AddrBook() p2p.AddrBook { + return sw.AddressBook +} + +// Peers implements Switch by returning sw.PeerSet +func (sw *Switch) Peers() p2p.IPeerSet { + return sw.PeerSet +} + +// AddPersistentPeers implements Switch by marking provided addresses as persistent +func (sw *Switch) AddPersistentPeers(addrs []string) error { + for _, addr := range addrs { + addr = simplifyAddress(addr) + sw.PersistentPeers[addr] = true + } + return nil +} + +// RemovePersistentPeer implements Switch. It checks if the addr is persistent, and +// marks it as non-persistent if needed. +func (sw Switch) RemovePersistentPeer(addr string) error { + addr = simplifyAddress(addr) + if !sw.PersistentPeers[addr] { + return fmt.Errorf("peer is not persisitent, addr=%s", addr) + } + + delete(sw.PersistentPeers, addr) + return nil +} + +// DialPeersAsync implements Switch. It emulates connecting to provided addresses +// and adds them as peers and emits history event OpDialMany. +func (sw *Switch) DialPeersAsync(addrs []string) error { + for _, addr := range addrs { + peer := &mocks.Peer{} + parsed, err := dashtypes.ParseValidatorAddress(addr) + if err != nil { + return err + } + + peer.On("ID").Return(parsed.NodeID) + peer.On("String").Return(addr) + if err := sw.PeerSet.Add(peer); err != nil { + return err + } + } + sw.history(OpDialMany, addrs...) + return nil +} + +// IsDialingOrExistingAddress implements Switch. It checks if provided peer has been dialed +// before. +func (sw *Switch) IsDialingOrExistingAddress(addr *p2p.NetAddress) bool { + return sw.PeerSet.Has(addr.ID) +} + +// StopPeerGracefully implements Switch. It removes the peer from Peers() and emits history +// event OpStopOne. +func (sw *Switch) StopPeerGracefully(peer p2p.Peer) { + sw.PeerSet.Remove(peer) + sw.history(OpStopOne, peer.String()) +} + +// history adds info about an operation to sw.History and sends it to sw.HistoryChan +func (sw *Switch) history(op string, args ...string) { + event := SwitchHistoryEvent{ + Timestamp: time.Now(), + Operation: op, + Params: args, + } + sw.History = append(sw.History, event) + + sw.HistoryChan <- event +} + +// simplifyAddress converts provided `addr` to a simplified form, to make +// comparisons inside the tests easier +func simplifyAddress(addr string) string { + return strings.TrimPrefix(addr, "tcp://") +} diff --git a/dash/quorum/mock/test_helpers.go b/dash/quorum/mock/test_helpers.go new file mode 100644 index 0000000000..fcd1654191 --- /dev/null +++ b/dash/quorum/mock/test_helpers.go @@ -0,0 +1,81 @@ +package mock + +import ( + "encoding/binary" + "fmt" + "math" + + "github.com/tendermint/tendermint/crypto" + dashtypes "github.com/tendermint/tendermint/dash/types" + "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/types" +) + +// NewNodeAddress generates a string that is accepted as validator address. +// For given `n`, the address will always be the same. +func NewNodeAddress(n uint64) string { + nodeID := make([]byte, 20) + binary.LittleEndian.PutUint64(nodeID, n) + if n == 0 { + n = math.MaxUint16 + } + return fmt.Sprintf("tcp://%x@127.0.0.1:%d", nodeID, uint16(n)) +} + +// NewValidator generates a validator with only fields needed for node selection filled. +// For the same `id`, mock validator will always have the same data (proTxHash, NodeID) +func NewValidator(id uint64) *types.Validator { + address, err := dashtypes.ParseValidatorAddress(NewNodeAddress(id)) + if err != nil { + panic(err) + } + return &types.Validator{ + ProTxHash: NewProTxHash(id), + NodeAddress: address, + } +} + +// NewValidators generates a slice containing `n` mock validators. +// Each element is generated using `mock.NewValidator()`. +func NewValidators(n uint64) []*types.Validator { + vals := make([]*types.Validator, 0, n) + for i := uint64(0); i < n; i++ { + vals = append(vals, NewValidator(i)) + } + return vals +} + +// NewProTxHash generates a deterministic proTxHash. +// For the same `id`, generated data is always the same. +func NewProTxHash(id uint64) []byte { + data := make([]byte, crypto.ProTxHashSize) + binary.LittleEndian.PutUint64(data, id) + return data +} + +// NewQuorumHash generates a deterministic quorum hash. +// For the same `id`, generated data is always the same. +func NewQuorumHash(id uint64) []byte { + data := make([]byte, crypto.QuorumHashSize) + binary.LittleEndian.PutUint64(data, id) + return data +} + +// NewProTxHashes generates multiple deterministic proTxHash'es using mockProTxHash. +// Each argument will be passed to mockProTxHash. +func NewProTxHashes(ids ...uint64) []bytes.HexBytes { + hashes := make([]bytes.HexBytes, 0, len(ids)) + for _, id := range ids { + hashes = append(hashes, NewProTxHash(id)) + } + return hashes +} + +// ValidatorsProTxHashes returns slice of proTxHashes for provided list of validators +func ValidatorsProTxHashes(vals []*types.Validator) []bytes.HexBytes { + hashes := make([]bytes.HexBytes, len(vals)) + for id, val := range vals { + hashes[id] = val.ProTxHash + } + return hashes +} diff --git a/dash/quorum/selectpeers/dip6.go b/dash/quorum/selectpeers/dip6.go new file mode 100644 index 0000000000..d4621e10d8 --- /dev/null +++ b/dash/quorum/selectpeers/dip6.go @@ -0,0 +1,78 @@ +// Package selectpeers is package contains algorithm that selects peers based on the deterministic connection +// selection algorithm described in DIP-6 +package selectpeers + +import ( + "fmt" + "math" + + "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/types" +) + +// minValidators is a minimum number of validators needed in order to execute the selection +// algorithm. For less than this number, we connect to all validators. +const minValidators = 5 + +// DIP6 selector selects validators from the `validatorSetMembers`, based on algorithm +// described in DIP-6 https://github.com/dashpay/dips/blob/master/dip-0006.md +type dip6PeerSelector struct { + quorumHash bytes.HexBytes +} + +// NewDIP6ValidatorSelector creates new implementation of validator selector algorithm +func NewDIP6ValidatorSelector(quorumHash bytes.HexBytes) ValidatorSelector { + return &dip6PeerSelector{quorumHash: quorumHash} +} + +// SelectValidator implements ValidtorSelector. +// SelectValidators selects some validators from `validatorSetMembers`, according to the algorithm +// described in DIP-6 https://github.com/dashpay/dips/blob/master/dip-0006.md +func (s *dip6PeerSelector) SelectValidators( + validatorSetMembers []*types.Validator, + me *types.Validator, +) ([]*types.Validator, error) { + if len(validatorSetMembers) < 2 { + return nil, fmt.Errorf("not enough validators: got %d, need 2", len(validatorSetMembers)) + } + // Build the deterministic list of quorum members: + // 1. Retrieve the deterministic masternode list which is valid at quorumHeight + // 2. Calculate SHA256(proTxHash, quorumHash) for each entry in the list + // 3. Sort the resulting list by the calculated hashes + sortedValidators := newSortedValidatorList(validatorSetMembers, s.quorumHash) + + // Loop through the list until the member finds itself in the list. The index at which it finds itself is called i. + meSortable := newSortableValidator(*me, s.quorumHash) + myIndex := sortedValidators.index(meSortable) + if myIndex < 0 { + return []*types.Validator{}, fmt.Errorf("current node is not a member of provided validator set") + } + + // Fallback if we don't have enough validators, we connect to all of them + if sortedValidators.Len() < minValidators { + ret := make([]*types.Validator, 0, len(validatorSetMembers)-1) + // We connect to all validators + for index, val := range sortedValidators { + if index != myIndex { + ret = append(ret, val.Copy()) + } + } + return ret, nil + } + + // Calculate indexes (i+2^k)%n where k is in the range 0..floor(log2(n-1))-1 + // and n is equal to the size of the list. + n := float64(sortedValidators.Len()) + count := math.Floor(math.Log2(n-1.0)) - 1.0 + + ret := make([]*types.Validator, 0, int(count)) + i := float64(myIndex) + for k := float64(0); k <= count; k++ { + index := int(math.Mod(i+math.Pow(2, k), n)) + // Add addresses of masternodes at indexes calculated at previous step + // to the set of deterministic connections. + ret = append(ret, sortedValidators[index].Validator.Copy()) + } + + return ret, nil +} diff --git a/dash/quorum/selectpeers/dip6_test.go b/dash/quorum/selectpeers/dip6_test.go new file mode 100644 index 0000000000..9c4970f092 --- /dev/null +++ b/dash/quorum/selectpeers/dip6_test.go @@ -0,0 +1,121 @@ +package selectpeers + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/dash/quorum/mock" + "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/types" +) + +func TestDIP6(t *testing.T) { + tests := []struct { + name string + validators []*types.Validator + me *types.Validator + quorumHash bytes.HexBytes + wantProTxHashes []bytes.HexBytes + wantLen int + wantErr bool + }{ + { + name: "No validators", + me: mock.NewValidator(0), + wantErr: true, + }, + { + name: "Only me", + validators: mock.NewValidators(1), + me: mock.NewValidator(0), + quorumHash: mock.NewQuorumHash(0), + wantErr: true, + }, + { + name: "4 validators", + validators: mock.NewValidators(4), + me: mock.NewValidator(0), + quorumHash: mock.NewQuorumHash(0), + wantLen: 3, + wantProTxHashes: mock.NewProTxHashes(0x01, 0x02, 0x03), + }, + { + name: "5 validators", + validators: mock.NewValidators(5), + me: mock.NewValidator(0), + quorumHash: mock.NewQuorumHash(0), + wantProTxHashes: mock.NewProTxHashes(0x01, 0x02), + wantLen: 2, + }, + { + name: "5 validators, not me", + validators: mock.NewValidators(5), + me: mock.NewValidator(1000), + quorumHash: mock.NewQuorumHash(0), + wantErr: true, + }, + { + name: "5 validators, different quorum hash", + validators: mock.NewValidators(5), + me: mock.NewValidator(0), + quorumHash: mock.NewQuorumHash(1), + wantProTxHashes: mock.NewProTxHashes(0x01, 0x03), + wantLen: 2, + }, + { + name: "8 validators", + validators: mock.NewValidators(6), + me: mock.NewValidator(0), + quorumHash: mock.NewQuorumHash(0), + wantProTxHashes: mock.NewProTxHashes(0x01, 0x02), + wantLen: 2, + }, + { + name: "9 validators", + validators: mock.NewValidators(9), + me: mock.NewValidator(0), + quorumHash: mock.NewQuorumHash(0), + wantProTxHashes: mock.NewProTxHashes(0x06, 0x01, 0x02), + wantLen: 3, + }, + { + name: "37 validators", + validators: mock.NewValidators(37), + me: mock.NewValidator(0), + quorumHash: mock.NewQuorumHash(0), + wantProTxHashes: mock.NewProTxHashes(0xc, 0xa, 0x15, 0x19, 0x1d), + wantLen: 5, + }, + { + name: "37 validators, I am last", + validators: mock.NewValidators(37), + me: mock.NewValidator(36), + quorumHash: mock.NewQuorumHash(0), + wantProTxHashes: mock.NewProTxHashes(0x08, 0x1b, 0x22, 0x23, 0x16), + wantLen: 5, + }, + { + name: "37 validators, not me", + validators: mock.NewValidators(37), + me: mock.NewValidator(37), + quorumHash: mock.NewQuorumHash(0), + wantErr: true, + }, + } + + // nolint:scopelint + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewDIP6ValidatorSelector(tt.quorumHash).SelectValidators(tt.validators, tt.me) + if (err == nil) == tt.wantErr { + assert.FailNow(t, "unexpected error: %s", err) + } + if tt.wantProTxHashes != nil { + assert.EqualValues(t, tt.wantProTxHashes, mock.ValidatorsProTxHashes(got)) + } + if tt.wantLen > 0 { + assert.Len(t, got, tt.wantLen) + } + }) + } +} diff --git a/dash/quorum/selectpeers/sortable_validator.go b/dash/quorum/selectpeers/sortable_validator.go new file mode 100644 index 0000000000..78e86e18fb --- /dev/null +++ b/dash/quorum/selectpeers/sortable_validator.go @@ -0,0 +1,50 @@ +package selectpeers + +import ( + "bytes" + "crypto/sha256" + + "github.com/tendermint/tendermint/crypto" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/types" +) + +// sortableValidator is a `types.Validator` which can generate SortKey(), as specified in DIP-6 +type sortableValidator struct { + types.Validator + quorumHash tmbytes.HexBytes + sortKey []byte +} + +// newSortableValidator extends the validator with an option to generate DIP-6 compatible SortKey() +func newSortableValidator(validator types.Validator, quorumHash tmbytes.HexBytes) sortableValidator { + sv := sortableValidator{ + Validator: validator, + quorumHash: make([]byte, crypto.QuorumHashSize), + } + copy(sv.quorumHash, quorumHash) + sv.sortKey = calculateDIP6SortKey(sv.ProTxHash, sv.quorumHash) + return sv +} + +// equal returns info if this sortable validator is equal to the other one, based on the SortKey +func (v sortableValidator) equal(other sortableValidator) bool { + return v.compare(other) == 0 +} + +// compare returns info if this sortable validator is smaller equal or bigger to the other one, based on the SortKey. +// It returns negative value when `v` is smaller than `other`, 0 when they are equal, +// and positive value when `v` is bigger than `other`. +func (v sortableValidator) compare(other sortableValidator) int { + return bytes.Compare(v.sortKey, other.sortKey) +} + +// calculateDIP6SortKey calculates new key for this SortableValidator, which is +// SHA256(proTxHash, quorumHash), as per DIP-6. +func calculateDIP6SortKey(proTxHash, quorumHash tmbytes.HexBytes) []byte { + keyBytes := make([]byte, 0, len(proTxHash)+len(quorumHash)) + keyBytes = append(keyBytes, proTxHash...) + keyBytes = append(keyBytes, quorumHash...) + keySHA := sha256.Sum256(keyBytes) + return keySHA[:] +} diff --git a/dash/quorum/selectpeers/sortable_validator_test.go b/dash/quorum/selectpeers/sortable_validator_test.go new file mode 100644 index 0000000000..1c89471fc4 --- /dev/null +++ b/dash/quorum/selectpeers/sortable_validator_test.go @@ -0,0 +1,80 @@ +package selectpeers + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/dash/quorum/mock" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/types" +) + +// Test_sortableValidator_SortKey checks if the sortableValidatorList.Index() works correctly +func Test_sortableValidator_SortKey(t *testing.T) { + tests := []struct { + name string + Validator *types.Validator + quorumHash tmbytes.HexBytes + want []byte + }{ + { + name: "zero input", + Validator: mock.NewValidator(0), + quorumHash: mock.NewQuorumHash(0), + want: []byte{0xf5, 0xa5, 0xfd, 0x42, 0xd1, 0x6a, 0x20, 0x30, 0x27, 0x98, 0xef, 0x6e, 0xd3, 0x9, 0x97, + 0x9b, 0x43, 0x0, 0x3d, 0x23, 0x20, 0xd9, 0xf0, 0xe8, 0xea, 0x98, 0x31, 0xa9, 0x27, 0x59, 0xfb, 0x4b}, + }, + { + name: "base value", + Validator: mock.NewValidator(2362), + quorumHash: mock.NewQuorumHash(6454), + want: []byte{0x8d, 0xde, 0xe8, 0xc6, 0xe3, 0xaf, 0xda, 0xbe, 0x48, 0xc0, 0x6e, 0x5b, 0x1, 0x10, 0xd8, + 0x57, 0xa1, 0xba, 0xba, 0xca, 0x9b, 0xa0, 0x17, 0xad, 0x27, 0x2d, 0x11, 0x70, 0x89, 0x53, 0x6f, 0xbe}, + }, + { + name: "changed quorum hash", + Validator: mock.NewValidator(2362), + quorumHash: mock.NewQuorumHash(8765), + want: []byte{0xe5, 0x63, 0xb4, 0x7e, 0x1a, 0xeb, 0x98, 0x81, 0x4b, 0xe4, 0x69, 0x95, 0x79, 0xc4, 0x4b, + 0xe8, 0xea, 0x51, 0x8a, 0x9f, 0x51, 0x52, 0xa4, 0xba, 0x47, 0x80, 0x63, 0x90, 0x88, 0xde, 0xa6, 0x39}, + }, + { + name: "changed validator hash", + Validator: mock.NewValidator(7812), + quorumHash: mock.NewQuorumHash(6454), + want: []byte{0xdd, 0x90, 0xcc, 0x2f, 0xde, 0x12, 0xb3, 0x90, 0x1, 0x66, 0x1f, 0xcc, 0x69, 0xec, 0x8b, + 0x80, 0xc9, 0x72, 0xf2, 0x82, 0x5c, 0xf6, 0x73, 0xc1, 0x93, 0x1a, 0xd7, 0x33, 0x3f, 0x95, 0x33, 0x63}, + }, + } + + // nolint:scopelint + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := newSortableValidator(*tt.Validator, tt.quorumHash) + assert.EqualValues(t, tt.want, v.sortKey) + }) + } +} + +func Test_sortableValidator_Compare(t *testing.T) { + tests := []struct { + name string + left sortableValidator + right sortableValidator + want int + }{ + { + name: "equal", + left: newSortableValidator(*mock.NewValidator(1000), mock.NewQuorumHash(1000)), + right: newSortableValidator(*mock.NewValidator(1000), mock.NewQuorumHash(1000)), + want: 0, + }, + } + // nolint:scopelint + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.left.compare(tt.right) + assert.Equal(t, tt.want, got, "sortableValidator.Compare() = %v, want %v", got, tt.want) + }) + } +} diff --git a/dash/quorum/selectpeers/sorted_validator_list.go b/dash/quorum/selectpeers/sorted_validator_list.go new file mode 100644 index 0000000000..592f902d49 --- /dev/null +++ b/dash/quorum/selectpeers/sorted_validator_list.go @@ -0,0 +1,59 @@ +package selectpeers + +import ( + "bytes" + "sort" + + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/types" +) + +// sortedValidatorList is a list of sortableValidators that are sorted by `sortableValidator.SortKey()` +type sortedValidatorList []sortableValidator + +// newSortedValidatorList generates a sorted validator list containing provided `validators`. +// Sorting is executed based on sortableValidator.SortKey() +func newSortedValidatorList(validators []*types.Validator, quorumHash tmbytes.HexBytes) sortedValidatorList { + ret := make(sortedValidatorList, 0, len(validators)) + for _, validator := range validators { + ret = append(ret, newSortableValidator(*validator, quorumHash)) + } + + ret.Sort() + return ret +} + +// Sort() sorts this sortableValidatorList +func (vl sortedValidatorList) Sort() { + sort.Sort(vl) +} + +// Len implements sort.Interface. It returns length of sortableValidatorList +func (vl sortedValidatorList) Len() int { + return len(vl) +} + +// Less implements sort.Interface. It returns true when i'th element +// of sortableValidatorList has lower key than j'th element. +func (vl sortedValidatorList) Less(i, j int) bool { + return bytes.Compare(vl[i].sortKey, vl[j].sortKey) < 0 +} + +// Swap implements sort.Interface. It swaps i'th element with j'th element. +func (vl sortedValidatorList) Swap(i, j int) { + vl[i], vl[j] = vl[j], vl[i] +} + +// index finds a validator on the list and returns its index. +// It uses sortableValidator.Equal() (which uses ProTxHash) to compare validators. +// Returns -1 when validator was not found. +func (vl sortedValidatorList) index(search sortableValidator) int { + found := sort.Search(vl.Len(), func(i int) bool { + return search.compare(vl[i]) <= 0 + }) + + if found < 0 || found >= vl.Len() || !search.equal(vl[found]) { + return -1 + } + return found +} diff --git a/dash/quorum/selectpeers/sorted_validator_list_test.go b/dash/quorum/selectpeers/sorted_validator_list_test.go new file mode 100644 index 0000000000..ed61b85f3c --- /dev/null +++ b/dash/quorum/selectpeers/sorted_validator_list_test.go @@ -0,0 +1,49 @@ +package selectpeers + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/dash/quorum/mock" +) + +// Test_sortableValidatorList_Index checks if the sortableValidatorList.Index() works correctly +func Test_sortedValidatorList_Index(t *testing.T) { + tests := []struct { + name string + list sortedValidatorList + search sortableValidator + want int + }{ + { + name: "miss", + list: newSortedValidatorList(mock.NewValidators(5), mock.NewQuorumHash(0)), + search: newSortableValidator(*mock.NewValidator(10), mock.NewQuorumHash(0)), + want: -1, + }, + { + name: "i=0", + list: newSortedValidatorList(mock.NewValidators(500), mock.NewQuorumHash(0)), + search: newSortableValidator(*mock.NewValidator(0), mock.NewQuorumHash(0)), + want: 0, + }, + { + name: "i=4", + list: newSortedValidatorList(mock.NewValidators(500), mock.NewQuorumHash(2054231)), + search: newSortableValidator(*mock.NewValidator(4), mock.NewQuorumHash(2054231)), + want: 4, + }, + } + // nolint:scopelint + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := tt.list.index(tt.search) + if tt.want < 0 { + assert.Less(t, got, 0) + } else { + assert.GreaterOrEqual(t, got, 0, "not found: SearchKey=%x", tt.search.sortKey) + assert.EqualValues(t, tt.search.ProTxHash, tt.list[got].ProTxHash, "invalid index: SearchKey=%x", tt.search.sortKey) + } + }) + } +} diff --git a/dash/quorum/selectpeers/validatorselector.go b/dash/quorum/selectpeers/validatorselector.go new file mode 100644 index 0000000000..a0887d039a --- /dev/null +++ b/dash/quorum/selectpeers/validatorselector.go @@ -0,0 +1,9 @@ +package selectpeers + +import "github.com/tendermint/tendermint/types" + +// ValidatorSelector represents an algorithm that chooses some validators from provided list +type ValidatorSelector interface { + // SelectValidators selects some validators from `validators` slice + SelectValidators(validators []*types.Validator, me *types.Validator) ([]*types.Validator, error) +} diff --git a/dash/quorum/validator_conn_executor.go b/dash/quorum/validator_conn_executor.go new file mode 100644 index 0000000000..68027530ec --- /dev/null +++ b/dash/quorum/validator_conn_executor.go @@ -0,0 +1,432 @@ +package quorum + +import ( + "context" + "errors" + "fmt" + "reflect" + "time" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/dash/quorum/selectpeers" + dashtypes "github.com/tendermint/tendermint/dash/types" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/libs/service" + "github.com/tendermint/tendermint/libs/sync" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/types" +) + +const ( + // validatorConnExecutorName contains name that is used to represent the ValidatorConnExecutor in BaseService and logs + validatorConnExecutorName = "ValidatorConnExecutor" + // defaultTimeout is timeout that is applied to setup / cleanup code + defaultTimeout = 1 * time.Second + // defaultEventBusCapacity determines how many events can wait in the event bus for processing. 10 looks very safe. + defaultEventBusCapacity = 10 +) + +// Switch defines p2p.Switch methods that are used by this Executor. +// Useful to create a mock of the p2p.Switch. +type Switch interface { + Peers() p2p.IPeerSet + + AddPersistentPeers(addrs []string) error + RemovePersistentPeer(addr string) error + + DialPeersAsync(addrs []string) error + IsDialingOrExistingAddress(*p2p.NetAddress) bool + StopPeerGracefully(p2p.Peer) + + AddrBook() p2p.AddrBook +} + +type optionFunc func(vc *ValidatorConnExecutor) error + +// ValidatorConnExecutor retrieves validator update events and establishes new validator connections +// within the ValidatorSet. +// If it's already connected to a member of current validator set, it will keep that connection. +// Otherwise, it will randomly select some members of the active validator set and connect to them, to ensure +// it has connectivity with at least `NumConnections` members of the active validator set. +// +// Note that we mark peers that are members of active validator set as Persistent, so p2p subsystem +// will retry the connection if it fails. +type ValidatorConnExecutor struct { + service.BaseService + proTxHash types.ProTxHash + eventBus *types.EventBus + p2pSwitch Switch + subscription types.Subscription + + // validatorSetMembers contains validators active in the current Validator Set, indexed by node ID + validatorSetMembers validatorMap + // connectedValidators contains validators we should be connected to, indexed by node ID + connectedValidators validatorMap + // quorumHash contains current quorum hash + quorumHash tmbytes.HexBytes + // nodeIDResolvers can be used to determine a node ID for a validator + nodeIDResolvers []dashtypes.NodeIDResolver + // mux is a mutex to ensure only one goroutine is processing connections + mux sync.Mutex + + // *** configuration *** // + + // EventBusCapacity sets event bus buffer capacity, defaults to 10 + EventBusCapacity int +} + +var ( + errPeerNotFound = fmt.Errorf("cannot stop peer: not found") +) + +// NewValidatorConnExecutor creates a Service that connects to other validators within the same Validator Set. +// Don't forget to Start() and Stop() the service. +func NewValidatorConnExecutor( + proTxHash types.ProTxHash, + eventBus *types.EventBus, + sw Switch, + opts ...optionFunc, +) (*ValidatorConnExecutor, error) { + vc := &ValidatorConnExecutor{ + proTxHash: proTxHash, + eventBus: eventBus, + p2pSwitch: sw, + EventBusCapacity: defaultEventBusCapacity, + validatorSetMembers: validatorMap{}, + connectedValidators: validatorMap{}, + quorumHash: make(tmbytes.HexBytes, crypto.QuorumHashSize), + nodeIDResolvers: []dashtypes.NodeIDResolver{ + dashtypes.NewAddrbookNodeIDResolver(sw.AddrBook()), + dashtypes.NewTCPNodeIDResolver(), + }, + } + baseService := service.NewBaseService(log.NewNopLogger(), validatorConnExecutorName, vc) + vc.BaseService = *baseService + + for _, opt := range opts { + err := opt(vc) + if err != nil { + return nil, err + } + } + return vc, nil +} + +// WithValidatorsSet sets the validators and quorum-hash as default values +func WithValidatorsSet(valSet *types.ValidatorSet) func(vc *ValidatorConnExecutor) error { + return func(vc *ValidatorConnExecutor) error { + if len(valSet.Validators) == 0 { + return nil + } + err := vc.setQuorumHash(valSet.QuorumHash) + if err != nil { + return err + } + vc.validatorSetMembers = newValidatorMap(valSet.Validators) + return nil + } +} + +// WithLogger sets a logger +func WithLogger(logger log.Logger) func(vc *ValidatorConnExecutor) error { + return func(vc *ValidatorConnExecutor) error { + vc.Logger = logger.With("module", "ValidatorConnExecutor") + return nil + } +} + +// OnStart implements Service to subscribe to Validator Update events +func (vc *ValidatorConnExecutor) OnStart() error { + if err := vc.subscribe(); err != nil { + return err + } + // establish connection with validators retrieves from genesis or init-chain + err := vc.updateConnections() + if err != nil { + return err + } + go func() { + var err error + for err == nil { + err = vc.receiveEvents() + } + vc.Logger.Error("ValidatorConnExecutor goroutine finished", "reason", err) + }() + return nil +} + +// OnStop implements Service to clean up (unsubscribe from all events, stop timers, etc.) +func (vc *ValidatorConnExecutor) OnStop() { + if vc.eventBus != nil { + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + err := vc.eventBus.UnsubscribeAll(ctx, validatorConnExecutorName) + if err != nil { + vc.Logger.Error("cannot unsubscribe from channels", "error", err) + } + vc.eventBus = nil + } +} + +// subscribe subscribes to event bus to receive validator update messages +func (vc *ValidatorConnExecutor) subscribe() error { + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + updatesSub, err := vc.eventBus.Subscribe( + ctx, + validatorConnExecutorName, + types.EventQueryValidatorSetUpdates, + vc.EventBusCapacity, + ) + if err != nil { + return err + } + + vc.subscription = updatesSub + return nil +} + +// receiveEvents processes received events and executes all the logic. +// Returns non-nil error only if fatal error occurred and the main goroutine should be terminated. +func (vc *ValidatorConnExecutor) receiveEvents() error { + vc.Logger.Debug("ValidatorConnExecutor: waiting for an event") + select { + case msg := <-vc.subscription.Out(): + event, ok := msg.Data().(types.EventDataValidatorSetUpdates) + if !ok { + return fmt.Errorf("invalid type of validator set update message: %T", event) + } + if err := vc.handleValidatorUpdateEvent(event); err != nil { + vc.Logger.Error("cannot handle validator update", "error", err) + return nil // non-fatal, so no error returned to continue the loop + } + vc.Logger.Debug("validator updates processed successfully", "event", event) + case <-vc.subscription.Cancelled(): + return fmt.Errorf("subscription cancelled due to error: %w", vc.subscription.Err()) + case <-vc.BaseService.Quit(): + return fmt.Errorf("quit signal received") + } + + return nil +} + +// handleValidatorUpdateEvent checks and executes event of type EventDataValidatorSetUpdates, received from event bus. +func (vc *ValidatorConnExecutor) handleValidatorUpdateEvent(event types.EventDataValidatorSetUpdates) error { + vc.mux.Lock() + defer vc.mux.Unlock() + + if len(event.ValidatorUpdates) < 1 { + vc.Logger.Debug("no validators in ValidatorUpdates") + return nil // not really an error + } + vc.validatorSetMembers = newValidatorMap(event.ValidatorUpdates) + if len(event.QuorumHash) > 0 { + if err := vc.setQuorumHash(event.QuorumHash); err != nil { + vc.Logger.Error("received invalid quorum hash", "error", err) + return fmt.Errorf("received invalid quorum hash: %w", err) + } + } else { + vc.Logger.Debug("received empty quorum hash") + } + if err := vc.updateConnections(); err != nil { + return fmt.Errorf("inter-validator set connections error: %w", err) + } + return nil +} + +// setQuorumHash sets quorum hash to provided bytes +func (vc *ValidatorConnExecutor) setQuorumHash(newQuorumHash tmbytes.HexBytes) error { + // New quorum hash must be exactly `crypto.QuorumHashSize` bytes long + if len(newQuorumHash) != crypto.QuorumHashSize { + return fmt.Errorf("invalid quorum hash size: got %d, expected %d; quorum hash: %x", + len(newQuorumHash), crypto.QuorumHashSize, newQuorumHash) + } + copy(vc.quorumHash, newQuorumHash) + return nil +} + +// me returns current node's validator object, if any. +// `ok` is false when current node is not a validator. +func (vc *ValidatorConnExecutor) me() (validator *types.Validator, ok bool) { + v, ok := vc.validatorSetMembers[validatorMapIndexType(vc.proTxHash.String())] + return &v, ok +} + +// resolveNodeID adds node ID to the validator address if it's not set +func (vc *ValidatorConnExecutor) resolveNodeID(va *dashtypes.ValidatorAddress) error { + if va.NodeID != "" { + return nil + } + for _, resolver := range vc.nodeIDResolvers { + nid, err := resolver.Resolve(*va) + if err == nil && nid != "" { + va.NodeID = nid + return nil + } + vc.Logger.Debug( + "validator node id lookup method failed", + "url", va.String(), + "method", reflect.TypeOf(resolver).String(), + "error", err, + ) + } + return dashtypes.ErrNoNodeID +} + +// selectValidators selects `count` validators from current ValidatorSet. +// It uses algorithm described in DIP-6. +// Returns map indexed by validator address. +func (vc *ValidatorConnExecutor) selectValidators() (validatorMap, error) { + activeValidators := vc.validatorSetMembers + me, ok := vc.me() + if !ok { + return validatorMap{}, fmt.Errorf("current node is not member of active validator set") + } + + selector := selectpeers.NewDIP6ValidatorSelector(vc.quorumHash) + selectedValidators, err := selector.SelectValidators(activeValidators.values(), me) + if err != nil { + return validatorMap{}, err + } + // fetch node IDs + for _, validator := range selectedValidators { + err := vc.resolveNodeID(&validator.NodeAddress) + if err != nil { + vc.Logger.Debug("cannot determine node id for validator", "url", validator.String(), "error", err) + // no return, as it's not critical + } + } + + return newValidatorMap(selectedValidators), nil +} + +func (vc *ValidatorConnExecutor) disconnectValidator(validator types.Validator) error { + vc.Logger.Debug("disconnect Validator", "validator", validator) + address := validator.NodeAddress.String() + + err := vc.p2pSwitch.RemovePersistentPeer(address) + if err != nil { + return err + } + + err = vc.resolveNodeID(&validator.NodeAddress) + if err != nil { + return err + } + id := validator.NodeAddress.NodeID + + peer := vc.p2pSwitch.Peers().Get(id) + if peer == nil { + return errPeerNotFound + } + + vc.Logger.Debug("stopping peer", "id", id, "address", address) + vc.p2pSwitch.StopPeerGracefully(peer) + return nil +} + +// disconnectValidators disconnects connected validators which are not a part of the exceptions map +func (vc *ValidatorConnExecutor) disconnectValidators(exceptions validatorMap) error { + for currentKey, validator := range vc.connectedValidators { + if exceptions.contains(validator) { + continue + } + if err := vc.disconnectValidator(validator); err != nil { + if !errors.Is(err, errPeerNotFound) { + // no return, as we see it as non-fatal + vc.Logger.Error("cannot disconnect Validator", "error", err) + continue + } + vc.Logger.Debug("Validator already disconnected", "error", err) + // We still delete the validator from vc.connectedValidators + } + delete(vc.connectedValidators, currentKey) + } + + return nil +} + +// isValidator returns true when current node is a validator +func (vc *ValidatorConnExecutor) isValidator() bool { + _, ok := vc.me() + return ok +} + +// updateConnections processes current validator set, selects a few validators and schedules connections +// to be established. It will also disconnect previous validators. +func (vc *ValidatorConnExecutor) updateConnections() error { + // We only do something if we are part of new ValidatorSet + if !vc.isValidator() { + vc.Logger.Debug("not a member of active ValidatorSet") + // We need to disconnect connected validators. It needs to be done explicitly + // because they are marked as persistent and will never disconnect themselves. + return vc.disconnectValidators(validatorMap{}) + } + + // Find new newValidators + newValidators, err := vc.selectValidators() + if err != nil { + vc.Logger.Error("cannot determine list of validators to connect", "error", err) + // no return, as we still need to disconnect unused validators + } + // Disconnect existing validators unless they are selected to be connected again + if err := vc.disconnectValidators(newValidators); err != nil { + return fmt.Errorf("cannot disconnect unused validators: %w", err) + } + vc.Logger.Debug("filtering validators", "validators", newValidators.String()) + // ensure that we can connect to all validators + newValidators = vc.filterAddresses(newValidators) + // Connect to new validators + vc.Logger.Debug("dialing validators", "validators", newValidators.String()) + if err := vc.dial(newValidators); err != nil { + return fmt.Errorf("cannot dial validators: %w", err) + } + vc.Logger.P2PDebug("connected to Validators", "validators", newValidators.String()) + return nil +} + +// filterValidators returns new validatorMap that contains only validators to which we can connect +func (vc *ValidatorConnExecutor) filterAddresses(validators validatorMap) validatorMap { + filtered := make(validatorMap, len(validators)) + for id, validator := range validators { + if vc.connectedValidators.contains(validator) { + vc.Logger.Debug("validator already connected", "id", id) + continue + } + address, err := validator.NodeAddress.NetAddress() + if err != nil { + vc.Logger.Error("cannot parse validator address", "address", validator.NodeAddress, "err", err) + continue + } + if vc.p2pSwitch.IsDialingOrExistingAddress(address) { + vc.Logger.Debug("already dialing this validator", "id", id, "address", address.String()) + continue + } + filtered[id] = validator + } + return filtered +} + +// dial dials the validators and ensures they will be persistent +func (vc *ValidatorConnExecutor) dial(vals validatorMap) error { + if len(vals) < 1 { + return nil + } + + // we mark all validators as connected, to disconnect it in future validator rotation even if sth went wrong + for id, validator := range vals { + vc.connectedValidators[id] = validator + } + addresses := vals.URIs() + if err := vc.p2pSwitch.AddPersistentPeers(addresses); err != nil { + vc.Logger.Error("cannot set validators as persistent", "peers", addresses, "err", err) + return fmt.Errorf("cannot set validators as persistent: %w", err) + } + // TODO in tendermint 0.35, we will use router.connectPeer instead of DialPeersAsync + if err := vc.p2pSwitch.DialPeersAsync(addresses); err != nil { + vc.Logger.Error("cannot dial validators", "peers", addresses, "err", err) + return fmt.Errorf("cannot dial peers: %w", err) + } + return nil +} diff --git a/dash/quorum/validator_conn_executor_test.go b/dash/quorum/validator_conn_executor_test.go new file mode 100644 index 0000000000..2724e827be --- /dev/null +++ b/dash/quorum/validator_conn_executor_test.go @@ -0,0 +1,624 @@ +package quorum + +import ( + "context" + "encoding/hex" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/dash/quorum/mock" + "github.com/tendermint/tendermint/dash/quorum/selectpeers" + dashtypes "github.com/tendermint/tendermint/dash/types" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + "github.com/tendermint/tendermint/libs/log" + mmock "github.com/tendermint/tendermint/mempool/mock" + "github.com/tendermint/tendermint/p2p" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "github.com/tendermint/tendermint/proxy" + sm "github.com/tendermint/tendermint/state" + "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" +) + +var ( + chainID = "execution_chain" + testPartSize uint32 = 65536 + nTxsPerBlock = 10 +) + +type validatorUpdate struct { + validators []*types.Validator + expectedHistory []mock.SwitchHistoryEvent +} +type testCase struct { + me *types.Validator + validatorUpdates []validatorUpdate +} + +// TestValidatorConnExecutorNotValidator checks what happens if current node is not a validator. +// Expected: nothing happens +func TestValidatorConnExecutor_NotValidator(t *testing.T) { + + me := mock.NewValidator(65535) + tc := testCase{ + me: me, + validatorUpdates: []validatorUpdate{ + 0: { + validators: []*types.Validator{ + mock.NewValidator(1), + mock.NewValidator(2), + mock.NewValidator(3), + }, + expectedHistory: []mock.SwitchHistoryEvent{}, + }}, + } + executeTestCase(t, tc) +} + +// TestValidatorConnExecutor_WrongAddress checks behavior in case of several issues in the address. +// Expected behavior: invalid address is dialed. Previous addresses are disconnected. +func TestValidatorConnExecutor_WrongAddress(t *testing.T) { + me := mock.NewValidator(65535) + zeroBytes := make([]byte, p2p.IDByteLength) + nodeID := hex.EncodeToString(zeroBytes) + addr1, err := dashtypes.ParseValidatorAddress("http://" + nodeID + "@www.domain-that-does-not-exist.com:80") + require.NoError(t, err) + + val1 := mock.NewValidator(100) + val1.NodeAddress = addr1 + + valsWithoutAddress := make([]*types.Validator, 5) + for i := 0; i < len(valsWithoutAddress); i++ { + valsWithoutAddress[i] = mock.NewValidator(uint64(200 + i)) + valsWithoutAddress[i].NodeAddress = dashtypes.ValidatorAddress{} + } + + tc := testCase{ + me: me, + validatorUpdates: []validatorUpdate{ + 0: { + validators: []*types.Validator{ + me, + mock.NewValidator(1), + mock.NewValidator(2), + mock.NewValidator(3), + mock.NewValidator(4), + }, + expectedHistory: []mock.SwitchHistoryEvent{{Operation: mock.OpDialMany}}, // Params: []string{mockNodeAddress()}, + }, + 1: { + validators: []*types.Validator{ + me, + val1, + }, + expectedHistory: []mock.SwitchHistoryEvent{ + {Operation: mock.OpStopOne}, + {Operation: mock.OpStopOne}, + // {Operation: mock.OpStopOne}, + }, + }, + 2: { + validators: []*types.Validator{ + me, + valsWithoutAddress[0], + mock.NewValidator(1), + mock.NewValidator(2), + mock.NewValidator(3), + mock.NewValidator(4), + mock.NewValidator(5), + }, + expectedHistory: []mock.SwitchHistoryEvent{ + {Operation: mock.OpDialMany, Params: []string{ + mock.NewNodeAddress(2), + mock.NewNodeAddress(5), + }}, + }, + }, + 3: { // this should disconnect everyone because none of the validators has correct address + validators: append([]*types.Validator{me}, valsWithoutAddress...), + expectedHistory: []mock.SwitchHistoryEvent{ + {Operation: mock.OpStopOne}, + {Operation: mock.OpStopOne}, + }, + }, + }, + } + executeTestCase(t, tc) +} + +// TestValidatorConnExecutor_Myself checks what happens if we want to connect to ourselves. +// Expected: connections from previous update are stopped, no new connection established. +func TestValidatorConnExecutor_Myself(t *testing.T) { + + me := mock.NewValidator(65535) + + tc := testCase{ + me: me, + validatorUpdates: []validatorUpdate{ + 0: { + validators: []*types.Validator{ + me, + mock.NewValidator(1), + mock.NewValidator(2), + mock.NewValidator(3), + }, + expectedHistory: []mock.SwitchHistoryEvent{ + {Operation: mock.OpDialMany}, + }, + }, + 1: { + validators: []*types.Validator{ + me, + mock.NewValidator(1), + mock.NewValidator(2), + mock.NewValidator(3), + mock.NewValidator(4), + mock.NewValidator(5), + }, + expectedHistory: []mock.SwitchHistoryEvent{ + {Operation: mock.OpStopOne}, + {Operation: mock.OpStopOne}, + {Operation: mock.OpDialMany}, + }, + }, + 2: { + validators: []*types.Validator{me}, + expectedHistory: []mock.SwitchHistoryEvent{{ + Operation: mock.OpStopOne, + Params: []string{mock.NewNodeAddress(2), mock.NewNodeAddress(5)}, + }, + { + Operation: mock.OpStopOne, + Params: []string{mock.NewNodeAddress(2), mock.NewNodeAddress(5)}, + }, + }, + }, + }, + } + executeTestCase(t, tc) +} + +// TestValidatorConnExecutor_EmptyVSet checks what will happen if the ABCI App provides an empty validator set. +// Expected: nothing happens +func TestValidatorConnExecutor_EmptyVSet(t *testing.T) { + me := mock.NewValidator(65535) + tc := testCase{ + me: me, + validatorUpdates: []validatorUpdate{ + 0: { + validators: []*types.Validator{me, + mock.NewValidator(1), + mock.NewValidator(2), + mock.NewValidator(3), + mock.NewValidator(4), + mock.NewValidator(5), + }, + expectedHistory: []mock.SwitchHistoryEvent{{Operation: mock.OpDialMany, + Params: []string{ + mock.NewNodeAddress(2), + mock.NewNodeAddress(5), + }}}, + }, + 1: {}, + }, + } + executeTestCase(t, tc) +} + +// TestValidatorConnExecutor_ValidatorUpdatesSequence checks sequence of multiple validators switched +func TestValidatorConnExecutor_ValidatorUpdatesSequence(t *testing.T) { + me := mock.NewValidator(65535) + tc := testCase{ + me: me, + validatorUpdates: []validatorUpdate{ + 0: { + validators: []*types.Validator{me, + mock.NewValidator(1), + mock.NewValidator(2), + mock.NewValidator(3), + mock.NewValidator(4), + }, + expectedHistory: []mock.SwitchHistoryEvent{{ + Comment: "dialing initial validators", + Operation: mock.OpDialMany, + }}, + }, + 1: { + validators: []*types.Validator{ + me, + mock.NewValidator(2), + mock.NewValidator(3), + mock.NewValidator(4), + mock.NewValidator(5), + }, + expectedHistory: []mock.SwitchHistoryEvent{ + { + Comment: "Stop old peers that are not part of new validator set", + Operation: mock.OpStopOne, + Params: []string{mock.NewNodeAddress(1)}, + }, + { + Comment: "Dial two members of current validator set", + Operation: mock.OpDialMany, + }, + }, + }, + 2: { // the same validator set as above, nothing should happen + validators: []*types.Validator{ + me, + mock.NewValidator(2), + mock.NewValidator(3), + mock.NewValidator(4), + mock.NewValidator(5), + }, + expectedHistory: []mock.SwitchHistoryEvent{}, + }, + 3: { // only 1 validator (except myself), we should stop existing validators and don't dial anyone + validators: []*types.Validator{ + me, + mock.NewValidator(1), + }, + expectedHistory: []mock.SwitchHistoryEvent{ + 0: {Operation: mock.OpStopOne}, + 1: {Operation: mock.OpStopOne}, + 2: {Operation: mock.OpDialMany, Params: []string{mock.NewNodeAddress(1)}}, + }, + }, + 4: { // everything stops + validators: []*types.Validator{me}, + expectedHistory: []mock.SwitchHistoryEvent{ + 0: {Operation: mock.OpStopOne}, + }, + }, + 5: { // 20 validators + validators: append(mock.NewValidators(20), me), + expectedHistory: []mock.SwitchHistoryEvent{ + { + Comment: "Nothing dialed in previous round, so we just dial new validators", + Operation: mock.OpDialMany}, + }, + }, + }, + } + + executeTestCase(t, tc) +} + +// TestEndBlock verifies if ValidatorConnExecutor is called correctly during processing of EndBlock +// message from the ABCI app. +func TestEndBlock(t *testing.T) { + const timeout = 3 * time.Second // how long we'll wait for connection + app := newTestApp() + cc := proxy.NewLocalClientCreator(app) + proxyApp := proxy.NewAppConns(cc) + err := proxyApp.Start() + require.Nil(t, err) + defer proxyApp.Stop() //nolint:errcheck // ignore for tests + + state, stateDB, _ := makeState(3, 1) + nodeProTxHash := &state.Validators.Validators[0].ProTxHash + stateStore := sm.NewStore(stateDB) + + blockExec := sm.NewBlockExecutor( + stateStore, + log.TestingLogger(), + proxyApp.Consensus(), + proxyApp.Query(), + mmock.Mempool{}, + sm.EmptyEvidencePool{}, + nil, + ) + + eventBus := types.NewEventBus() + err = eventBus.Start() + require.NoError(t, err) + defer eventBus.Stop() //nolint:errcheck // ignore for tests + + blockExec.SetEventBus(eventBus) + + updatesSub, err := eventBus.Subscribe( + context.Background(), + "TestEndBlockValidatorUpdates", + types.EventQueryValidatorSetUpdates, + ) + require.NoError(t, err) + + block := makeBlock(state, 1, new(types.Commit)) + blockID := types.BlockID{ + Hash: block.Hash(), + PartSetHeader: block.MakePartSet(testPartSize).Header(), + } + + vals := state.Validators + proTxHashes := vals.GetProTxHashes() + addProTxHashes := make([]tmbytes.HexBytes, 0, 100) + for i := 0; i < 100; i++ { + addProTxHash := crypto.RandProTxHash() + addProTxHashes = append(addProTxHashes, addProTxHash) + } + proTxHashes = append(proTxHashes, addProTxHashes...) + newVals, _ := types.GenerateValidatorSetUsingProTxHashes(proTxHashes) + + // Ensure new validators have some IP addresses set + for _, validator := range newVals.Validators { + validator.NodeAddress = dashtypes.RandValidatorAddress() + } + + // setup ValidatorConnExecutor + sw := mock.NewMockSwitch() + proTxHash := newVals.Validators[0].ProTxHash + vc, err := NewValidatorConnExecutor(proTxHash, eventBus, sw) + require.NoError(t, err) + err = vc.Start() + require.NoError(t, err) + defer func() { err := vc.Stop(); require.NoError(t, err) }() + + app.ValidatorSetUpdates[1] = newVals.ABCIEquivalentValidatorUpdates() + + state, _, err = blockExec.ApplyBlock(state, nodeProTxHash, blockID, block) + require.Nil(t, err) + // test new validator was added to NextValidators + require.Equal(t, state.Validators.Size()+100, state.NextValidators.Size()) + nextValidatorsProTxHashes := mock.ValidatorsProTxHashes(state.NextValidators.Validators) + for _, addProTxHash := range addProTxHashes { + assert.Contains(t, nextValidatorsProTxHashes, addProTxHash) + } + + // test we threw an event + select { + case msg := <-updatesSub.Out(): + event, ok := msg.Data().(types.EventDataValidatorSetUpdates) + require.True( + t, + ok, + "Expected event of type EventDataValidatorSetUpdates, got %T", + msg.Data(), + ) + if assert.NotEmpty(t, event.ValidatorUpdates) { + for _, addProTxHash := range addProTxHashes { + assert.Contains(t, mock.ValidatorsProTxHashes(event.ValidatorUpdates), addProTxHash) + } + assert.EqualValues( + t, + types.DefaultDashVotingPower, + event.ValidatorUpdates[1].VotingPower, + ) + assert.NotEmpty(t, event.QuorumHash) + } + case <-updatesSub.Cancelled(): + t.Fatalf("updatesSub was cancelled (reason: %v)", updatesSub.Err()) + case <-time.After(1 * time.Second): + t.Fatal("Did not receive EventValidatorSetUpdates within 1 sec.") + } + + // ensure some history got generated inside the Switch; we expect 1 dial event + select { + case msg := <-sw.HistoryChan: + t.Logf("Got message: %s %+v", msg.Operation, msg.Params) + assert.EqualValues(t, mock.OpDialMany, msg.Operation) + case <-time.After(timeout): + t.Error("Timed out waiting for switch history message") + t.FailNow() + } +} + +// ****** utility functions ****** // + +// executeTestCase feeds validator update messages into the event bus +// and ensures operations executed on MockSwitch (history records) match `expectedHistory`, that is: +// * operation in history record is the same as in `expectedHistory` +// * params in history record are a subset of params in `expectedHistory` +func executeTestCase(t *testing.T, tc testCase) { + // const TIMEOUT = 100 * time.Millisecond + const TIMEOUT = 5 * time.Second + + eventBus, sw, vc := setup(t, tc.me) + defer cleanup(t, eventBus, sw, vc) + + for updateID, update := range tc.validatorUpdates { + updateEvent := types.EventDataValidatorSetUpdates{ + ValidatorUpdates: update.validators, + QuorumHash: mock.NewQuorumHash(1000), + } + err := eventBus.PublishEventValidatorSetUpdates(updateEvent) + assert.NoError(t, err) + + // checks + for checkID, check := range update.expectedHistory { + select { + case msg := <-sw.HistoryChan: + // t.Logf("History event: %+v", msg) + assert.EqualValues(t, check.Operation, msg.Operation, + "Update %d: wrong operation %s in expected event %d, comment: %s", + updateID, check.Operation, checkID, check.Comment) + allowedParams := check.Params + // if params are nil, we default to all validator addresses; use []string{} to allow no addresses + if allowedParams == nil { + allowedParams = allowedParamsDefaults(t, tc, updateID, check, updateEvent.QuorumHash) + } + for _, param := range msg.Params { + // Params of the call need to "contains" only these values as: + // * we don't dial again already connected validators, and + // * we randomly select a few validators from new validator set + assert.Contains(t, allowedParams, param, + "Update %d: wrong params in expected event %d, op %s, comment: %s", + updateID, checkID, check.Operation, check.Comment) + } + + // assert.EqualValues(t, check.Params, msg.Params, "check %d", i) + case <-time.After(TIMEOUT): + t.Logf("Update %d: timed out waiting for history event %d: %+v", updateID, checkID, check) + t.FailNow() + } + } + + // ensure no new history message arrives, eg. there are no additional operations done on the switch + select { + case msg := <-sw.HistoryChan: + t.Errorf("unexpected history event: %+v", msg) + case <-time.After(50 * time.Millisecond): + // this is correct - we time out + } + } + +} + +func allowedParamsDefaults( + t *testing.T, + tc testCase, + updateID int, + check mock.SwitchHistoryEvent, + quorumHash tmbytes.HexBytes) []string { + + var ( + validators []*types.Validator + ) + + switch check.Operation { + case mock.OpDialMany: + validators = tc.validatorUpdates[updateID].validators + case mock.OpStopOne: + if updateID > 0 { + validators = tc.validatorUpdates[updateID-1].validators + } + } + + selector := selectpeers.NewDIP6ValidatorSelector(quorumHash) + allowedValidators, err := selector.SelectValidators(validators, tc.me) + require.NoError(t, err) + return newValidatorMap(allowedValidators).URIs() +} + +// setup creates ValidatorConnExecutor and some dependencies. +// Use `defer cleanup()` to free the resources. +func setup( + t *testing.T, + me *types.Validator, +) (eventBus *types.EventBus, sw *mock.Switch, vc *ValidatorConnExecutor) { + eventBus = types.NewEventBus() + err := eventBus.Start() + require.NoError(t, err) + + sw = mock.NewMockSwitch() + + proTxHash := me.ProTxHash + vc, err = NewValidatorConnExecutor(proTxHash, eventBus, sw) + require.NoError(t, err) + err = vc.Start() + require.NoError(t, err) + + return eventBus, sw, vc +} + +// cleanup frees some resources allocated for tests +func cleanup(t *testing.T, bus *types.EventBus, sw Switch, vc *ValidatorConnExecutor) { + assert.NoError(t, bus.Stop()) + assert.NoError(t, vc.Stop()) +} + +// SOME UTILS // + +// make some bogus txs +func makeTxs(height int64) (txs []types.Tx) { + for i := 0; i < nTxsPerBlock; i++ { + txs = append(txs, types.Tx([]byte{byte(height), byte(i)})) + } + return txs +} + +func makeState(nVals int, height int64) (sm.State, dbm.DB, map[string]types.PrivValidator) { + privValsByProTxHash := make(map[string]types.PrivValidator, nVals) + vals, privVals, quorumHash, thresholdPublicKey := types.GenerateMockGenesisValidators(nVals) + // vals and privals are sorted + for i := 0; i < nVals; i++ { + vals[i].Name = fmt.Sprintf("test%d", i) + proTxHash := vals[i].ProTxHash + privValsByProTxHash[proTxHash.String()] = privVals[i] + } + s, _ := sm.MakeGenesisState(&types.GenesisDoc{ + ChainID: chainID, + Validators: vals, + ThresholdPublicKey: thresholdPublicKey, + QuorumHash: quorumHash, + AppHash: nil, + }) + + stateDB := dbm.NewMemDB() + stateStore := sm.NewStore(stateDB) + if err := stateStore.Save(s); err != nil { + panic(err) + } + + for i := int64(1); i < height; i++ { + s.LastBlockHeight++ + s.LastValidators = s.Validators.Copy() + if err := stateStore.Save(s); err != nil { + panic(err) + } + } + + return s, stateDB, privValsByProTxHash +} + +func makeBlock(state sm.State, height int64, commit *types.Commit) *types.Block { + block, _ := state.MakeBlock(height, nil, makeTxs(state.LastBlockHeight), + commit, nil, state.Validators.GetProposer().ProTxHash, 0) + return block +} + +// TEST APP // + +// testApp which changes validators according to updates defined in testApp.ValidatorSetUpdates +type testApp struct { + abci.BaseApplication + + ByzantineValidators []abci.Evidence + ValidatorSetUpdates map[int64]*abci.ValidatorSetUpdate +} + +func newTestApp() *testApp { + return &testApp{ + ByzantineValidators: []abci.Evidence{}, + ValidatorSetUpdates: map[int64]*abci.ValidatorSetUpdate{}, + } +} + +var _ abci.Application = (*testApp)(nil) + +func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) { + return abci.ResponseInfo{} +} + +func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { + app.ByzantineValidators = req.ByzantineValidators + return abci.ResponseBeginBlock{} +} + +func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { + return abci.ResponseEndBlock{ + ValidatorSetUpdate: app.ValidatorSetUpdates[req.Height], + ConsensusParamUpdates: &abci.ConsensusParams{ + Version: &tmproto.VersionParams{ + AppVersion: 1}}} +} + +func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { + return abci.ResponseDeliverTx{Events: []abci.Event{}} +} + +func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { + return abci.ResponseCheckTx{} +} + +func (app *testApp) Commit() abci.ResponseCommit { + return abci.ResponseCommit{RetainHeight: 1} +} + +func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { + return +} diff --git a/dash/quorum/validator_map.go b/dash/quorum/validator_map.go new file mode 100644 index 0000000000..3c1d555f4d --- /dev/null +++ b/dash/quorum/validator_map.go @@ -0,0 +1,64 @@ +package quorum + +import ( + "sort" + "strings" + + "github.com/tendermint/tendermint/types" +) + +// validatorMapIndexType represents data that is used to index `validatorMap` elements +type validatorMapIndexType string + +// validatorMap maps validator ID to the validator +type validatorMap map[validatorMapIndexType]types.Validator + +// validatorMapIndex returns index value to use inside validator map +func validatorMapIndex(v types.Validator) validatorMapIndexType { + return validatorMapIndexType(v.ProTxHash.String()) +} + +// newValidatorMap creates a new validatoMap based on a slice of Validators +func newValidatorMap(validators []*types.Validator) validatorMap { + newMap := make(validatorMap, len(validators)) + for _, validator := range validators { + if !validator.NodeAddress.Zero() { + newMap[validatorMapIndex(*validator)] = *validator + } + } + return newMap +} + +// values returns content (values) of the map as a slice +func (vm validatorMap) values() []*types.Validator { + vals := make([]*types.Validator, 0, len(vm)) + for _, v := range vm { + vals = append(vals, v.Copy()) + } + return vals +} + +// contains returns true if the validatorMap contains `What`, false otherwise. +// Items are compared using node ID. +func (vm validatorMap) contains(what types.Validator) bool { + _, ok := vm[validatorMapIndex(what)] + return ok +} + +// URIs returns URIs of all validators in this map +func (vm validatorMap) URIs() []string { + uris := make([]string, 0, len(vm)) + for _, v := range vm { + uris = append(uris, v.NodeAddress.String()) + } + return uris +} + +func (vm validatorMap) String() string { + resp := make(sort.StringSlice, 0, len(vm)) + for _, v := range vm { + resp = append(resp, v.String()) + } + resp.Sort() + return strings.Join(resp, "\n") +} diff --git a/dash/quorum/validator_map_test.go b/dash/quorum/validator_map_test.go new file mode 100644 index 0000000000..81c3a1923e --- /dev/null +++ b/dash/quorum/validator_map_test.go @@ -0,0 +1,40 @@ +package quorum + +import ( + "hash/crc64" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/dash/quorum/mock" + "github.com/tendermint/tendermint/types" +) + +func Test_validatorMap_String(t *testing.T) { + vals := mock.NewValidators(5) + + tests := []struct { + vm validatorMap + wantCrc uint64 + }{ + { + vm: newValidatorMap([]*types.Validator{}), + wantCrc: 0x0, + }, + { + vm: newValidatorMap(vals[:2]), + wantCrc: 0x1f78f540f6d32856, + }, + { + vm: newValidatorMap(vals), + wantCrc: 0xc7ca01eea9504bdf, + }, + } + + for _, tt := range tests { + t.Run("", func(t *testing.T) { + got := tt.vm.String() + crcTable := crc64.MakeTable(crc64.ISO) + assert.EqualValues(t, tt.wantCrc, crc64.Checksum([]byte(got), crcTable), got) + }) + } +} diff --git a/dash/types/nodeid_resolver.go b/dash/types/nodeid_resolver.go new file mode 100644 index 0000000000..9f7ba5a960 --- /dev/null +++ b/dash/types/nodeid_resolver.go @@ -0,0 +1,104 @@ +package types + +import ( + "fmt" + "net" + "time" + + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/conn" +) + +const ( + // DefaultDialTimeout when resolving node id using TCP connection + DefaultDialTimeout = 1000 * time.Millisecond + // DefaultConnectionTimeout is a connection timeout when resolving node id using TCP connection + DefaultConnectionTimeout = 1 * time.Second +) + +// NodeIDResolver determines a node ID based on validator address +type NodeIDResolver interface { + // Resolve retrieves a node ID from remote node. + Resolve(ValidatorAddress) (p2p.ID, error) +} + +type tcpNodeIDResolver struct { + DialerTimeout time.Duration + ConnectionTimeout time.Duration + // other dependencies +} + +// NewTCPNodeIDResolver creates new NodeIDResolver that connects to remote host with p2p protocol and +// derives node ID from remote p2p public key. +func NewTCPNodeIDResolver() NodeIDResolver { + return &tcpNodeIDResolver{ + DialerTimeout: DefaultDialTimeout, + ConnectionTimeout: DefaultConnectionTimeout, + } +} + +// connect establishes a TCP connection to remote host. +// When err == nil, caller is responsible for closing of the connection +func (resolver tcpNodeIDResolver) connect(host string, port uint16) (net.Conn, error) { + dialer := net.Dialer{ + Timeout: resolver.DialerTimeout, + } + connection, err := dialer.Dial("tcp4", fmt.Sprintf("%s:%d", host, port)) + if err != nil { + return nil, fmt.Errorf("cannot lookup node ID: %w", err) + } + if err := connection.SetDeadline(time.Now().Add(resolver.ConnectionTimeout)); err != nil { + connection.Close() + return nil, err + } + + return connection, nil +} + +// Resolve implements NodeIDResolver +// Resolve retrieves a node ID from remote node. +// Note that it is quite expensive, as it establishes secure connection to the other node +// which is dropped afterwards. +func (resolver tcpNodeIDResolver) Resolve(va ValidatorAddress) (p2p.ID, error) { + connection, err := resolver.connect(va.Hostname, va.Port) + if err != nil { + return "", err + } + defer connection.Close() + + sc, err := conn.MakeSecretConnection(connection, nil) + if err != nil { + return "", err + } + return p2p.PubKeyToID(sc.RemotePubKey()), nil +} + +type addrbookNodeIDResolver struct { + addrBook p2p.AddrBook +} + +// NewAddrbookNodeIDResolver creates new node ID resolver. +// It looks up for the node ID based on IP address, using the p2p addressbook. +func NewAddrbookNodeIDResolver(addrBook p2p.AddrBook) NodeIDResolver { + return addrbookNodeIDResolver{addrBook: addrBook} +} + +// Resolve implements NodeIDResolver +// Resolve retrieves a node ID from the address book. +func (resolver addrbookNodeIDResolver) Resolve(va ValidatorAddress) (p2p.ID, error) { + ip := net.ParseIP(va.Hostname) + if ip == nil { + ips, err := net.LookupIP(va.Hostname) + if err != nil { + return "", p2p.ErrNetAddressLookup{Addr: va.Hostname, Err: err} + } + ip = ips[0] + } + + id := resolver.addrBook.FindIP(ip, va.Port) + if id == "" { + return "", ErrNoNodeID + } + + return id, nil +} diff --git a/dash/types/validator_address.go b/dash/types/validator_address.go new file mode 100644 index 0000000000..d96096bbbf --- /dev/null +++ b/dash/types/validator_address.go @@ -0,0 +1,83 @@ +package types + +import ( + "errors" + "fmt" + "math" + + tmrand "github.com/tendermint/tendermint/libs/rand" + "github.com/tendermint/tendermint/p2p" +) + +// ValidatorAddress is a NodeAddress that does not require node ID to be set +type ValidatorAddress struct { + p2p.NodeAddress +} + +var ( + // ErrNoHostname is returned when no hostname is set for the validator address + ErrNoHostname error = errors.New("no hostname") + // ErrNoPort is returned when no valid port is set for the validator address + ErrNoPort error = errors.New("no port") + // ErrNoProtocol is returned when no protocol (like `tcp://`) is set for the validator address + ErrNoProtocol error = errors.New("no protocol") + // ErrNoNodeID is returned when node ID is not set for the node ID + ErrNoNodeID error = errors.New("no node ID") +) + +// ParseValidatorAddress parses provided address, which should be in `proto://nodeID@host:port` form. +// `proto://` and `nodeID@` parts are optional. +func ParseValidatorAddress(address string) (ValidatorAddress, error) { + addr, err := p2p.ParseNodeAddressWithoutValidation(address) + if err != nil { + return ValidatorAddress{}, err + } + va := ValidatorAddress{ + NodeAddress: addr, + } + return va, va.Validate() +} + +// Validate ensures the validator address is correct. +// It ignores missing node IDs. +func (va ValidatorAddress) Validate() error { + if va.NodeAddress.Protocol == "" { + return ErrNoProtocol + } + if va.NodeAddress.Hostname == "" { + return ErrNoHostname + } + if va.NodeAddress.Port <= 0 { + return ErrNoPort + } + if len(va.NodeAddress.NodeID) > 0 { + if err := p2p.ValidateID(va.NodeAddress.NodeID); err != nil { + return err + } + } + + return nil +} + +// NetAddress converts this ValidatorAddress to `*p2p.NetAddress` that can be used to establish connection +func (va ValidatorAddress) NetAddress() (*p2p.NetAddress, error) { + if va.NodeID == "" { + return nil, fmt.Errorf("cannot determine node id for address %s", va.String()) + } + return va.NodeAddress.NetAddress() +} + +// RandValidatorAddress generates a random validator address. Used in tests. +// It will panic in (very unlikely) case of error. +func RandValidatorAddress() ValidatorAddress { + nodeID := tmrand.Bytes(20) + port := tmrand.Int()%math.MaxUint16 + 1 + addr, err := ParseValidatorAddress(fmt.Sprintf("tcp://%x@127.0.0.1:%d", nodeID, port)) + if err != nil { + panic(fmt.Sprintf("cannot generate random validator address: %s", err)) + } + if err := addr.Validate(); err != nil { + panic(fmt.Sprintf("randomly generated validator address %s is invalid: %s", addr.String(), err)) + } + return addr +} diff --git a/dash/types/validator_address_test.go b/dash/types/validator_address_test.go new file mode 100644 index 0000000000..042f8f6ea8 --- /dev/null +++ b/dash/types/validator_address_test.go @@ -0,0 +1,166 @@ +package types + +import ( + "encoding/hex" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/libs/rand" + "github.com/tendermint/tendermint/p2p" +) + +func TestValidatorAddress_String(t *testing.T) { + nodeID := randNodeID() + tests := []struct { + uri string + want string + }{ + { + uri: "tcp://" + nodeID + "@fqdn.address.com:1234", + want: "tcp://" + nodeID + "@fqdn.address.com:1234", + }, + { + uri: "tcp://fqdn.address.com:1234", + want: "tcp://fqdn.address.com:1234", + }, + } + for _, tt := range tests { + t.Run(tt.uri, func(t *testing.T) { + va, err := ParseValidatorAddress(tt.uri) + assert.NoError(t, err) + got := va.String() + assert.EqualValues(t, tt.want, got) + }) + } +} + +// TestValidatorAddress_NodeID_fail checks if NodeID lookup fails when trying to connect to ssh port +// NOTE: Positive flow is tested as part of node_test.go TestNodeStartStop() +func TestValidatorAddress_NodeID_fail(t *testing.T) { + nodeID := randNodeID() + + tests := []struct { + uri string + want string + wantErr bool + }{ + { + uri: "tcp://" + nodeID + "@fqdn.address.com:1234", + want: nodeID, + }, + { + uri: "tcp://127.0.0.1:22", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.uri, func(t *testing.T) { + va, err := ParseValidatorAddress(tt.uri) + assert.NoError(t, err) + // todo lookup for an address + got := va.NodeID + // assert.Equal(t, err != nil, tt.wantErr, "wantErr=%t, but err = %s", tt.wantErr, err) + assert.EqualValues(t, tt.want, got) + }) + } +} + +// TestValidatorAddress_HostPortProto verifies if host, port and proto is detected correctly when parsing +// ValidatorAddress +func TestValidatorAddress_HostPortProto(t *testing.T) { + nodeID := randNodeID() + + tests := []struct { + uri string + wantHost string + wantPort uint16 + wantProto string + wantNodeID string + wantValid bool + wantParseErr bool + }{ + { + uri: "tcp://" + nodeID + "@fqdn.address.com:1234", + wantHost: "fqdn.address.com", + wantPort: 1234, + wantProto: "tcp", + wantNodeID: nodeID, + wantValid: true, + }, + { + uri: "tcp://test@fqdn.address.com:1234", + wantHost: "fqdn.address.com", + wantPort: 1234, + wantProto: "tcp", + wantValid: false, + wantParseErr: true, + }, + { + uri: "tcp://127.0.0.1:22", + wantHost: "127.0.0.1", + wantPort: 22, + wantProto: "tcp", + wantValid: true, + }, + { + uri: "", + wantValid: false, + wantParseErr: true, + }, + { + uri: "tcp://127.0.0.1", + wantHost: "127.0.0.1", + wantPort: 0, + wantProto: "tcp", + wantValid: false, + wantParseErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.uri, func(t *testing.T) { + va, err := ParseValidatorAddress(tt.uri) + if tt.wantParseErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.EqualValues(t, tt.wantHost, va.Hostname) + assert.EqualValues(t, tt.wantPort, va.Port) + assert.EqualValues(t, tt.wantProto, va.Protocol) + + if tt.wantNodeID != "" { + nodeID := va.NodeID + assert.EqualValues(t, tt.wantNodeID, nodeID) + } + err = va.Validate() + if tt.wantValid { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + } + }) + } +} + +func TestValidatorAddress_NetAddress(t *testing.T) { + nodeID := randNodeID() + uri := "tcp://" + nodeID + "@127.0.0.1:1234" + + va, err := ParseValidatorAddress(uri) + assert.NoError(t, err) + + naddr, err := va.NetAddress() + assert.NoError(t, err) + assert.NoError(t, naddr.Valid()) + assert.EqualValues(t, naddr.IP.String(), "127.0.0.1") + assert.EqualValues(t, naddr.Port, 1234) + assert.EqualValues(t, naddr.ID, nodeID) +} + +// utility functions + +func randNodeID() string { + nodeID := rand.Bytes(p2p.IDByteLength) + return hex.EncodeToString(nodeID) +} diff --git a/docs/app-dev/indexing-transactions.md b/docs/app-dev/indexing-transactions.md index 78161a0400..0019df311b 100644 --- a/docs/app-dev/indexing-transactions.md +++ b/docs/app-dev/indexing-transactions.md @@ -31,24 +31,61 @@ For example: would be equal to the composite key of `jack.account.number`. -Let's take a look at the `[tx_index]` config section: +By default, Tendermint will index all transactions by their respective hashes +and height and blocks by their height. + +## Configuration + +Operators can configure indexing via the `[tx_index]` section. The `indexer` +field takes a series of supported indexers. If `null` is included, indexing will +be turned off regardless of other values provided. ```toml -##### transactions indexer configuration options ##### -[tx_index] +[tx-index] -# What indexer to use for transactions +# The backend database to back the indexer. +# If indexer is "null", no indexer service will be used. +# +# The application will set which txs to index. In some cases a node operator will be able +# to decide which txs to index based on configuration set in the application. # # Options: # 1) "null" # 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend). -indexer = "kv" +# - When "kv" is chosen "tx.height" and "tx.hash" will always be indexed. +# 3) "psql" - the indexer services backed by PostgreSQL. +# indexer = "kv" ``` -By default, Tendermint will index all transactions by their respective hashes -and height and blocks by their height. +### Supported Indexers + +#### KV -You can turn off indexing completely by setting `tx_index` to `null`. +The `kv` indexer type is an embedded key-value store supported by the main +underlying Tendermint database. Using the `kv` indexer type allows you to query +for block and transaction events directly against Tendermint's RPC. However, the +query syntax is limited and so this indexer type might be deprecated or removed +entirely in the future. + +#### PostgreSQL + +The `psql` indexer type allows an operator to enable block and transaction event +indexing by proxying it to an external PostgreSQL instance allowing for the events +to be stored in relational models. Since the events are stored in a RDBMS, operators +can leverage SQL to perform a series of rich and complex queries that are not +supported by the `kv` indexer type. Since operators can leverage SQL directly, +searching is not enabled for the `psql` indexer type via Tendermint's RPC -- any +such query will fail. + +Note, the SQL schema is stored in `state/indexer/sink/psql/schema.sql` and operators +must explicitly create the relations prior to starting Tendermint and enabling +the `psql` indexer type. + +Example: + +```shell +$ psql ... -f state/indexer/sink/psql/schema.sql +``` ## Default Indexes diff --git a/docs/architecture/adr-d001-inter-validator-set-messaging.md b/docs/architecture/adr-d001-inter-validator-set-messaging.md new file mode 100644 index 0000000000..dc379b50bc --- /dev/null +++ b/docs/architecture/adr-d001-inter-validator-set-messaging.md @@ -0,0 +1,281 @@ +# ADR D001: Inter validator set messaging + +## Changelog + +* 2021-09-29: Initial version of the document +* 2021-10-05: Clarification of the document based on peer review +* 2021-10-22: Updated document based on implementation +* 2022-01-05: Review after implementation is done + +## Table of contents + +1. [Context](#context) + 1. [Definitions](#definitions) + 2. [Problem statement](#problem-statement) +2. [Alternative Approaches](#alternative-approaches) + 1. [Scenario 1: Trigger rotation from ABCI app passing addresses of all quorum members](#scenario-1-trigger-rotation-from-abci-app-passing-addresses-of-all-quorum-members) + 2. [Scenario 2: Trigger rotation from ABCI app passing quorum id to rotate](#scenario-2-trigger-rotation-from-abci-app-passing-quorum-id-to-rotate) + 3. [Scenario 3: Rotate on Tenderdash](#scenario-3-rotate-on-tenderdash) +3. [Decision](#decision) +4. [Detailed Design](#detailed-design) + 1. [What are the user requirements?](#what-are-the-user-requirements) + 2. [What systems will be affected?](#what-systems-will-be-affected) + 3. [What new data structures are needed, what data structures need changes?](#what-new-data-structures-are-needed-what-data-structures-need-changes) + 4. [What new APIs will be needed, what APIs will change?](#what-new-apis-will-be-needed-what-apis-will-change) + 1. [ABCI Protocol](#abci-protocol) + 2. [P2P Handshake](#p2p-handshake) + 5. [What are the efficiency considerations (time/space)?](#what-are-the-efficiency-considerations-timespace) + 6. [What are the expected access patterns (load/throughput)?](#what-are-the-expected-access-patterns-loadthroughput) + 7. [Are there any logging, monitoring, or observability needs?](#are-there-any-logging-monitoring-or-observability-needs) + 8. [Are there any security considerations?](#are-there-any-security-considerations) + 9. [Are there any privacy considerations?](#are-there-any-privacy-considerations) + 10. [How will the changes be tested?](#how-will-the-changes-be-tested) + 11. [How will the changes be broken up for ease of review?](#how-will-the-changes-be-broken-up-for-ease-of-review) + 12. [Will these changes require a breaking (major) release?](#will-these-changes-require-a-breaking-major-release) + 13. [Does this change require coordination with the SDK or any other software?](#does-this-change-require-coordination-with-the-sdk-or-any-other-software) + 14. [Future work](#future-work) +5. [Status](#status) +6. [Consequences](#consequences) + 1. [Positive](#positive) + 2. [Negative](#negative) + 3. [Neutral](#neutral) +7. [References](#references) + +## Context + +### Definitions + +For the needs of this document, we define the following types of nodes: + +* Validator nodes, which participate in the consensus protocol, verify and sign blocks, +* Full nodes, which verify and execute blocks. Full nodes verify the recovered threshold signature of the block +commit message against the current validator set's quorum threshold public key. + +Note: there are also light nodes, but they are not relevant for this discussion. + +A Validator Set is a group of Validator nodes responsible for driving consensus at any given time. For each block, one +of the validator nodes is a proposer. Each other validator votes on the validity of the block. + +Only one Validator Set can be active at the given time. After a predefined number of blocks, the ABCI app initiates the Validator Set rotation to make another Validator Set active. + +### Problem statement + +The consensus process requires direct communication between Validators, as Full nodes do not know the public keys of +Validators and therefore can neither verify nor propagate any consensus protocol message individually signed by these +Validators. It means that only Validators can propagate votes. However, Full nodes can propagate the final commit +message, which instead contains the recovered threshold signature of the pre-commit voting round. Full nodes can perform +this verification because they know the Validator Set's quorum threshold public key. + +Each node (regardless of its type) randomly selects peer nodes to connect. There is no differentiation +between Full and Validator nodes, and there is no method to ensure that each Validator is (directly or through other +Validators) connected to some other Validators. In an extreme case, a Validator node can be connected only to Full +nodes, effectively blocking all inbound and outbound block proposals and voting, hence excluding it from +participation in the consensus. It is also possible that Validator nodes form "islands" where a few Validators are +connected between themselves but then are separated from other Validator nodes by full nodes. + +## Alternative Approaches + +### Scenario 1: Trigger rotation from ABCI app passing addresses of all quorum members + +During Validator Set rotation, each Validator node shall connect to a predefined number of other Validators that belong to the same Validator Set. + +In the current implementation, the ABCI application manages the rotation of Validator Sets. When rotation is needed, +the application sends information about the new Validator Set in response to the `EndBlock` message. + +`EndBlock` message shall be extended by adding a network address of each Validator node. Based on that, each +member of the active Validator Set will connect with a predefined set of Peer Validators belonging to the same +Validator Set. A list of Peer Validators will be determined using the algorithm described in +[DIP 0006](https://github.com/dashpay/dips/blob/master/dip-0006.md#building-the-set-of-deterministic-connections). + +This solution introduces a risk of delayed propagation of new block due to limited connection slots between Full and +Validator nodes. To mitigate this risk, we can implement the number of Full node connection slots independently from +Validator node connection slots. + +### Scenario 2: Trigger rotation from ABCI app passing quorum id to rotate + +This scenario is very similar to Scenario 1. However, instead of passing addresses of all Validators that are members of +a Validator Set, only quorum ID is passed. + +This solution requires Tendermint to determine Validators addresses, effectively re-implementing logic already done in +the ABCI App. + +### Scenario 3: Rotate on Tenderdash + +ABCI application can send the whole master node list and quorum list to Tenderdash and +potentially rotate on its side. However, it would split the responsibility for +Validator Set rotation between multiple components. It would also require +much more additional implementation. It would be better to keep the current Tendermint +design and concept being a logic-less consensus library and keep all business logic in +the ABCI application. + +## Decision + +After discussion, we will implement the solution described in **Scenario 1**. It will allow us to: + +* reuse logic already implemented as part of ABCI App, +* keep Tenderdash a logic-less consensus library, +* keep single responsibility principle. + +## Detailed Design + +### What are the user requirements? + +Having a network with much more Full nodes than Validators ensure each Validator Node can connect (directly or +through another Validator) to any other Validator which is a member of the same active Validator Set. + +### What systems will be affected? + +* Tenderdash +* ABCI App (Drive) + +### What new data structures are needed, what data structures need changes? + +1. New package `dash` to group Tenderdash-specific code +2. `dashtypes.ValidatorAddress` representing an address of a validator received from the ABCI app +3. Introduced several internal helper data structures (`sortableValidator`, `sortedValidatorList`, `validatorMap`) + +### What new APIs will be needed, what APIs will change? + +#### ABCI Protocol + +In the ABCI protocol, we add a network address of each member of the active Validator Set to the +`ResponseEndBlock.ValidatorSetUpdate.ValidatorUpdate` structure. The network address should be a URI: + +```uri +tcp://@
: +``` + +where: + +* `` - node ID (can be generated based on node P2P public key, see [p2p.PubKeyToID](https://github.com/dashevo/tenderdash/blob/20d1f91c0adb29d1b5d03a945b17513a368759e4/p2p/key.go#L45)) +* `
` - IP address of the validator node +* `` - p2p port number of the validator node + +The `ValidatorUpdate` structure looks as follows: + +```protobuf +message ValidatorUpdate { + tendermint.crypto.PublicKey pub_key = 1 [(gogoproto.nullable) = true]; + int64 power = 2; + bytes pro_tx_hash = 3; + string node_address = 4 [(gogoproto.nullable) = true]; // address of the Validator +} +``` + + +If the ABCI app cannot determine ``, it can leave it empty. In this case, Tenderdash will try to retrieve a public +key from its address book or remote host and derive node ID from it, as described below. + +#### P2P Handshake + +To retrieve node ID from a **remote node**, **local node** can establish a short-lived connection to the **remote node** to download its public key. This feature does not require any changes to the P2P protocol; it just uses existing capabilities. + +A public key request starts with a standard flow Diffie-Hellman protocol, as implemented in `conn.MakeSecretConnection()`. Once ephemeral keys are exchanged between nodes, **local node** proceeds to `shareAuthSignature()`. However, instead of sending its own `AuthSigMessage`, it waits for **remote node**'s auth signature to arrive. As the communication is asynchronous, the **remote node** sends its `AuthSigMessage`. That is when the **local node** closes the connection. + +This algorithm introduces a potential node ID spoofing risk, discussed later. + +### What are the efficiency considerations (time/space)? + +1. This change will increase the amount of data sent by the ABCI application to Tenderdash. The connection between them is local. Therefore the change should not have any noticeable performance or bandwidth impact. +2. Improved consensus performance thanks to a shorter and more predictable distance between each Validator in a Validator Set. + +In the future, additional optimizations can further improve the performance, for example: + +1. use Unix sockets for communication between Tenderdash and ABCI App, +2. use shared memory to speed up inter-process communication; for instance, ABCI should keep (signed) information about active validator-set in shared memory and send only a trigger to take them from there. + + +### What are the expected access patterns (load/throughput)? + +1. Each rotation of the active Validator Set will cause the ABCI application to send additional data to Tenderdash. The number of blocks between rotation events is a configuration parameter of the ABCI application. +2. Each Validator in the active validator set will establish a few more tcp connections. +3. Retrieving public keys for validators can be resource and time-intensive, as it establishes new connections to validators and performs a DH handshake. This issue is mitigated by: + a. ensuring that this feature is used only when needed + b. refactoring and simplification of the P2P protocol and source code used to retrieve public keys + + Note that for the needs of Inter-Validator Set Messaging, retrieving public keys is executed only for `log2(n-1)` Validators, where `n` is size of active Validator Set. + +### Are there any logging, monitoring, or observability needs? + +1. Operator can determine a list of node peers, together with their type (Full, inactive Validator, active Validator) and connection status. This information can be part of debug logs. + +### Are there any security considerations? + +1. This change makes connectivity between Validators more predictable. +It can make it a bit easier for a malicious user to block communication +for a given Validator node to make it unable to participate in the Validator Set. +However, this risk is already present at the ABCI application level, so the change +does not introduce new risks. +2. Missing node ID can be determined based on a public key, retrieved without proper validation. It cannot be guaranteed that it will not be spoofed. + + That can allow an attacker who controls the network connectivity to perform a man-in-the-middle attack. As the attacker does not have the Validator's private keys, he/she cannot perform unauthorized operations. However, it is possible to intercept communication and isolate the attacked Validator from the quorum. In the future, additional validation of the Validator's private key based on proTxHash can help mitigate this risk. + + If the attacker controls the network connectivity, he can still drop that traffic on the network level. + + + +### Are there any privacy considerations? + +This change does not impact user privacy. + +### How will the changes be tested? + +Testing strategy for this change involves: + +1. Reproduction of the problem statement. +2. Implementation of unit tests for the new code +3. Starting a long-living end-to-end cluster (at least a few days of operation) and monitoring of the outcome +4. Deployment to the test net + +### How will the changes be broken up for ease of review? + +This change consists of the following tasks: + +1. Enhancement of the ABCI protocol: + 1. Addition of new field on Tenderdash + 2. Update of end-to-end tests to reflect ABCI protocol changes + 3. Implementation of ABCI protocol changes inside the ABCI application +2. Tenderdash reactions on Validator Set rotation events: + 1. Storing and updating Validator Set members network addresses + 2. Establishing connections within Validator Sets + 3. Unit tests that emulate and validate Inter Validator Set implementation + +### Will these changes require a breaking (major) release? + +As the change introduces a new required field into the ABCI app, it requires implementation on the ABCI App side. +However, this feature shall be backward-compatible. + +### Does this change require coordination with the SDK or any other software? + +The change requires coordination with the ABCI application. + +### Future work + +1. Implement additional validation of the Validator's private key based on proTxHash. +2. Extend service data in Deterministic Masternode Lists (DIP-3) to keep node ID, p2p port, and other platform information. It will allow us to remove the handshake process described in "Security considerations" above. + +## Status + +> {Deprecated|Proposed|Accepted|Declined} + +Accepted + +## Consequences + +### Positive + +1. Improved stability of active Validator Set operations. +2. Quicker Voting rounds. + +### Negative + +1. Additional complexity of ABCI protocol +2. Additional changes compared to the upstream Tendermint project make backporting harder. + +### Neutral + +## References + +* [Dash Core Group Release Announcement: Dash Platform v0.20 on Testnet](https://blog.dash.org/dash-core-group-release-announcement-dash-platform-v0-20-on-testnet-c8fa00d28af7) +* [DIP 0006: Long Living Masternode Quorums (LLMQ)](https://github.com/dashpay/dips/blob/master/dip-0006.md) +* [DIP 0003: Deterministic Masternode Lists (DML)](https://github.com/dashpay/dips/blob/master/dip-0003.md) diff --git a/docs/tendermint-core/block-structure.md b/docs/tendermint-core/block-structure.md index 4563084a6e..63c4862e1f 100644 --- a/docs/tendermint-core/block-structure.md +++ b/docs/tendermint-core/block-structure.md @@ -14,3 +14,7 @@ exactly is stored in these blocks? The [specification](https://github.com/tendermint/spec/blob/8dd2ed4c6fe12459edeb9b783bdaaaeb590ec15c/spec/core/data_structures.md) contains a detailed description of each component - that's the best place to get started. To dig deeper, check out the [types package documentation](https://godoc.org/github.com/tendermint/tendermint/types). + +See also: + +* [state-id.md](state-id.md) diff --git a/docs/tendermint-core/state-id.md b/docs/tendermint-core/state-id.md new file mode 100644 index 0000000000..4069956302 --- /dev/null +++ b/docs/tendermint-core/state-id.md @@ -0,0 +1,62 @@ +# State ID + +## State ID definition + +`StateID` is a unique identifier of the most recent application state validated in consensus at the time of block generation. + +## State ID rationale + +In Dash Platform documents are returned to clients that request them. The validity of these documents is verified by checking that the root hash of the document tree is signed by the one of the platform quorums which are used as Tenderdash validator sets. + +Before the introduction of the `StateID` verification that the last app hash was properly signed would have required passing all the components that are used in the blockID generation to light clients. StateID should be considered a subset of the BlockID which allows for small proofs for Dash Platform clients. + +## State ID generation + +Let's define the following: + +* `State(height=N)` - State where `LastBlockHeight == N` +* `StateID(height=N)` - State ID generated for `State(height=N)` +* `Block(height=N)` - Block at `height == N` +* `Vote(height=B, stateID=S)` - Vote for `Block(height=B)`, with state ID generated from `State(height=S)` +* `Commit(height=B, stateID=S)` - Commit for `Block(height=B)`, with state ID generated from `State(height=S)` + +StateID logic works as follows: + +1. System is in `State(height=N-1)` +2. New block `Block(height=N)` is proposed. +3. Validators vote for `Block(height=N)` by creating `Vote(height=N, stateID=N-1)`; note that this vote contains state ID for `State(height=N-1)`, as this is the most recent confirmed state +4. Block is accepted and `Commit(height=N, stateID=N-1)` is generated +5. System state is updated to `State(height=N)` +6. New block `Block(height=N+1)` is proposed that contains last commit `Commit(height=N, stateID=N-1)` + + +```mermaid +graph TD + subgraph "StateID logic diagram" + + State["State(height=N-1)"] -->|"2. Proposal"| Block["Block(height=N)"] + Block -->|"3. Vote for block"| Vote["Vote (height=N, stateID=N-1)"] + Vote -->|"4. Block accepted"| Commit["Commit (height=N, stateID=N-1)"] + Commit -->|"5. Update system state"| NewState["State (height=N)"] + NewState -->|"6. New block proposal"| NewBlock["Block (height=N+1, stateID=N)
+ with LastCommit=Commit (height=N, stateID=N-1)"] + end +``` + +As a result: + +* verification of a vote for the block at height `N` requires information about the AppHash returned from ABCI App in response to a commit for the height `N-1`, +* block at height `N` contains the commit referring to state ID at height `N-2`. + +## Commit StateID verification + +Commit for a block at height `N` is a part of block `N+1`. This commit requires AppHash generated after the commit of +block `N-2`. To verify StateID, blocks `N` and `N+1` are needed. + +To verify StateID from a commit at height `N` : + +1. Fetch block `Block(Height=N+1)` which contains the commit to verify +2. Fetch block `Block(Height=N)` which contains AppHash needed for the verification +3. Ensure `Block(Height=N+1).LastCommit.StateID.Height == N-1` +4. Ensure `Block(Height=N+1).LastCommit.StateID.LastAppHash == Block(Height=N).Header.AppHash` +5. Ensure `Block(Height=N+1).LastCommit.StateSignature` is correct diff --git a/evidence/pool_test.go b/evidence/pool_test.go index aa1f12cf3f..a600dd0e94 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -88,7 +88,7 @@ func TestEvidencePoolSingleValidator(t *testing.T) { next := pool.EvidenceFront() assert.Equal(t, ev, next.Value.(types.Evidence)) - const evidenceBytes int64 = 712 // bls is 64 more than edwards + const evidenceBytes int64 = 640 evs, size = pool.PendingEvidence(evidenceBytes) assert.Equal(t, 1, len(evs)) assert.Equal(t, evidenceBytes, size) // check that the size of the single evidence in bytes is correct, bls is 64 more than edwards @@ -147,7 +147,7 @@ func TestEvidencePoolQuorum(t *testing.T) { next := pool.EvidenceFront() assert.Equal(t, ev, next.Value.(types.Evidence)) - const evidenceBytes int64 = 713 // bls is 64 more than edwards + const evidenceBytes int64 = 641 evs, size = pool.PendingEvidence(evidenceBytes) assert.Equal(t, 1, len(evs)) assert.Equal(t, evidenceBytes, size) // check that the size of the single evidence in bytes is correct, bls is 64 more than edwards @@ -447,7 +447,7 @@ func initializeBlockStore(db dbm.DB, state sm.State, valProTxHash []byte) *store } func makeCommit(height int64, quorumHash []byte, valProTxHash []byte) *types.Commit { - return types.NewCommit(height, 0, types.BlockID{}, types.StateID{}, quorumHash, crypto.CRandBytes(types.SignatureSize), crypto.CRandBytes(types.SignatureSize)) + return types.NewCommit(height, 0, types.BlockID{}, types.StateID{Height: height - 1}, quorumHash, crypto.CRandBytes(types.SignatureSize), crypto.CRandBytes(types.SignatureSize)) } func defaultTestPool(height int64) (*evidence.Pool, *types.MockPV) { diff --git a/evidence/reactor_test.go b/evidence/reactor_test.go index 236c9800c3..4326a08be2 100644 --- a/evidence/reactor_test.go +++ b/evidence/reactor_test.go @@ -12,7 +12,7 @@ import ( "github.com/tendermint/tendermint/crypto/bls12381" "github.com/fortytw2/leaktest" - "github.com/go-kit/kit/log/term" + "github.com/go-kit/log/term" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -382,9 +382,6 @@ func exampleVote(t byte) *types.Vote { Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")), }, }, - StateID: types.StateID{ - LastAppHash: tmhash.Sum([]byte("stateID_hash")), - }, ValidatorProTxHash: crypto.ProTxHashFromSeedBytes([]byte("validator_pro_tx_hash")), ValidatorIndex: 56789, } @@ -413,7 +410,7 @@ func TestEvidenceVectors(t *testing.T) { evidenceList []types.Evidence expBytes string }{ - {"DuplicateVoteEvidence", []types.Evidence{dupl}, "0acd020aca020a9c01080210031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a3220959a8f5ef2be68d0ed3a07ed8cff85991ee7995c2ac17030f742c135f9729fbe38d5bb034a220a2062b1d24a04df3db8c9735668e2fc0f9dad612cef4fed678fe07e67388ffd99c6129c01080110031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a3220959a8f5ef2be68d0ed3a07ed8cff85991ee7995c2ac17030f742c135f9729fbe38d5bb034a220a2062b1d24a04df3db8c9735668e2fc0f9dad612cef4fed678fe07e67388ffd99c6186420642a060880dbaae105"}, + {"DuplicateVoteEvidence", []types.Evidence{dupl}, "0a83020a80020a78080210031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a3220959a8f5ef2be68d0ed3a07ed8cff85991ee7995c2ac17030f742c135f9729fbe38d5bb031278080110031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a3220959a8f5ef2be68d0ed3a07ed8cff85991ee7995c2ac17030f742c135f9729fbe38d5bb03186420642a060880dbaae105"}, } for _, tc := range testCases { diff --git a/evidence/verify_test.go b/evidence/verify_test.go index ab97fc2336..5a4bf930dd 100644 --- a/evidence/verify_test.go +++ b/evidence/verify_test.go @@ -28,6 +28,8 @@ type voteData struct { } func TestVerifyDuplicateVoteEvidence(t *testing.T) { + const height int64 = 10 + quorumHash := crypto.RandQuorumHash() val := types.NewMockPVForQuorum(quorumHash) val2 := types.NewMockPVForQuorum(quorumHash) @@ -42,17 +44,17 @@ func TestVerifyDuplicateVoteEvidence(t *testing.T) { blockID3 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash")) blockID4 := makeBlockID([]byte("blockhash"), 10000, []byte("partshash2")) - stateID := makeStateID([]byte("lastapphash")) + stateID := types.RandStateID().WithHeight(height - 1) const chainID = "mychain" - vote1 := makeVote(t, val, chainID, quorumType, quorumHash, 0, 10, 2, 1, blockID, stateID) + vote1 := makeVote(t, val, chainID, quorumType, quorumHash, 0, height, 2, 1, blockID, stateID) v1 := vote1.ToProto() - err = val.SignVote(chainID, quorumType, quorumHash, v1, nil) + err = val.SignVote(chainID, quorumType, quorumHash, v1, stateID, nil) require.NoError(t, err) - badVote := makeVote(t, val, chainID, quorumType, quorumHash, 0, 10, 2, 1, blockID, stateID) + badVote := makeVote(t, val, chainID, quorumType, quorumHash, 0, height, 2, 1, blockID, stateID) bv := badVote.ToProto() - err = val2.SignVote(chainID, quorumType, quorumHash, bv, nil) + err = val2.SignVote(chainID, quorumType, quorumHash, bv, stateID, nil) require.NoError(t, err) vote1.BlockSignature = v1.BlockSignature @@ -60,21 +62,21 @@ func TestVerifyDuplicateVoteEvidence(t *testing.T) { cases := []voteData{ // different block ids - {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, 10, 2, 1, blockID2, stateID), true}, - {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, 10, 2, 1, blockID3, stateID), true}, - {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, 10, 2, 1, blockID4, stateID), true}, + {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, height, 2, 1, blockID2, stateID), true}, + {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, height, 2, 1, blockID3, stateID), true}, + {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, height, 2, 1, blockID4, stateID), true}, // wrong block id - {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, 10, 2, 1, blockID, stateID), false}, + {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, height, 2, 1, blockID, stateID), false}, // wrong chain id - {vote1, makeVote(t, val, "mychain2", quorumType, quorumHash, 0, 10, 2, 1, blockID2, stateID), false}, + {vote1, makeVote(t, val, "mychain2", quorumType, quorumHash, 0, height, 2, 1, blockID2, stateID), false}, // wrong height - {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, 11, 2, 1, blockID2, stateID), false}, + {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, height+1, 2, 1, blockID2, stateID), false}, // wrong round - {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, 10, 3, 1, blockID2, stateID), false}, + {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, height, 3, 1, blockID2, stateID), false}, // wrong step - {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, 10, 2, 2, blockID2, stateID), false}, + {vote1, makeVote(t, val, chainID, quorumType, quorumHash, 0, height, 2, 2, blockID2, stateID), false}, // wrong validator - {vote1, makeVote(t, val2, chainID, quorumType, quorumHash, 0, 10, 2, 1, blockID2, stateID), false}, + {vote1, makeVote(t, val2, chainID, quorumType, quorumHash, 0, height, 2, 1, blockID2, stateID), false}, // signed by wrong key {vote1, badVote, false}, } @@ -97,31 +99,31 @@ func TestVerifyDuplicateVoteEvidence(t *testing.T) { // create good evidence and correct validator power goodEv := types.NewMockDuplicateVoteEvidenceWithValidator( - 10, defaultEvidenceTime, val, chainID, quorumType, quorumHash, + height, defaultEvidenceTime, val, chainID, quorumType, quorumHash, ) goodEv.ValidatorPower = types.DefaultDashVotingPower goodEv.TotalVotingPower = types.DefaultDashVotingPower badEv := types.NewMockDuplicateVoteEvidenceWithValidator( - 10, defaultEvidenceTime, val, chainID, quorumType, quorumHash, + height, defaultEvidenceTime, val, chainID, quorumType, quorumHash, ) badEv.ValidatorPower = types.DefaultDashVotingPower + 1 badEv.TotalVotingPower = types.DefaultDashVotingPower badTimeEv := types.NewMockDuplicateVoteEvidenceWithValidator( - 10, defaultEvidenceTime.Add(1*time.Minute), val, chainID, quorumType, quorumHash, + height, defaultEvidenceTime.Add(1*time.Minute), val, chainID, quorumType, quorumHash, ) badTimeEv.ValidatorPower = types.DefaultDashVotingPower badTimeEv.TotalVotingPower = types.DefaultDashVotingPower state := sm.State{ ChainID: chainID, LastBlockTime: defaultEvidenceTime.Add(1 * time.Minute), - LastBlockHeight: 11, + LastBlockHeight: height + 1, ConsensusParams: *types.DefaultConsensusParams(), } stateStore := &smmocks.Store{} - stateStore.On("LoadValidators", int64(10)).Return(valSet, nil) + stateStore.On("LoadValidators", height).Return(valSet, nil) stateStore.On("Load").Return(state, nil) blockStore := &mocks.BlockStore{} - blockStore.On("LoadBlockMeta", int64(10)).Return( + blockStore.On("LoadBlockMeta", height).Return( &types.BlockMeta{Header: types.Header{Time: defaultEvidenceTime}}, ) @@ -165,11 +167,10 @@ func makeVote( Round: round, Type: tmproto.SignedMsgType(step), BlockID: blockID, - StateID: stateID, } vpb := v.ToProto() - err = val.SignVote(chainID, quorumType, quorumHash, vpb, nil) + err = val.SignVote(chainID, quorumType, quorumHash, vpb, stateID, nil) if err != nil { panic(err) } @@ -213,13 +214,3 @@ func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.Bloc }, } } - -func makeStateID(lastAppHash []byte) types.StateID { - var ( - ah = make([]byte, tmhash.Size) - ) - copy(ah, lastAppHash) - return types.StateID{ - LastAppHash: ah, - } -} diff --git a/go.mod b/go.mod index 6ab70a0ca9..7b9338440e 100644 --- a/go.mod +++ b/go.mod @@ -3,38 +3,50 @@ module github.com/tendermint/tendermint go 1.15 require ( - github.com/BurntSushi/toml v0.3.1 - github.com/Workiva/go-datastructures v1.0.52 - github.com/btcsuite/btcd v0.21.0-beta - github.com/btcsuite/btcutil v1.0.2 + github.com/BurntSushi/toml v1.0.0 + github.com/Workiva/go-datastructures v1.0.53 + github.com/adlio/schema v1.2.3 + github.com/btcsuite/btcd v0.22.0-beta + github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce github.com/dashevo/dashd-go v0.0.0-20210630125816-b417ad8eb165 github.com/dashpay/bls-signatures/go-bindings v0.0.0-20201127091120-745324b80143 + github.com/dgraph-io/badger/v2 v2.2007.3 // indirect + github.com/didip/tollbooth/v6 v6.1.1 // indirect + github.com/didip/tollbooth_chi v0.0.0-20200828173446-a7173453ea21 // indirect github.com/fortytw2/leaktest v1.3.0 - github.com/go-kit/kit v0.10.0 - github.com/go-logfmt/logfmt v0.5.0 + github.com/go-chi/chi v4.1.2+incompatible // indirect + github.com/go-kit/kit v0.12.0 + github.com/go-kit/log v0.2.0 + github.com/go-logfmt/logfmt v0.5.1 github.com/go-pkgz/jrpc v0.2.0 + github.com/go-pkgz/rest v1.11.0 // indirect github.com/gogo/protobuf v1.3.2 - github.com/golang/protobuf v1.4.3 + github.com/golang/protobuf v1.5.2 + github.com/google/btree v1.0.1 // indirect github.com/google/orderedcode v0.0.1 github.com/gorilla/websocket v1.4.2 github.com/gtank/merlin v0.1.1 + github.com/lib/pq v1.10.4 github.com/libp2p/go-buffer-pool v0.0.2 - github.com/minio/highwayhash v1.0.1 + github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect + github.com/minio/highwayhash v1.0.2 + github.com/ory/dockertest v3.3.5+incompatible github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.8.0 - github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 - github.com/rs/cors v1.7.0 - github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa + github.com/prometheus/client_golang v1.12.0 + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 + github.com/rs/cors v1.8.2 + github.com/sasha-s/go-deadlock v0.3.1 github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa - github.com/spf13/cobra v1.1.1 - github.com/spf13/viper v1.7.1 + github.com/spf13/cobra v1.2.1 + github.com/spf13/viper v1.9.0 + github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/testify v1.7.0 github.com/tendermint/tm-db v0.6.4 - golang.org/x/crypto v0.0.0-20210812204632-0ba0e8f03122 - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 - golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect - google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4 // indirect - google.golang.org/grpc v1.37.0 + go.etcd.io/bbolt v1.3.6 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 + golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b + google.golang.org/genproto v0.0.0-20210929214142-896c89f843d2 // indirect + google.golang.org/grpc v1.41.0 ) replace github.com/tendermint/tendermint => ./ diff --git a/go.sum b/go.sum index aa849a6cc0..60059234de 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -5,57 +6,108 @@ cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6A cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= +github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= -github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= +github.com/Workiva/go-datastructures v1.0.53 h1:J6Y/52yX10Xc5JjXmGtWoSSxs3mZnGSaq37xZZh7Yig= +github.com/Workiva/go-datastructures v1.0.53/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/adlio/schema v1.2.3 h1:GfKThfEsjS9cCz7gaF8zdXv4cpTdUqdljkKGDTbJjys= +github.com/adlio/schema v1.2.3/go.mod h1:nD7ZWmMMbwU12Pqwg+qL0rTvHBrBXfNz+5UQxTfy38M= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= +github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= +github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M= -github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= +github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= +github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= @@ -65,32 +117,46 @@ github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/continuity v0.2.1 h1:/EeEo2EtN3umhbbgCveyjifoMYg0pS+nMMEemaYw634= +github.com/containerd/continuity v0.2.1/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 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/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= github.com/dashevo/dashd-go v0.0.0-20210630125816-b417ad8eb165 h1:l3s0vn+21RstJba8XUdyBYJEZcYPRQ5fLqJ42wndz20= github.com/dashevo/dashd-go v0.0.0-20210630125816-b417ad8eb165/go.mod h1:gGsiaC3jhyhau6+/LoMCN1LjP2rrPfU4dhPVZ5t030E= github.com/dashpay/bls-signatures/go-bindings v0.0.0-20201127091120-745324b80143 h1:Q13WvpP9zZTmQy7O3q2tCXHXOB+dM0nnvPu7TuSieQQ= @@ -100,30 +166,39 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +github.com/dgraph-io/badger/v2 v2.2007.3 h1:Sl9tQWz92WCbVSe8pj04Tkqlm2boW+KAxd+XSs58SQI= +github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/didip/tollbooth/v6 v6.0.1 h1:QvLvRpB1G2bzKvkRze0muMUBlGN9H1z7tJ4DH4ypWOU= github.com/didip/tollbooth/v6 v6.0.1/go.mod h1:j2pKs+JQ5PvU/K4jFnrnwntrmfUbYLJE5oSdxR37FD0= -github.com/didip/tollbooth_chi v0.0.0-20200524181329-8b84cd7183d9 h1:gTh8fKuI/yLqQtZEPlDX3ZGsiTPZIe0ADHsxXSbwO1I= +github.com/didip/tollbooth/v6 v6.1.1 h1:Nt7PvWLa9Y94OrykXsFNBinVRQIu8xdy4avpl99Dc1M= +github.com/didip/tollbooth/v6 v6.1.1/go.mod h1:xjcse6CTHCLuOkzsWrEgdy9WPJFv+p/x6v+MyfP+O9s= github.com/didip/tollbooth_chi v0.0.0-20200524181329-8b84cd7183d9/go.mod h1:YWyIfq3y4ArRfWZ9XksmuusP+7Mad+T0iFZ0kv0XG/M= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/didip/tollbooth_chi v0.0.0-20200828173446-a7173453ea21 h1:x7YpwKSBIBcKe9I3aTNOqgSyJ6QKDdtOxnEkxBTsi9w= +github.com/didip/tollbooth_chi v0.0.0-20200828173446-a7173453ea21/go.mod h1:0ZVa6kSzS011nfTC1rELyxK4tjVf6vqBnOv7oY2KlsA= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ= github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= @@ -132,52 +207,79 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi v4.1.1+incompatible h1:MmTgB0R8Bt/jccxp+t6S/1VGIKdJw5J74CK/c9tTfA4= github.com/go-chi/chi v4.1.1+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= +github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6dksJLXQDc= github.com/go-pkgz/expirable-cache v0.0.3/go.mod h1:+IauqN00R2FqNRLCLA+X5YljQJrwB179PfiAoMPlTlQ= github.com/go-pkgz/jrpc v0.2.0 h1:CLy/eZyekjraVrxZV18N2R1mYLMJ/nWrgdfyIOGPY/E= github.com/go-pkgz/jrpc v0.2.0/go.mod h1:wd8vtQ4CgtCnuqua6x2b1SKIgv0VSOh5Dn0uUITbiUE= -github.com/go-pkgz/rest v1.5.0 h1:C8SxXcXza4GiUUAn/95iCkvoIrGbS30qpwK19iqlrWQ= github.com/go-pkgz/rest v1.5.0/go.mod h1:nQaM3RhSTUAmbBZWY4hfe4buyeC9VckvhoCktiQXJxI= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-pkgz/rest v1.11.0 h1:Z//qgmM0NhBYfhXYEP/aJtDVLK5XlJGxqcb4sHFNN0E= +github.com/go-pkgz/rest v1.11.0/go.mod h1:wZ/dGipZUaF9to0vIQl7PwDHgWQDB0jsrFg1xnAKLDw= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -185,241 +287,316 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us= github.com/google/orderedcode v0.0.1/go.mod h1:iVyU4/qPKHY5h/wSd6rZZCDcLJNxiWO6dvsYES2Sb20= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= +github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0= +github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= +github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= +github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= +github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= +github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.2 h1:HFB2fbVIlhIfCfOW81bZFbiC/RvnpXSdhbF2/DJr134= +github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= +github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k= +github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= +github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ= github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw= -github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= +github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= -github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= +github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa h1:0U2s5loxrTy6/VgfVoLuVLFJcURKLH49ie0zSch7gh4= -github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= +github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= +github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= +github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa h1:YJfZp12Z3AFhSBeXOlv4BO55RMwPn2NoQeDsrdWnBtY= github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= @@ -428,71 +605,94 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA= +github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk= +github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= +github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca h1:Ld/zXl5t4+D69SiV4JoN7kkfvJdOWlPpfxrzxpLMoUk= github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tendermint/tm-db v0.6.4 h1:3N2jlnYQkXNQclQwd/eKV/NzlqPlfK21cpRRIx80XXQ= github.com/tendermint/tm-db v0.6.4/go.mod h1:dptYhIpJ2M5kUuenLr+Yyf3zQOv1SgBZcl8/BmWlMBw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +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= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -500,18 +700,33 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210812204632-0ba0e8f03122 h1:AOT7vJYHE32m61R8d1WlcqhOO1AocesDsKpcMq+UOaA= -golang.org/x/crypto v0.0.0-20210812204632-0ba0e8f03122/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -521,13 +736,22 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -537,35 +761,77 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b h1:MWaHNqZy3KTpuTMAGvv+Kw+ylsEpmyJZizz1dqxnu28= +golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -573,54 +839,106 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -633,59 +951,178 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4 h1:Rt0FRalMgdSlXAVJvX4pr65KfqaxHXSLkSJRD9pw6g0= -google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210929214142-896c89f843d2 h1:G7kbolbig6UyfoRItVUeBGV/38VzxjAZYTavVGbYYss= +google.golang.org/genproto v0.0.0-20210929214142-896c89f843d2/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +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= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -695,19 +1132,23 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c= +gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -715,19 +1156,27 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/libs/log/logger.go b/libs/log/logger.go index 0b81bd7938..289f0c90b0 100644 --- a/libs/log/logger.go +++ b/libs/log/logger.go @@ -3,7 +3,7 @@ package log import ( "io" - kitlog "github.com/go-kit/kit/log" + kitlog "github.com/go-kit/log" ) // Logger is what any Tendermint library should take. diff --git a/libs/log/testing_logger.go b/libs/log/testing_logger.go index 690b07a3c5..ea23fb9922 100644 --- a/libs/log/testing_logger.go +++ b/libs/log/testing_logger.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "github.com/go-kit/kit/log/term" + "github.com/go-kit/log/term" ) var ( diff --git a/libs/log/tm_json_logger.go b/libs/log/tm_json_logger.go index 3ebeddaa31..786b618da8 100644 --- a/libs/log/tm_json_logger.go +++ b/libs/log/tm_json_logger.go @@ -3,7 +3,7 @@ package log import ( "io" - kitlog "github.com/go-kit/kit/log" + kitlog "github.com/go-kit/log" ) // NewTMJSONLogger returns a Logger that encodes keyvals to the Writer as a diff --git a/libs/log/tm_logger.go b/libs/log/tm_logger.go index 50db809cdb..4fafedc810 100644 --- a/libs/log/tm_logger.go +++ b/libs/log/tm_logger.go @@ -4,9 +4,9 @@ import ( "fmt" "io" - kitlog "github.com/go-kit/kit/log" - kitlevel "github.com/go-kit/kit/log/level" - "github.com/go-kit/kit/log/term" + kitlog "github.com/go-kit/log" + kitlevel "github.com/go-kit/log/level" + "github.com/go-kit/log/term" ) const ( diff --git a/libs/log/tmfmt_logger.go b/libs/log/tmfmt_logger.go index c6004cc62c..01cf721926 100644 --- a/libs/log/tmfmt_logger.go +++ b/libs/log/tmfmt_logger.go @@ -9,8 +9,8 @@ import ( "sync" "time" - kitlog "github.com/go-kit/kit/log" - kitlevel "github.com/go-kit/kit/log/level" + kitlog "github.com/go-kit/log" + kitlevel "github.com/go-kit/log/level" "github.com/go-logfmt/logfmt" ) diff --git a/libs/log/tmfmt_logger_test.go b/libs/log/tmfmt_logger_test.go index 955a9e4058..4b82455f13 100644 --- a/libs/log/tmfmt_logger_test.go +++ b/libs/log/tmfmt_logger_test.go @@ -8,7 +8,7 @@ import ( "regexp" "testing" - kitlog "github.com/go-kit/kit/log" + kitlog "github.com/go-kit/log" "github.com/stretchr/testify/assert" "github.com/tendermint/tendermint/libs/log" diff --git a/libs/sync/sync.go b/libs/sync/sync.go index a0880e7de9..c6e7101c60 100644 --- a/libs/sync/sync.go +++ b/libs/sync/sync.go @@ -1,3 +1,4 @@ +//go:build !deadlock // +build !deadlock package sync diff --git a/light/client.go b/light/client.go index 8d53ad9bbf..3608882d19 100644 --- a/light/client.go +++ b/light/client.go @@ -229,7 +229,7 @@ func NewClientFromTrustedStore( } // Validate the number of witnesses. - if len(c.witnesses) < 1 { + if len(c.witnesses) == 0 { return nil, ErrNoWitnesses } @@ -513,24 +513,33 @@ func (c *Client) verifyLightBlock(ctx context.Context, newLightBlock *types.Ligh return c.updateTrustedLightBlock(newLightBlock) } +// verifyBlockWithDashCore verifies block and StateID signature using Dash Core RPC service // This method is called from verifyLightBlock if verification mode is dashcore, // verifyLightBlock in its turn is called by VerifyHeader. func (c *Client) verifyBlockWithDashCore(ctx context.Context, newLightBlock *types.LightBlock) error { + if err := c.verifyBlockSignatureWithDashCore(ctx, newLightBlock); err != nil { + return err + } + + if err := c.verifyStateIDSignatureWithDashCore(ctx, newLightBlock); err != nil { + return err + } + + return nil +} + +func (c *Client) verifyBlockSignatureWithDashCore(ctx context.Context, newLightBlock *types.LightBlock) error { + quorumHash := newLightBlock.ValidatorSet.QuorumHash quorumType := newLightBlock.ValidatorSet.QuorumType protoVote := newLightBlock.Commit.GetCanonicalVote().ToProto() blockSignBytes := types.VoteBlockSignBytes(c.chainID, protoVote) - stateSignBytes := types.VoteStateSignBytes(c.chainID, protoVote) blockMessageHash := crypto.Sha256(blockSignBytes) blockRequestID := types.VoteBlockRequestIDProto(protoVote) - stateMessageHash := crypto.Sha256(stateSignBytes) - stateRequestID := types.VoteStateRequestIDProto(protoVote) - stateSignature := newLightBlock.Commit.ThresholdStateSignature - blockSignatureIsValid, err := c.dashCoreRPCClient.QuorumVerify( quorumType, blockRequestID, @@ -547,6 +556,23 @@ func (c *Client) verifyBlockWithDashCore(ctx context.Context, newLightBlock *typ return fmt.Errorf("block signature is invalid") } + return nil +} + +// This method is called from verifyLightBlock if verification mode is dashcore, +// verifyLightBlock in its turn is called by VerifyHeader. +func (c *Client) verifyStateIDSignatureWithDashCore(ctx context.Context, newLightBlock *types.LightBlock) error { + quorumHash := newLightBlock.ValidatorSet.QuorumHash + quorumType := newLightBlock.ValidatorSet.QuorumType + + stateID := newLightBlock.StateID() + + stateSignBytes := stateID.SignBytes(c.chainID) + + stateMessageHash := crypto.Sha256(stateSignBytes) + stateRequestID := stateID.SignRequestID() + stateSignature := newLightBlock.Commit.ThresholdStateSignature + stateSignatureIsValid, err := c.dashCoreRPCClient.QuorumVerify( quorumType, stateRequestID, @@ -680,6 +706,7 @@ func (c *Client) lightBlockFromPrimary(ctx context.Context) (*types.LightBlock, return c.lightBlockFromPrimaryAtHeight(ctx, 0) } +// lightBlockFromPrimaryAtHeight retrieves a light block from the primary provider func (c *Client) lightBlockFromPrimaryAtHeight(ctx context.Context, height int64) (*types.LightBlock, error) { c.providerMutex.Lock() l, err := c.primary.LightBlock(ctx, height) @@ -700,17 +727,21 @@ func (c *Client) lightBlockFromPrimaryAtHeight(ctx context.Context, height int64 err = nil } + case context.Canceled, context.DeadlineExceeded: + return l, err + case provider.ErrNoResponse, provider.ErrLightBlockNotFound, provider.ErrHeightTooHigh: // we find a new witness to replace the primary - c.logger.Debug("error from light block request from primary, replacing...", - "error", err) - l, err = c.findNewPrimary(ctx, height, false) + c.logger.Info("error from light block request from primary, replacing...", + "error", err, "height", height, "primary", c.primary) + return c.findNewPrimary(ctx, height, false) + default: // The light client has most likely received either provider.ErrUnreliableProvider or provider.ErrBadLightBlock // These errors mean that the light client should drop the primary and try with another provider instead - c.logger.Error("error from light block request from primary, removing...", - "error", err) - l, err = c.findNewPrimary(ctx, height, true) + c.logger.Info("error from light block request from primary, removing...", + "error", err, "height", height, "primary", c.primary) + return c.findNewPrimary(ctx, height, true) } // err was re-evaluated inside switch statement above @@ -757,7 +788,7 @@ func (c *Client) findNewPrimary(ctx context.Context, height int64, remove bool) c.providerMutex.Lock() defer c.providerMutex.Unlock() - if len(c.witnesses) <= 1 { + if len(c.witnesses) == 0 { return nil, ErrNoWitnesses } @@ -827,6 +858,11 @@ func (c *Client) findNewPrimary(ctx context.Context, height int64, remove bool) } } + // remove witnesses marked as bad. Removal is done in descending order + if err := c.removeWitnesses(witnessesToRemove); err != nil { + c.logger.Error("failed to remove witnesses", "err", err, "witnessesToRemove", witnessesToRemove) + } + return nil, lastError } @@ -839,7 +875,7 @@ func (c *Client) compareFirstHeaderWithWitnesses(ctx context.Context, h *types.S c.providerMutex.Lock() defer c.providerMutex.Unlock() - if len(c.witnesses) < 1 { + if len(c.witnesses) == 0 { return ErrNoWitnesses } @@ -863,21 +899,26 @@ func (c *Client) compareFirstHeaderWithWitnesses(ctx context.Context, h *types.S c.witnesses[e.WitnessIndex]) return err case errBadWitness: - // If witness sent us an invalid header, then remove it. If it didn't - // respond or couldn't find the block, then we ignore it and move on to - // the next witness. - if _, ok := e.Reason.(provider.ErrBadLightBlock); ok { - c.logger.Info("Witness sent us invalid header / vals -> removing it", - "witness", c.witnesses[e.WitnessIndex], "err", err) - witnessesToRemove = append(witnessesToRemove, e.WitnessIndex) + // If witness sent us an invalid header, then remove it + c.logger.Info("witness sent an invalid light block, removing...", + "witness", c.witnesses[e.WitnessIndex], + "err", err) + witnessesToRemove = append(witnessesToRemove, e.WitnessIndex) + default: // benign errors can be ignored with the exception of context errors + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + return err } + + // the witness either didn't respond or didn't have the block. We ignore it. + c.logger.Info("error comparing first header with witness. You may want to consider removing the witness", + "err", err) } } // remove witnesses that have misbehaved if err := c.removeWitnesses(witnessesToRemove); err != nil { - return err + c.logger.Error("failed to remove witnesses", "err", err, "witnessesToRemove", witnessesToRemove) } return nil diff --git a/light/client_benchmark_test.go b/light/client_benchmark_test.go index 42ee868e86..5037cd23b5 100644 --- a/light/client_benchmark_test.go +++ b/light/client_benchmark_test.go @@ -23,11 +23,22 @@ import ( // // Remember that none of these benchmarks account for network latency. var ( - blocks = int64(1000) - benchmarkFullNode = mockp.New(genMockNode(chainID, blocks, 20, bTime)) + blocks = int64(1000) + benchmarkNode *mockp.Mock = nil // lazy-load with getBenchmarkFullNode() ) +func getBenchmarkFullNode() *mockp.Mock { + if benchmarkNode == nil { + // TODO: Investigate why the line blow takes so much time + benchmarkNode = mockp.New(genMockNode(chainID, blocks, 20, bTime)) + } + + return benchmarkNode +} + func setupDashCoreRPCMockForBenchmark(b *testing.B) { + benchmarkFullNode := getBenchmarkFullNode() + dashCoreMockClient = dashcore.NewMockClient(chainID, 100, benchmarkFullNode.MockPV, false) b.Cleanup(func() { @@ -36,6 +47,7 @@ func setupDashCoreRPCMockForBenchmark(b *testing.B) { } func BenchmarkSequence(b *testing.B) { + benchmarkFullNode := getBenchmarkFullNode() setupDashCoreRPCMockForBenchmark(b) c, err := light.NewClient( @@ -62,6 +74,7 @@ func BenchmarkSequence(b *testing.B) { func BenchmarkBisection(b *testing.B) { setupDashCoreRPCMockForBenchmark(b) + benchmarkFullNode := getBenchmarkFullNode() c, err := light.NewClient( context.Background(), @@ -87,6 +100,8 @@ func BenchmarkBisection(b *testing.B) { func BenchmarkBackwards(b *testing.B) { setupDashCoreRPCMockForBenchmark(b) + benchmarkFullNode := getBenchmarkFullNode() + _, err := benchmarkFullNode.LightBlock(context.Background(), 0) if err != nil { b.Fatal(err) diff --git a/light/client_test.go b/light/client_test.go index ca45806086..cb9e3d2ec1 100644 --- a/light/client_test.go +++ b/light/client_test.go @@ -2,24 +2,23 @@ package light_test import ( "context" + "errors" + "fmt" "sync" "testing" "time" - dashcore "github.com/tendermint/tendermint/dashcore/rpc" - "github.com/tendermint/tendermint/light" - "github.com/tendermint/tendermint/light/store" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - dbm "github.com/tendermint/tm-db" - + dashcore "github.com/tendermint/tendermint/dashcore/rpc" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/light" "github.com/tendermint/tendermint/light/provider" mockp "github.com/tendermint/tendermint/light/provider/mock" + "github.com/tendermint/tendermint/light/store" dbs "github.com/tendermint/tendermint/light/store/db" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) const ( @@ -319,7 +318,7 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) { require.NoError(t, err) assert.NotEqual(t, c.Primary(), deadNode) - assert.Equal(t, 1, len(c.Witnesses())) + assert.Equal(t, 2, len(c.Witnesses())) } // func TestClient_BackwardsVerification(t *testing.T) { @@ -565,31 +564,81 @@ func TestClientEnsureValidHeadersAndValSets(t *testing.T) { }, } - for _, tc := range testCases { - setupTrustedStore(t) - badNode := mockp.New( - chainID, - tc.headers, - tc.vals, - privVals[0], - ) - c, err := light.NewClient( - ctx, - chainID, - badNode, - []provider.Provider{badNode, badNode}, - trustedStore, - dashCoreMockClient, - light.MaxRetryAttempts(1), - ) - require.NoError(t, err) - - _, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(2*time.Hour)) - if tc.err { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } + //nolint:scopelint + for tcID, tc := range testCases { + t.Run(fmt.Sprintf("tc_%d", tcID), func(t *testing.T) { + setupTrustedStore(t) + badNode := mockp.New( + chainID, + tc.headers, + tc.vals, + privVals[0], + ) + c, err := light.NewClient( + ctx, + chainID, + badNode, + []provider.Provider{badNode, badNode}, + trustedStore, + dashCoreMockClient, + light.MaxRetryAttempts(1), + ) + require.NoError(t, err) + + _, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(2*time.Hour)) + if tc.err { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) } +} + +func TestClientHandlesContexts(t *testing.T) { + p := mockp.New(genMockNode(chainID, 100, 10, bTime)) + dashCoreMockClient := dashcore.NewMockClient(chainID, llmqType, p.MockPV, true) + + // instantiate the light client with a timeout + ctxTimeOut, cancel := context.WithTimeout(ctx, 10*time.Millisecond) + defer cancel() + _, err := light.NewClient( + ctxTimeOut, + chainID, + p, + []provider.Provider{p, p}, + dbs.New(dbm.NewMemDB(), chainID), + dashCoreMockClient, + ) + require.Error(t, ctxTimeOut.Err()) + require.Error(t, err) + require.True(t, errors.Is(err, context.DeadlineExceeded)) + // instantiate the client for real + c, err := light.NewClient( + ctx, + chainID, + p, + []provider.Provider{p, p}, + dbs.New(dbm.NewMemDB(), chainID), + dashCoreMockClient, + ) + require.NoError(t, err) + + // verify a block with a timeout + ctxTimeOutBlock, cancel := context.WithTimeout(ctx, 10*time.Millisecond) + defer cancel() + _, err = c.VerifyLightBlockAtHeight(ctxTimeOutBlock, 101, bTime.Add(100*time.Minute)) + require.Error(t, ctxTimeOutBlock.Err()) + require.Error(t, err) + require.True(t, errors.Is(err, context.DeadlineExceeded)) + + // verify a block with a cancel + ctxCancel, cancel := context.WithCancel(ctx) + defer cancel() + time.AfterFunc(10*time.Millisecond, cancel) + _, err = c.VerifyLightBlockAtHeight(ctxCancel, 101, bTime.Add(100*time.Minute)) + require.Error(t, ctxCancel.Err()) + require.Error(t, err) + require.True(t, errors.Is(err, context.Canceled)) } diff --git a/light/detector.go b/light/detector.go index 0da8c035c9..cf1fdec6f1 100644 --- a/light/detector.go +++ b/light/detector.go @@ -3,6 +3,7 @@ package light import ( "bytes" "context" + "errors" "time" "github.com/tendermint/tendermint/light/provider" @@ -27,7 +28,7 @@ func (c *Client) compareNewHeaderWithWitness(ctx context.Context, errc chan erro // the witness hasn't been helpful in comparing headers, we mark the response and continue // comparing with the rest of the witnesses - case provider.ErrNoResponse, provider.ErrLightBlockNotFound: + case provider.ErrNoResponse, provider.ErrLightBlockNotFound, context.DeadlineExceeded, context.Canceled: errc <- err return @@ -40,7 +41,11 @@ func (c *Client) compareNewHeaderWithWitness(ctx context.Context, errc chan erro var isTargetHeight bool isTargetHeight, lightBlock, err = c.getTargetBlockOrLatest(ctx, h.Height, witness) if err != nil { - errc <- err + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + errc <- err + } else { + errc <- errBadWitness{Reason: err, WitnessIndex: witnessIndex} + } return } diff --git a/light/helpers_test.go b/light/helpers_test.go index d41d511b7e..98e7f23fcb 100644 --- a/light/helpers_test.go +++ b/light/helpers_test.go @@ -109,6 +109,7 @@ func (pkz privKeys) signHeader(header *types.Header, valSet *types.ValidatorSet, } stateID := types.StateID{ + Height: header.Height - 1, LastAppHash: header.AppHash, } @@ -152,19 +153,18 @@ func makeVote(header *types.Header, valset *types.ValidatorSet, proTxHash crypto Round: 1, Type: tmproto.PrecommitType, BlockID: blockID, - StateID: stateID, } v := vote.ToProto() - // SignDigest it + // SignDigest the vote signID := types.VoteBlockSignID(header.ChainID, v, valset.QuorumType, valset.QuorumHash) sig, err := key.SignDigest(signID) if err != nil { panic(err) } - // SignDigest it - stateSignID := types.VoteStateSignID(header.ChainID, v, valset.QuorumType, valset.QuorumHash) + // SignDigest the state + stateSignID := stateID.SignID(header.ChainID, valset.QuorumType, valset.QuorumHash) sigState, err := key.SignDigest(stateSignID) if err != nil { panic(err) diff --git a/light/provider/http/http.go b/light/provider/http/http.go index ba232c4a7b..ca393734a5 100644 --- a/light/provider/http/http.go +++ b/light/provider/http/http.go @@ -22,8 +22,9 @@ var ( // This is very brittle, see: https://github.com/tendermint/tendermint/issues/4740 regexpMissingHeight = regexp.MustCompile(`height \d+ is not available`) regexpTooHigh = regexp.MustCompile(`height \d+ must be less than or equal to`) + regexpTimedOut = regexp.MustCompile(`Timeout exceeded`) - maxRetryAttempts = 10 + maxRetryAttempts = 5 timeout uint = 5 // sec. ) @@ -137,51 +138,56 @@ func (p *http) validatorSet(ctx context.Context, height *int64) (*types.Validato total = -1 ) +OUTER_LOOP: for len(vals) != total && page <= maxPages { for attempt := 1; attempt <= maxRetryAttempts; attempt++ { requestThresholdPublicKey := attempt == 1 res, err := p.client.Validators(ctx, height, &page, &perPage, &requestThresholdPublicKey) - if err != nil { - // TODO: standardize errors on the RPC side - if regexpTooHigh.MatchString(err.Error()) { - return nil, provider.ErrHeightTooHigh + switch { + case err == nil: + // Validate response. + if len(res.Validators) == 0 { + return nil, provider.ErrBadLightBlock{ + Reason: fmt.Errorf("validator set is empty (height: %d, page: %d, per_page: %d)", + height, page, perPage), + } } - - if regexpMissingHeight.MatchString(err.Error()) { - return nil, provider.ErrLightBlockNotFound + if res.Total <= 0 { + return nil, provider.ErrBadLightBlock{ + Reason: fmt.Errorf("total number of vals is <= 0: %d (height: %d, page: %d, per_page: %d)", + res.Total, height, page, perPage), + } } - // if we have exceeded retry attempts then return no response error - if attempt == maxRetryAttempts { - return nil, provider.ErrNoResponse + + total = res.Total + vals = append(vals, res.Validators...) + if requestThresholdPublicKey { + thresholdPublicKey = *res.ThresholdPublicKey + quorumHash = *res.QuorumHash + quorumType = res.QuorumType } - // else we wait and try again with exponential backoff + page++ + continue OUTER_LOOP + + case regexpTooHigh.MatchString(err.Error()): + return nil, provider.ErrHeightTooHigh + + case regexpMissingHeight.MatchString(err.Error()): + return nil, provider.ErrLightBlockNotFound + + // if we have exceeded retry attempts then return no response error + case attempt == maxRetryAttempts: + return nil, provider.ErrNoResponse + + case regexpTimedOut.MatchString(err.Error()): + // we wait and try again with exponential backoff time.Sleep(backoffTimeout(uint16(attempt))) continue - } - - // Validate response. - if len(res.Validators) == 0 { - return nil, provider.ErrBadLightBlock{ - Reason: fmt.Errorf("validator set is empty (height: %d, page: %d, per_page: %d)", - height, page, perPage), - } - } - if res.Total <= 0 { - return nil, provider.ErrBadLightBlock{ - Reason: fmt.Errorf("total number of vals is <= 0: %d (height: %d, page: %d, per_page: %d)", - res.Total, height, page, perPage), - } - } - total = res.Total - vals = append(vals, res.Validators...) - if requestThresholdPublicKey { - thresholdPublicKey = *res.ThresholdPublicKey - quorumHash = *res.QuorumHash - quorumType = res.QuorumType + // context canceled or connection refused we return the error + default: + return nil, err } - page++ - break } } valSet, err := types.ValidatorSetFromExistingValidators(vals, thresholdPublicKey, quorumType, quorumHash) @@ -194,20 +200,29 @@ func (p *http) validatorSet(ctx context.Context, height *int64) (*types.Validato func (p *http) signedHeader(ctx context.Context, height *int64) (*types.SignedHeader, error) { for attempt := 1; attempt <= maxRetryAttempts; attempt++ { commit, err := p.client.Commit(ctx, height) - if err != nil { - // TODO: standardize errors on the RPC side - if regexpTooHigh.MatchString(err.Error()) { - return nil, provider.ErrHeightTooHigh + switch { + case err == nil: + if commit == nil { + time.Sleep(backoffTimeout(uint16(attempt))) + continue } + return &commit.SignedHeader, nil - if regexpMissingHeight.MatchString(err.Error()) { - return nil, provider.ErrLightBlockNotFound - } + case regexpTooHigh.MatchString(err.Error()): + return nil, provider.ErrHeightTooHigh + + case regexpMissingHeight.MatchString(err.Error()): + return nil, provider.ErrLightBlockNotFound + + case regexpTimedOut.MatchString(err.Error()): // we wait and try again with exponential backoff time.Sleep(backoffTimeout(uint16(attempt))) continue + + // either context was cancelled or connection refused. + default: + return nil, err } - return &commit.SignedHeader, nil } return nil, provider.ErrNoResponse } diff --git a/light/provider/http/http_test.go b/light/provider/http/http_test.go index 51860d3877..6abf92e953 100644 --- a/light/provider/http/http_test.go +++ b/light/provider/http/http_test.go @@ -5,18 +5,17 @@ import ( "fmt" "os" "testing" - - lighthttp "github.com/tendermint/tendermint/light/provider/http" + "time" "github.com/stretchr/testify/assert" + testify "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/abci/example/kvstore" "github.com/tendermint/tendermint/light/provider" + lighthttp "github.com/tendermint/tendermint/light/provider/http" rpcclient "github.com/tendermint/tendermint/rpc/client" - rpcmock "github.com/tendermint/tendermint/rpc/client/mocks" - - testify "github.com/stretchr/testify/mock" rpchttp "github.com/tendermint/tendermint/rpc/client/http" + rpcmock "github.com/tendermint/tendermint/rpc/client/mocks" coretypes "github.com/tendermint/tendermint/rpc/core/types" rpctest "github.com/tendermint/tendermint/rpc/test" "github.com/tendermint/tendermint/types" @@ -36,27 +35,17 @@ func TestNewProvider(t *testing.T) { require.Equal(t, fmt.Sprintf("%s", c), "http{http://153.200.0.1}") } -func TestMain(m *testing.M) { +func TestProvider(t *testing.T) { app := kvstore.NewApplication() - app.RetainBlocks = 9 + app.RetainBlocks = 10 node := rpctest.StartTendermint(app) - code := m.Run() - - rpctest.StopTendermint(node) - os.Exit(code) -} - -func TestProvider(t *testing.T) { cfg := rpctest.GetConfig() defer os.RemoveAll(cfg.RootDir) rpcAddr := cfg.RPC.ListenAddress genDoc, err := types.GenesisDocFromFile(cfg.GenesisFile()) - if err != nil { - panic(err) - } + require.NoError(t, err) chainID := genDoc.ChainID - t.Log("chainID:", chainID) c, err := rpchttp.New(rpcAddr, "/websocket") require.Nil(t, err) @@ -70,28 +59,39 @@ func TestProvider(t *testing.T) { require.NoError(t, err) // let's get the highest block - sh, err := p.LightBlock(context.Background(), 0) + lb, err := p.LightBlock(context.Background(), 0) require.NoError(t, err) - assert.True(t, sh.Height < 1000) + require.NotNil(t, lb) + assert.True(t, lb.Height < 1000) // let's check this is valid somehow - assert.Nil(t, sh.ValidateBasic(chainID)) + assert.Nil(t, lb.ValidateBasic(chainID)) // historical queries now work :) - lower := sh.Height - 3 - sh, err = p.LightBlock(context.Background(), lower) + lower := lb.Height - 3 + lb, err = p.LightBlock(context.Background(), lower) require.NoError(t, err) - assert.Equal(t, lower, sh.Height) + assert.Equal(t, lower, lb.Height) // fetching missing heights (both future and pruned) should return appropriate errors - lb, err := p.LightBlock(context.Background(), 1000) + lb, err = p.LightBlock(context.Background(), 1000) require.Error(t, err) require.Nil(t, lb) assert.Equal(t, provider.ErrHeightTooHigh, err) _, err = p.LightBlock(context.Background(), 1) require.Error(t, err) + require.Nil(t, lb) assert.Equal(t, provider.ErrLightBlockNotFound, err) + + // stop the full node and check that a no response error is returned + rpctest.StopTendermint(node) + time.Sleep(10 * time.Second) + lb, err = p.LightBlock(context.Background(), lower+2) + // we should see a connection refused + require.Error(t, err) + require.Contains(t, err.Error(), "connection refused") + require.Nil(t, lb) } // TestLightClient_NilCommit ensures correct handling of a case where commit returned by http client is nil @@ -101,8 +101,8 @@ func TestLightClient_NilCommit(t *testing.T) { p := lighthttp.NewWithClient(chainID, c) require.NotNil(t, p) - c.On("Commit", testify.Anything, testify.Anything).Return( - &coretypes.ResultCommit{ + c.On("Commit", testify.Anything, testify.Anything). + Return(&coretypes.ResultCommit{ SignedHeader: types.SignedHeader{ Header: &types.Header{}, Commit: nil, diff --git a/light/provider/mock/deadmock.go b/light/provider/mock/deadmock.go index e32e6372ab..dbf5b779e0 100644 --- a/light/provider/mock/deadmock.go +++ b/light/provider/mock/deadmock.go @@ -2,14 +2,11 @@ package mock import ( "context" - "errors" "github.com/tendermint/tendermint/light/provider" "github.com/tendermint/tendermint/types" ) -var errNoResp = errors.New("no response from provider") - type deadMock struct { chainID string } @@ -24,9 +21,9 @@ func (p *deadMock) ChainID() string { return p.chainID } func (p *deadMock) String() string { return "deadMock" } func (p *deadMock) LightBlock(_ context.Context, height int64) (*types.LightBlock, error) { - return nil, errNoResp + return nil, provider.ErrNoResponse } func (p *deadMock) ReportEvidence(_ context.Context, ev types.Evidence) error { - return errNoResp + return provider.ErrNoResponse } diff --git a/light/provider/mock/mock.go b/light/provider/mock/mock.go index a514ff00fe..84d0cc05c6 100644 --- a/light/provider/mock/mock.go +++ b/light/provider/mock/mock.go @@ -6,6 +6,7 @@ import ( "fmt" "strings" "sync" + "time" "github.com/tendermint/tendermint/light/provider" "github.com/tendermint/tendermint/types" @@ -67,11 +68,18 @@ func (p *Mock) String() string { return fmt.Sprintf("Mock{headers: %s, vals: %v}", headers.String(), vals.String()) } -func (p *Mock) LightBlock(_ context.Context, height int64) (*types.LightBlock, error) { +func (p *Mock) LightBlock(ctx context.Context, height int64) (*types.LightBlock, error) { p.mtx.Lock() defer p.mtx.Unlock() var lb *types.LightBlock + // allocate a window of time for contexts to be canceled + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(10 * time.Millisecond): + } + if height > p.latestHeight { return nil, provider.ErrHeightTooHigh } diff --git a/light/proxy/routes.go b/light/proxy/routes.go index 22ee48640a..76998b8013 100644 --- a/light/proxy/routes.go +++ b/light/proxy/routes.go @@ -18,17 +18,18 @@ func RPCRoutes(c *lrpc.Client) map[string]*rpcserver.RPCFunc { "unsubscribe_all": rpcserver.NewWSRPCFunc(c.UnsubscribeAllWS, ""), // info API - "health": rpcserver.NewRPCFunc(makeHealthFunc(c), ""), - "status": rpcserver.NewRPCFunc(makeStatusFunc(c), ""), - "net_info": rpcserver.NewRPCFunc(makeNetInfoFunc(c), ""), - "blockchain": rpcserver.NewRPCFunc(makeBlockchainInfoFunc(c), "minHeight,maxHeight"), - "genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), ""), - "block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height"), - "block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash"), - "block_results": rpcserver.NewRPCFunc(makeBlockResultsFunc(c), "height"), - "commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"), - "tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove"), - "tx_search": rpcserver.NewRPCFunc(makeTxSearchFunc(c), "query,prove,page,per_page,order_by"), + "health": rpcserver.NewRPCFunc(makeHealthFunc(c), ""), + "status": rpcserver.NewRPCFunc(makeStatusFunc(c), ""), + "net_info": rpcserver.NewRPCFunc(makeNetInfoFunc(c), ""), + "blockchain": rpcserver.NewRPCFunc(makeBlockchainInfoFunc(c), "minHeight,maxHeight"), + "genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), ""), + "genesis_chunked": rpcserver.NewRPCFunc(makeGenesisChunkedFunc(c), ""), + "block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height"), + "block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash"), + "block_results": rpcserver.NewRPCFunc(makeBlockResultsFunc(c), "height"), + "commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"), + "tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove"), + "tx_search": rpcserver.NewRPCFunc(makeTxSearchFunc(c), "query,prove,page,per_page,order_by"), "validators": rpcserver.NewRPCFunc(makeValidatorsFunc(c), "height,page,per_page,request_threshold_public_key"), @@ -93,6 +94,14 @@ func makeGenesisFunc(c *lrpc.Client) rpcGenesisFunc { } } +type rpcGenesisChunkedFunc func(ctx *rpctypes.Context, chunk uint) (*ctypes.ResultGenesisChunk, error) + +func makeGenesisChunkedFunc(c *lrpc.Client) rpcGenesisChunkedFunc { + return func(ctx *rpctypes.Context, chunk uint) (*ctypes.ResultGenesisChunk, error) { + return c.GenesisChunked(ctx.Context(), chunk) + } +} + type rpcBlockFunc func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlock, error) func makeBlockFunc(c *lrpc.Client) rpcBlockFunc { diff --git a/light/rpc/client.go b/light/rpc/client.go index 467cf4523f..eea9d3ce1a 100644 --- a/light/rpc/client.go +++ b/light/rpc/client.go @@ -306,6 +306,10 @@ func (c *Client) Genesis(ctx context.Context) (*ctypes.ResultGenesis, error) { return c.next.Genesis(ctx) } +func (c *Client) GenesisChunked(ctx context.Context, id uint) (*ctypes.ResultGenesisChunk, error) { + return c.next.GenesisChunked(ctx, id) +} + // Block calls rpcclient#Block and then verifies the result. func (c *Client) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) { res, err := c.next.Block(ctx, height) diff --git a/mempool/reactor_test.go b/mempool/reactor_test.go index 51ee7e46fd..70a4e9bd9c 100644 --- a/mempool/reactor_test.go +++ b/mempool/reactor_test.go @@ -11,7 +11,7 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/fortytw2/leaktest" - "github.com/go-kit/kit/log/term" + "github.com/go-kit/log/term" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/node/node.go b/node/node.go index 3bf1e091aa..9830024869 100644 --- a/node/node.go +++ b/node/node.go @@ -12,6 +12,7 @@ import ( "time" "github.com/dashevo/dashd-go/btcjson" + dashquorum "github.com/tendermint/tendermint/dash/quorum" dashcore "github.com/tendermint/tendermint/dashcore/rpc" "github.com/prometheus/client_golang/prometheus" @@ -43,6 +44,7 @@ import ( "github.com/tendermint/tendermint/state/indexer" blockidxkv "github.com/tendermint/tendermint/state/indexer/block/kv" blockidxnull "github.com/tendermint/tendermint/state/indexer/block/null" + "github.com/tendermint/tendermint/state/indexer/sink/psql" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/state/txindex/kv" "github.com/tendermint/tendermint/state/txindex/null" @@ -96,9 +98,10 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { return nil, fmt.Errorf("failed to load or gen node key %s: %w", config.NodeKeyFile(), err) } + appClient, _ := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) return NewNode(config, nodeKey, - proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), + appClient, DefaultGenesisDocProviderFunc(config), DefaultDBProvider, DefaultMetricsProvider(config.Instrumentation), @@ -233,6 +236,8 @@ type Node struct { indexerService *txindex.IndexerService prometheusSrv *http.Server + validatorConnExecutor *dashquorum.ValidatorConnExecutor + dashCoreRPCClient dashcore.Client } @@ -278,6 +283,7 @@ func createAndStartEventBus(logger log.Logger) (*types.EventBus, error) { func createAndStartIndexerService( config *cfg.Config, + chainID string, dbProvider DBProvider, eventBus *types.EventBus, logger log.Logger, @@ -297,6 +303,18 @@ func createAndStartIndexerService( txIndexer = kv.NewTxIndex(store) blockIndexer = blockidxkv.New(dbm.NewPrefixDB(store, []byte("block_events"))) + + case "psql": + if config.TxIndex.PsqlConn == "" { + return nil, nil, nil, errors.New(`no psql-conn is set for the "psql" indexer`) + } + es, err := psql.NewEventSink(config.TxIndex.PsqlConn, chainID) + if err != nil { + return nil, nil, nil, fmt.Errorf("creating psql indexer: %w", err) + } + txIndexer = es.TxIndexer() + blockIndexer = es.BlockIndexer() + default: txIndexer = &null.TxIndex{} blockIndexer = &blockidxnull.BlockerIndexer{} @@ -738,12 +756,8 @@ func NewNode(config *cfg.Config, return nil, err } - indexerService, txIndexer, blockIndexer, err := createAndStartIndexerService( - config, - dbProvider, - eventBus, - logger, - ) + indexerService, txIndexer, blockIndexer, err := createAndStartIndexerService(config, + genDoc.ChainID, dbProvider, eventBus, logger) if err != nil { return nil, err } @@ -851,6 +865,9 @@ func NewNode(config *cfg.Config, // Create the handshaker, which calls RequestInfo, sets the AppVersion on the state, // and replays any blocks as necessary to sync tenderdash with the app. consensusLogger := logger.With("module", "consensus") + if proTxHashP != nil { + consensusLogger = consensusLogger.With("proTxHash", proTxHashP.ShortString()) + } proposedAppVersion := uint64(0) if !stateSync { handshaker := cs.NewHandshaker( @@ -1029,6 +1046,21 @@ func NewNode(config *cfg.Config, }() } + // Initialize ValidatorConnExecutor (only on Validators) + var validatorConnExecutor *dashquorum.ValidatorConnExecutor + if proTxHashP != nil { + validatorConnExecutor, err = dashquorum.NewValidatorConnExecutor( + *proTxHashP, + eventBus, + sw, + dashquorum.WithLogger(logger), + dashquorum.WithValidatorsSet(state.Validators), + ) + if err != nil { + return nil, err + } + } + node := &Node{ config: config, genesisDoc: genDoc, @@ -1058,7 +1090,8 @@ func NewNode(config *cfg.Config, blockIndexer: blockIndexer, eventBus: eventBus, - dashCoreRPCClient: dashCoreRPCClient, + validatorConnExecutor: validatorConnExecutor, + dashCoreRPCClient: dashCoreRPCClient, } node.BaseService = *service.NewBaseService(logger, "Node", node) @@ -1129,6 +1162,12 @@ func (n *Node) OnStart() error { return fmt.Errorf("could not dial peers from persistent_peers field: %w", err) } + // start + if n.validatorConnExecutor != nil { + if err := n.validatorConnExecutor.Start(); err != nil { + return fmt.Errorf("cannot start ValidatorConnExecutor: %w", err) + } + } // Run state sync if n.stateSync { bcR, ok := n.bcReactor.(fastSyncReactor) @@ -1241,6 +1280,11 @@ func (n *Node) ConfigureRPC() error { } rpccore.SetEnvironment(&env) + + if err := rpccore.InitGenesisChunks(); err != nil { + return err + } + return nil } diff --git a/node/node_test.go b/node/node_test.go index b566f47b06..166c117689 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/tendermint/tendermint/crypto" + dashtypes "github.com/tendermint/tendermint/dash/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -57,6 +58,13 @@ func TestNodeStartStop(t *testing.T) { t.Fatal("timed out waiting for the node to produce a block") } + // check if we can read node ID of this node + va, err := dashtypes.ParseValidatorAddress(config.P2P.ListenAddress) + assert.NoError(t, err) + id, err := dashtypes.NewTCPNodeIDResolver().Resolve(va) + assert.Equal(t, n.nodeInfo.ID(), id) + assert.NoError(t, err) + // stop the node go func() { err = n.Stop() @@ -428,9 +436,11 @@ func TestNodeNewNodeCustomReactors(t *testing.T) { nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) require.NoError(t, err) + appClient, closer := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) + t.Cleanup(func() { closer.Close() }) n, err := NewNode(config, nodeKey, - proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), + appClient, DefaultGenesisDocProviderFunc(config), DefaultDBProvider, DefaultMetricsProvider(config.Instrumentation), diff --git a/p2p/address.go b/p2p/address.go new file mode 100644 index 0000000000..430480145c --- /dev/null +++ b/p2p/address.go @@ -0,0 +1,232 @@ +package p2p + +// BACKPORTING NOTE: +// +// This file was downloaded from Tendermint master, revision bc1a20dbb86e4fa2120b2c8a9de88814471f4a2c: +// https://raw.githubusercontent.com/tendermint/tendermint/bc1a20dbb86e4fa2120b2c8a9de88814471f4a2c/internal/p2p/address.go +// and refactored to workaround some dependencies. +// Changes: +// 1. ParseNodeAddress divided into 2 functions (added ParseNodeAddressWithoutValidation) +// 2. Added some missing types at the end of file +import ( + "context" + "errors" + "fmt" + "net" + "net/url" + "reflect" + "regexp" + "strconv" + "strings" +) + +var ( + // stringHasScheme tries to detect URLs with schemes. It looks for a : before a / (if any). + stringHasScheme = func(str string) bool { + return strings.Contains(str, "://") + } + + // reSchemeIsHost tries to detect URLs where the scheme part is instead a + // hostname, i.e. of the form "host:80/path" where host: is a hostname. + reSchemeIsHost = regexp.MustCompile(`^[^/:]+:\d+(/|$)`) + ErrEmptyNodeAddress = errors.New("empty node address") +) + +// NodeAddress is a node address URL. It differs from a transport Endpoint in +// that it contains the node's ID, and that the address hostname may be resolved +// into multiple IP addresses (and thus multiple endpoints). +// +// If the URL is opaque, i.e. of the form "scheme:opaque", then the opaque part +// is expected to contain a node ID. +type NodeAddress struct { + NodeID ID + Protocol string + Hostname string + Port uint16 + Path string +} + +// ParseNodeAddress parses a node address URL into a NodeAddress, normalizing +// and validating it. +func ParseNodeAddress(urlString string) (NodeAddress, error) { + if urlString == "" { + return NodeAddress{}, ErrEmptyNodeAddress + } + address, err := ParseNodeAddressWithoutValidation(urlString) + if err != nil { + return address, err + } + return address, address.Validate() +} + +// ParseNodeAddressWithoutValidation parses a node address URL into a NodeAddress, normalizing it. +// It does NOT validate parsed address +func ParseNodeAddressWithoutValidation(urlString string) (NodeAddress, error) { + // url.Parse requires a scheme, so if it fails to parse a scheme-less URL + // we try to apply a default scheme. + url, err := url.Parse(urlString) + if (err != nil || url.Scheme == "") && + (!stringHasScheme(urlString) || reSchemeIsHost.MatchString(urlString)) { + url, err = url.Parse("tcp://" + urlString) + } + if err != nil { + return NodeAddress{}, fmt.Errorf("invalid node address %q: %w", urlString, err) + } + + address := NodeAddress{ + Protocol: (strings.ToLower(url.Scheme)), + } + + // Opaque URLs are expected to contain only a node ID. + if url.Opaque != "" { + address.NodeID = ID(url.Opaque) + return address, nil + } + + // Otherwise, just parse a normal networked URL. + if url.User != nil { + address.NodeID = ID(strings.ToLower(url.User.Username())) + } + + address.Hostname = strings.ToLower(url.Hostname()) + + if portString := url.Port(); portString != "" { + port64, err := strconv.ParseUint(portString, 10, 16) + if err != nil { + return NodeAddress{}, fmt.Errorf("invalid port %q: %w", portString, err) + } + address.Port = uint16(port64) + } + + address.Path = url.Path + if url.RawQuery != "" { + address.Path += "?" + url.RawQuery + } + if url.Fragment != "" { + address.Path += "#" + url.Fragment + } + if address.Path != "" { + switch address.Path[0] { + case '/', '#', '?': + default: + address.Path = "/" + address.Path + } + } + + return address, nil +} + +// Resolve resolves a NodeAddress into a set of Endpoints, by expanding +// out a DNS hostname to IP addresses. +func (a NodeAddress) Resolve(ctx context.Context) ([]Endpoint, error) { + if a.Protocol == "" { + return nil, errors.New("address has no protocol") + } + + // If there is no hostname, this is an opaque URL in the form + // "scheme:opaque", and the opaque part is assumed to be node ID used as + // Path. + if a.Hostname == "" { + if a.NodeID == "" { + return nil, errors.New("local address has no node ID") + } + return []Endpoint{{ + Protocol: a.Protocol, + Path: string(a.NodeID), + }}, nil + } + + ips, err := net.DefaultResolver.LookupIP(ctx, "ip", a.Hostname) + if err != nil { + return nil, err + } + endpoints := make([]Endpoint, len(ips)) + for i, ip := range ips { + endpoints[i] = Endpoint{ + Protocol: a.Protocol, + IP: ip, + Port: a.Port, + Path: a.Path, + } + } + return endpoints, nil +} + +// String formats the address as a URL string. +func (a NodeAddress) String() string { + if a.Zero() { + return "" + } + + u := url.URL{Scheme: a.Protocol} + if a.NodeID != "" { + u.User = url.User(string(a.NodeID)) + } + switch { + case a.Hostname != "": + if a.Port > 0 { + u.Host = net.JoinHostPort(a.Hostname, strconv.Itoa(int(a.Port))) + } else { + u.Host = a.Hostname + } + u.Path = a.Path + + case a.Protocol != "" && (a.Path == "" || ID(a.Path) == a.NodeID): + u.User = nil + u.Opaque = string(a.NodeID) // e.g. memory:id + + case a.Path != "" && a.Path[0] != '/': + u.Path = "/" + a.Path // e.g. some/path + + default: + u.Path = a.Path // e.g. /some/path + } + return strings.TrimPrefix(u.String(), "//") +} + +// Validate validates a NodeAddress. +func (a NodeAddress) Validate() error { + if a.Protocol == "" { + return errors.New("no protocol") + } + if a.NodeID == "" { + return errors.New("no peer ID") + } + if a.Port > 0 && a.Hostname == "" { + return errors.New("cannot specify port without hostname") + } + return nil +} + +func (a NodeAddress) Zero() bool { + return reflect.ValueOf(a).IsZero() +} + +func (a NodeAddress) NetAddress() (*NetAddress, error) { + addr, err := NewNetAddressString(a.String()) + if err != nil || addr == nil { + return nil, err + } + return addr, nil +} + +// Endpoint represents a transport connection endpoint, either local or remote. +// +// Endpoints are not necessarily networked (see e.g. MemoryTransport) but all +// networked endpoints must use IP as the underlying transport protocol to allow +// e.g. IP address filtering. Either IP or Path (or both) must be set. +type Endpoint struct { + // Protocol specifies the transport protocol. + Protocol string + + // IP is an IP address (v4 or v6) to connect to. If set, this defines the + // endpoint as a networked endpoint. + IP net.IP + + // Port is a network port (either TCP or UDP). If 0, a default port may be + // used depending on the protocol. + Port uint16 + + // Path is an optional transport-specific path or identifier. + Path string +} diff --git a/p2p/address_test.go b/p2p/address_test.go new file mode 100644 index 0000000000..98954107a5 --- /dev/null +++ b/p2p/address_test.go @@ -0,0 +1,19 @@ +package p2p_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/p2p" +) + +func TestNodeAddress_Zero(t *testing.T) { + peerID := make([]byte, 20) + a, err := p2p.ParseNodeAddress(fmt.Sprintf("tcp://%x@1.2.3.4:123", peerID)) + assert.NoError(t, err) + assert.False(t, a.Zero()) + + a = p2p.NodeAddress{} + assert.True(t, a.Zero()) +} diff --git a/p2p/conn/conn_go110.go b/p2p/conn/conn_go110.go index 6821881015..459c3169b1 100644 --- a/p2p/conn/conn_go110.go +++ b/p2p/conn/conn_go110.go @@ -1,3 +1,4 @@ +//go:build go1.10 // +build go1.10 package conn diff --git a/p2p/conn/connection.go b/p2p/conn/connection.go index 10ac44a322..4ee53b0b6d 100644 --- a/p2p/conn/connection.go +++ b/p2p/conn/connection.go @@ -443,14 +443,14 @@ FOR_LOOP: channel.updateStats() } case <-c.pingTimer.C: - c.Logger.Debug("Send Ping") + c.Logger.P2PDebug("Send Ping") _n, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPing{})) if err != nil { c.Logger.Error("Failed to send PacketPing", "err", err) break SELECTION } c.sendMonitor.Update(_n) - c.Logger.Debug("Starting pong timer", "dur", c.config.PongTimeout) + c.Logger.P2PDebug("Starting pong timer", "dur", c.config.PongTimeout) c.pongTimer = time.AfterFunc(c.config.PongTimeout, func() { select { case c.pongTimeoutCh <- true: @@ -460,13 +460,13 @@ FOR_LOOP: c.flush() case timeout := <-c.pongTimeoutCh: if timeout { - c.Logger.Debug("Pong timeout") + c.Logger.P2PDebug("Pong timeout") err = errors.New("pong timeout") } else { c.stopPongTimer() } case <-c.pong: - c.Logger.Debug("Send Pong") + c.Logger.P2PDebug("Send Pong") _n, err = protoWriter.WriteMsg(mustWrapPacket(&tmp2p.PacketPong{})) if err != nil { c.Logger.Error("Failed to send PacketPong", "err", err) @@ -615,14 +615,14 @@ FOR_LOOP: case *tmp2p.Packet_PacketPing: // TODO: prevent abuse, as they cause flush()'s. // https://github.com/tendermint/tendermint/issues/1190 - c.Logger.Debug("Receive Ping") + c.Logger.P2PDebug("Receive Ping") select { case c.pong <- struct{}{}: default: // never block } case *tmp2p.Packet_PacketPong: - c.Logger.Debug("Receive Pong") + c.Logger.P2PDebug("Receive Pong") select { case c.pongTimeoutCh <- false: default: diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index 6d43dee177..0165a26ed0 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -90,10 +90,6 @@ type SecretConnection struct { // Caller should call conn.Close() // See docs/sts-final.pdf for more information. func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (*SecretConnection, error) { - var ( - locPubKey = locPrivKey.PubKey() - ) - // Generate ephemeral keys for perfect forward secrecy. locEphPub, locEphPriv := genEphKeys() @@ -157,10 +153,20 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (* sendAead: sendAead, } - // SignDigest the challenge bytes for authentication. - locSignature, err := signChallenge(&challenge, locPrivKey) - if err != nil { - return nil, fmt.Errorf("sign challenge: %v", err) + // Exchange public keys + var ( + locPubKey crypto.PubKey + locSignature []byte + ) + // Allow no local private key to just retrieve remote privkey + if locPrivKey != nil { + locPubKey = locPrivKey.PubKey() + + // SignDigest the challenge bytes for authentication. + locSignature, err = signChallenge(&challenge, locPrivKey) + if err != nil { + return nil, fmt.Errorf("sign challenge: %v", err) + } } // Share (in secret) each other's pubkey & challenge signature @@ -404,18 +410,22 @@ type authSigMessage struct { Sig []byte } +// shareAuthSignature exchanges public keys, authorized by the signed challenge. +// If `pubKey` is nil, it will just retrieve public key without sending ours to the other party. func shareAuthSignature(sc io.ReadWriter, pubKey crypto.PubKey, signature []byte) (recvMsg authSigMessage, err error) { // Send our info and receive theirs in tandem. var trs, _ = async.Parallel( func(_ int) (val interface{}, abort bool, err error) { - pbpk, err := cryptoenc.PubKeyToProto(pubKey) - if err != nil { - return nil, true, err - } - _, err = protoio.NewDelimitedWriter(sc).WriteMsg(&tmp2p.AuthSigMessage{PubKey: pbpk, Sig: signature}) - if err != nil { - return nil, true, err // abort + if pubKey != nil { + pbpk, err := cryptoenc.PubKeyToProto(pubKey) + if err != nil { + return nil, true, err + } + _, err = protoio.NewDelimitedWriter(sc).WriteMsg(&tmp2p.AuthSigMessage{PubKey: pbpk, Sig: signature}) + if err != nil { + return nil, true, err // abort + } } return nil, false, nil }, diff --git a/p2p/conn/secret_connection_test.go b/p2p/conn/secret_connection_test.go index ca20c433ea..d1c4f541b2 100644 --- a/p2p/conn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -17,7 +17,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/async" tmos "github.com/tendermint/tendermint/libs/os" @@ -42,18 +41,6 @@ func (drw kvstoreConn) Close() (err error) { return err1 } -type privKeyWithNilPubKey struct { - orig crypto.PrivKey -} - -func (pk privKeyWithNilPubKey) Bytes() []byte { return pk.orig.Bytes() } -func (pk privKeyWithNilPubKey) Sign(msg []byte) ([]byte, error) { return pk.orig.Sign(msg) } -func (pk privKeyWithNilPubKey) SignDigest(msg []byte) ([]byte, error) { return pk.orig.SignDigest(msg) } -func (pk privKeyWithNilPubKey) PubKey() crypto.PubKey { return nil } -func (pk privKeyWithNilPubKey) Equals(pk2 crypto.PrivKey) bool { return pk.orig.Equals(pk2) } -func (pk privKeyWithNilPubKey) Type() string { return "privKeyWithNilPubKey" } -func (pk privKeyWithNilPubKey) TypeValue() crypto.KeyType { return crypto.KeyTypeAny } - func TestSecretConnectionHandshake(t *testing.T) { fooSecConn, barSecConn := makeSecretConnPair(t) if err := fooSecConn.Close(); err != nil { @@ -258,19 +245,30 @@ func TestDeriveSecretsAndChallengeGolden(t *testing.T) { } } +// TestNilPubkey ensures that we can retrieve peer public key without revealing ours func TestNilPubkey(t *testing.T) { var fooConn, barConn = makeKVStoreConnPair() - defer fooConn.Close() - defer barConn.Close() var fooPrvKey = ed25519.GenPrivKey() - var barPrvKey = privKeyWithNilPubKey{ed25519.GenPrivKey()} - go MakeSecretConnection(fooConn, fooPrvKey) //nolint:errcheck // ignore for tests - - _, err := MakeSecretConnection(barConn, barPrvKey) - require.Error(t, err) - wantErr := "toproto: key type is not supported" - assert.Containsf(t, err.Error(), wantErr, "expected error containing %q, got %s", wantErr, err) + wg := sync.WaitGroup{} + wg.Add(2) + // Foo thread never receives peer's public key + go func() { + _, err := MakeSecretConnection(fooConn, fooPrvKey) + fooConn.Close() + assert.Error(t, err) + wg.Done() + }() + // Bar thread never sends its public key, but receives peers + go func() { + sc, err := MakeSecretConnection(barConn, nil) + barConn.Close() + require.NoError(t, err) + require.NotNil(t, sc) + assert.NotNil(t, sc.RemotePubKey()) + wg.Done() + }() + wg.Wait() } func writeLots(t *testing.T, wg *sync.WaitGroup, conn io.Writer, txt string, n int) { diff --git a/p2p/netaddress.go b/p2p/netaddress.go index 77209217b2..925e0ba1ea 100644 --- a/p2p/netaddress.go +++ b/p2p/netaddress.go @@ -52,7 +52,7 @@ func NewNetAddress(id ID, addr net.Addr) *NetAddress { } } - if err := validateID(id); err != nil { + if err := ValidateID(id); err != nil { panic(fmt.Sprintf("Invalid ID %v: %v (addr: %v)", id, err, addr)) } @@ -75,7 +75,7 @@ func NewNetAddressString(addr string) (*NetAddress, error) { } // get ID - if err := validateID(ID(spl[0])); err != nil { + if err := ValidateID(ID(spl[0])); err != nil { return nil, ErrNetAddressInvalid{addrWithoutProtocol, err} } var id ID @@ -262,7 +262,7 @@ func (na *NetAddress) Routable() bool { // For IPv4 these are either a 0 or all bits set address. For IPv6 a zero // address or one that matches the RFC3849 documentation address format. func (na *NetAddress) Valid() error { - if err := validateID(na.ID); err != nil { + if err := ValidateID(na.ID); err != nil { return fmt.Errorf("invalid ID: %w", err) } @@ -405,7 +405,7 @@ func removeProtocolIfDefined(addr string) string { } -func validateID(id ID) error { +func ValidateID(id ID) error { if len(id) == 0 { return errors.New("no ID") } diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 76b21e8dc4..f29d8712ad 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -50,6 +50,9 @@ type AddrBook interface { // Check if the address is in the book HasAddress(*p2p.NetAddress) bool + // FindIP finds node ID based on IP address + FindIP(net.IP, uint16) p2p.ID + // Do we need more peers? NeedMoreAddrs() bool // Is Address Book Empty? Answer should not depend on being in your own @@ -695,6 +698,18 @@ func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { return a.addToNewBucket(ka, bucket) } +// FindIP implements AddBook. +// Finds node ID based on IP address +func (a *addrBook) FindIP(ip net.IP, port uint16) p2p.ID { + for nodeID, item := range a.addrLookup { + if item.Addr != nil && item.Addr.IP.Equal(ip) && item.Addr.Port == port { + return nodeID + } + } + + return "" +} + func (a *addrBook) randomPickAddresses(bucketType byte, num int) []*p2p.NetAddress { var buckets []map[string]*knownAddress switch bucketType { diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index caced5413c..e1fd35a39d 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -43,6 +43,11 @@ const ( // check some peers every this crawlPeerPeriod = 30 * time.Second + // try to connect to at least 1 peer every this + seedConnectMaxDelayPeriod = 5 * time.Second + // limit number of retries to dial other seeds during initialization + seedInitMaxAttemptToDial = 12 + maxAttemptsToDial = 16 // ~ 35h in total (last attempt - 18h) // if node connects to seed, it does not have any trusted peers. @@ -581,8 +586,7 @@ func (r *Reactor) dialPeer(addr *p2p.NetAddress) error { // cleanup any history r.attemptsToDial.Delete(addr.DialString()) - - fmt.Printf("dialing success to %v", addr.DialString()) + r.Logger.P2PDebug("dialing success", "address", addr.DialString()) return nil } @@ -620,9 +624,9 @@ func (r *Reactor) checkSeeds() (numOnline int, netAddrs []*p2p.NetAddress, err e } // randomly dial seeds until we connect to one or exhaust them -func (r *Reactor) dialSeeds() { +// It returns true if at least one peer is connected or being dialed, false otherwise +func (r *Reactor) dialSeeds() bool { perm := tmrand.Perm(len(r.seedAddrs)) - // perm := r.Switch.rng.Perm(lSeeds) for _, i := range perm { // dial a random seed seedAddr := r.seedAddrs[i] @@ -630,7 +634,7 @@ func (r *Reactor) dialSeeds() { switch err.(type) { case nil, p2p.ErrCurrentlyDialingOrExistingAddress: - return + return true } r.Switch.Logger.Error("Error dialing seed", "err", err, "seed", seedAddr) } @@ -638,6 +642,7 @@ func (r *Reactor) dialSeeds() { if len(r.seedAddrs) > 0 { r.Switch.Logger.Error("Couldn't connect to any seeds") } + return false } // AttemptsToDial returns the number of attempts to dial specific address. It @@ -658,7 +663,15 @@ func (r *Reactor) AttemptsToDial(addr *p2p.NetAddress) int { func (r *Reactor) crawlPeersRoutine() { // If we have any seed nodes, consult them first if len(r.seedAddrs) > 0 { - r.dialSeeds() + for try := 0; try < seedInitMaxAttemptToDial; try++ { + // we sleep a few (random) secs to avoid connection storm when whole network restarts + delay := time.Duration(tmrand.Int63n(seedConnectMaxDelayPeriod.Nanoseconds())) + time.Sleep(delay) + + if r.dialSeeds() { + break + } + } } else { // Do an initial crawl r.crawlPeers(r.book.GetSelection()) diff --git a/p2p/switch.go b/p2p/switch.go index 361e698270..901cbd8125 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -3,6 +3,7 @@ package p2p import ( "fmt" "math" + "net" "sync" "time" @@ -52,6 +53,7 @@ type AddrBook interface { MarkGood(ID) RemoveAddress(*NetAddress) HasAddress(*NetAddress) bool + FindIP(ip net.IP, port uint16) ID Save() } @@ -433,6 +435,11 @@ func (sw *Switch) SetAddrBook(addrBook AddrBook) { sw.addrBook = addrBook } +// AddrBook() returns address book used by a switch +func (sw *Switch) AddrBook() AddrBook { + return sw.addrBook +} + // MarkPeerAsGood marks the given peer as good when it did something useful // like contributed to consensus. func (sw *Switch) MarkPeerAsGood(peer Peer) { @@ -557,7 +564,7 @@ func (sw *Switch) IsDialingOrExistingAddress(addr *NetAddress) bool { (!sw.config.AllowDuplicateIP && sw.peers.HasIP(addr.IP)) } -// AddPersistentPeers allows you to set persistent peers. It ignores +// AddPersistentPeers allows you to add new persistent peers. It ignores // ErrNetAddressLookup. However, if there are other errors, first encounter is // returned. func (sw *Switch) AddPersistentPeers(addrs []string) error { @@ -574,14 +581,38 @@ func (sw *Switch) AddPersistentPeers(addrs []string) error { } return err } - sw.persistentPeersAddrs = netAddrs + sw.persistentPeersAddrs = append(sw.persistentPeersAddrs, netAddrs...) + return nil +} + +// RemovePersistentPeer allows you to delete persistent peer from persistent peers list. +// It ignores ErrNetAddressLookup. However, if there are other errors, first encounter is +// returned. +func (sw *Switch) RemovePersistentPeer(addr string) error { + if len(sw.persistentPeersAddrs) == 0 { + return nil + } + sw.Logger.Info("Removing persistent peer", "addr", addr) + toDelete, err := NewNetAddressString(addr) + if err != nil { + return err + } + + newPersistentPeers := make([]*NetAddress, 0, len(sw.persistentPeersAddrs)-1) + for _, existingAddr := range sw.persistentPeersAddrs { + if !existingAddr.Equals(toDelete) { + newPersistentPeers = append(newPersistentPeers, existingAddr) + } + } + + sw.persistentPeersAddrs = newPersistentPeers return nil } func (sw *Switch) AddUnconditionalPeerIDs(ids []string) error { sw.Logger.Info("Adding unconditional peer ids", "ids", ids) for i, id := range ids { - err := validateID(ID(id)) + err := ValidateID(ID(id)) if err != nil { return fmt.Errorf("wrong ID #%d: %w", i, err) } @@ -593,7 +624,7 @@ func (sw *Switch) AddUnconditionalPeerIDs(ids []string) error { func (sw *Switch) AddPrivatePeerIDs(ids []string) error { validIDs := make([]string, 0, len(ids)) for i, id := range ids { - err := validateID(ID(id)) + err := ValidateID(ID(id)) if err != nil { return fmt.Errorf("wrong ID #%d: %w", i, err) } diff --git a/p2p/test_util.go b/p2p/test_util.go index d3534e8dcc..1f0778750e 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -1,17 +1,17 @@ package p2p import ( + "encoding/hex" "fmt" "net" "time" + "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/log" tmnet "github.com/tendermint/tendermint/libs/net" tmrand "github.com/tendermint/tendermint/libs/rand" - - "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/p2p/conn" ) @@ -316,3 +316,8 @@ func (book *AddrBookMock) AddPrivateIDs(addrs []string) { book.PrivateAddrs[addr] = struct{}{} } } + +// FindIP implements AddrBook +func (book *AddrBookMock) FindIP(net.IP, uint16) ID { + return ID(hex.EncodeToString(tmrand.Bytes(IDByteLength))) +} diff --git a/p2p/transport_test.go b/p2p/transport_test.go index 5192e2af45..a22ab6b508 100644 --- a/p2p/transport_test.go +++ b/p2p/transport_test.go @@ -7,6 +7,7 @@ import ( "reflect" "runtime" "strings" + "sync" "testing" "time" @@ -569,12 +570,13 @@ func TestTransportHandshake(t *testing.T) { if err != nil { t.Fatal(err) } - + time.Sleep(100 * time.Microsecond) var ( peerPV = ed25519.GenPrivKey() peerNodeInfo = testNodeInfo(PubKeyToID(peerPV.PubKey()), defaultNodeName, nil) + wg sync.WaitGroup ) - + wg.Add(2) go func() { c, err := net.Dial(ln.Addr().Network(), ln.Addr().String()) if err != nil { @@ -583,6 +585,7 @@ func TestTransportHandshake(t *testing.T) { } go func(c net.Conn) { + defer wg.Done() _, err := protoio.NewDelimitedWriter(c).WriteMsg(peerNodeInfo.(DefaultNodeInfo).ToProto()) if err != nil { t.Error(err) @@ -593,6 +596,7 @@ func TestTransportHandshake(t *testing.T) { // ni DefaultNodeInfo pbni tmp2p.DefaultNodeInfo ) + defer wg.Done() protoReader := protoio.NewDelimitedReader(c, MaxNodeInfoSize()) _, err := protoReader.ReadMsg(&pbni) @@ -620,6 +624,8 @@ func TestTransportHandshake(t *testing.T) { if have, want := ni, peerNodeInfo; !reflect.DeepEqual(have, want) { t.Errorf("have %v, want %v", have, want) } + + wg.Wait() // ensure all goroutines are done } func TestTransportAddChannel(t *testing.T) { diff --git a/privval/dash_core_signer_client.go b/privval/dash_core_signer_client.go index 9ee038b420..7f2c392452 100644 --- a/privval/dash_core_signer_client.go +++ b/privval/dash_core_signer_client.go @@ -225,7 +225,7 @@ func (sc *DashCoreSignerClient) GetProTxHash() (crypto.ProTxHash, error) { // SignVote requests a remote signer to sign a vote func (sc *DashCoreSignerClient) SignVote( chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, - protoVote *tmproto.Vote, logger log.Logger) error { + protoVote *tmproto.Vote, stateID types.StateID, logger log.Logger) error { if len(quorumHash) != crypto.DefaultHashSize { return fmt.Errorf("quorum hash is not the right length %s", quorumHash.String()) } @@ -236,12 +236,12 @@ func (sc *DashCoreSignerClient) SignVote( blockResponse, err := sc.dashCoreRPCClient.QuorumSign(quorumType, blockRequestID, blockMessageHash, quorumHash) - if blockResponse == nil { - return ErrUnexpectedResponse - } if err != nil { return &RemoteSignerError{Code: 500, Description: err.Error()} } + if blockResponse == nil { + return ErrUnexpectedResponse + } // fmt.Printf("blockResponse %v", blockResponse) // @@ -285,19 +285,19 @@ func (sc *DashCoreSignerClient) SignVote( // Only sign the state when voting for the block if protoVote.BlockID.Hash != nil { - stateSignBytes := types.VoteStateSignBytes(chainID, protoVote) + stateSignBytes := stateID.SignBytes(chainID) stateMessageHash := crypto.Sha256(stateSignBytes) - stateRequestID := types.VoteStateRequestIDProto(protoVote) + stateRequestID := stateID.SignRequestID() stateResponse, err := sc.dashCoreRPCClient.QuorumSign( sc.defaultQuorumType, stateRequestID, stateMessageHash, quorumHash) - if stateResponse == nil { - return ErrUnexpectedResponse - } if err != nil { return &RemoteSignerError{Code: 500, Description: err.Error()} } + if stateResponse == nil { + return ErrUnexpectedResponse + } stateDecodedSignature, err := hex.DecodeString(stateResponse.Signature) if err != nil { @@ -349,12 +349,12 @@ func (sc *DashCoreSignerClient) SignProposal( response, err := sc.dashCoreRPCClient.QuorumSign(quorumType, requestIDHash, messageHash, quorumHash) - if response == nil { - return nil, ErrUnexpectedResponse - } if err != nil { return nil, &RemoteSignerError{Code: 500, Description: err.Error()} } + if response == nil { + return nil, ErrUnexpectedResponse + } decodedSignature, err := hex.DecodeString(response.Signature) if err != nil { diff --git a/privval/file.go b/privval/file.go index 18ea3ed7c9..0b4c042013 100644 --- a/privval/file.go +++ b/privval/file.go @@ -483,8 +483,8 @@ func (pv *FilePV) GetProTxHash() (crypto.ProTxHash, error) { // chainID. Implements PrivValidator. func (pv *FilePV) SignVote( chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, - vote *tmproto.Vote, logger log.Logger) error { - if err := pv.signVote(chainID, quorumType, quorumHash, vote); err != nil { + vote *tmproto.Vote, stateID types.StateID, logger log.Logger) error { + if err := pv.signVote(chainID, quorumType, quorumHash, vote, stateID); err != nil { return fmt.Errorf("error signing vote: %v", err) } return nil @@ -554,31 +554,30 @@ func (pv *FilePV) UpdatePrivateKey( // It may need to set the timestamp as well if the vote is otherwise the same as // a previously signed vote (ie. we crashed after signing but before the vote hit the WAL). func (pv *FilePV) signVote( - chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, vote *tmproto.Vote, + chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, + vote *tmproto.Vote, stateID types.StateID, ) error { height, round, step := vote.Height, vote.Round, voteToStep(vote) lss := pv.LastSignState - // The vote should not have a state ID set if the block ID is set to nil - - if vote.BlockID.Hash == nil && vote.StateID.LastAppHash != nil { - return fmt.Errorf("error : vote should not have a state ID set if the"+ - " block ID for the round (%d/%d) is not set", vote.Height, vote.Round) - } - sameHRS, err := lss.CheckHRS(height, round, step) if err != nil { return err } + // StateID should refer to previous height in order to be valid + if stateID.Height != height-1 { + return fmt.Errorf("invalid height in StateID: is %d, should be %d", stateID.Height, height-1) + } + blockSignID := types.VoteBlockSignID(chainID, vote, quorumType, quorumHash) - stateSignID := types.VoteStateSignID(chainID, vote, quorumType, quorumHash) + stateSignID := stateID.SignID(chainID, quorumType, quorumHash) blockSignBytes := types.VoteBlockSignBytes(chainID, vote) - stateSignBytes := types.VoteStateSignBytes(chainID, vote) + stateSignBytes := stateID.SignBytes(chainID) // We might crash before writing to the wal, // causing us to try to re-sign for the same HRS. diff --git a/privval/file_test.go b/privval/file_test.go index c9776768ff..15d63de304 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -66,13 +66,14 @@ func TestResetValidator(t *testing.T) { height, round := int64(10), int32(1) voteType := tmproto.PrevoteType randBytes := tmrand.Bytes(tmhash.Size) - randStateBytes := tmrand.Bytes(tmhash.Size) blockID := types.BlockID{Hash: randBytes, PartSetHeader: types.PartSetHeader{}} - stateID := types.StateID{LastAppHash: randStateBytes} + + stateID := types.RandStateID().WithHeight(height - 1) + vote := newVote(privVal.Key.ProTxHash, 0, height, round, voteType, blockID, stateID) quorumHash, err := privVal.GetFirstQuorumHash() assert.NoError(t, err) - err = privVal.SignVote("mychainid", 0, quorumHash, vote.ToProto(), nil) + err = privVal.SignVote("mychainid", 0, quorumHash, vote.ToProto(), stateID, nil) assert.NoError(t, err, "expected no error signing vote") // priv val after signing is not same as empty @@ -208,40 +209,40 @@ func TestSignVote(t *testing.T) { randbytes := tmrand.Bytes(tmhash.Size) randbytes2 := tmrand.Bytes(tmhash.Size) - randLastAppHash := tmrand.Bytes(tmhash.Size) block1 := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} block2 := types.BlockID{Hash: randbytes2, PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}} - state := types.StateID{LastAppHash: randLastAppHash} height, round := int64(10), int32(1) voteType := tmproto.PrevoteType + stateID := types.RandStateID().WithHeight(height - 1) + // sign a vote for first time - vote := newVote(privVal.Key.ProTxHash, 0, height, round, voteType, block1, state) + vote := newVote(privVal.Key.ProTxHash, 0, height, round, voteType, block1, stateID) v := vote.ToProto() quorumHash, err := privVal.GetFirstQuorumHash() assert.NoError(err) - err = privVal.SignVote("mychainid", 0, quorumHash, v, nil) + err = privVal.SignVote("mychainid", 0, quorumHash, v, stateID, nil) assert.NoError(err, "expected no error signing vote") // try to sign the same vote again; should be fine - err = privVal.SignVote("mychainid", 0, quorumHash, v, nil) + err = privVal.SignVote("mychainid", 0, quorumHash, v, stateID, nil) assert.NoError(err, "expected no error on signing same vote") // now try some bad votes cases := []*types.Vote{ - newVote(privVal.Key.ProTxHash, 0, height, round-1, voteType, block1, state), // round regression - newVote(privVal.Key.ProTxHash, 0, height-1, round, voteType, block1, state), // height regression - newVote(privVal.Key.ProTxHash, 0, height-2, round+4, voteType, block1, state), // height reg and diff round - newVote(privVal.Key.ProTxHash, 0, height, round, voteType, block2, state), // different block + newVote(privVal.Key.ProTxHash, 0, height, round-1, voteType, block1, stateID), // round regression + newVote(privVal.Key.ProTxHash, 0, height-1, round, voteType, block1, stateID), // height regression + newVote(privVal.Key.ProTxHash, 0, height-2, round+4, voteType, block1, stateID), // height reg and diff round + newVote(privVal.Key.ProTxHash, 0, height, round, voteType, block2, stateID), // different block } for _, c := range cases { cpb := c.ToProto() - err = privVal.SignVote("mychainid", 0, crypto.QuorumHash{}, cpb, nil) + err = privVal.SignVote("mychainid", 0, crypto.QuorumHash{}, cpb, stateID, nil) assert.Error(err, "expected error on signing conflicting vote") } @@ -249,7 +250,7 @@ func TestSignVote(t *testing.T) { blockSignature := vote.BlockSignature stateSignature := vote.StateSignature - err = privVal.SignVote("mychainid", 0, crypto.QuorumHash{}, v, nil) + err = privVal.SignVote("mychainid", 0, crypto.QuorumHash{}, v, stateID, nil) assert.NoError(err) assert.Equal(blockSignature, vote.BlockSignature) assert.Equal(stateSignature, vote.StateSignature) @@ -356,7 +357,6 @@ func newVote(proTxHash types.ProTxHash, idx int32, height int64, round int32, Round: round, Type: typ, BlockID: blockID, - StateID: stateID, } } diff --git a/privval/msgs_test.go b/privval/msgs_test.go index 3b1c077644..e11d710db1 100644 --- a/privval/msgs_test.go +++ b/privval/msgs_test.go @@ -33,9 +33,6 @@ func exampleVote() *types.Vote { Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")), }, }, - StateID: types.StateID{ - LastAppHash: tmhash.Sum([]byte("stateID_hash")), - }, ValidatorProTxHash: crypto.ProTxHashFromSeedBytes([]byte("validator_pro_tx_hash")), ValidatorIndex: 56789, } @@ -74,6 +71,10 @@ func TestPrivvalVectors(t *testing.T) { proposal := exampleProposal() proposalpb := proposal.ToProto() + // Previous state ID + stateID := types.StateID{Height: vote.Height - 1, LastAppHash: []byte("12345678901234567890123456789012")} + stateIDpb := stateID.ToProto() + // Create a Reuseable remote error remoteError := &privproto.RemoteSignerError{Code: 1, Description: "it's a error"} @@ -87,9 +88,9 @@ func TestPrivvalVectors(t *testing.T) { {"pubKey request", &privproto.PubKeyRequest{}, "0a00"}, {"pubKey response", &privproto.PubKeyResponse{PubKey: ppk, Error: nil}, "12340a321a3011c7f5ac5a6d01fd9dde3840f7ebbb6a20deed6fba72a347dd66da2f8c9c977c6604b2cd2e0148206c2add9a8f5ddd74"}, {"pubKey response with error", &privproto.PubKeyResponse{PubKey: crypto2.PublicKey{}, Error: remoteError}, "12140a0012100801120c697427732061206572726f72"}, - {"Vote Request", &privproto.SignVoteRequest{Vote: votepb}, "1a9f010a9c01080110031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a3220959a8f5ef2be68d0ed3a07ed8cff85991ee7995c2ac17030f742c135f9729fbe38d5bb034a220a2062b1d24a04df3db8c9735668e2fc0f9dad612cef4fed678fe07e67388ffd99c6"}, - {"Vote Response", &privproto.SignedVoteResponse{Vote: *votepb, Error: nil}, "229f010a9c01080110031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a3220959a8f5ef2be68d0ed3a07ed8cff85991ee7995c2ac17030f742c135f9729fbe38d5bb034a220a2062b1d24a04df3db8c9735668e2fc0f9dad612cef4fed678fe07e67388ffd99c6"}, - {"Vote Response with error", &privproto.SignedVoteResponse{Vote: tmproto.Vote{}, Error: remoteError}, "221a0a06220212004a0012100801120c697427732061206572726f72"}, + {"Vote Request", &privproto.SignVoteRequest{Vote: votepb, StateId: &stateIDpb}, "1aa0010a78080110031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a3220959a8f5ef2be68d0ed3a07ed8cff85991ee7995c2ac17030f742c135f9729fbe38d5bb032a240a2031323334353637383930313233343536373839303132333435363738393031321002"}, + {"Vote Response", &privproto.SignedVoteResponse{Vote: *votepb, Error: nil}, "227a0a78080110031802224a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a3220959a8f5ef2be68d0ed3a07ed8cff85991ee7995c2ac17030f742c135f9729fbe38d5bb03"}, + {"Vote Response with error", &privproto.SignedVoteResponse{Vote: tmproto.Vote{}, Error: remoteError}, "22180a042202120012100801120c697427732061206572726f72"}, {"Proposal Request", &privproto.SignProposalRequest{Proposal: proposalpb}, "2a700a6e08011003180220022a4a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a320608f49a8ded053a10697427732061207369676e6174757265"}, {"Proposal Response", &privproto.SignedProposalResponse{Proposal: *proposalpb, Error: nil}, "32700a6e08011003180220022a4a0a208b01023386c371778ecb6368573e539afc3cc860ec3a2f614e54fe5652f4fc80122608c0843d122072db3d959635dff1bb567bedaa70573392c5159666a3f8caf11e413aac52207a320608f49a8ded053a10697427732061207369676e6174757265"}, {"Proposal Response with error", &privproto.SignedProposalResponse{Proposal: tmproto.Proposal{}, Error: remoteError}, "32250a112a021200320b088092b8c398feffffff0112100801120c697427732061206572726f72"}, diff --git a/privval/retry_signer_client.go b/privval/retry_signer_client.go index f51b071ce3..85acd50cd6 100644 --- a/privval/retry_signer_client.go +++ b/privval/retry_signer_client.go @@ -131,10 +131,10 @@ func (sc *RetrySignerClient) GetHeight(quorumHash crypto.QuorumHash) (int64, err func (sc *RetrySignerClient) SignVote( chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, - vote *tmproto.Vote, logger log.Logger) error { + vote *tmproto.Vote, stateID types.StateID, logger log.Logger) error { var err error for i := 0; i < sc.retries || sc.retries == 0; i++ { - err = sc.next.SignVote(chainID, quorumType, quorumHash, vote, nil) + err = sc.next.SignVote(chainID, quorumType, quorumHash, vote, stateID, nil) if err == nil { return nil } diff --git a/privval/signer_client.go b/privval/signer_client.go index e71d8c87ae..6d39c52cd6 100644 --- a/privval/signer_client.go +++ b/privval/signer_client.go @@ -169,10 +169,19 @@ func (sc *SignerClient) GetHeight(quorumHash crypto.QuorumHash) (int64, error) { // SignVote requests a remote signer to sign a vote func (sc *SignerClient) SignVote( chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, - vote *tmproto.Vote, logger log.Logger) error { + vote *tmproto.Vote, stateID types.StateID, logger log.Logger) error { // fmt.Printf("--> sending request to sign vote (%d/%d) %v - %v", vote.Height, vote.Round, vote.BlockID, vote) - response, err := sc.endpoint.SendRequest(mustWrapMsg(&privvalproto.SignVoteRequest{Vote: vote, ChainId: chainID, - QuorumType: int32(quorumType), QuorumHash: quorumHash})) + stateIDProto := stateID.ToProto() + + voteRequest := privvalproto.SignVoteRequest{ + Vote: vote, + ChainId: chainID, + QuorumType: int32(quorumType), + QuorumHash: quorumHash, + StateId: &stateIDProto, + } + + response, err := sc.endpoint.SendRequest(mustWrapMsg(&voteRequest)) if err != nil { return err } diff --git a/privval/signer_client_test.go b/privval/signer_client_test.go index 70c63a2585..91992dfd69 100644 --- a/privval/signer_client_test.go +++ b/privval/signer_client_test.go @@ -170,14 +170,12 @@ func TestSignerProposal(t *testing.T) { func TestSignerVote(t *testing.T) { for _, tc := range getSignerTestCases(t) { hash := tmrand.Bytes(tmhash.Size) - stateHash := tmrand.Bytes(tmhash.Size) valProTxHash := tmrand.Bytes(crypto.DefaultHashSize) want := &types.Vote{ Type: tmproto.PrecommitType, Height: 1, Round: 2, BlockID: types.BlockID{Hash: hash, PartSetHeader: types.PartSetHeader{Hash: hash, Total: 2}}, - StateID: types.StateID{LastAppHash: stateHash}, ValidatorProTxHash: valProTxHash, ValidatorIndex: 1, } @@ -187,7 +185,6 @@ func TestSignerVote(t *testing.T) { Height: 1, Round: 2, BlockID: types.BlockID{Hash: hash, PartSetHeader: types.PartSetHeader{Hash: hash, Total: 2}}, - StateID: types.StateID{LastAppHash: stateHash}, ValidatorProTxHash: valProTxHash, ValidatorIndex: 1, } @@ -204,8 +201,12 @@ func TestSignerVote(t *testing.T) { } }) - require.NoError(t, tc.mockPV.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, want.ToProto(), nil)) - require.NoError(t, tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, have.ToProto(), nil)) + stateID := types.RandStateID().WithHeight(want.Height - 1) + + require.NoError(t, tc.mockPV.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, want.ToProto(), + stateID, nil)) + require.NoError(t, tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, have.ToProto(), + stateID, nil)) assert.Equal(t, want.BlockSignature, have.BlockSignature) assert.Equal(t, want.StateSignature, have.StateSignature) @@ -215,14 +216,12 @@ func TestSignerVote(t *testing.T) { func TestSignerVoteResetDeadline(t *testing.T) { for _, tc := range getSignerTestCases(t) { hash := tmrand.Bytes(tmhash.Size) - stateHash := tmrand.Bytes(tmhash.Size) valProTxHash := tmrand.Bytes(crypto.DefaultHashSize) want := &types.Vote{ Type: tmproto.PrecommitType, Height: 1, Round: 2, BlockID: types.BlockID{Hash: hash, PartSetHeader: types.PartSetHeader{Hash: hash, Total: 2}}, - StateID: types.StateID{LastAppHash: stateHash}, ValidatorProTxHash: valProTxHash, ValidatorIndex: 1, } @@ -232,7 +231,6 @@ func TestSignerVoteResetDeadline(t *testing.T) { Height: 1, Round: 2, BlockID: types.BlockID{Hash: hash, PartSetHeader: types.PartSetHeader{Hash: hash, Total: 2}}, - StateID: types.StateID{LastAppHash: stateHash}, ValidatorProTxHash: valProTxHash, ValidatorIndex: 1, } @@ -251,8 +249,12 @@ func TestSignerVoteResetDeadline(t *testing.T) { time.Sleep(testTimeoutReadWrite2o3) - require.NoError(t, tc.mockPV.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, want.ToProto(), nil)) - require.NoError(t, tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, have.ToProto(), nil)) + stateID := types.RandStateID().WithHeight(want.Height - 1) + + require.NoError(t, + tc.mockPV.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, want.ToProto(), stateID, nil)) + require.NoError(t, + tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, have.ToProto(), stateID, nil)) assert.Equal(t, want.BlockSignature, have.BlockSignature) assert.Equal(t, want.StateSignature, have.StateSignature) @@ -261,8 +263,10 @@ func TestSignerVoteResetDeadline(t *testing.T) { // This would exceed the deadline if it was not extended by the previous message time.Sleep(testTimeoutReadWrite2o3) - require.NoError(t, tc.mockPV.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, want.ToProto(), nil)) - require.NoError(t, tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, have.ToProto(), nil)) + require.NoError(t, + tc.mockPV.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, want.ToProto(), stateID, nil)) + require.NoError(t, + tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, have.ToProto(), stateID, nil)) assert.Equal(t, want.BlockSignature, have.BlockSignature) assert.Equal(t, want.StateSignature, have.StateSignature) } @@ -271,14 +275,12 @@ func TestSignerVoteResetDeadline(t *testing.T) { func TestSignerVoteKeepAlive(t *testing.T) { for _, tc := range getSignerTestCases(t) { hash := tmrand.Bytes(tmhash.Size) - stateHash := tmrand.Bytes(tmhash.Size) valProTxHash := tmrand.Bytes(crypto.DefaultHashSize) want := &types.Vote{ Type: tmproto.PrecommitType, Height: 1, Round: 2, BlockID: types.BlockID{Hash: hash, PartSetHeader: types.PartSetHeader{Hash: hash, Total: 2}}, - StateID: types.StateID{LastAppHash: stateHash}, ValidatorProTxHash: valProTxHash, ValidatorIndex: 1, } @@ -288,7 +290,6 @@ func TestSignerVoteKeepAlive(t *testing.T) { Height: 1, Round: 2, BlockID: types.BlockID{Hash: hash, PartSetHeader: types.PartSetHeader{Hash: hash, Total: 2}}, - StateID: types.StateID{LastAppHash: stateHash}, ValidatorProTxHash: valProTxHash, ValidatorIndex: 1, } @@ -305,6 +306,8 @@ func TestSignerVoteKeepAlive(t *testing.T) { } }) + stateID := types.RandStateID().WithHeight(want.Height - 1) + // Check that even if the client does not request a // signature for a long time. The service is still available @@ -314,8 +317,10 @@ func TestSignerVoteKeepAlive(t *testing.T) { time.Sleep(testTimeoutReadWrite * 3) tc.signerServer.Logger.Debug("TEST: Forced Wait DONE---------------------------------------------") - require.NoError(t, tc.mockPV.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, want.ToProto(), nil)) - require.NoError(t, tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, have.ToProto(), nil)) + require.NoError(t, + tc.mockPV.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, want.ToProto(), stateID, nil)) + require.NoError(t, + tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, have.ToProto(), stateID, nil)) assert.Equal(t, want.BlockSignature, have.BlockSignature) assert.Equal(t, want.StateSignature, have.StateSignature) @@ -365,14 +370,12 @@ func TestSignerSignProposalErrors(t *testing.T) { func TestSignerSignVoteErrors(t *testing.T) { for _, tc := range getSignerTestCases(t) { hash := tmrand.Bytes(tmhash.Size) - stateHash := tmrand.Bytes(tmhash.Size) valProTxHash := tmrand.Bytes(crypto.DefaultHashSize) vote := &types.Vote{ Type: tmproto.PrecommitType, Height: 1, Round: 2, BlockID: types.BlockID{Hash: hash, PartSetHeader: types.PartSetHeader{Hash: hash, Total: 2}}, - StateID: types.StateID{LastAppHash: stateHash}, ValidatorProTxHash: valProTxHash, ValidatorIndex: 1, BlockSignature: []byte("signature"), @@ -395,13 +398,15 @@ func TestSignerSignVoteErrors(t *testing.T) { } }) - err := tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, vote.ToProto(), nil) + stateID := types.RandStateID().WithHeight(vote.Height - 1) + + err := tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, vote.ToProto(), stateID, nil) require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) - err = tc.mockPV.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, vote.ToProto(), nil) + err = tc.mockPV.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, vote.ToProto(), stateID, nil) require.Error(t, err) - err = tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, vote.ToProto(), nil) + err = tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, vote.ToProto(), stateID, nil) require.Error(t, err) } } @@ -451,9 +456,11 @@ func TestSignerUnexpectedResponse(t *testing.T) { } }) - want := &types.Vote{Type: tmproto.PrecommitType} + want := &types.Vote{Type: tmproto.PrecommitType, Height: 1} + + stateID := types.RandStateID().WithHeight(want.Height - 1) - e := tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, want.ToProto(), nil) + e := tc.signerClient.SignVote(tc.chainID, tc.quorumType, tc.quorumHash, want.ToProto(), stateID, nil) assert.EqualError(t, e, "empty response") } } diff --git a/privval/signer_requestHandler.go b/privval/signer_requestHandler.go index dc5044a21d..6feba5c130 100644 --- a/privval/signer_requestHandler.go +++ b/privval/signer_requestHandler.go @@ -65,8 +65,24 @@ func DefaultValidationRequestHandler( vote := r.SignVoteRequest.Vote voteQuorumHash := r.SignVoteRequest.QuorumHash voteQuorumType := r.SignVoteRequest.QuorumType + stateIDProto := r.SignVoteRequest.GetStateId() - err = privVal.SignVote(chainID, btcjson.LLMQType(voteQuorumType), voteQuorumHash, vote, nil) + // Convert and validate StateID + stateID, err := types.StateIDFromProto(stateIDProto) + if err == nil { + err = stateID.ValidateBasic() + } + if err != nil { + res = mustWrapMsg(&privvalproto.SignedVoteResponse{ + Vote: tmproto.Vote{}, + Error: &privvalproto.RemoteSignerError{ + Code: 0, + Description: fmt.Sprintf("Cannot parse State ID: %s", err.Error())}, + }) + break + } + + err = privVal.SignVote(chainID, btcjson.LLMQType(voteQuorumType), voteQuorumHash, vote, *stateID, nil) if err != nil { res = mustWrapMsg(&privvalproto.SignedVoteResponse{ Vote: tmproto.Vote{}, Error: &privvalproto.RemoteSignerError{Code: 0, Description: err.Error()}}) diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index 648d8221c5..6be2803871 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -167,9 +167,11 @@ message ResponseInfo { string version = 2; uint64 app_version = 3; - int64 last_block_height = 4; - bytes last_block_app_hash = 5; - uint32 last_core_chain_locked_height = 100; + int64 last_block_height = 4; + bytes last_block_app_hash = 5; + + reserved "last_core_chain_locked_height"; + reserved 100; } // nondeterministic @@ -186,6 +188,7 @@ message ResponseInitChain { bytes app_hash = 3; ValidatorSetUpdate validator_set_update = 100 [(gogoproto.nullable) = false]; tendermint.types.CoreChainLock next_core_chain_lock_update = 101; + uint32 initial_core_height = 102; } message ResponseQuery { @@ -349,9 +352,10 @@ message Validator { // ValidatorUpdate message ValidatorUpdate { - tendermint.crypto.PublicKey pub_key = 1 [(gogoproto.nullable) = true]; - int64 power = 2; - bytes pro_tx_hash = 3; + tendermint.crypto.PublicKey pub_key = 1 [(gogoproto.nullable) = true]; + int64 power = 2; + bytes pro_tx_hash = 3; + string node_address = 4 [(gogoproto.nullable) = true]; // address of the Validator, correct URI } message ValidatorSetUpdate { diff --git a/proto/tendermint/privval/types.pb.go b/proto/tendermint/privval/types.pb.go index 900e558a8c..3768198eba 100644 --- a/proto/tendermint/privval/types.pb.go +++ b/proto/tendermint/privval/types.pb.go @@ -426,10 +426,11 @@ func (m *ProTxHashResponse) GetError() *RemoteSignerError { // SignVoteRequest is a request to sign a vote type SignVoteRequest struct { - Vote *types.Vote `protobuf:"bytes,1,opt,name=vote,proto3" json:"vote,omitempty"` - ChainId string `protobuf:"bytes,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` - QuorumType int32 `protobuf:"varint,3,opt,name=quorum_type,json=quorumType,proto3" json:"quorum_type,omitempty"` - QuorumHash []byte `protobuf:"bytes,4,opt,name=quorum_hash,json=quorumHash,proto3" json:"quorum_hash,omitempty"` + Vote *types.Vote `protobuf:"bytes,1,opt,name=vote,proto3" json:"vote,omitempty"` + ChainId string `protobuf:"bytes,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + QuorumType int32 `protobuf:"varint,3,opt,name=quorum_type,json=quorumType,proto3" json:"quorum_type,omitempty"` + QuorumHash []byte `protobuf:"bytes,4,opt,name=quorum_hash,json=quorumHash,proto3" json:"quorum_hash,omitempty"` + StateId *types.StateID `protobuf:"bytes,5,opt,name=state_id,json=stateId,proto3" json:"state_id,omitempty"` } func (m *SignVoteRequest) Reset() { *m = SignVoteRequest{} } @@ -493,6 +494,13 @@ func (m *SignVoteRequest) GetQuorumHash() []byte { return nil } +func (m *SignVoteRequest) GetStateId() *types.StateID { + if m != nil { + return m.StateId + } + return nil +} + // SignedVoteResponse is a response containing a signed vote or an error type SignedVoteResponse struct { Vote types.Vote `protobuf:"bytes,1,opt,name=vote,proto3" json:"vote"` @@ -978,67 +986,68 @@ func init() { func init() { proto.RegisterFile("tendermint/privval/types.proto", fileDescriptor_cb4e437a5328cf9c) } var fileDescriptor_cb4e437a5328cf9c = []byte{ - // 949 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0x5d, 0x6f, 0xe3, 0x44, - 0x17, 0xb6, 0xdb, 0xa4, 0x1f, 0x27, 0x69, 0x9b, 0x4e, 0xfb, 0xb6, 0x69, 0xb4, 0xaf, 0x5b, 0xc2, - 0xd7, 0xaa, 0x88, 0x04, 0x2d, 0x12, 0x12, 0x5a, 0x6e, 0x68, 0x6b, 0xe1, 0x28, 0x5a, 0x27, 0x4c, - 0x5c, 0x76, 0xb5, 0x12, 0xb2, 0xf2, 0x31, 0x38, 0x66, 0x1b, 0xcf, 0xac, 0xc7, 0xa9, 0x36, 0xd7, - 0xdc, 0x71, 0x85, 0xb4, 0x77, 0xfc, 0x02, 0xc4, 0x2f, 0xe0, 0x27, 0xec, 0xe5, 0x5e, 0x72, 0x85, - 0x50, 0xfb, 0x47, 0x90, 0xc7, 0x13, 0xc7, 0x89, 0x1d, 0x58, 0x54, 0x10, 0x77, 0x9e, 0x73, 0xce, - 0x3c, 0xe7, 0xf1, 0x79, 0xce, 0x99, 0x19, 0xd0, 0x02, 0xe2, 0x0d, 0x88, 0x3f, 0x72, 0xbd, 0xa0, - 0xce, 0x7c, 0xf7, 0xfa, 0xba, 0x7b, 0x55, 0x0f, 0x26, 0x8c, 0xf0, 0x1a, 0xf3, 0x69, 0x40, 0x11, - 0x9a, 0xf9, 0x6b, 0xd2, 0x5f, 0xb9, 0x97, 0xd8, 0xd3, 0xf7, 0x27, 0x2c, 0xa0, 0xf5, 0x67, 0x64, - 0x22, 0x77, 0xcc, 0x79, 0x05, 0x52, 0x12, 0xaf, 0xb2, 0xef, 0x50, 0x87, 0x8a, 0xcf, 0x7a, 0xf8, - 0x15, 0x59, 0xab, 0x0d, 0xd8, 0xc5, 0x64, 0x44, 0x03, 0xd2, 0x71, 0x1d, 0x8f, 0xf8, 0xba, 0xef, - 0x53, 0x1f, 0x21, 0xc8, 0xf5, 0xe9, 0x80, 0x94, 0xd5, 0x13, 0xf5, 0x7e, 0x1e, 0x8b, 0x6f, 0x74, - 0x02, 0x85, 0x01, 0xe1, 0x7d, 0xdf, 0x65, 0x81, 0x4b, 0xbd, 0xf2, 0xca, 0x89, 0x7a, 0x7f, 0x13, - 0x27, 0x4d, 0xd5, 0x26, 0x6c, 0xb5, 0xc7, 0xbd, 0x26, 0x99, 0x60, 0xf2, 0x7c, 0x4c, 0x78, 0x80, - 0x8e, 0x60, 0xa3, 0x3f, 0xec, 0xba, 0x9e, 0xed, 0x0e, 0x04, 0xd4, 0x26, 0x5e, 0x17, 0xeb, 0xc6, - 0x00, 0x1d, 0x43, 0xe1, 0xf9, 0x98, 0xfa, 0xe3, 0x91, 0x3d, 0xec, 0xf2, 0xa1, 0x40, 0x2b, 0x62, - 0x88, 0x4c, 0x46, 0x97, 0x0f, 0xab, 0x16, 0x1c, 0x58, 0x43, 0x9f, 0xf0, 0x21, 0xbd, 0x1a, 0xfc, - 0x73, 0xa8, 0x1f, 0x42, 0xa9, 0xed, 0x53, 0xeb, 0x45, 0xb8, 0xf8, 0x6b, 0xbc, 0xea, 0xf7, 0x2a, - 0x6c, 0x4f, 0x93, 0x73, 0x46, 0x3d, 0x4e, 0xd0, 0x43, 0x58, 0x67, 0xe3, 0x9e, 0xfd, 0x8c, 0x4c, - 0x44, 0x70, 0xe1, 0xc1, 0xbd, 0x5a, 0x42, 0xa7, 0x48, 0x93, 0x5a, 0x7b, 0xdc, 0xbb, 0x72, 0xfb, - 0x4d, 0x32, 0x39, 0xcb, 0xbd, 0xfa, 0xed, 0x58, 0xc1, 0x6b, 0x4c, 0x80, 0xa0, 0x87, 0x90, 0x27, - 0x61, 0x81, 0x05, 0xb3, 0xc2, 0x83, 0x77, 0x6b, 0x69, 0x89, 0x6b, 0x29, 0x35, 0x70, 0xb4, 0xa7, - 0xfa, 0x52, 0x85, 0xc3, 0x54, 0x49, 0xfe, 0x73, 0x56, 0x0c, 0x76, 0x13, 0x15, 0x95, 0x74, 0x34, - 0x28, 0x30, 0x9f, 0xda, 0xc1, 0x8b, 0x48, 0x07, 0x55, 0xe8, 0xb0, 0xc9, 0xa6, 0x71, 0x77, 0xcb, - 0xf8, 0xa3, 0x0a, 0x3b, 0xa1, 0xf9, 0x2b, 0x1a, 0x90, 0xa9, 0x86, 0xa7, 0x90, 0xbb, 0xa6, 0x01, - 0x91, 0x3f, 0x7f, 0x90, 0xc4, 0x8b, 0x46, 0x40, 0x04, 0x8b, 0x98, 0x39, 0xbd, 0x57, 0x96, 0xf5, - 0x4f, 0xb8, 0xab, 0xbc, 0x2a, 0xda, 0x5f, 0xf6, 0x8f, 0x35, 0x61, 0x64, 0xb1, 0xc1, 0x72, 0xa9, - 0x06, 0xfb, 0x4e, 0x05, 0x24, 0x38, 0x0f, 0x22, 0x7a, 0xb2, 0x20, 0x1f, 0xbd, 0x09, 0x3f, 0x29, - 0x4b, 0xc4, 0xf2, 0x4e, 0x25, 0xfa, 0x59, 0x85, 0xbd, 0xd0, 0xdc, 0xf6, 0x29, 0xa3, 0xbc, 0x7b, - 0x35, 0x2d, 0xd3, 0x27, 0xb0, 0xc1, 0xa4, 0x49, 0x52, 0xa9, 0xa4, 0xa9, 0xc4, 0x9b, 0xe2, 0xd8, - 0x7f, 0xb7, 0x64, 0x2f, 0x55, 0x38, 0x88, 0x4a, 0x36, 0xa3, 0x2b, 0xcb, 0xf6, 0xd9, 0xdf, 0xe1, - 0x2b, 0xcb, 0x37, 0x63, 0x7d, 0xa7, 0x12, 0x6e, 0x41, 0xa1, 0xed, 0x7a, 0x8e, 0xac, 0x5c, 0x75, - 0x1b, 0x8a, 0xd1, 0x32, 0x62, 0x56, 0xfd, 0x65, 0x03, 0xd6, 0x1f, 0x11, 0xce, 0xbb, 0x0e, 0x41, - 0x4d, 0xd8, 0x91, 0xc3, 0x67, 0xfb, 0x51, 0xb8, 0x24, 0xfb, 0x56, 0x56, 0xc6, 0xb9, 0xc3, 0xcc, - 0x50, 0xf0, 0x16, 0x9b, 0x3b, 0xdd, 0x4c, 0x28, 0xcd, 0xc0, 0xa2, 0x64, 0x92, 0x7f, 0xf5, 0xcf, - 0xd0, 0xa2, 0x48, 0x43, 0xc1, 0xdb, 0x6c, 0xfe, 0x64, 0xf8, 0x12, 0x76, 0xb9, 0xeb, 0x78, 0x76, - 0xd8, 0x54, 0x31, 0xbd, 0x55, 0x01, 0xf8, 0x76, 0x16, 0xe0, 0xc2, 0x64, 0x19, 0x0a, 0xde, 0xe1, - 0x0b, 0xc3, 0xf6, 0x14, 0xf6, 0xb9, 0xd0, 0x6b, 0x0a, 0x2a, 0x69, 0xe6, 0x04, 0xea, 0x7b, 0xcb, - 0x50, 0xe7, 0x47, 0xc2, 0x50, 0x30, 0xe2, 0xe9, 0x41, 0xf9, 0x1a, 0xfe, 0x27, 0xe8, 0x4e, 0x45, - 0x8c, 0x29, 0xe7, 0x05, 0xf8, 0xfb, 0xcb, 0xc0, 0x17, 0x3a, 0xdd, 0x50, 0xf0, 0x1e, 0xcf, 0x18, - 0x80, 0x6f, 0xa0, 0x2c, 0xa9, 0x27, 0x12, 0x48, 0xfa, 0x6b, 0x22, 0xc3, 0xe9, 0x72, 0xfa, 0x8b, - 0xed, 0x69, 0x28, 0xf8, 0x80, 0x67, 0x37, 0xee, 0x05, 0x14, 0x99, 0xeb, 0x39, 0x31, 0xfb, 0x75, - 0x81, 0x7d, 0x9c, 0xa9, 0xe0, 0xac, 0xcb, 0x0c, 0x05, 0x17, 0xd8, 0x6c, 0x89, 0xbe, 0x80, 0x2d, - 0x89, 0x22, 0x29, 0x6e, 0x08, 0x98, 0x93, 0xe5, 0x30, 0x31, 0xb1, 0x22, 0x4b, 0xac, 0xd1, 0x25, - 0xec, 0x25, 0xce, 0xe3, 0x98, 0xd5, 0xa6, 0x80, 0x7b, 0x27, 0x13, 0x6e, 0xe1, 0x96, 0x34, 0x14, - 0x5c, 0x62, 0x8b, 0x37, 0xe7, 0x13, 0xd8, 0x9f, 0x87, 0x95, 0x34, 0x61, 0xf9, 0xbc, 0xa5, 0xee, - 0x0a, 0x43, 0xc1, 0xbb, 0x2c, 0x75, 0x81, 0x38, 0x70, 0x14, 0x4c, 0xaf, 0x3a, 0x7b, 0x71, 0xb8, - 0x0a, 0xcb, 0x85, 0xca, 0x7e, 0x32, 0x84, 0x42, 0x05, 0xd9, 0x8f, 0x89, 0x6f, 0xa1, 0x92, 0x95, - 0x48, 0xfe, 0x48, 0x51, 0x64, 0xfa, 0xe0, 0x8d, 0x32, 0xc5, 0xbf, 0x73, 0x18, 0x64, 0xbb, 0xce, - 0xf2, 0xb0, 0xca, 0xc7, 0xa3, 0xd3, 0x9f, 0x54, 0x58, 0x13, 0x47, 0x0d, 0x47, 0x08, 0xb6, 0x75, - 0x8c, 0x5b, 0xb8, 0x63, 0x5f, 0x9a, 0x4d, 0xb3, 0xf5, 0xd8, 0x2c, 0x29, 0x48, 0x83, 0x4a, 0x6c, - 0xd3, 0x9f, 0xb4, 0xf5, 0x73, 0x4b, 0xbf, 0xb0, 0xb1, 0xde, 0x69, 0xb7, 0xcc, 0x8e, 0x5e, 0x52, - 0x51, 0x19, 0xf6, 0xa5, 0xdf, 0x6c, 0xd9, 0xe7, 0x2d, 0xd3, 0xd4, 0xcf, 0xad, 0x46, 0xcb, 0x2c, - 0xad, 0xa0, 0xff, 0xc3, 0x91, 0xf4, 0xcc, 0xcc, 0xb6, 0xd5, 0x78, 0xa4, 0xb7, 0x2e, 0xad, 0xd2, - 0x2a, 0x3a, 0x84, 0x3d, 0xe9, 0xc6, 0xfa, 0xe7, 0x17, 0xb1, 0x23, 0x97, 0x40, 0x7c, 0x8c, 0x1b, - 0x96, 0x1e, 0x7b, 0xf2, 0x67, 0x9d, 0x57, 0x37, 0x9a, 0xfa, 0xfa, 0x46, 0x53, 0x7f, 0xbf, 0xd1, - 0xd4, 0x1f, 0x6e, 0x35, 0xe5, 0xf5, 0xad, 0xa6, 0xfc, 0x7a, 0xab, 0x29, 0x4f, 0x3f, 0x75, 0xdc, - 0x60, 0x38, 0xee, 0xd5, 0xfa, 0x74, 0x54, 0x4f, 0xbe, 0x3a, 0x93, 0x4f, 0xda, 0xf0, 0xa5, 0x99, - 0x7e, 0xe3, 0xf6, 0xd6, 0x84, 0xe7, 0xe3, 0x3f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x8a, 0x7c, 0xe0, - 0x47, 0x00, 0x0b, 0x00, 0x00, + // 970 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0xdd, 0x6e, 0xe3, 0x44, + 0x14, 0xb6, 0xb7, 0x49, 0x93, 0x9e, 0xa4, 0x6d, 0x3a, 0x2d, 0x6d, 0x1a, 0x2d, 0x69, 0x09, 0x7f, + 0xab, 0x22, 0x12, 0xb4, 0x20, 0x24, 0xb4, 0xdc, 0xd0, 0xd6, 0xc2, 0x51, 0xb4, 0x49, 0x98, 0xb8, + 0xec, 0x6a, 0x25, 0x64, 0xe5, 0x67, 0x70, 0xcc, 0x36, 0x9e, 0x59, 0x8f, 0x53, 0x6d, 0xae, 0xb9, + 0xe3, 0x0a, 0x89, 0x97, 0x40, 0x3c, 0x01, 0x8f, 0xb0, 0x97, 0xbd, 0xe4, 0x0a, 0xa1, 0xf6, 0x45, + 0x90, 0xc7, 0x13, 0xc7, 0xb1, 0x1d, 0x58, 0x54, 0x10, 0x77, 0x33, 0xe7, 0x9c, 0xf9, 0xce, 0x77, + 0xfe, 0x66, 0x06, 0xaa, 0x1e, 0x71, 0x46, 0xc4, 0x9d, 0xd8, 0x8e, 0xd7, 0x60, 0xae, 0x7d, 0x75, + 0xd5, 0xbf, 0x6c, 0x78, 0x33, 0x46, 0x78, 0x9d, 0xb9, 0xd4, 0xa3, 0x08, 0x2d, 0xf4, 0x75, 0xa9, + 0xaf, 0xdc, 0x8f, 0x9c, 0x19, 0xba, 0x33, 0xe6, 0xd1, 0xc6, 0x73, 0x32, 0x93, 0x27, 0x96, 0xb4, + 0x02, 0x29, 0x8a, 0x57, 0xd9, 0xb3, 0xa8, 0x45, 0xc5, 0xb2, 0xe1, 0xaf, 0x02, 0x69, 0xad, 0x09, + 0x3b, 0x98, 0x4c, 0xa8, 0x47, 0x7a, 0xb6, 0xe5, 0x10, 0x57, 0x73, 0x5d, 0xea, 0x22, 0x04, 0x99, + 0x21, 0x1d, 0x91, 0xb2, 0x7a, 0xac, 0x3e, 0xc8, 0x62, 0xb1, 0x46, 0xc7, 0x50, 0x18, 0x11, 0x3e, + 0x74, 0x6d, 0xe6, 0xd9, 0xd4, 0x29, 0xdf, 0x3b, 0x56, 0x1f, 0x6c, 0xe0, 0xa8, 0xa8, 0xd6, 0x82, + 0xcd, 0xee, 0x74, 0xd0, 0x22, 0x33, 0x4c, 0x5e, 0x4c, 0x09, 0xf7, 0xd0, 0x21, 0xe4, 0x87, 0xe3, + 0xbe, 0xed, 0x98, 0xf6, 0x48, 0x40, 0x6d, 0xe0, 0x9c, 0xd8, 0x37, 0x47, 0xe8, 0x08, 0x0a, 0x2f, + 0xa6, 0xd4, 0x9d, 0x4e, 0xcc, 0x71, 0x9f, 0x8f, 0x05, 0x5a, 0x11, 0x43, 0x20, 0xd2, 0xfb, 0x7c, + 0x5c, 0x33, 0x60, 0xdf, 0x18, 0xbb, 0x84, 0x8f, 0xe9, 0xe5, 0xe8, 0xdf, 0x43, 0xfd, 0x10, 0x4a, + 0x5d, 0x97, 0x1a, 0x2f, 0xfd, 0xcd, 0xdf, 0xe3, 0xd5, 0x7e, 0x50, 0x61, 0x6b, 0xee, 0x9c, 0x33, + 0xea, 0x70, 0x82, 0x1e, 0x41, 0x8e, 0x4d, 0x07, 0xe6, 0x73, 0x32, 0x13, 0xc6, 0x85, 0x87, 0xf7, + 0xeb, 0x91, 0x3a, 0x05, 0x35, 0xa9, 0x77, 0xa7, 0x83, 0x4b, 0x7b, 0xd8, 0x22, 0xb3, 0xd3, 0xcc, + 0xab, 0xdf, 0x8f, 0x14, 0xbc, 0xce, 0x04, 0x08, 0x7a, 0x04, 0x59, 0xe2, 0x27, 0x58, 0x30, 0x2b, + 0x3c, 0x7c, 0xb7, 0x9e, 0x2c, 0x71, 0x3d, 0x51, 0x0d, 0x1c, 0x9c, 0xa9, 0xfd, 0xa4, 0xc2, 0x41, + 0x22, 0x25, 0xff, 0x3b, 0x2b, 0x06, 0x3b, 0x91, 0x8c, 0x4a, 0x3a, 0x55, 0x28, 0x30, 0x97, 0x9a, + 0xde, 0xcb, 0xa0, 0x0e, 0xaa, 0xa8, 0xc3, 0x06, 0x9b, 0xdb, 0xdd, 0xcd, 0xe3, 0xb5, 0x0a, 0xdb, + 0xbe, 0xf8, 0x6b, 0xea, 0x91, 0x79, 0x0d, 0x4f, 0x20, 0x73, 0x45, 0x3d, 0x22, 0x83, 0xdf, 0x8f, + 0xe2, 0x05, 0x23, 0x20, 0x8c, 0x85, 0xcd, 0x52, 0xbd, 0xef, 0xad, 0xea, 0x1f, 0xff, 0x54, 0x79, + 0x4d, 0xb4, 0xbf, 0xec, 0x1f, 0x63, 0xc6, 0x48, 0xbc, 0xc1, 0x32, 0xf1, 0x06, 0x43, 0x9f, 0x40, + 0x9e, 0x7b, 0x7d, 0x8f, 0xf8, 0xe0, 0x59, 0x41, 0xe6, 0x30, 0x49, 0xa6, 0xe7, 0x5b, 0x34, 0xcf, + 0x71, 0x4e, 0x98, 0x36, 0x47, 0xb5, 0xef, 0x55, 0x40, 0x22, 0xd2, 0x51, 0x10, 0x94, 0x4c, 0xe3, + 0x47, 0xaf, 0x13, 0x95, 0x2c, 0x66, 0x10, 0xdb, 0x9d, 0x12, 0xfb, 0x8b, 0x0a, 0xbb, 0xbe, 0xb8, + 0xeb, 0x52, 0x46, 0x79, 0xff, 0x72, 0x9e, 0xdc, 0x4f, 0x21, 0xcf, 0xa4, 0x48, 0x52, 0xa9, 0x24, + 0xa9, 0x84, 0x87, 0x42, 0xdb, 0xff, 0x34, 0xd1, 0xfe, 0x34, 0xec, 0x07, 0x29, 0x5b, 0xd0, 0x95, + 0x69, 0xfb, 0xfc, 0x9f, 0xf0, 0x95, 0xe9, 0x5b, 0xb0, 0xbe, 0x53, 0x0a, 0x37, 0xa1, 0xd0, 0xb5, + 0x1d, 0x4b, 0x66, 0xae, 0xb6, 0x05, 0xc5, 0x60, 0x1b, 0x30, 0xab, 0xfd, 0x9a, 0x87, 0xdc, 0x63, + 0xc2, 0x79, 0xdf, 0x22, 0xa8, 0x05, 0xdb, 0x72, 0x64, 0x4d, 0x37, 0x30, 0x97, 0x64, 0xdf, 0x4a, + 0xf3, 0xb8, 0x74, 0x05, 0xea, 0x0a, 0xde, 0x64, 0x4b, 0x77, 0x62, 0x1b, 0x4a, 0x0b, 0xb0, 0xc0, + 0x99, 0xe4, 0x5f, 0xfb, 0x2b, 0xb4, 0xc0, 0x52, 0x57, 0xf0, 0x16, 0x5b, 0xbe, 0x4f, 0xbe, 0x82, + 0x1d, 0x6e, 0x5b, 0x8e, 0xe9, 0x37, 0x55, 0x48, 0x6f, 0x4d, 0x00, 0xbe, 0x9d, 0x06, 0x18, 0x9b, + 0x47, 0x5d, 0xc1, 0xdb, 0x3c, 0x36, 0xa2, 0xcf, 0x60, 0x8f, 0x8b, 0x7a, 0xcd, 0x41, 0x25, 0xcd, + 0x8c, 0x40, 0x7d, 0x6f, 0x15, 0xea, 0xf2, 0x48, 0xe8, 0x0a, 0x46, 0x3c, 0x39, 0x28, 0xdf, 0xc0, + 0x1b, 0x82, 0xee, 0xbc, 0x88, 0x21, 0xe5, 0x60, 0x04, 0xdf, 0x5f, 0x05, 0x1e, 0xeb, 0x74, 0x5d, + 0xc1, 0xbb, 0x3c, 0x65, 0x00, 0xbe, 0x85, 0xb2, 0xa4, 0x1e, 0x71, 0x20, 0xe9, 0xaf, 0x0b, 0x0f, + 0x27, 0xab, 0xe9, 0xc7, 0xdb, 0x53, 0x57, 0xf0, 0x3e, 0x4f, 0x6f, 0xdc, 0x73, 0x28, 0x32, 0xdb, + 0xb1, 0x42, 0xf6, 0x39, 0x81, 0x7d, 0x94, 0x5a, 0xc1, 0x45, 0x97, 0xe9, 0x0a, 0x2e, 0xb0, 0xc5, + 0x16, 0x7d, 0x09, 0x9b, 0x12, 0x45, 0x52, 0xcc, 0x0b, 0x98, 0xe3, 0xd5, 0x30, 0x21, 0xb1, 0x22, + 0x8b, 0xec, 0xd1, 0x05, 0xec, 0x46, 0x6e, 0xf1, 0x90, 0xd5, 0x86, 0x80, 0x7b, 0x27, 0x15, 0x2e, + 0xf6, 0xb6, 0xea, 0x0a, 0x2e, 0xb1, 0xf8, 0x7b, 0xfb, 0x14, 0xf6, 0x96, 0x61, 0x25, 0x4d, 0x58, + 0x3d, 0x6f, 0x89, 0x17, 0x46, 0x57, 0xf0, 0x0e, 0x4b, 0x3c, 0x3b, 0x16, 0x1c, 0x7a, 0xf3, 0x07, + 0xd2, 0x8c, 0x0f, 0x57, 0x61, 0x75, 0xa1, 0xd2, 0x3f, 0x1a, 0x7e, 0xa1, 0xbc, 0xf4, 0x2f, 0xc8, + 0x77, 0x50, 0x49, 0x73, 0x24, 0x03, 0x29, 0x0a, 0x4f, 0x1f, 0xbc, 0x96, 0xa7, 0x30, 0x9c, 0x03, + 0x2f, 0x5d, 0x75, 0x9a, 0x85, 0x35, 0x3e, 0x9d, 0x9c, 0xfc, 0xac, 0xc2, 0xba, 0xb8, 0x6a, 0x38, + 0x42, 0xb0, 0xa5, 0x61, 0xdc, 0xc1, 0x3d, 0xf3, 0xa2, 0xdd, 0x6a, 0x77, 0x9e, 0xb4, 0x4b, 0x0a, + 0xaa, 0x42, 0x25, 0x94, 0x69, 0x4f, 0xbb, 0xda, 0x99, 0xa1, 0x9d, 0x9b, 0x58, 0xeb, 0x75, 0x3b, + 0xed, 0x9e, 0x56, 0x52, 0x51, 0x19, 0xf6, 0xa4, 0xbe, 0xdd, 0x31, 0xcf, 0x3a, 0xed, 0xb6, 0x76, + 0x66, 0x34, 0x3b, 0xed, 0xd2, 0x3d, 0xf4, 0x26, 0x1c, 0x4a, 0xcd, 0x42, 0x6c, 0x1a, 0xcd, 0xc7, + 0x5a, 0xe7, 0xc2, 0x28, 0xad, 0xa1, 0x03, 0xd8, 0x95, 0x6a, 0xac, 0x7d, 0x71, 0x1e, 0x2a, 0x32, + 0x11, 0xc4, 0x27, 0xb8, 0x69, 0x68, 0xa1, 0x26, 0x7b, 0xda, 0x7b, 0x75, 0x53, 0x55, 0xaf, 0x6f, + 0xaa, 0xea, 0x1f, 0x37, 0x55, 0xf5, 0xc7, 0xdb, 0xaa, 0x72, 0x7d, 0x5b, 0x55, 0x7e, 0xbb, 0xad, + 0x2a, 0xcf, 0x3e, 0xb3, 0x6c, 0x6f, 0x3c, 0x1d, 0xd4, 0x87, 0x74, 0xd2, 0x88, 0xfe, 0x55, 0xa3, + 0x1f, 0x61, 0xff, 0x7f, 0x9a, 0xfc, 0x19, 0x0f, 0xd6, 0x85, 0xe6, 0xe3, 0x3f, 0x03, 0x00, 0x00, + 0xff, 0xff, 0xac, 0x63, 0x2b, 0x5c, 0x36, 0x0b, 0x00, 0x00, } func (m *RemoteSignerError) Marshal() (dAtA []byte, err error) { @@ -1332,6 +1341,18 @@ func (m *SignVoteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.StateId != nil { + { + size, err := m.StateId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } if len(m.QuorumHash) > 0 { i -= len(m.QuorumHash) copy(dAtA[i:], m.QuorumHash) @@ -1982,6 +2003,10 @@ func (m *SignVoteRequest) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + if m.StateId != nil { + l = m.StateId.Size() + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -3143,6 +3168,42 @@ func (m *SignVoteRequest) Unmarshal(dAtA []byte) error { m.QuorumHash = []byte{} } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StateId", 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.StateId == nil { + m.StateId = &types.StateID{} + } + if err := m.StateId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/proto/tendermint/privval/types.proto b/proto/tendermint/privval/types.proto index 35dc02de5a..690e26154c 100644 --- a/proto/tendermint/privval/types.proto +++ b/proto/tendermint/privval/types.proto @@ -58,10 +58,11 @@ message ProTxHashResponse { // SignVoteRequest is a request to sign a vote message SignVoteRequest { - tendermint.types.Vote vote = 1; - string chain_id = 2; - int32 quorum_type = 3; - bytes quorum_hash = 4; + tendermint.types.Vote vote = 1; + string chain_id = 2; + int32 quorum_type = 3; + bytes quorum_hash = 4; + tendermint.types.StateID state_id = 5; } // SignedVoteResponse is a response containing a signed vote or an error diff --git a/proto/tendermint/types/canonical.pb.go b/proto/tendermint/types/canonical.pb.go index 8cc2ec0904..0bdbe94864 100644 --- a/proto/tendermint/types/canonical.pb.go +++ b/proto/tendermint/types/canonical.pb.go @@ -80,50 +80,6 @@ func (m *CanonicalBlockID) GetPartSetHeader() CanonicalPartSetHeader { return CanonicalPartSetHeader{} } -type CanonicalStateID struct { - LastAppHash []byte `protobuf:"bytes,1,opt,name=last_app_hash,json=lastAppHash,proto3" json:"last_app_hash,omitempty"` -} - -func (m *CanonicalStateID) Reset() { *m = CanonicalStateID{} } -func (m *CanonicalStateID) String() string { return proto.CompactTextString(m) } -func (*CanonicalStateID) ProtoMessage() {} -func (*CanonicalStateID) Descriptor() ([]byte, []int) { - return fileDescriptor_8d1a1a84ff7267ed, []int{1} -} -func (m *CanonicalStateID) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *CanonicalStateID) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_CanonicalStateID.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 *CanonicalStateID) XXX_Merge(src proto.Message) { - xxx_messageInfo_CanonicalStateID.Merge(m, src) -} -func (m *CanonicalStateID) XXX_Size() int { - return m.Size() -} -func (m *CanonicalStateID) XXX_DiscardUnknown() { - xxx_messageInfo_CanonicalStateID.DiscardUnknown(m) -} - -var xxx_messageInfo_CanonicalStateID proto.InternalMessageInfo - -func (m *CanonicalStateID) GetLastAppHash() []byte { - if m != nil { - return m.LastAppHash - } - return nil -} - type CanonicalPartSetHeader struct { Total uint32 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"` Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` @@ -133,7 +89,7 @@ func (m *CanonicalPartSetHeader) Reset() { *m = CanonicalPartSetHeader{} func (m *CanonicalPartSetHeader) String() string { return proto.CompactTextString(m) } func (*CanonicalPartSetHeader) ProtoMessage() {} func (*CanonicalPartSetHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_8d1a1a84ff7267ed, []int{2} + return fileDescriptor_8d1a1a84ff7267ed, []int{1} } func (m *CanonicalPartSetHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -190,7 +146,7 @@ func (m *CanonicalProposal) Reset() { *m = CanonicalProposal{} } func (m *CanonicalProposal) String() string { return proto.CompactTextString(m) } func (*CanonicalProposal) ProtoMessage() {} func (*CanonicalProposal) Descriptor() ([]byte, []int) { - return fileDescriptor_8d1a1a84ff7267ed, []int{3} + return fileDescriptor_8d1a1a84ff7267ed, []int{2} } func (m *CanonicalProposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -273,7 +229,6 @@ type CanonicalVote struct { 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"` - StateID *CanonicalStateID `protobuf:"bytes,7,opt,name=state_id,json=stateId,proto3" json:"state_id,omitempty"` ChainID string `protobuf:"bytes,6,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` } @@ -281,7 +236,7 @@ func (m *CanonicalVote) Reset() { *m = CanonicalVote{} } func (m *CanonicalVote) String() string { return proto.CompactTextString(m) } func (*CanonicalVote) ProtoMessage() {} func (*CanonicalVote) Descriptor() ([]byte, []int) { - return fileDescriptor_8d1a1a84ff7267ed, []int{4} + return fileDescriptor_8d1a1a84ff7267ed, []int{3} } func (m *CanonicalVote) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -338,13 +293,6 @@ func (m *CanonicalVote) GetBlockID() *CanonicalBlockID { return nil } -func (m *CanonicalVote) GetStateID() *CanonicalStateID { - if m != nil { - return m.StateID - } - return nil -} - func (m *CanonicalVote) GetChainID() string { if m != nil { return m.ChainID @@ -352,106 +300,48 @@ func (m *CanonicalVote) GetChainID() string { return "" } -type CanonicalStateVote struct { - Height int64 `protobuf:"fixed64,1,opt,name=height,proto3" json:"height,omitempty"` - StateID *CanonicalStateID `protobuf:"bytes,2,opt,name=state_id,json=stateId,proto3" json:"state_id,omitempty"` -} - -func (m *CanonicalStateVote) Reset() { *m = CanonicalStateVote{} } -func (m *CanonicalStateVote) String() string { return proto.CompactTextString(m) } -func (*CanonicalStateVote) ProtoMessage() {} -func (*CanonicalStateVote) Descriptor() ([]byte, []int) { - return fileDescriptor_8d1a1a84ff7267ed, []int{5} -} -func (m *CanonicalStateVote) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *CanonicalStateVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_CanonicalStateVote.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 *CanonicalStateVote) XXX_Merge(src proto.Message) { - xxx_messageInfo_CanonicalStateVote.Merge(m, src) -} -func (m *CanonicalStateVote) XXX_Size() int { - return m.Size() -} -func (m *CanonicalStateVote) XXX_DiscardUnknown() { - xxx_messageInfo_CanonicalStateVote.DiscardUnknown(m) -} - -var xxx_messageInfo_CanonicalStateVote proto.InternalMessageInfo - -func (m *CanonicalStateVote) GetHeight() int64 { - if m != nil { - return m.Height - } - return 0 -} - -func (m *CanonicalStateVote) GetStateID() *CanonicalStateID { - if m != nil { - return m.StateID - } - return nil -} - func init() { proto.RegisterType((*CanonicalBlockID)(nil), "tendermint.types.CanonicalBlockID") - proto.RegisterType((*CanonicalStateID)(nil), "tendermint.types.CanonicalStateID") proto.RegisterType((*CanonicalPartSetHeader)(nil), "tendermint.types.CanonicalPartSetHeader") proto.RegisterType((*CanonicalProposal)(nil), "tendermint.types.CanonicalProposal") proto.RegisterType((*CanonicalVote)(nil), "tendermint.types.CanonicalVote") - proto.RegisterType((*CanonicalStateVote)(nil), "tendermint.types.CanonicalStateVote") } func init() { proto.RegisterFile("tendermint/types/canonical.proto", fileDescriptor_8d1a1a84ff7267ed) } var fileDescriptor_8d1a1a84ff7267ed = []byte{ - // 556 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0xcd, 0x6a, 0xdb, 0x40, - 0x10, 0xf6, 0x3a, 0x8e, 0x2d, 0xaf, 0xe3, 0xd6, 0x5d, 0x42, 0x30, 0xa6, 0x48, 0x46, 0x87, 0xe2, - 0x5e, 0x24, 0x48, 0xa0, 0x3d, 0x57, 0xc9, 0x21, 0x0e, 0x2d, 0x4d, 0xd7, 0x21, 0x87, 0x5e, 0xc4, - 0xda, 0xda, 0x4a, 0xa2, 0xb2, 0x76, 0x91, 0xd6, 0xd0, 0x5c, 0xfa, 0x0c, 0x79, 0x96, 0x3e, 0x45, - 0x8e, 0x39, 0xb6, 0x17, 0xb7, 0xc8, 0x2f, 0x52, 0x76, 0x25, 0x5b, 0x72, 0x52, 0x7c, 0x09, 0xf4, - 0x22, 0xe6, 0x4f, 0xdf, 0x7c, 0xf3, 0xcd, 0xb0, 0x70, 0x28, 0x68, 0xec, 0xd1, 0x64, 0x1e, 0xc6, - 0xc2, 0x16, 0x37, 0x9c, 0xa6, 0xf6, 0x8c, 0xc4, 0x2c, 0x0e, 0x67, 0x24, 0xb2, 0x78, 0xc2, 0x04, - 0x43, 0xbd, 0xb2, 0xc2, 0x52, 0x15, 0x83, 0x43, 0x9f, 0xf9, 0x4c, 0x25, 0x6d, 0x69, 0xe5, 0x75, - 0x83, 0x97, 0x8f, 0x90, 0xd4, 0xb7, 0xc8, 0x1a, 0x3e, 0x63, 0x7e, 0x44, 0x6d, 0xe5, 0x4d, 0x17, - 0x5f, 0x6c, 0x11, 0xce, 0x69, 0x2a, 0xc8, 0x9c, 0xe7, 0x05, 0xe6, 0x77, 0xd8, 0x3b, 0x5d, 0x77, - 0x76, 0x22, 0x36, 0xfb, 0x3a, 0x3e, 0x43, 0x08, 0x36, 0x02, 0x92, 0x06, 0x7d, 0x30, 0x04, 0xa3, - 0x03, 0xac, 0x6c, 0x74, 0x0d, 0x9f, 0x73, 0x92, 0x08, 0x37, 0xa5, 0xc2, 0x0d, 0x28, 0xf1, 0x68, - 0xd2, 0xaf, 0x0f, 0xc1, 0xa8, 0x73, 0x3c, 0xb2, 0x1e, 0x12, 0xb5, 0x36, 0x80, 0x97, 0x24, 0x11, - 0x13, 0x2a, 0xce, 0x55, 0xbd, 0xd3, 0xb8, 0x5b, 0x1a, 0x35, 0xdc, 0xe5, 0xd5, 0xa0, 0xf9, 0xa6, - 0xd2, 0x7f, 0x22, 0x88, 0xa0, 0xe3, 0x33, 0x64, 0xc2, 0x6e, 0x44, 0x52, 0xe1, 0x12, 0xce, 0xdd, - 0x0a, 0x91, 0x8e, 0x0c, 0xbe, 0xe3, 0xfc, 0x9c, 0xa4, 0x81, 0xe9, 0xc0, 0xa3, 0x7f, 0xb7, 0x41, - 0x87, 0x70, 0x5f, 0x30, 0x41, 0x22, 0xf5, 0x57, 0x17, 0xe7, 0xce, 0x66, 0xa6, 0x7a, 0x39, 0x93, - 0xf9, 0xab, 0x0e, 0x5f, 0x94, 0x20, 0x09, 0xe3, 0x2c, 0x25, 0x11, 0x3a, 0x81, 0x0d, 0x39, 0x86, - 0xfa, 0xfd, 0xd9, 0xb1, 0xf1, 0x78, 0xbc, 0x49, 0xe8, 0xc7, 0xd4, 0xfb, 0x90, 0xfa, 0x57, 0x37, - 0x9c, 0x62, 0x55, 0x8c, 0x8e, 0x60, 0x33, 0xa0, 0xa1, 0x1f, 0x08, 0xd5, 0xa0, 0x87, 0x0b, 0x4f, - 0x92, 0x49, 0xd8, 0x22, 0xf6, 0xfa, 0x7b, 0x2a, 0x9c, 0x3b, 0xe8, 0x35, 0x6c, 0x73, 0x16, 0xb9, - 0x79, 0xa6, 0x31, 0x04, 0xa3, 0x3d, 0xe7, 0x20, 0x5b, 0x1a, 0xda, 0xe5, 0xc7, 0xf7, 0x58, 0xc6, - 0xb0, 0xc6, 0x59, 0xa4, 0x2c, 0x74, 0x01, 0xb5, 0xa9, 0x5c, 0x8b, 0x1b, 0x7a, 0xfd, 0x7d, 0x25, - 0xb8, 0xb9, 0x43, 0xf0, 0x62, 0x83, 0x4e, 0x27, 0x5b, 0x1a, 0xad, 0xc2, 0xc1, 0x2d, 0x05, 0x30, - 0xf6, 0x90, 0x03, 0xdb, 0x9b, 0xf5, 0xf7, 0x9b, 0x0a, 0x6c, 0x60, 0xe5, 0x07, 0x62, 0xad, 0x0f, - 0xc4, 0xba, 0x5a, 0x57, 0x38, 0x9a, 0xdc, 0xd7, 0xed, 0x6f, 0x03, 0xe0, 0xf2, 0x37, 0xf4, 0x0a, - 0x6a, 0xb3, 0x80, 0x84, 0xb1, 0xe4, 0xd3, 0x1a, 0x82, 0x51, 0x3b, 0xef, 0x75, 0x2a, 0x63, 0xb2, - 0x97, 0x4a, 0x8e, 0x3d, 0xf3, 0x47, 0x1d, 0x76, 0x37, 0xb4, 0xae, 0x99, 0xa0, 0xff, 0x43, 0xd7, - 0xaa, 0x58, 0x8d, 0x27, 0x8a, 0x75, 0x01, 0xb5, 0x54, 0xde, 0xe3, 0x7a, 0xd0, 0xdd, 0x58, 0xc5, - 0xe9, 0xe6, 0x58, 0x85, 0x83, 0x5b, 0x0a, 0x60, 0xec, 0x6d, 0x89, 0xd6, 0xdc, 0x21, 0xda, 0x37, - 0x88, 0xb6, 0x11, 0x95, 0x70, 0xa5, 0x06, 0x60, 0x4b, 0x83, 0x2a, 0xc3, 0xfa, 0xd3, 0x18, 0x3a, - 0x9f, 0xee, 0x32, 0x1d, 0xdc, 0x67, 0x3a, 0xf8, 0x93, 0xe9, 0xe0, 0x76, 0xa5, 0xd7, 0xee, 0x57, - 0x7a, 0xed, 0xe7, 0x4a, 0xaf, 0x7d, 0x7e, 0xeb, 0x87, 0x22, 0x58, 0x4c, 0xad, 0x19, 0x9b, 0xdb, - 0xd5, 0xa7, 0xa6, 0x34, 0xf3, 0x27, 0xe9, 0xe1, 0x33, 0x34, 0x6d, 0xaa, 0xf8, 0xc9, 0xdf, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x35, 0xd4, 0x12, 0x81, 0xeb, 0x04, 0x00, 0x00, + // 485 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x53, 0xbf, 0x6f, 0xd3, 0x40, + 0x14, 0xce, 0xa5, 0x6e, 0xe2, 0x5c, 0x1b, 0x08, 0xa7, 0xaa, 0xb2, 0x22, 0x64, 0x5b, 0x1e, 0x90, + 0x59, 0x6c, 0xa9, 0x1d, 0xd8, 0x5d, 0x06, 0x82, 0x40, 0x14, 0xb7, 0xea, 0xc0, 0x12, 0x5d, 0xec, + 0xc3, 0xb6, 0x70, 0x7c, 0x27, 0xfb, 0x32, 0x74, 0xe1, 0x6f, 0xe8, 0x9f, 0xd5, 0xb1, 0x23, 0x2c, + 0x01, 0x39, 0x12, 0x7f, 0x07, 0xba, 0xe7, 0x24, 0x8e, 0x5a, 0x60, 0x41, 0xea, 0x62, 0xbd, 0x1f, + 0x9f, 0xbf, 0xf7, 0xdd, 0xf7, 0xf4, 0xb0, 0x2d, 0x59, 0x11, 0xb3, 0x72, 0x9e, 0x15, 0xd2, 0x97, + 0xd7, 0x82, 0x55, 0x7e, 0x44, 0x0b, 0x5e, 0x64, 0x11, 0xcd, 0x3d, 0x51, 0x72, 0xc9, 0xc9, 0xa8, + 0x45, 0x78, 0x80, 0x18, 0x1f, 0x25, 0x3c, 0xe1, 0xd0, 0xf4, 0x55, 0xd4, 0xe0, 0xc6, 0xcf, 0x1f, + 0x30, 0xc1, 0x77, 0xdd, 0xb5, 0x12, 0xce, 0x93, 0x9c, 0xf9, 0x90, 0xcd, 0x16, 0x9f, 0x7d, 0x99, + 0xcd, 0x59, 0x25, 0xe9, 0x5c, 0x34, 0x00, 0xe7, 0x2b, 0x1e, 0x9d, 0x6d, 0x26, 0x07, 0x39, 0x8f, + 0xbe, 0x4c, 0x5e, 0x13, 0x82, 0xb5, 0x94, 0x56, 0xa9, 0x81, 0x6c, 0xe4, 0x1e, 0x86, 0x10, 0x93, + 0x2b, 0xfc, 0x54, 0xd0, 0x52, 0x4e, 0x2b, 0x26, 0xa7, 0x29, 0xa3, 0x31, 0x2b, 0x8d, 0xae, 0x8d, + 0xdc, 0x83, 0x13, 0xd7, 0xbb, 0x2f, 0xd4, 0xdb, 0x12, 0x9e, 0xd3, 0x52, 0x5e, 0x30, 0xf9, 0x06, + 0xf0, 0x81, 0x76, 0xbb, 0xb4, 0x3a, 0xe1, 0x50, 0xec, 0x16, 0x9d, 0x00, 0x1f, 0xff, 0x19, 0x4e, + 0x8e, 0xf0, 0xbe, 0xe4, 0x92, 0xe6, 0x20, 0x63, 0x18, 0x36, 0xc9, 0x56, 0x5b, 0xb7, 0xd5, 0xe6, + 0x7c, 0xef, 0xe2, 0x67, 0x2d, 0x49, 0xc9, 0x05, 0xaf, 0x68, 0x4e, 0x4e, 0xb1, 0xa6, 0xe4, 0xc0, + 0xef, 0x4f, 0x4e, 0xac, 0x87, 0x32, 0x2f, 0xb2, 0xa4, 0x60, 0xf1, 0xfb, 0x2a, 0xb9, 0xbc, 0x16, + 0x2c, 0x04, 0x30, 0x39, 0xc6, 0xbd, 0x94, 0x65, 0x49, 0x2a, 0x61, 0xc0, 0x28, 0x5c, 0x67, 0x4a, + 0x4c, 0xc9, 0x17, 0x45, 0x6c, 0xec, 0x41, 0xb9, 0x49, 0xc8, 0x4b, 0x3c, 0x10, 0x3c, 0x9f, 0x36, + 0x1d, 0xcd, 0x46, 0xee, 0x5e, 0x70, 0x58, 0x2f, 0x2d, 0xfd, 0xfc, 0xc3, 0xbb, 0x50, 0xd5, 0x42, + 0x5d, 0xf0, 0x1c, 0x22, 0xf2, 0x16, 0xeb, 0x33, 0x65, 0xef, 0x34, 0x8b, 0x8d, 0x7d, 0x30, 0xce, + 0xf9, 0x87, 0x71, 0xeb, 0x4d, 0x04, 0x07, 0xf5, 0xd2, 0xea, 0xaf, 0x93, 0xb0, 0x0f, 0x04, 0x93, + 0x98, 0x04, 0x78, 0xb0, 0x5d, 0xa3, 0xd1, 0x03, 0xb2, 0xb1, 0xd7, 0x2c, 0xda, 0xdb, 0x2c, 0xda, + 0xbb, 0xdc, 0x20, 0x02, 0x5d, 0xf9, 0x7e, 0xf3, 0xc3, 0x42, 0x61, 0xfb, 0x1b, 0x79, 0x81, 0xf5, + 0x28, 0xa5, 0x59, 0xa1, 0xf4, 0xf4, 0x6d, 0xe4, 0x0e, 0x9a, 0x59, 0x67, 0xaa, 0xa6, 0x66, 0x41, + 0x73, 0x12, 0x3b, 0xbf, 0x10, 0x1e, 0x6e, 0x65, 0x5d, 0x71, 0xc9, 0x1e, 0xc3, 0xd7, 0x5d, 0xb3, + 0xb4, 0xff, 0x34, 0x6b, 0xf7, 0xa1, 0xbd, 0xbf, 0x3f, 0x34, 0xf8, 0x78, 0x5b, 0x9b, 0xe8, 0xae, + 0x36, 0xd1, 0xcf, 0xda, 0x44, 0x37, 0x2b, 0xb3, 0x73, 0xb7, 0x32, 0x3b, 0xdf, 0x56, 0x66, 0xe7, + 0xd3, 0xab, 0x24, 0x93, 0xe9, 0x62, 0xe6, 0x45, 0x7c, 0xee, 0xef, 0x1e, 0x5b, 0x1b, 0x36, 0x47, + 0x79, 0xff, 0x10, 0x67, 0x3d, 0xa8, 0x9f, 0xfe, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x80, 0x68, 0xba, + 0x81, 0xed, 0x03, 0x00, 0x00, } func (m *CanonicalBlockID) Marshal() (dAtA []byte, err error) { @@ -494,36 +384,6 @@ func (m *CanonicalBlockID) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *CanonicalStateID) 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 *CanonicalStateID) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *CanonicalStateID) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.LastAppHash) > 0 { - i -= len(m.LastAppHash) - copy(dAtA[i:], m.LastAppHash) - i = encodeVarintCanonical(dAtA, i, uint64(len(m.LastAppHash))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - func (m *CanonicalPartSetHeader) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -651,18 +511,6 @@ func (m *CanonicalVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.StateID != nil { - { - size, err := m.StateID.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) @@ -702,47 +550,6 @@ func (m *CanonicalVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *CanonicalStateVote) 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 *CanonicalStateVote) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *CanonicalStateVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.StateID != nil { - { - size, err := m.StateID.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintCanonical(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - if m.Height != 0 { - i -= 8 - encoding_binary.LittleEndian.PutUint64(dAtA[i:], uint64(m.Height)) - i-- - dAtA[i] = 0x9 - } - return len(dAtA) - i, nil -} - func encodeVarintCanonical(dAtA []byte, offset int, v uint64) int { offset -= sovCanonical(v) base := offset @@ -769,19 +576,6 @@ func (m *CanonicalBlockID) Size() (n int) { return n } -func (m *CanonicalStateID) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.LastAppHash) - if l > 0 { - n += 1 + l + sovCanonical(uint64(l)) - } - return n -} - func (m *CanonicalPartSetHeader) Size() (n int) { if m == nil { return 0 @@ -852,26 +646,6 @@ func (m *CanonicalVote) Size() (n int) { if l > 0 { n += 1 + l + sovCanonical(uint64(l)) } - if m.StateID != nil { - l = m.StateID.Size() - n += 1 + l + sovCanonical(uint64(l)) - } - return n -} - -func (m *CanonicalStateVote) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Height != 0 { - n += 9 - } - if m.StateID != nil { - l = m.StateID.Size() - n += 1 + l + sovCanonical(uint64(l)) - } return n } @@ -998,90 +772,6 @@ func (m *CanonicalBlockID) Unmarshal(dAtA []byte) error { } return nil } -func (m *CanonicalStateID) 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: CanonicalStateID: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CanonicalStateID: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field LastAppHash", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowCanonical - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthCanonical - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthCanonical - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.LastAppHash = append(m.LastAppHash[:0], dAtA[iNdEx:postIndex]...) - if m.LastAppHash == nil { - m.LastAppHash = []byte{} - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipCanonical(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthCanonical - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *CanonicalPartSetHeader) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -1530,138 +1220,6 @@ 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 StateID", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowCanonical - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthCanonical - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthCanonical - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.StateID == nil { - m.StateID = &CanonicalStateID{} - } - if err := m.StateID.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipCanonical(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthCanonical - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} -func (m *CanonicalStateVote) 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: CanonicalStateVote: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: CanonicalStateVote: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - 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 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field StateID", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowCanonical - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthCanonical - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthCanonical - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if m.StateID == nil { - m.StateID = &CanonicalStateID{} - } - if err := m.StateID.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipCanonical(dAtA[iNdEx:]) diff --git a/proto/tendermint/types/canonical.proto b/proto/tendermint/types/canonical.proto index 2d4bb80603..ebbc5d8a0d 100644 --- a/proto/tendermint/types/canonical.proto +++ b/proto/tendermint/types/canonical.proto @@ -12,10 +12,6 @@ message CanonicalBlockID { CanonicalPartSetHeader part_set_header = 2 [(gogoproto.nullable) = false]; } -message CanonicalStateID { - bytes last_app_hash = 1; -} - message CanonicalPartSetHeader { uint32 total = 1; bytes hash = 2; @@ -36,11 +32,5 @@ message CanonicalVote { sfixed64 height = 2; // canonicalization requires fixed size encoding here sfixed64 round = 3; // canonicalization requires fixed size encoding here CanonicalBlockID block_id = 4 [(gogoproto.customname) = "BlockID"]; - CanonicalStateID state_id = 7 [(gogoproto.customname) = "StateID"]; string chain_id = 6 [(gogoproto.customname) = "ChainID"]; } - -message CanonicalStateVote { - sfixed64 height = 1; // canonicalization requires fixed size encoding here - CanonicalStateID state_id = 2 [(gogoproto.customname) = "StateID"]; -} diff --git a/proto/tendermint/types/types.pb.go b/proto/tendermint/types/types.pb.go index 63942a60e3..729c7d4f43 100644 --- a/proto/tendermint/types/types.pb.go +++ b/proto/tendermint/types/types.pb.go @@ -268,6 +268,7 @@ func (m *BlockID) GetPartSetHeader() PartSetHeader { // StateID type StateID struct { LastAppHash []byte `protobuf:"bytes,1,opt,name=last_app_hash,json=lastAppHash,proto3" json:"last_app_hash,omitempty"` + Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` } func (m *StateID) Reset() { *m = StateID{} } @@ -310,6 +311,13 @@ func (m *StateID) GetLastAppHash() []byte { return nil } +func (m *StateID) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + // Header defines the structure of a Tendermint block header. type Header struct { // basic block info @@ -596,7 +604,6 @@ type Vote struct { 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"` - StateID StateID `protobuf:"bytes,9,opt,name=state_id,json=stateId,proto3" json:"state_id"` ValidatorProTxHash []byte `protobuf:"bytes,6,opt,name=validator_pro_tx_hash,json=validatorProTxHash,proto3" json:"validator_pro_tx_hash,omitempty"` ValidatorIndex int32 `protobuf:"varint,7,opt,name=validator_index,json=validatorIndex,proto3" json:"validator_index,omitempty"` BlockSignature []byte `protobuf:"bytes,8,opt,name=block_signature,json=blockSignature,proto3" json:"block_signature,omitempty"` @@ -664,13 +671,6 @@ func (m *Vote) GetBlockID() BlockID { return BlockID{} } -func (m *Vote) GetStateID() StateID { - if m != nil { - return m.StateID - } - return StateID{} -} - func (m *Vote) GetValidatorProTxHash() []byte { if m != nil { return m.ValidatorProTxHash @@ -998,7 +998,6 @@ func (m *LightBlock) GetValidatorSet() *ValidatorSet { type BlockMeta struct { BlockID BlockID `protobuf:"bytes,1,opt,name=block_id,json=blockId,proto3" json:"block_id"` - StateID StateID `protobuf:"bytes,101,opt,name=state_id,json=stateId,proto3" json:"state_id"` BlockSize int64 `protobuf:"varint,2,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"` Header Header `protobuf:"bytes,3,opt,name=header,proto3" json:"header"` NumTxs int64 `protobuf:"varint,4,opt,name=num_txs,json=numTxs,proto3" json:"num_txs,omitempty"` @@ -1045,13 +1044,6 @@ func (m *BlockMeta) GetBlockID() BlockID { return BlockID{} } -func (m *BlockMeta) GetStateID() StateID { - if m != nil { - return m.StateID - } - return StateID{} -} - func (m *BlockMeta) GetBlockSize() int64 { if m != nil { return m.BlockSize @@ -1163,101 +1155,101 @@ func init() { func init() { proto.RegisterFile("tendermint/types/types.proto", fileDescriptor_d3a6e55e2345de56) } var fileDescriptor_d3a6e55e2345de56 = []byte{ - // 1501 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xcf, 0x6f, 0x1b, 0xc5, - 0x17, 0xcf, 0xda, 0x9b, 0xd8, 0x7e, 0xb6, 0x63, 0x67, 0xbf, 0x69, 0xeb, 0xb8, 0xad, 0x63, 0xf9, - 0x2b, 0xda, 0x10, 0x51, 0xa7, 0xb4, 0x88, 0x42, 0x25, 0x0e, 0xb1, 0x93, 0xb6, 0x56, 0xf3, 0xc3, - 0xac, 0xdd, 0x20, 0xb8, 0xac, 0x36, 0xde, 0xa9, 0x6d, 0x6a, 0xef, 0x2c, 0xbb, 0xe3, 0x90, 0xf4, - 0xca, 0x01, 0x94, 0x53, 0x4f, 0xdc, 0x22, 0x21, 0x01, 0x12, 0x7f, 0x02, 0x7f, 0x42, 0x8f, 0xbd, - 0x95, 0x0b, 0x05, 0xa5, 0x17, 0x0e, 0xfc, 0x11, 0x68, 0xde, 0xcc, 0x7a, 0x77, 0x9d, 0x04, 0xaa, - 0xa8, 0x17, 0x6b, 0xe7, 0xbd, 0xcf, 0x9b, 0x79, 0xef, 0xf3, 0x7e, 0xcc, 0x18, 0xae, 0x30, 0x62, - 0x5b, 0xc4, 0x1d, 0xf6, 0x6d, 0xb6, 0xc2, 0x0e, 0x1c, 0xe2, 0x89, 0xdf, 0xaa, 0xe3, 0x52, 0x46, - 0xb5, 0x7c, 0xa0, 0xad, 0xa2, 0xbc, 0x38, 0xdf, 0xa5, 0x5d, 0x8a, 0xca, 0x15, 0xfe, 0x25, 0x70, - 0xc5, 0xc5, 0x2e, 0xa5, 0xdd, 0x01, 0x59, 0xc1, 0xd5, 0xee, 0xe8, 0xf1, 0x0a, 0xeb, 0x0f, 0x89, - 0xc7, 0xcc, 0xa1, 0x23, 0x01, 0x57, 0x43, 0xc7, 0x74, 0xdc, 0x03, 0x87, 0x51, 0x8e, 0xa5, 0x8f, - 0xa5, 0xba, 0x14, 0x52, 0xef, 0x11, 0xd7, 0xeb, 0x53, 0x3b, 0xec, 0x47, 0xb1, 0x7c, 0xc2, 0xcb, - 0x3d, 0x73, 0xd0, 0xb7, 0x4c, 0x46, 0x5d, 0x81, 0xa8, 0x7c, 0x0c, 0xd9, 0xa6, 0xe9, 0xb2, 0x16, - 0x61, 0x0f, 0x88, 0x69, 0x11, 0x57, 0x9b, 0x87, 0x69, 0x46, 0x99, 0x39, 0x28, 0x28, 0x65, 0x65, - 0x29, 0xab, 0x8b, 0x85, 0xa6, 0x81, 0xda, 0x33, 0xbd, 0x5e, 0x21, 0x56, 0x56, 0x96, 0x32, 0x3a, - 0x7e, 0x57, 0x7a, 0xa0, 0x72, 0x53, 0x6e, 0xd1, 0xb7, 0x2d, 0xb2, 0xef, 0x5b, 0xe0, 0x82, 0x4b, - 0x77, 0x0f, 0x18, 0xf1, 0xa4, 0x89, 0x58, 0x68, 0x1f, 0xc0, 0x34, 0xfa, 0x5f, 0x88, 0x97, 0x95, - 0xa5, 0xf4, 0xad, 0x42, 0x35, 0x44, 0x94, 0x88, 0xaf, 0xda, 0xe4, 0xfa, 0x9a, 0xfa, 0xfc, 0xd5, - 0xe2, 0x94, 0x2e, 0xc0, 0x95, 0x01, 0x24, 0x6a, 0x03, 0xda, 0x79, 0xd2, 0x58, 0x1b, 0x3b, 0xa2, - 0x04, 0x8e, 0x68, 0x9b, 0x90, 0x73, 0x4c, 0x97, 0x19, 0x1e, 0x61, 0x46, 0x0f, 0xa3, 0xc0, 0x43, - 0xd3, 0xb7, 0x16, 0xab, 0x93, 0x79, 0xa8, 0x46, 0x82, 0x95, 0xa7, 0x64, 0x9d, 0xb0, 0xb0, 0x72, - 0x03, 0x12, 0x2d, 0x66, 0x32, 0xd2, 0x58, 0xd3, 0x2a, 0x90, 0x1d, 0x98, 0x1e, 0x33, 0x4c, 0xc7, - 0x31, 0x42, 0xc7, 0xa6, 0xb9, 0x70, 0xd5, 0x71, 0x1e, 0x70, 0x1a, 0x5e, 0x4e, 0xc3, 0x8c, 0xe4, - 0xee, 0x13, 0x48, 0xc8, 0x2c, 0x20, 0x30, 0x7d, 0xeb, 0x6a, 0xd8, 0x01, 0xa9, 0xaa, 0xd6, 0xa9, - 0xed, 0x11, 0xdb, 0x1b, 0x79, 0xf2, 0x78, 0xdf, 0x46, 0xbb, 0x06, 0xc9, 0x4e, 0xcf, 0xec, 0xdb, - 0x46, 0xdf, 0xc2, 0x00, 0x52, 0xb5, 0xf4, 0xf1, 0xab, 0xc5, 0x44, 0x9d, 0xcb, 0x1a, 0x6b, 0x7a, - 0x02, 0x95, 0x0d, 0x4b, 0xbb, 0x08, 0x33, 0x3d, 0xd2, 0xef, 0xf6, 0x18, 0xb2, 0x18, 0xd7, 0xe5, - 0x4a, 0xbb, 0x03, 0x85, 0x0e, 0x75, 0x89, 0x21, 0x36, 0xe1, 0x84, 0x11, 0xcb, 0x90, 0x48, 0x0b, - 0x73, 0x73, 0x81, 0xeb, 0x71, 0xbf, 0x0d, 0xd4, 0x3e, 0x10, 0x86, 0x1f, 0x81, 0xca, 0x0b, 0xaf, - 0xa0, 0xa2, 0xd3, 0xc5, 0xaa, 0xa8, 0xca, 0xaa, 0x5f, 0x95, 0xd5, 0xb6, 0x5f, 0x95, 0xb5, 0x24, - 0xf7, 0xf8, 0xd9, 0x1f, 0x8b, 0x8a, 0x8e, 0x16, 0x5a, 0x5d, 0x12, 0xb4, 0xcb, 0x4f, 0xe3, 0x7e, - 0x4f, 0xe3, 0x16, 0x0b, 0x27, 0x89, 0x97, 0x09, 0x94, 0x31, 0x23, 0x83, 0x42, 0x64, 0x69, 0x4b, - 0x90, 0xc7, 0x4d, 0x3a, 0x74, 0x38, 0xec, 0x33, 0x41, 0xf4, 0x0c, 0x12, 0x3d, 0xcb, 0xe5, 0x75, - 0x14, 0x73, 0xae, 0xb5, 0xcb, 0x90, 0xb2, 0x4c, 0x66, 0x0a, 0x48, 0x02, 0x21, 0x49, 0x2e, 0x40, - 0xe5, 0x75, 0xc8, 0x8d, 0xab, 0xdb, 0x13, 0x90, 0xa4, 0xd8, 0x25, 0x10, 0x23, 0xf0, 0x26, 0xcc, - 0xdb, 0x64, 0x9f, 0x19, 0x93, 0xe8, 0x14, 0xa2, 0x35, 0xae, 0xdb, 0x89, 0x5a, 0xbc, 0x03, 0xb3, - 0x1d, 0x3f, 0x6b, 0x02, 0x0b, 0x88, 0xcd, 0x8e, 0xa5, 0x08, 0x5b, 0x80, 0xe4, 0xb8, 0x52, 0xd2, - 0x08, 0x48, 0x98, 0xa2, 0x4a, 0xb4, 0x65, 0x98, 0xc3, 0x18, 0x5d, 0xe2, 0x8d, 0x06, 0x4c, 0x6e, - 0x92, 0x41, 0x4c, 0x8e, 0x2b, 0x74, 0x21, 0x47, 0xec, 0xff, 0x21, 0x4b, 0xf6, 0xfa, 0x16, 0xb1, - 0x3b, 0x44, 0xe0, 0xb2, 0x88, 0xcb, 0xf8, 0x42, 0x04, 0xad, 0xc0, 0xbc, 0xe3, 0x52, 0x87, 0x7a, - 0xc4, 0x35, 0x1c, 0x97, 0x1a, 0x6c, 0x5f, 0x60, 0x09, 0x62, 0xe7, 0x7c, 0x5d, 0xd3, 0xa5, 0xed, - 0x7d, 0x3f, 0x6a, 0x29, 0xb4, 0xb0, 0x9e, 0xfd, 0x4a, 0x7d, 0x5c, 0x56, 0x96, 0x54, 0x5d, 0xf3, - 0x75, 0xab, 0x8e, 0xb3, 0x23, 0x34, 0x95, 0x6f, 0x15, 0xc8, 0xd6, 0xc3, 0x05, 0xc3, 0xa3, 0xc0, - 0x0a, 0x13, 0xe9, 0x96, 0xa5, 0x25, 0xda, 0x3e, 0xc7, 0x15, 0x98, 0x51, 0x59, 0x54, 0xd7, 0x20, - 0x17, 0xc6, 0x06, 0xd3, 0x23, 0x1b, 0x20, 0xb9, 0x5f, 0x57, 0x20, 0xe5, 0xf5, 0xbb, 0xb6, 0xc9, - 0x46, 0x2e, 0xc1, 0x82, 0xce, 0xe8, 0x81, 0xe0, 0xae, 0xfa, 0xd7, 0x0f, 0x8b, 0x4a, 0xa5, 0x00, - 0xea, 0x9a, 0xc9, 0x4c, 0x2d, 0x0f, 0x71, 0xb6, 0xef, 0x15, 0x94, 0x72, 0x7c, 0x29, 0xa3, 0xf3, - 0xcf, 0xca, 0xcf, 0x71, 0x50, 0x77, 0x28, 0x23, 0xda, 0x6d, 0x50, 0x79, 0xa1, 0xa1, 0x37, 0xb3, - 0xa7, 0x75, 0x7e, 0xab, 0xdf, 0xb5, 0x89, 0xb5, 0xe9, 0x75, 0xdb, 0x07, 0x0e, 0xd1, 0x11, 0x1c, - 0xea, 0xa4, 0x58, 0xa4, 0x93, 0xe6, 0x61, 0xda, 0xa5, 0x23, 0xdb, 0x42, 0x7f, 0xa6, 0x75, 0xb1, - 0xd0, 0xd6, 0x21, 0x39, 0xae, 0x73, 0xf5, 0xbf, 0xea, 0x3c, 0xc7, 0xeb, 0x9c, 0xb7, 0xaf, 0x14, - 0xe8, 0x89, 0x5d, 0x59, 0xee, 0xeb, 0x90, 0xf4, 0xf8, 0x7c, 0xe1, 0xdb, 0xa4, 0xce, 0xda, 0x46, - 0x4e, 0xa0, 0x60, 0x1b, 0x29, 0xd0, 0x13, 0x68, 0xdb, 0xb0, 0xb4, 0xf7, 0xe1, 0xc2, 0xb8, 0x80, - 0x23, 0x15, 0x20, 0x5a, 0x47, 0x1b, 0x2b, 0x83, 0x12, 0x08, 0x77, 0x88, 0x21, 0x66, 0x76, 0x02, - 0x03, 0x0c, 0x3a, 0xa4, 0x81, 0xc3, 0xfb, 0x3a, 0xe4, 0x44, 0xa4, 0x41, 0x66, 0x64, 0x2b, 0xa1, - 0xb8, 0xe5, 0x4b, 0x39, 0x50, 0xc4, 0x12, 0x00, 0x45, 0x67, 0xcc, 0xa2, 0x78, 0x0c, 0xac, 0xfc, - 0x1e, 0x83, 0x19, 0xd1, 0xc8, 0x21, 0xd2, 0x95, 0xd3, 0x49, 0x8f, 0x9d, 0x45, 0x7a, 0xfc, 0xed, - 0x90, 0xae, 0x9e, 0x9f, 0xf4, 0x45, 0x48, 0x7f, 0x35, 0xa2, 0xee, 0x68, 0x18, 0xa6, 0x1a, 0x84, - 0x08, 0x29, 0xbe, 0x0b, 0x0b, 0xac, 0xe7, 0x12, 0xaf, 0x47, 0x07, 0x96, 0x31, 0xc9, 0xa1, 0x98, - 0x58, 0x97, 0xc6, 0x80, 0x5a, 0x94, 0xcc, 0x88, 0xed, 0x24, 0xad, 0xc9, 0x09, 0xdb, 0x56, 0x94, - 0xdf, 0xbf, 0x63, 0x90, 0x6c, 0x62, 0x0b, 0x9b, 0x83, 0xb7, 0xdb, 0x0b, 0xe7, 0xbe, 0x55, 0x4e, - 0x6f, 0xa2, 0xcb, 0x90, 0x72, 0xe8, 0xc0, 0x10, 0x1a, 0x15, 0x35, 0x49, 0x87, 0x0e, 0xf4, 0x13, - 0xc9, 0x9e, 0x3e, 0x7f, 0xb2, 0x6b, 0x90, 0x1a, 0x3f, 0xa4, 0x30, 0x47, 0x6f, 0x7a, 0xa9, 0x05, - 0x66, 0xd1, 0xb1, 0x94, 0x98, 0x18, 0x4b, 0x15, 0x17, 0x32, 0x82, 0x43, 0x79, 0xf3, 0xdf, 0xe4, - 0xe4, 0xe1, 0xcb, 0x43, 0x39, 0xf9, 0xb0, 0x11, 0x6e, 0x0b, 0xa4, 0x2e, 0x71, 0xdc, 0x42, 0xdc, - 0x77, 0xf2, 0xad, 0x72, 0x8a, 0x85, 0xe8, 0x17, 0x5d, 0xe2, 0x2a, 0xdf, 0x2b, 0x00, 0x1b, 0x9c, - 0x59, 0x8c, 0x97, 0x5f, 0xbd, 0x1e, 0xba, 0x60, 0x44, 0x4e, 0x2e, 0x9d, 0x95, 0x6d, 0x79, 0x7e, - 0xc6, 0x0b, 0xfb, 0x5d, 0x87, 0x6c, 0x30, 0x11, 0x3c, 0xe2, 0x3b, 0x73, 0xca, 0x26, 0xe3, 0x1b, - 0xb1, 0x45, 0x98, 0x9e, 0xd9, 0x0b, 0xad, 0x2a, 0xbf, 0xc6, 0x20, 0x85, 0x3e, 0x6d, 0x12, 0x66, - 0x46, 0x72, 0xa8, 0xbc, 0x9d, 0x86, 0x25, 0xe7, 0x6f, 0xd8, 0xab, 0x00, 0x7e, 0x17, 0x3e, 0x25, - 0xb2, 0xb2, 0x53, 0x72, 0x88, 0x3d, 0x25, 0xda, 0x87, 0xe3, 0xbc, 0xc5, 0xff, 0x3d, 0x6f, 0xf2, - 0xdd, 0xe2, 0x67, 0xef, 0x12, 0x24, 0xec, 0xd1, 0xd0, 0xe0, 0x97, 0x91, 0x2a, 0xba, 0xc5, 0x1e, - 0x0d, 0xdb, 0xfb, 0x9e, 0x76, 0x03, 0xfe, 0xd7, 0x33, 0x3d, 0x63, 0xa2, 0x63, 0xb0, 0x51, 0x92, - 0x7a, 0xbe, 0x67, 0x7a, 0x91, 0x0b, 0xb5, 0xf2, 0x25, 0x24, 0xda, 0xfb, 0xf8, 0xe2, 0xe5, 0x8d, - 0xe1, 0x52, 0xca, 0xc2, 0xef, 0xcc, 0x24, 0x17, 0xe0, 0x58, 0xd1, 0x40, 0xe5, 0xef, 0x1c, 0xff, - 0xfd, 0xcd, 0xbf, 0xb5, 0xea, 0x1b, 0xbe, 0xa5, 0xe5, 0x2b, 0x7a, 0xf9, 0xa5, 0x02, 0x69, 0x49, - 0xf3, 0xbd, 0x81, 0xd9, 0xe5, 0x17, 0x48, 0x6d, 0x63, 0xbb, 0xfe, 0xd0, 0x68, 0xac, 0x19, 0xf7, - 0x36, 0x56, 0xef, 0x1b, 0x8f, 0xb6, 0x1e, 0x6e, 0x6d, 0x7f, 0xb6, 0x95, 0x9f, 0x2a, 0x5e, 0x3c, - 0x3c, 0x2a, 0x6b, 0x21, 0xec, 0x23, 0xfb, 0x89, 0x4d, 0xbf, 0xb6, 0xf9, 0xa3, 0x23, 0x6a, 0xb2, - 0x5a, 0x6b, 0xad, 0x6f, 0xb5, 0xf3, 0x4a, 0xf1, 0xc2, 0xe1, 0x51, 0x79, 0x2e, 0x64, 0xb1, 0xba, - 0xeb, 0x11, 0x9b, 0x9d, 0x34, 0xa8, 0x6f, 0x6f, 0x6e, 0x36, 0xda, 0xf9, 0xd8, 0x09, 0x03, 0x79, - 0x39, 0xbc, 0x0b, 0x73, 0x51, 0x83, 0xad, 0xc6, 0x46, 0x3e, 0x5e, 0xd4, 0x0e, 0x8f, 0xca, 0xb3, - 0x21, 0xf4, 0x56, 0x7f, 0x50, 0x4c, 0x7e, 0xf7, 0x63, 0x69, 0xea, 0x97, 0x9f, 0x4a, 0xca, 0xf2, - 0x37, 0x31, 0xc8, 0x46, 0x46, 0x9a, 0xf6, 0x1e, 0x5c, 0x6a, 0x35, 0xee, 0x6f, 0xad, 0xaf, 0x19, - 0x9b, 0xad, 0xfb, 0x46, 0xfb, 0xf3, 0xe6, 0x7a, 0x28, 0xba, 0xdc, 0xe1, 0x51, 0x39, 0x2d, 0x43, - 0x3a, 0x0b, 0xdd, 0xd4, 0xd7, 0x77, 0xb6, 0xdb, 0xeb, 0x79, 0x45, 0xa0, 0x9b, 0x2e, 0xd9, 0xa3, - 0x8c, 0x20, 0xfa, 0x26, 0x2c, 0x9c, 0x82, 0x1e, 0x07, 0x36, 0x77, 0x78, 0x54, 0xce, 0x36, 0x5d, - 0x22, 0xba, 0x16, 0x2d, 0x96, 0xe1, 0xe2, 0xa4, 0x85, 0x84, 0xc7, 0x8b, 0xb3, 0x87, 0x47, 0x65, - 0xa8, 0x07, 0xd8, 0x2a, 0x14, 0x4e, 0xee, 0xbe, 0xdd, 0xdc, 0x6e, 0xad, 0x6e, 0xe4, 0xcb, 0xc5, - 0xfc, 0xe1, 0x51, 0x39, 0xe3, 0xcf, 0x79, 0x8e, 0x0f, 0x58, 0xa8, 0x7d, 0xfa, 0xfc, 0xb8, 0xa4, - 0xbc, 0x38, 0x2e, 0x29, 0x7f, 0x1e, 0x97, 0x94, 0x67, 0xaf, 0x4b, 0x53, 0x2f, 0x5e, 0x97, 0xa6, - 0x7e, 0x7b, 0x5d, 0x9a, 0xfa, 0xe2, 0x4e, 0xb7, 0xcf, 0x7a, 0xa3, 0xdd, 0x6a, 0x87, 0x0e, 0x57, - 0xc2, 0xff, 0x08, 0x83, 0x4f, 0xf1, 0xcf, 0x74, 0xf2, 0xdf, 0xe2, 0xee, 0x0c, 0xca, 0x6f, 0xff, - 0x13, 0x00, 0x00, 0xff, 0xff, 0x6d, 0xda, 0xe5, 0x90, 0xee, 0x0e, 0x00, 0x00, + // 1490 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xcf, 0x6f, 0xdb, 0xc6, + 0x12, 0x36, 0x2d, 0xd9, 0x92, 0x46, 0x92, 0x25, 0xf3, 0x39, 0x89, 0xac, 0x24, 0xb2, 0xc0, 0x87, + 0x97, 0xf8, 0x19, 0xef, 0xc9, 0x69, 0x52, 0x34, 0x6d, 0x80, 0x1e, 0x2c, 0x59, 0x49, 0x84, 0xf8, + 0x87, 0x4a, 0x29, 0x2e, 0xda, 0x0b, 0x41, 0x8b, 0x1b, 0x89, 0x8d, 0xc4, 0x65, 0xc9, 0x95, 0x6b, + 0xe7, 0xda, 0x43, 0x0b, 0x9f, 0x72, 0xea, 0xcd, 0x40, 0x81, 0xf6, 0xd0, 0x3f, 0x25, 0xc7, 0xdc, + 0xd2, 0x4b, 0xd3, 0xc2, 0xb9, 0xf4, 0xd0, 0x4b, 0xff, 0x83, 0x62, 0x67, 0x97, 0x22, 0x25, 0xd9, + 0x69, 0x60, 0xf4, 0x22, 0x70, 0x67, 0xbe, 0xd9, 0x9d, 0xfd, 0xe6, 0xdb, 0xd9, 0x15, 0x5c, 0x63, + 0xc4, 0xb1, 0x88, 0x37, 0xb0, 0x1d, 0xb6, 0xce, 0x8e, 0x5c, 0xe2, 0x8b, 0xdf, 0x8a, 0xeb, 0x51, + 0x46, 0xd5, 0x7c, 0xe8, 0xad, 0xa0, 0xbd, 0xb8, 0xd4, 0xa5, 0x5d, 0x8a, 0xce, 0x75, 0xfe, 0x25, + 0x70, 0xc5, 0x95, 0x2e, 0xa5, 0xdd, 0x3e, 0x59, 0xc7, 0xd1, 0xfe, 0xf0, 0xc9, 0x3a, 0xb3, 0x07, + 0xc4, 0x67, 0xe6, 0xc0, 0x95, 0x80, 0xeb, 0x91, 0x65, 0x3a, 0xde, 0x91, 0xcb, 0x28, 0xc7, 0xd2, + 0x27, 0xd2, 0x5d, 0x8a, 0xb8, 0x0f, 0x88, 0xe7, 0xdb, 0xd4, 0x89, 0xe6, 0x51, 0x2c, 0x4f, 0x65, + 0x79, 0x60, 0xf6, 0x6d, 0xcb, 0x64, 0xd4, 0x13, 0x08, 0xed, 0x23, 0xc8, 0x36, 0x4d, 0x8f, 0xb5, + 0x08, 0x7b, 0x48, 0x4c, 0x8b, 0x78, 0xea, 0x12, 0xcc, 0x31, 0xca, 0xcc, 0x7e, 0x41, 0x29, 0x2b, + 0xab, 0x59, 0x5d, 0x0c, 0x54, 0x15, 0xe2, 0x3d, 0xd3, 0xef, 0x15, 0x66, 0xcb, 0xca, 0x6a, 0x46, + 0xc7, 0x6f, 0xad, 0x07, 0x71, 0x1e, 0xca, 0x23, 0x6c, 0xc7, 0x22, 0x87, 0x41, 0x04, 0x0e, 0xb8, + 0x75, 0xff, 0x88, 0x11, 0x5f, 0x86, 0x88, 0x81, 0xfa, 0x3e, 0xcc, 0x61, 0xfe, 0x85, 0x58, 0x59, + 0x59, 0x4d, 0xdf, 0x2e, 0x54, 0x22, 0x44, 0x89, 0xfd, 0x55, 0x9a, 0xdc, 0x5f, 0x8d, 0xbf, 0x78, + 0xbd, 0x32, 0xa3, 0x0b, 0xb0, 0xd6, 0x87, 0x44, 0xb5, 0x4f, 0x3b, 0x4f, 0x1b, 0x9b, 0xa3, 0x44, + 0x94, 0x30, 0x11, 0x75, 0x1b, 0x72, 0xae, 0xe9, 0x31, 0xc3, 0x27, 0xcc, 0xe8, 0xe1, 0x2e, 0x70, + 0xd1, 0xf4, 0xed, 0x95, 0xca, 0x64, 0x1d, 0x2a, 0x63, 0x9b, 0x95, 0xab, 0x64, 0xdd, 0xa8, 0x51, + 0xab, 0x43, 0xa2, 0xc5, 0x4c, 0x46, 0x1a, 0x9b, 0xaa, 0x06, 0xd9, 0xbe, 0xe9, 0x33, 0xc3, 0x74, + 0x5d, 0x23, 0xb2, 0x6c, 0x9a, 0x1b, 0x37, 0x5c, 0xf7, 0x21, 0x5f, 0xfd, 0x32, 0xcc, 0xf7, 0x88, + 0xdd, 0xed, 0x31, 0x5c, 0x34, 0xa6, 0xcb, 0x91, 0xf6, 0x6a, 0x0e, 0xe6, 0x25, 0xa7, 0x1f, 0x43, + 0x42, 0x56, 0x07, 0x27, 0x48, 0xdf, 0xbe, 0x1e, 0x4d, 0x4c, 0xba, 0x2a, 0x35, 0xea, 0xf8, 0xc4, + 0xf1, 0x87, 0xbe, 0x4c, 0x2b, 0x88, 0x51, 0x6f, 0x40, 0xb2, 0xd3, 0x33, 0x6d, 0xc7, 0xb0, 0x2d, + 0x5c, 0x23, 0x55, 0x4d, 0x9f, 0xbe, 0x5e, 0x49, 0xd4, 0xb8, 0xad, 0xb1, 0xa9, 0x27, 0xd0, 0xd9, + 0xb0, 0x22, 0x99, 0xc4, 0xa2, 0x99, 0xa8, 0x77, 0xa1, 0xd0, 0xa1, 0x1e, 0x31, 0xc4, 0x24, 0x9c, + 0x48, 0x62, 0x19, 0x12, 0x69, 0x61, 0xcd, 0x2e, 0x71, 0x3f, 0xce, 0xb7, 0x85, 0xde, 0x87, 0x22, + 0xf0, 0x43, 0x88, 0x73, 0x41, 0x16, 0xe2, 0x98, 0x74, 0xb1, 0x22, 0xd4, 0x5a, 0x09, 0xd4, 0x5a, + 0x69, 0x07, 0x6a, 0xad, 0x26, 0x79, 0xc6, 0xcf, 0x7f, 0x5d, 0x51, 0x74, 0x8c, 0x50, 0x6b, 0x92, + 0xb8, 0x7d, 0xbe, 0x1a, 0xcf, 0x7b, 0x0e, 0xa7, 0x58, 0x9e, 0x2e, 0x88, 0x2c, 0xac, 0xdc, 0x33, + 0x32, 0x2b, 0x4c, 0x96, 0xba, 0x0a, 0x79, 0x9c, 0xa4, 0x43, 0x07, 0x03, 0x9b, 0x89, 0x02, 0xcc, + 0x63, 0x01, 0x16, 0xb8, 0xbd, 0x86, 0x66, 0xac, 0xc1, 0x55, 0x48, 0x59, 0x26, 0x33, 0x05, 0x24, + 0x81, 0x90, 0x24, 0x37, 0xa0, 0xf3, 0x26, 0xe4, 0x46, 0xaa, 0xf7, 0x05, 0x24, 0x29, 0x66, 0x09, + 0xcd, 0x08, 0xbc, 0x05, 0x4b, 0x0e, 0x39, 0x64, 0xc6, 0x24, 0x3a, 0x85, 0x68, 0x95, 0xfb, 0xf6, + 0xc6, 0x23, 0xfe, 0x03, 0x0b, 0x9d, 0xa0, 0x6a, 0x02, 0x0b, 0x88, 0xcd, 0x8e, 0xac, 0x08, 0x5b, + 0x86, 0xe4, 0x48, 0x41, 0x69, 0x04, 0x24, 0x4c, 0xa9, 0x9e, 0x35, 0x58, 0xc4, 0x3d, 0x7a, 0xc4, + 0x1f, 0xf6, 0x99, 0x9c, 0x24, 0x83, 0x98, 0x1c, 0x77, 0xe8, 0xc2, 0x8e, 0xd8, 0x7f, 0x43, 0x96, + 0x1c, 0xd8, 0x16, 0x71, 0x3a, 0x44, 0xe0, 0xb2, 0x88, 0xcb, 0x04, 0x46, 0x04, 0xad, 0xc3, 0x92, + 0xeb, 0x51, 0x97, 0xfa, 0xc4, 0x33, 0x5c, 0x8f, 0x1a, 0xec, 0x50, 0x60, 0x09, 0x62, 0x17, 0x03, + 0x5f, 0xd3, 0xa3, 0xed, 0xc3, 0x60, 0xd7, 0xd2, 0x68, 0xa1, 0xce, 0x03, 0xa5, 0x3e, 0x29, 0x2b, + 0xab, 0x71, 0x5d, 0x0d, 0x7c, 0x1b, 0xae, 0xbb, 0x27, 0x3c, 0xda, 0x37, 0x0a, 0x64, 0x6b, 0x51, + 0xc1, 0xf0, 0x5d, 0xa0, 0xc2, 0x44, 0xb9, 0xa5, 0xb4, 0x44, 0x3b, 0xc8, 0x71, 0x07, 0x56, 0x54, + 0x8a, 0xea, 0x06, 0xe4, 0xa2, 0xd8, 0xb0, 0xab, 0x64, 0x43, 0x24, 0xcf, 0xeb, 0x1a, 0xa4, 0x7c, + 0xbb, 0xeb, 0x98, 0x6c, 0xe8, 0x11, 0x14, 0x74, 0x46, 0x0f, 0x0d, 0xf7, 0xe2, 0xbf, 0x7f, 0xbf, + 0xa2, 0x68, 0x05, 0x88, 0x6f, 0x9a, 0xcc, 0x54, 0xf3, 0x10, 0x63, 0x87, 0x7e, 0x41, 0x29, 0xc7, + 0x56, 0x33, 0x3a, 0xff, 0xd4, 0x5e, 0xcf, 0x42, 0x7c, 0x8f, 0x32, 0xa2, 0xde, 0x81, 0x38, 0x17, + 0x1a, 0x66, 0xb3, 0x70, 0x56, 0x47, 0x68, 0xd9, 0x5d, 0x87, 0x58, 0xdb, 0x7e, 0xb7, 0x7d, 0xe4, + 0x12, 0x1d, 0xc1, 0xe7, 0x9d, 0x69, 0xde, 0xd4, 0x3c, 0x3a, 0x74, 0x2c, 0xcc, 0x67, 0x4e, 0x17, + 0x03, 0xb5, 0x0e, 0xc9, 0x91, 0xce, 0xe3, 0x7f, 0xa7, 0xf3, 0x1c, 0xd7, 0x39, 0x3f, 0xbe, 0xd2, + 0xa0, 0x27, 0xf6, 0xa5, 0xdc, 0xdf, 0x83, 0x4b, 0x23, 0xe5, 0x8d, 0x95, 0x4e, 0x68, 0x5e, 0x1d, + 0x39, 0xc3, 0xda, 0x45, 0xa5, 0x6d, 0x88, 0x26, 0x9c, 0xc0, 0xcc, 0x42, 0x69, 0x37, 0xb0, 0x1b, + 0xdf, 0x84, 0x9c, 0x48, 0x31, 0xa4, 0x54, 0x9e, 0x01, 0x34, 0xb7, 0x02, 0x2b, 0x07, 0xfa, 0xbc, + 0xf9, 0x45, 0x80, 0x42, 0xd2, 0x0b, 0x68, 0x1e, 0x01, 0xb5, 0x5f, 0x66, 0x61, 0x5e, 0x9c, 0xc0, + 0x08, 0x5b, 0xca, 0xd9, 0x6c, 0xcd, 0x9e, 0xc7, 0x56, 0xec, 0xe2, 0x6c, 0xd5, 0x21, 0x29, 0x12, + 0x7d, 0x1b, 0xe9, 0xb2, 0x8f, 0x87, 0xd3, 0x48, 0x83, 0x9e, 0xc0, 0xd8, 0x86, 0xa5, 0xae, 0x40, + 0xfa, 0xcb, 0x21, 0xf5, 0x86, 0x83, 0x28, 0xd5, 0x20, 0x4c, 0x48, 0xf1, 0x3d, 0x58, 0x66, 0x3d, + 0x8f, 0xf8, 0x3d, 0xda, 0xb7, 0x8c, 0x49, 0x0e, 0x45, 0xab, 0xb9, 0x32, 0x02, 0x54, 0xc7, 0xc9, + 0x1c, 0x8b, 0x9d, 0xa4, 0x35, 0x39, 0x11, 0xdb, 0x1a, 0xe7, 0xf7, 0x8f, 0x59, 0x48, 0x36, 0xf1, + 0xec, 0x99, 0xfd, 0x7f, 0x56, 0xc4, 0x17, 0xbe, 0x0e, 0xce, 0x56, 0xff, 0x55, 0x48, 0xb9, 0xb4, + 0x6f, 0x08, 0x4f, 0x1c, 0x3d, 0x49, 0x97, 0xf6, 0xf5, 0xa9, 0x62, 0xcf, 0x5d, 0xbc, 0xd8, 0x55, + 0x48, 0x8d, 0x5e, 0x46, 0x58, 0xa3, 0x77, 0xbd, 0x8d, 0xc2, 0xb0, 0xf1, 0x7e, 0x92, 0x98, 0xe8, + 0x27, 0x9a, 0x07, 0x19, 0xc1, 0xa1, 0xbc, 0xb2, 0x6f, 0x71, 0xf2, 0xf0, 0x29, 0xa1, 0x4c, 0xbf, + 0x54, 0x44, 0xda, 0x02, 0xa9, 0x4b, 0x1c, 0x8f, 0x10, 0x17, 0x95, 0x7c, 0x7c, 0x9c, 0x11, 0x21, + 0xce, 0x8b, 0x2e, 0x71, 0xda, 0x77, 0x0a, 0xc0, 0x16, 0x67, 0x16, 0xf7, 0xcb, 0xef, 0x4c, 0x1f, + 0x53, 0x30, 0xc6, 0x56, 0x2e, 0x9d, 0x57, 0x6d, 0xb9, 0x7e, 0xc6, 0x8f, 0xe6, 0x5d, 0x83, 0x6c, + 0xd8, 0x11, 0x7c, 0x12, 0x24, 0x73, 0xc6, 0x24, 0xa3, 0xab, 0xac, 0x45, 0x98, 0x9e, 0x39, 0x88, + 0x8c, 0xb4, 0x3f, 0x15, 0x48, 0x61, 0x4e, 0xdb, 0x84, 0x99, 0x63, 0x35, 0x54, 0x2e, 0x5e, 0xc3, + 0xeb, 0x00, 0xc1, 0xf1, 0x79, 0x46, 0xa4, 0x24, 0x53, 0xb2, 0xfb, 0x3c, 0x23, 0xea, 0x07, 0x23, + 0xc2, 0x63, 0x6f, 0x27, 0x5c, 0xbe, 0x14, 0x02, 0xda, 0xaf, 0x40, 0xc2, 0x19, 0x0e, 0x0c, 0xde, + 0xfe, 0xe3, 0x42, 0xe6, 0xce, 0x70, 0xd0, 0x3e, 0xf4, 0xd5, 0xff, 0xc3, 0xbf, 0x7a, 0xa6, 0x6f, + 0x4c, 0x48, 0x1d, 0x15, 0x9e, 0xd4, 0xf3, 0x3d, 0xd3, 0x1f, 0xbb, 0xc2, 0xb4, 0x2f, 0x20, 0xd1, + 0x3e, 0xc4, 0xb7, 0x27, 0x57, 0xb4, 0x47, 0x29, 0x8b, 0xbe, 0xf8, 0x92, 0xdc, 0x80, 0xfd, 0x40, + 0x85, 0x38, 0x7f, 0x59, 0x04, 0x2f, 0x61, 0xfe, 0xad, 0x56, 0xde, 0xf1, 0x55, 0x2b, 0xdf, 0xb3, + 0x6b, 0xaf, 0x14, 0x48, 0x4b, 0x7e, 0xee, 0xf7, 0xcd, 0x2e, 0xef, 0xfc, 0xd5, 0xad, 0xdd, 0xda, + 0x23, 0xa3, 0xb1, 0x69, 0xdc, 0xdf, 0xda, 0x78, 0x60, 0x3c, 0xde, 0x79, 0xb4, 0xb3, 0xfb, 0xe9, + 0x4e, 0x7e, 0xa6, 0x78, 0xf9, 0xf8, 0xa4, 0xac, 0x46, 0xb0, 0x8f, 0x9d, 0xa7, 0x0e, 0xfd, 0xca, + 0xe1, 0xd7, 0xfc, 0x78, 0xc8, 0x46, 0xb5, 0x55, 0xdf, 0x69, 0xe7, 0x95, 0xe2, 0xa5, 0xe3, 0x93, + 0xf2, 0x62, 0x24, 0x62, 0x63, 0xdf, 0x27, 0x0e, 0x9b, 0x0e, 0xa8, 0xed, 0x6e, 0x6f, 0x37, 0xda, + 0xf9, 0xd9, 0xa9, 0x00, 0xd9, 0xd5, 0xff, 0x0b, 0x8b, 0xe3, 0x01, 0x3b, 0x8d, 0xad, 0x7c, 0xac, + 0xa8, 0x1e, 0x9f, 0x94, 0x17, 0x22, 0xe8, 0x1d, 0xbb, 0x5f, 0x4c, 0x7e, 0xfb, 0x43, 0x69, 0xe6, + 0xa7, 0x1f, 0x4b, 0xca, 0xda, 0xd7, 0xb3, 0x90, 0x1d, 0xeb, 0x45, 0xea, 0xff, 0xe0, 0x4a, 0xab, + 0xf1, 0x60, 0xa7, 0xbe, 0x69, 0x6c, 0xb7, 0x1e, 0x18, 0xed, 0xcf, 0x9a, 0xf5, 0xc8, 0xee, 0x72, + 0xc7, 0x27, 0xe5, 0xb4, 0xdc, 0xd2, 0x79, 0xe8, 0xa6, 0x5e, 0xdf, 0xdb, 0x6d, 0xd7, 0xf3, 0x8a, + 0x40, 0x37, 0x3d, 0x72, 0x40, 0x19, 0x41, 0xf4, 0x2d, 0x58, 0x3e, 0x03, 0x3d, 0xda, 0xd8, 0xe2, + 0xf1, 0x49, 0x39, 0xdb, 0xf4, 0x88, 0x38, 0x6e, 0x18, 0xb1, 0x06, 0x97, 0x27, 0x23, 0x24, 0x3c, + 0x56, 0x5c, 0x38, 0x3e, 0x29, 0x43, 0x2d, 0xc4, 0x56, 0xa0, 0x30, 0x3d, 0xfb, 0x6e, 0x73, 0xb7, + 0xb5, 0xb1, 0x95, 0x2f, 0x17, 0xf3, 0xc7, 0x27, 0xe5, 0x4c, 0xd0, 0xa0, 0x39, 0x3e, 0x64, 0xa1, + 0xfa, 0xc9, 0x8b, 0xd3, 0x92, 0xf2, 0xf2, 0xb4, 0xa4, 0xfc, 0x76, 0x5a, 0x52, 0x9e, 0xbf, 0x29, + 0xcd, 0xbc, 0x7c, 0x53, 0x9a, 0xf9, 0xf9, 0x4d, 0x69, 0xe6, 0xf3, 0xbb, 0x5d, 0x9b, 0xf5, 0x86, + 0xfb, 0x95, 0x0e, 0x1d, 0xac, 0x47, 0xff, 0x9b, 0x85, 0x9f, 0xe2, 0x3f, 0xe2, 0xe4, 0xff, 0xb6, + 0xfd, 0x79, 0xb4, 0xdf, 0xf9, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x8a, 0xaf, 0xa8, 0xc3, 0x78, 0x0e, + 0x00, 0x00, } func (this *CoreChainLock) Equal(that interface{}) bool { @@ -1430,6 +1422,11 @@ func (m *StateID) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Height != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x10 + } if len(m.LastAppHash) > 0 { i -= len(m.LastAppHash) copy(dAtA[i:], m.LastAppHash) @@ -1683,16 +1680,6 @@ func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x52 } - { - size, err := m.StateID.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTypes(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x4a if len(m.BlockSignature) > 0 { i -= len(m.BlockSignature) copy(dAtA[i:], m.BlockSignature) @@ -1848,12 +1835,12 @@ func (m *Proposal) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x3a } - n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) - if err10 != nil { - return 0, err10 + 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 -= n10 - i = encodeVarintTypes(dAtA, i, uint64(n10)) + i -= n9 + i = encodeVarintTypes(dAtA, i, uint64(n9)) i-- dAtA[i] = 0x32 { @@ -2003,18 +1990,6 @@ func (m *BlockMeta) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - { - size, err := m.StateID.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintTypes(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x6 - i-- - dAtA[i] = 0xaa if m.HasCoreChainLock { i-- if m.HasCoreChainLock { @@ -2179,6 +2154,9 @@ func (m *StateID) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + if m.Height != 0 { + n += 1 + sovTypes(uint64(m.Height)) + } return n } @@ -2309,8 +2287,6 @@ func (m *Vote) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } - l = m.StateID.Size() - n += 1 + l + sovTypes(uint64(l)) l = len(m.StateSignature) if l > 0 { n += 1 + l + sovTypes(uint64(l)) @@ -2434,8 +2410,6 @@ func (m *BlockMeta) Size() (n int) { if m.HasCoreChainLock { n += 3 } - l = m.StateID.Size() - n += 2 + l + sovTypes(uint64(l)) return n } @@ -2885,6 +2859,25 @@ func (m *StateID) Unmarshal(dAtA []byte) error { m.LastAppHash = []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:]) @@ -3875,39 +3868,6 @@ func (m *Vote) Unmarshal(dAtA []byte) error { m.BlockSignature = []byte{} } iNdEx = postIndex - case 9: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field StateID", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.StateID.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StateSignature", wireType) @@ -4861,39 +4821,6 @@ func (m *BlockMeta) Unmarshal(dAtA []byte) error { } } m.HasCoreChainLock = bool(v != 0) - case 101: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field StateID", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.StateID.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 f61bb9605d..33d9744bba 100644 --- a/proto/tendermint/types/types.proto +++ b/proto/tendermint/types/types.proto @@ -56,6 +56,7 @@ message BlockID { // StateID message StateID { bytes last_app_hash = 1; + int64 height = 2; } // -------------------------------- @@ -87,7 +88,7 @@ message Header { bytes evidence_hash = 13; // evidence included in the block bytes proposer_pro_tx_hash = 101; // original proposer of the block - uint64 proposed_app_version = 102; // proposer's latest available app protocol version + uint64 proposed_app_version = 102; // proposer's latest available app protocol version } // CoreChainLock represents a core chain lock for synchronization between state data and core chain @@ -114,11 +115,10 @@ message Vote { int32 round = 3; BlockID block_id = 4 [(gogoproto.nullable) = false, (gogoproto.customname) = "BlockID"]; // zero if vote is nil. - StateID state_id = 9 [(gogoproto.customname) = "StateID", (gogoproto.nullable) = false]; - bytes validator_pro_tx_hash = 6; - int32 validator_index = 7; - bytes block_signature = 8; - bytes state_signature = 10; + bytes validator_pro_tx_hash = 6; + int32 validator_index = 7; + bytes block_signature = 8; + bytes state_signature = 10; } // Commit contains the evidence that a block was committed by a set of validators. @@ -155,7 +155,6 @@ message LightBlock { message BlockMeta { BlockID block_id = 1 [(gogoproto.customname) = "BlockID", (gogoproto.nullable) = false]; - StateID state_id = 101 [(gogoproto.customname) = "StateID", (gogoproto.nullable) = false]; int64 block_size = 2; Header header = 3 [(gogoproto.nullable) = false]; int64 num_txs = 4; diff --git a/proto/tendermint/types/validator.pb.go b/proto/tendermint/types/validator.pb.go index b7169594e0..8740f4d073 100644 --- a/proto/tendermint/types/validator.pb.go +++ b/proto/tendermint/types/validator.pb.go @@ -121,6 +121,7 @@ type Validator struct { VotingPower int64 `protobuf:"varint,3,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` ProposerPriority int64 `protobuf:"varint,4,opt,name=proposer_priority,json=proposerPriority,proto3" json:"proposer_priority,omitempty"` ProTxHash []byte `protobuf:"bytes,5,opt,name=pro_tx_hash,json=proTxHash,proto3" json:"pro_tx_hash,omitempty"` + NodeAddress string `protobuf:"bytes,6,opt,name=node_address,json=nodeAddress,proto3" json:"node_address,omitempty"` } func (m *Validator) Reset() { *m = Validator{} } @@ -184,6 +185,13 @@ func (m *Validator) GetProTxHash() []byte { return nil } +func (m *Validator) GetNodeAddress() string { + if m != nil { + return m.NodeAddress + } + return "" +} + type SimpleValidator struct { PubKey *crypto.PublicKey `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` VotingPower int64 `protobuf:"varint,2,opt,name=voting_power,json=votingPower,proto3" json:"voting_power,omitempty"` @@ -245,36 +253,37 @@ func init() { func init() { proto.RegisterFile("tendermint/types/validator.proto", fileDescriptor_4e92274df03d3088) } var fileDescriptor_4e92274df03d3088 = []byte{ - // 455 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x4d, 0x6e, 0xd3, 0x40, - 0x14, 0xce, 0x24, 0x4d, 0xda, 0xbe, 0x04, 0x35, 0x8c, 0xba, 0xb0, 0x4a, 0xe5, 0x9a, 0x2c, 0x90, - 0x25, 0x90, 0x2d, 0x81, 0x50, 0x17, 0xdd, 0x75, 0x85, 0xc4, 0x26, 0xb8, 0x51, 0x17, 0x6c, 0x2c, - 0x3b, 0x19, 0xc5, 0xa3, 0x38, 0x79, 0xc3, 0xcc, 0x38, 0xd4, 0xb7, 0xe0, 0x38, 0x1c, 0xa1, 0xcb, - 0x2e, 0x59, 0x21, 0x94, 0xdc, 0x80, 0x13, 0x20, 0x8f, 0x89, 0x6d, 0x15, 0x50, 0xd8, 0x8d, 0xde, - 0xf7, 0xbd, 0xf9, 0x7e, 0xa4, 0x07, 0x8e, 0x66, 0xab, 0x19, 0x93, 0x4b, 0xbe, 0xd2, 0xbe, 0xce, - 0x05, 0x53, 0xfe, 0x3a, 0x4a, 0xf9, 0x2c, 0xd2, 0x28, 0x3d, 0x21, 0x51, 0x23, 0x1d, 0xd6, 0x0c, - 0xcf, 0x30, 0xce, 0x4e, 0xe7, 0x38, 0x47, 0x03, 0xfa, 0xc5, 0xab, 0xe4, 0x9d, 0x9d, 0x37, 0x7e, - 0x9a, 0xca, 0x5c, 0x68, 0xf4, 0x17, 0x2c, 0x57, 0x25, 0x3a, 0xfa, 0xd9, 0x86, 0xc1, 0xed, 0xee, - 0xe7, 0x1b, 0xa6, 0xe9, 0x15, 0x40, 0xa5, 0xa4, 0x2c, 0xe2, 0x74, 0xdc, 0xfe, 0xeb, 0x67, 0xde, - 0x63, 0x2d, 0xaf, 0xda, 0x09, 0x1a, 0x74, 0x7a, 0x09, 0x47, 0x42, 0xa2, 0x40, 0xc5, 0xa4, 0xd5, - 0x76, 0xc8, 0xbe, 0xd5, 0x8a, 0x4c, 0x5f, 0x01, 0xd5, 0xa8, 0xa3, 0x34, 0x5c, 0xa3, 0xe6, 0xab, - 0x79, 0x28, 0xf0, 0x33, 0x93, 0x56, 0xc7, 0x21, 0x6e, 0x27, 0x18, 0x1a, 0xe4, 0xd6, 0x00, 0xe3, - 0x62, 0x4e, 0x27, 0x70, 0xaa, 0x13, 0xc9, 0x54, 0x82, 0xe9, 0x2c, 0x14, 0x59, 0x9c, 0xf2, 0x69, - 0xb8, 0x60, 0xb9, 0x75, 0x60, 0x24, 0xcf, 0x9b, 0x92, 0x65, 0x62, 0x6f, 0x6c, 0x48, 0xef, 0x59, - 0x7e, 0x7d, 0x70, 0xff, 0xfd, 0xa2, 0x15, 0xd0, 0x6a, 0xbf, 0x42, 0xe8, 0x05, 0xf4, 0x3f, 0x65, - 0x28, 0xb3, 0x65, 0x58, 0xf8, 0xb4, 0xba, 0x0e, 0x71, 0xbb, 0x01, 0x94, 0xa3, 0x49, 0x2e, 0x58, - 0x83, 0x90, 0x44, 0x2a, 0xb1, 0x7a, 0x0e, 0x71, 0x07, 0x3b, 0xc2, 0xbb, 0x48, 0x25, 0xf4, 0x05, - 0x9c, 0x24, 0x91, 0x6a, 0x38, 0x52, 0xd6, 0xa1, 0x43, 0xdc, 0xa3, 0xe0, 0x49, 0x12, 0xa9, 0x4a, - 0x48, 0x8d, 0xbe, 0x12, 0x38, 0xae, 0x5a, 0xa0, 0x57, 0x70, 0x28, 0xb2, 0xd8, 0x04, 0x68, 0xff, - 0x67, 0x00, 0x12, 0xf4, 0x44, 0x16, 0x17, 0xa6, 0x9f, 0xc3, 0xe0, 0x2f, 0x95, 0xf5, 0xd7, 0x8d, - 0xb6, 0x5e, 0xc2, 0xd3, 0x5d, 0xcf, 0xa1, 0x90, 0x1c, 0x25, 0xd7, 0x65, 0x55, 0x9d, 0x60, 0xb8, - 0x03, 0xc6, 0xbf, 0xe7, 0xd4, 0x86, 0xbe, 0x90, 0x18, 0xea, 0xbb, 0x32, 0x63, 0xd7, 0x64, 0x3c, - 0x16, 0x12, 0x27, 0x77, 0x45, 0xc4, 0xd1, 0x02, 0x4e, 0x6e, 0xf8, 0x52, 0xa4, 0xac, 0xf6, 0xff, - 0xb6, 0xf6, 0x4f, 0xf6, 0xfb, 0xff, 0xa7, 0xf3, 0xf6, 0x1f, 0xce, 0xaf, 0x3f, 0xdc, 0x6f, 0x6c, - 0xf2, 0xb0, 0xb1, 0xc9, 0x8f, 0x8d, 0x4d, 0xbe, 0x6c, 0xed, 0xd6, 0xc3, 0xd6, 0x6e, 0x7d, 0xdb, - 0xda, 0xad, 0x8f, 0x97, 0x73, 0xae, 0x93, 0x2c, 0xf6, 0xa6, 0xb8, 0xf4, 0x9b, 0x97, 0x52, 0x3f, - 0xcb, 0x3b, 0x78, 0x7c, 0x45, 0x71, 0xcf, 0xcc, 0xdf, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x71, - 0x41, 0x97, 0x2e, 0x60, 0x03, 0x00, 0x00, + // 475 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0xc1, 0x6a, 0xdb, 0x40, + 0x14, 0x45, 0x3d, 0x76, 0xec, 0xc4, 0x4f, 0x2e, 0x71, 0x87, 0x2c, 0x44, 0x1a, 0x14, 0xc5, 0x8b, + 0x22, 0x68, 0x91, 0xa0, 0xa5, 0x64, 0x91, 0x55, 0xb3, 0x2a, 0x74, 0xe3, 0x2a, 0x26, 0x8b, 0x6e, + 0x84, 0x64, 0x0d, 0x96, 0xb0, 0xec, 0x37, 0x9d, 0x19, 0xb9, 0xd1, 0x5f, 0xf4, 0xb3, 0xb2, 0xcc, + 0xb2, 0x50, 0x28, 0xc5, 0xfe, 0x83, 0x7e, 0x41, 0xd1, 0xa8, 0x96, 0x45, 0xda, 0xe2, 0xee, 0x86, + 0x7b, 0xef, 0xcc, 0x9b, 0x73, 0xe1, 0x81, 0xad, 0xd8, 0x32, 0x66, 0x62, 0x91, 0x2e, 0x95, 0xa7, + 0x0a, 0xce, 0xa4, 0xb7, 0x0a, 0xb3, 0x34, 0x0e, 0x15, 0x0a, 0x97, 0x0b, 0x54, 0x48, 0x87, 0xbb, + 0x84, 0xab, 0x13, 0xa7, 0x27, 0x33, 0x9c, 0xa1, 0x36, 0xbd, 0xf2, 0x54, 0xe5, 0x4e, 0xcf, 0x1a, + 0x2f, 0x4d, 0x45, 0xc1, 0x15, 0x7a, 0x73, 0x56, 0xc8, 0xca, 0x1d, 0xfd, 0x6c, 0xc3, 0xe0, 0x76, + 0xfb, 0xf2, 0x0d, 0x53, 0xf4, 0x0a, 0xa0, 0x9e, 0x24, 0x4d, 0x62, 0x77, 0x1c, 0xe3, 0xd5, 0x33, + 0xf7, 0xf1, 0x2c, 0xb7, 0xbe, 0xe3, 0x37, 0xe2, 0xf4, 0x12, 0x8e, 0xb8, 0x40, 0x8e, 0x92, 0x09, + 0xb3, 0x6d, 0x93, 0x7d, 0x57, 0xeb, 0x30, 0x7d, 0x09, 0x54, 0xa1, 0x0a, 0xb3, 0x60, 0x85, 0x2a, + 0x5d, 0xce, 0x02, 0x8e, 0x9f, 0x99, 0x30, 0x3b, 0x36, 0x71, 0x3a, 0xfe, 0x50, 0x3b, 0xb7, 0xda, + 0x18, 0x97, 0x3a, 0x9d, 0xc0, 0x89, 0x4a, 0x04, 0x93, 0x09, 0x66, 0x71, 0xc0, 0xf3, 0x28, 0x4b, + 0xa7, 0xc1, 0x9c, 0x15, 0xe6, 0x81, 0x1e, 0x79, 0xd6, 0x1c, 0x59, 0x11, 0xbb, 0x63, 0x1d, 0x7a, + 0xcf, 0x8a, 0xeb, 0x83, 0xfb, 0xef, 0xe7, 0x2d, 0x9f, 0xd6, 0xf7, 0x6b, 0x87, 0x9e, 0x83, 0xf1, + 0x29, 0x47, 0x91, 0x2f, 0x82, 0xf2, 0x9f, 0x66, 0xd7, 0x26, 0x4e, 0xd7, 0x87, 0x4a, 0x9a, 0x14, + 0x9c, 0x35, 0x02, 0x49, 0x28, 0x13, 0xb3, 0x67, 0x13, 0x67, 0xb0, 0x0d, 0xbc, 0x0b, 0x65, 0x42, + 0x9f, 0xc3, 0x71, 0x12, 0xca, 0xc6, 0x8f, 0xa4, 0x79, 0x68, 0x13, 0xe7, 0xc8, 0x7f, 0x92, 0x84, + 0xb2, 0x1e, 0x24, 0x47, 0xdf, 0x08, 0xf4, 0xeb, 0x16, 0xe8, 0x15, 0x1c, 0xf2, 0x3c, 0xd2, 0x00, + 0xed, 0xff, 0x04, 0x20, 0x7e, 0x8f, 0xe7, 0x51, 0xf9, 0xe9, 0x0b, 0x18, 0xfc, 0xa5, 0x32, 0x63, + 0xd5, 0x68, 0xeb, 0x05, 0x3c, 0xdd, 0xf6, 0x1c, 0x70, 0x91, 0xa2, 0x48, 0x55, 0x55, 0x55, 0xc7, + 0x1f, 0x6e, 0x8d, 0xf1, 0x6f, 0x9d, 0x5a, 0x60, 0x70, 0x81, 0x81, 0xba, 0xab, 0x18, 0xbb, 0x9a, + 0xb1, 0xcf, 0x05, 0x4e, 0xee, 0x34, 0xe2, 0x05, 0x0c, 0x96, 0x18, 0xb3, 0x20, 0x8c, 0x63, 0xc1, + 0xa4, 0xd4, 0x25, 0xf4, 0x7d, 0xa3, 0xd4, 0xde, 0x56, 0xd2, 0x68, 0x0e, 0xc7, 0x37, 0xe9, 0x82, + 0x67, 0x6c, 0x87, 0xf8, 0x66, 0x87, 0x48, 0xf6, 0x23, 0xfe, 0x13, 0xae, 0xfd, 0x07, 0xdc, 0xf5, + 0x87, 0xfb, 0xb5, 0x45, 0x1e, 0xd6, 0x16, 0xf9, 0xb1, 0xb6, 0xc8, 0x97, 0x8d, 0xd5, 0x7a, 0xd8, + 0x58, 0xad, 0xaf, 0x1b, 0xab, 0xf5, 0xf1, 0x72, 0x96, 0xaa, 0x24, 0x8f, 0xdc, 0x29, 0x2e, 0xbc, + 0xe6, 0x32, 0xed, 0x8e, 0xd5, 0xaa, 0x3c, 0x5e, 0xb4, 0xa8, 0xa7, 0xf5, 0xd7, 0xbf, 0x02, 0x00, + 0x00, 0xff, 0xff, 0xc2, 0xac, 0x7b, 0x0a, 0x83, 0x03, 0x00, 0x00, } func (m *ValidatorSet) Marshal() (dAtA []byte, err error) { @@ -383,6 +392,13 @@ func (m *Validator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.NodeAddress) > 0 { + i -= len(m.NodeAddress) + copy(dAtA[i:], m.NodeAddress) + i = encodeVarintValidator(dAtA, i, uint64(len(m.NodeAddress))) + i-- + dAtA[i] = 0x32 + } if len(m.ProTxHash) > 0 { i -= len(m.ProTxHash) copy(dAtA[i:], m.ProTxHash) @@ -520,6 +536,10 @@ func (m *Validator) Size() (n int) { if l > 0 { n += 1 + l + sovValidator(uint64(l)) } + l = len(m.NodeAddress) + if l > 0 { + n += 1 + l + sovValidator(uint64(l)) + } return n } @@ -927,6 +947,38 @@ func (m *Validator) Unmarshal(dAtA []byte) error { m.ProTxHash = []byte{} } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NodeAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowValidator + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthValidator + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthValidator + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NodeAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipValidator(dAtA[iNdEx:]) diff --git a/proto/tendermint/types/validator.proto b/proto/tendermint/types/validator.proto index b35539396b..3f981fd04e 100644 --- a/proto/tendermint/types/validator.proto +++ b/proto/tendermint/types/validator.proto @@ -21,6 +21,7 @@ message Validator { int64 voting_power = 3; int64 proposer_priority = 4; bytes pro_tx_hash = 5; + string node_address = 6; // address of the Validator, correct URI (RFC 3986) } message SimpleValidator { diff --git a/proxy/client.go b/proxy/client.go index 1dc6d8853f..f57c603ca2 100644 --- a/proxy/client.go +++ b/proxy/client.go @@ -2,6 +2,7 @@ package proxy import ( "fmt" + "io" abcicli "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/abci/example/counter" @@ -20,7 +21,7 @@ type ClientCreator interface { // local proxy uses a mutex on an in-proc app type localClientCreator struct { - mtx *tmsync.Mutex + mtx *tmsync.RWMutex app types.Application } @@ -28,7 +29,7 @@ type localClientCreator struct { // which will be running locally. func NewLocalClientCreator(app types.Application) ClientCreator { return &localClientCreator{ - mtx: new(tmsync.Mutex), + mtx: new(tmsync.RWMutex), app: app, } } @@ -69,20 +70,28 @@ func (r *remoteClientCreator) NewABCIClient() (abcicli.Client, error) { // DefaultClientCreator returns a default ClientCreator, which will create a // local client if addr is one of: 'counter', 'counter_serial', 'kvstore', // 'persistent_kvstore' or 'noop', otherwise - a remote client. -func DefaultClientCreator(addr, transport, dbDir string) ClientCreator { +// +// The Closer is a noop except for persistent_kvstore applications, +// which will clean up the store. +func DefaultClientCreator(addr, transport, dbDir string) (ClientCreator, io.Closer) { switch addr { case "counter": - return NewLocalClientCreator(counter.NewApplication(false)) + return NewLocalClientCreator(counter.NewApplication(false)), noopCloser{} case "counter_serial": - return NewLocalClientCreator(counter.NewApplication(true)) + return NewLocalClientCreator(counter.NewApplication(true)), noopCloser{} case "kvstore": - return NewLocalClientCreator(kvstore.NewApplication()) + return NewLocalClientCreator(kvstore.NewApplication()), noopCloser{} case "persistent_kvstore": - return NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(dbDir)) + app := kvstore.NewPersistentKVStoreApplication(dbDir) + return NewLocalClientCreator(app), app case "noop": - return NewLocalClientCreator(types.NewBaseApplication()) + return NewLocalClientCreator(types.NewBaseApplication()), noopCloser{} default: mustConnect := false // loop retrying - return NewRemoteClientCreator(addr, transport, mustConnect) + return NewRemoteClientCreator(addr, transport, mustConnect), noopCloser{} } } + +type noopCloser struct{} + +func (noopCloser) Close() error { return nil } diff --git a/rpc/client/evidence_test.go b/rpc/client/evidence_test.go index e3d2b85971..4c72d631d1 100644 --- a/rpc/client/evidence_test.go +++ b/rpc/client/evidence_test.go @@ -32,7 +32,8 @@ var defaultTestTime = time.Date(2018, 10, 10, 8, 20, 13, 695936996, time.UTC) func newEvidence(t *testing.T, val *privval.FilePV, vote *types.Vote, vote2 *types.Vote, - chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash) *types.DuplicateVoteEvidence { + chainID string, stateID types.StateID, + quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash) *types.DuplicateVoteEvidence { var err error @@ -48,13 +49,13 @@ func newEvidence(t *testing.T, val *privval.FilePV, vote2.BlockSignature, err = privKey.SignDigest(types.VoteBlockSignID(chainID, v2, quorumType, quorumHash)) require.NoError(t, err) - vote.StateSignature, err = privKey.SignDigest(types.VoteStateSignID(chainID, v, quorumType, quorumHash)) + vote.StateSignature, err = privKey.SignDigest(stateID.SignID(chainID, quorumType, quorumHash)) require.NoError(t, err) - vote2.StateSignature, err = privKey.SignDigest(types.VoteStateSignID(chainID, v2, quorumType, quorumHash)) + vote2.StateSignature, err = privKey.SignDigest(stateID.SignID(chainID, quorumType, quorumHash)) require.NoError(t, err) - validator := types.NewValidator(privKey.PubKey(), 100, val.Key.ProTxHash) + validator := types.NewValidator(privKey.PubKey(), 100, val.Key.ProTxHash, "") valSet := types.NewValidatorSet([]*types.Validator{validator}, validator.PubKey, quorumType, quorumHash, true) return types.NewDuplicateVoteEvidence(vote, vote2, defaultTestTime, valSet) @@ -80,14 +81,13 @@ func makeEvidences( Hash: tmhash.Sum([]byte("partset")), }, }, - StateID: types.StateID{ - LastAppHash: tmhash.Sum(tmrand.Bytes(tmhash.Size)), - }, } + stateID := types.RandStateID().WithHeight(vote.Height - 1) + vote2 := vote vote2.BlockID.Hash = tmhash.Sum([]byte("blockhash2")) - correct = newEvidence(t, val, &vote, &vote2, chainID, quorumType, quorumHash) + correct = newEvidence(t, val, &vote, &vote2, chainID, stateID, quorumType, quorumHash) fakes = make([]*types.DuplicateVoteEvidence, 0) @@ -95,34 +95,34 @@ func makeEvidences( { v := vote2 v.ValidatorProTxHash = []byte("some_pro_tx_hash") - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, quorumType, quorumHash)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, stateID, quorumType, quorumHash)) } // different height { v := vote2 v.Height = vote.Height + 1 - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, quorumType, quorumHash)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, stateID, quorumType, quorumHash)) } // different round { v := vote2 v.Round = vote.Round + 1 - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, quorumType, quorumHash)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, stateID, quorumType, quorumHash)) } // different type { v := vote2 v.Type = tmproto.PrecommitType - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, quorumType, quorumHash)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, stateID, quorumType, quorumHash)) } // exactly same vote { v := vote - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, quorumType, quorumHash)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, stateID, quorumType, quorumHash)) } return correct, fakes @@ -137,7 +137,8 @@ func TestBroadcastEvidence_DuplicateVoteEvidence(t *testing.T) { for i, c := range GetClients() { h := int64(1) - vals, _ := c.Validators(context.Background(), &h, nil, nil, nil) + vals, err := c.Validators(context.Background(), &h, nil, nil, nil) + require.NoError(t, err) correct, fakes := makeEvidences(t, pv, chainID, vals.QuorumType, *vals.QuorumHash) t.Logf("client %d", i) diff --git a/rpc/client/http/http.go b/rpc/client/http/http.go index 516a28b0e1..bb1e47fe50 100644 --- a/rpc/client/http/http.go +++ b/rpc/client/http/http.go @@ -395,6 +395,15 @@ func (c *baseRPCClient) Genesis(ctx context.Context) (*ctypes.ResultGenesis, err return result, nil } +func (c *baseRPCClient) GenesisChunked(ctx context.Context, id uint) (*ctypes.ResultGenesisChunk, error) { + result := new(ctypes.ResultGenesisChunk) + _, err := c.caller.Call(ctx, "genesis_chunked", map[string]interface{}{"chunk": id}, result) + if err != nil { + return nil, err + } + return result, nil +} + func (c *baseRPCClient) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) { result := new(ctypes.ResultBlock) params := make(map[string]interface{}) diff --git a/rpc/client/interface.go b/rpc/client/interface.go index 5f8bbb6b55..dfec3c166f 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -95,6 +95,7 @@ type SignClient interface { // HistoryClient provides access to data from genesis to now in large chunks. type HistoryClient interface { Genesis(context.Context) (*ctypes.ResultGenesis, error) + GenesisChunked(context.Context, uint) (*ctypes.ResultGenesisChunk, error) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) } diff --git a/rpc/client/local/local.go b/rpc/client/local/local.go index 2022929140..0aab33ce83 100644 --- a/rpc/client/local/local.go +++ b/rpc/client/local/local.go @@ -154,6 +154,10 @@ func (c *Local) Genesis(ctx context.Context) (*ctypes.ResultGenesis, error) { return core.Genesis(c.ctx) } +func (c *Local) GenesisChunked(ctx context.Context, id uint) (*ctypes.ResultGenesisChunk, error) { + return core.GenesisChunked(c.ctx, id) +} + func (c *Local) Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error) { return core.Block(c.ctx, height) } diff --git a/rpc/client/main_test.go b/rpc/client/main_test.go index c97311c810..cab8b7cdd4 100644 --- a/rpc/client/main_test.go +++ b/rpc/client/main_test.go @@ -26,6 +26,7 @@ func TestMain(m *testing.M) { // and shut down proper at the end rpctest.StopTendermint(node) + app.Close() _ = os.RemoveAll(dir) os.Exit(code) } diff --git a/rpc/client/mocks/client.go b/rpc/client/mocks/client.go index 03331c93cf..9b6019c014 100644 --- a/rpc/client/mocks/client.go +++ b/rpc/client/mocks/client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.6.0. DO NOT EDIT. +// Code generated by mockery 2.7.4. DO NOT EDIT. package mocks @@ -436,6 +436,29 @@ func (_m *Client) Genesis(_a0 context.Context) (*coretypes.ResultGenesis, error) return r0, r1 } +// GenesisChunked provides a mock function with given fields: _a0, _a1 +func (_m *Client) GenesisChunked(_a0 context.Context, _a1 uint) (*coretypes.ResultGenesisChunk, error) { + ret := _m.Called(_a0, _a1) + + var r0 *coretypes.ResultGenesisChunk + if rf, ok := ret.Get(0).(func(context.Context, uint) *coretypes.ResultGenesisChunk); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.ResultGenesisChunk) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, uint) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Health provides a mock function with given fields: _a0 func (_m *Client) Health(_a0 context.Context) (*coretypes.ResultHealth, error) { ret := _m.Called(_a0) diff --git a/rpc/client/mocks/remote_client.go b/rpc/client/mocks/remote_client.go index bda7bc8f87..30fe31a18f 100644 --- a/rpc/client/mocks/remote_client.go +++ b/rpc/client/mocks/remote_client.go @@ -436,6 +436,29 @@ func (_m *RemoteClient) Genesis(_a0 context.Context) (*coretypes.ResultGenesis, return r0, r1 } +// GenesisChunked provides a mock function with given fields: _a0, _a1 +func (_m *RemoteClient) GenesisChunked(_a0 context.Context, _a1 uint) (*coretypes.ResultGenesisChunk, error) { + ret := _m.Called(_a0, _a1) + + var r0 *coretypes.ResultGenesisChunk + if rf, ok := ret.Get(0).(func(context.Context, uint) *coretypes.ResultGenesisChunk); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*coretypes.ResultGenesisChunk) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, uint) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Health provides a mock function with given fields: _a0 func (_m *RemoteClient) Health(_a0 context.Context) (*coretypes.ResultHealth, error) { ret := _m.Called(_a0) diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 577c9ef028..671cec4a02 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -2,6 +2,7 @@ package client_test import ( "context" + "encoding/base64" "fmt" "math" "net/http" @@ -14,6 +15,7 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" mempl "github.com/tendermint/tendermint/mempool" @@ -186,6 +188,31 @@ func TestGenesisAndValidators(t *testing.T) { } } +func TestGenesisChunked(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + for _, c := range GetClients() { + first, err := c.GenesisChunked(ctx, 0) + require.NoError(t, err) + + decoded := make([]string, 0, first.TotalChunks) + for i := 0; i < first.TotalChunks; i++ { + chunk, err := c.GenesisChunked(ctx, uint(i)) + require.NoError(t, err) + data, err := base64.StdEncoding.DecodeString(chunk.Data) + require.NoError(t, err) + decoded = append(decoded, string(data)) + + } + doc := []byte(strings.Join(decoded, "")) + + var out types.GenesisDoc + require.NoError(t, tmjson.Unmarshal(doc, &out), + "first: %+v, doc: %s", first, string(doc)) + } +} + func TestABCIQuery(t *testing.T) { for i, c := range GetClients() { // write something diff --git a/rpc/core/env.go b/rpc/core/env.go index 371f5c7c07..8993a3d51b 100644 --- a/rpc/core/env.go +++ b/rpc/core/env.go @@ -1,12 +1,14 @@ package core import ( + "encoding/base64" "fmt" "time" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/crypto" + tmjson "github.com/tendermint/tendermint/libs/json" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" @@ -26,6 +28,10 @@ const ( // SubscribeTimeout is the maximum time we wait to subscribe for an event. // must be less than the server's write timeout (see rpcserver.DefaultConfig) SubscribeTimeout = 5 * time.Second + + // genesisChunkSize is the maximum size, in bytes, of each + // chunk in the genesis structure for the chunked API + genesisChunkSize = 16 * 1024 * 1024 // 16 ) var ( @@ -91,6 +97,9 @@ type Environment struct { Logger log.Logger Config cfg.RPCConfig + + // cache of chunked genesis data. + genChunks []string } //---------------------------------------------- @@ -138,6 +147,35 @@ func validateRequestThresholdPublicKey(requestThresholdPublicKeyPtr *bool) bool return requestThresholdPublicKey } +// InitGenesisChunks configures the environment and should be called on service +// startup. +func InitGenesisChunks() error { + if env.genChunks != nil { + return nil + } + + if env.GenDoc == nil { + return nil + } + + data, err := tmjson.Marshal(env.GenDoc) + if err != nil { + return err + } + + for i := 0; i < len(data); i += genesisChunkSize { + end := i + genesisChunkSize + + if end > len(data) { + end = len(data) + } + + env.genChunks = append(env.genChunks, base64.StdEncoding.EncodeToString(data[i:end])) + } + + return nil +} + func validateSkipCount(page, perPage int) int { skipCount := (page - 1) * perPage if skipCount < 0 { diff --git a/rpc/core/net.go b/rpc/core/net.go index a8aedf9e0e..2a0e2c92d4 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -94,9 +94,35 @@ func UnsafeDialPeers(ctx *rpctypes.Context, peers []string, persistent, uncondit // Genesis returns genesis file. // More: https://docs.tendermint.com/master/rpc/#/Info/genesis func Genesis(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) { + if len(env.genChunks) > 1 { + return nil, errors.New("genesis response is large, please use the genesis_chunked API instead") + } + return &ctypes.ResultGenesis{Genesis: env.GenDoc}, nil } +func GenesisChunked(ctx *rpctypes.Context, chunk uint) (*ctypes.ResultGenesisChunk, error) { + if env.genChunks == nil { + return nil, fmt.Errorf("service configuration error, genesis chunks are not initialized") + } + + if len(env.genChunks) == 0 { + return nil, fmt.Errorf("service configuration error, there are no chunks") + } + + id := int(chunk) + + if id > len(env.genChunks)-1 { + return nil, fmt.Errorf("there are %d chunks, %d is invalid", len(env.genChunks)-1, id) + } + + return &ctypes.ResultGenesisChunk{ + TotalChunks: len(env.genChunks), + ChunkNumber: id, + Data: env.genChunks[id], + }, nil +} + func getIDs(peers []string) ([]string, error) { ids := make([]string, 0, len(peers)) diff --git a/rpc/core/routes.go b/rpc/core/routes.go index e97da5dc34..a13e78bae9 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -19,6 +19,7 @@ var Routes = map[string]*rpc.RPCFunc{ "net_info": rpc.NewRPCFunc(NetInfo, ""), "blockchain": rpc.NewRPCFunc(BlockchainInfo, "minHeight,maxHeight"), "genesis": rpc.NewRPCFunc(Genesis, ""), + "genesis_chunked": rpc.NewRPCFunc(GenesisChunked, "chunk"), "block": rpc.NewRPCFunc(Block, "height"), "block_by_hash": rpc.NewRPCFunc(BlockByHash, "hash"), "block_results": rpc.NewRPCFunc(BlockResults, "height"), diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 339544cce1..9f791bd5d8 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -25,6 +25,16 @@ type ResultGenesis struct { Genesis *types.GenesisDoc `json:"genesis"` } +// ResultGenesisChunk is the output format for the chunked/paginated +// interface. These chunks are produced by converting the genesis +// document to JSON and then splitting the resulting payload into +// 16 megabyte blocks and then base64 encoding each block. +type ResultGenesisChunk struct { + ChunkNumber int `json:"chunk"` + TotalChunks int `json:"total"` + Data string `json:"data"` +} + // Single block (with meta) type ResultBlock struct { BlockID types.BlockID `json:"block_id"` diff --git a/rpc/jsonrpc/jsonrpc_test.go b/rpc/jsonrpc/jsonrpc_test.go index ec12f85d7d..84956bae95 100644 --- a/rpc/jsonrpc/jsonrpc_test.go +++ b/rpc/jsonrpc/jsonrpc_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/go-kit/kit/log/term" + "github.com/go-kit/log/term" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index a7bfa96660..5c6767ff89 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -141,7 +141,7 @@ func StartTendermint(app abci.Application, opts ...func(*Options)) *nm.Node { // cleans up test/config files. func StopTendermint(node *nm.Node) { if err := node.Stop(); err != nil { - node.Logger.Error("Error when tryint to stop node", "err", err) + node.Logger.Error("Error when trying to stop node", "err", err) } node.Wait() os.RemoveAll(node.Config().RootDir) diff --git a/scripts/release/cliff.toml b/scripts/release/cliff.toml new file mode 100644 index 0000000000..e57b38b17b --- /dev/null +++ b/scripts/release/cliff.toml @@ -0,0 +1,60 @@ +# configuration file for git-cliff (0.1.0) + +[changelog] +# changelog header +header = """ +# Changelog +All notable changes to this project will be documented in this file.\n +""" +# template for the changelog body +# https://tera.netlify.app/docs/#introduction +body = """ +{% if version %}\ + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} +{% else %}\ + ## [unreleased] +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | upper_first }} + {% for commit in commits %} + - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\ + {% endfor %} +{% endfor %}\n +""" +# remove the leading and trailing whitespaces from the template +trim = true +# changelog footer +footer = """ + +""" + +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not conventional +filter_unconventional = true +# regex for parsing and grouping commits +commit_parsers = [ + { message = "^feat", group = "Features"}, + { message = "^fix", group = "Bug Fixes"}, + { message = "^doc", group = "Documentation"}, + { message = "^perf", group = "Performance"}, + { message = "^refactor", group = "Refactor"}, + { message = "^style", group = "Styling"}, + { message = "^test", group = "Testing"}, + { message = "^chore\\(release\\): prepare for", skip = true}, + { message = "^chore", group = "Miscellaneous Tasks"}, + { body = ".*security", group = "Security"}, +] +# filter out the commits that are not matched by commit parsers +filter_commits = false +# glob pattern for matching git tags +tag_pattern = "v[0-9]*" +# regex for skipping tags +skip_tags = "v0.1.0-beta.1" +# regex for ignoring tags +ignore_tags = "" +# sort the tags topologically +topo_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "oldest" diff --git a/scripts/release/pr_description.md b/scripts/release/pr_description.md new file mode 100644 index 0000000000..7b9db6e7dd --- /dev/null +++ b/scripts/release/pr_description.md @@ -0,0 +1,32 @@ + + + +## Issue being fixed or feature implemented + + +Release new Dash Platform version + +## What was done? + +- Updated changelog +- Bumped packages version + +## How Has This Been Tested? + + + +None + +## Breaking Changes + + +None + +## Checklist: + +- [ ] I have performed a self-review of the generated changelog +- [ ] I have checked that P2PProtocol in version/version.go is bumped if needed +- [ ] I have checked that BlockProtocol in version/version.go is bumped if needed + +**For repository code-owners and collaborators only** +- [ ] I have assigned this pull request to a milestone diff --git a/scripts/release/release.sh b/scripts/release/release.sh new file mode 100755 index 0000000000..4a58daec4a --- /dev/null +++ b/scripts/release/release.sh @@ -0,0 +1,226 @@ +#!/usr/bin/env bash + +set -e + +function success { + [ -t 1 ] && echo -e "\e[32mSUCCESS:\e[0m" "$@" || echo "SUCCESS:" "$@" +} + +function debug { + [ -t 1 ] && echo -e "\e[93mDEBUG:\e[0m" "$@" || echo "DEBUG:" "$@" +} + +function error { + debug Error: "$@" + cleanup + [ -t 1 ] && echo -e '\e[91mERROR:\e[0m' "$@" || echo "ERROR:" "$@" + exit 1 +} +function displayHelp { + cat < + +where flags can be one of: + -r=, --release= - release number, like 0.7.0 (REQUIRED) + --cleanup - clean up before releasing; it can remove your local changes + -h, --help - display this help message + +## Examples + +### Full release of 0.7.4 + +git checkout v0.7-dev +$0 --release=0.7.4 + +### Prerelease of 0.8.0-dev.3 + +git checkout v0.8-dev +$0 --release=0.8.0-dev.3 + +EOF +} + +function detectVersion { + # @see https://tldp.org/LDP/abs/html/string-manipulation.html for bash syntax hints + CURRENT_VERSION="${LATEST_TAG#[vV]}" # 0.34.12-dev.589-g8acb1d0c8 +} + +function configureDefaults { + debug Configuring default values + REPO_DIR="$(realpath "$(dirname "${0}")/../..")" + CACHE_DIR="/tmp/tenderdash-changelog-cache" + mkdir -p "$CACHE_DIR" + LATEST_TAG="$(git describe --tags --abbrev=0)" # v0.34.12-dev.589-g8acb1d0c8 +} + +function parseArgs { + debug Parsing command line + while [ "$#" -ge 1 ]; do + # for arg in "$@"; do + arg="$1" + case $arg in + --cleanup) + CLEANUP=yes + shift + ;; + -r=* | --release=*) + NEW_PACKAGE_VERSION="${arg#*=}" + shift + ;; + -r | --release) + shift + if [ -n "$1" ]; then + NEW_PACKAGE_VERSION="${1#*=}" + fi + shift + ;; + -h | --help) + displayHelp + shift + exit 0 + ;; + *) + error "Unrecoginzed command line argument '$arg'; try '$0 --help'" + ;; + esac + done +} + +function configureFinal() { + debug Finalizing configuration + VERSION_WITHOUT_PRERELEASE=${NEW_PACKAGE_VERSION%-*} + + if [ "${VERSION_WITHOUT_PRERELEASE}" == "${NEW_PACKAGE_VERSION}" ]; then + ## Full release + RELEASE_TYPE=release + else + RELEASE_TYPE=prerelease + fi + + CURRENT_BRANCH="$(git branch --show-current)" + SOURCE_BRANCH="v${VERSION_WITHOUT_PRERELEASE%.*}-dev" + RELEASE_BRANCH="release_${NEW_PACKAGE_VERSION}" + MILESTONE="v${VERSION_WITHOUT_PRERELEASE}" + + if [[ $RELEASE_TYPE != "prerelease" ]]; then # full release + TARGET_BRANCH="master" + else # prerelease + TARGET_BRANCH="v${VERSION_WITHOUT_PRERELEASE%.*}-dev" + fi + + debug "Release type: ${RELEASE_TYPE}" + debug "Latest tag: ${LATEST_TAG}" + debug "Previous version: ${CURRENT_VERSION}" + debug "New version: ${NEW_PACKAGE_VERSION}" + debug "Source branch: ${SOURCE_BRANCH}" + debug "Target branch: ${TARGET_BRANCH}" +} + +function validate { + debug Validating configuration + if [ -z "${NEW_PACKAGE_VERSION}" ]; then + error "You must provide new release version with --release=x.y.z; see '$0 --help' for more details" + fi + + if [[ "${CURRENT_BRANCH}" != "${SOURCE_BRANCH}" ]]; then + error "you must run this script from the \"${SOURCE_BRANCH}\" branch" + fi + + local UNCOMMITTED_FILES + UNCOMMITTED_FILES="$(git status -su)" + if [ -n "$UNCOMMITTED_FILES" ]; then + error "Commit or stash your changes before running this script" + fi + + # ensure github authentication + if ! gh auth status &>/dev/null; then + gh auth login + fi +} + +function generateChangelog { + debug Generating CHANGELOG + docker run -ti -u "$(id -u)" \ + -v "${REPO_DIR}/.git":/app/:ro -v "${REPO_DIR}/scripts/release/cliff.toml":/cliff.toml:ro \ + -v "${REPO_DIR}/CHANGELOG.md":/CHANGELOG.md \ + orhunp/git-cliff:latest \ + --config /cliff.toml \ + --strip all \ + --tag "$NEW_PACKAGE_VERSION" \ + --prepend /CHANGELOG.md \ + --unreleased +} + +function updateVersionGo { + sed -i'' -e "s/TMVersionDefault = \"[^\"]*\"\s*\$/TMVersionDefault = \"${NEW_PACKAGE_VERSION}\"/g" "${REPO_DIR}/version/version.go" +} + +function createReleasePR { + debug "Creating release branch ${RELEASE_BRANCH}" + git pull -q + git checkout -q -b "${RELEASE_BRANCH}" + + # commit changes + git commit -m "chore(release): update changelog and version to $NEW_PACKAGE_VERSION" \ + "$REPO_DIR/CHANGELOG.md" \ + "$REPO_DIR/version/version.go" + + # push changes + git push --force -u origin "${RELEASE_BRANCH}" + + debug "Creating milestone $MILESTONE if it doesn't exist yet" + gh api --silent --method POST 'repos/dashevo/tenderdash/milestones' --field "title=${MILESTONE}" || true + + if gh pr view release_0.7.0-dev.7 >/dev/null; then + debug "PR for branch $TARGET_BRANCH already exists, skipping creation" + else + debug "Creating PR for branch $TARGET_BRANCH" + gh pr create --base "$TARGET_BRANCH" \ + --fill \ + --title "chore(release): update changelog and bump version to $NEW_PACKAGE_VERSION" \ + --body-file "$REPO_DIR/scripts/release/pr_description.md" \ + --milestone "$MILESTONE" + fi +} + +function cleanup() { + debug Cleaning up + git checkout --quiet -- "${REPO_DIR}/CHANGELOG.md" + git checkout --quiet "${SOURCE_BRANCH}" || true + git branch --quiet -D "${RELEASE_BRANCH}" || true + + # We need to re-detect current branch again + CURRENT_BRANCH="$(git branch --show-current)" +} + +configureDefaults +parseArgs "$@" +detectVersion +configureFinal + +if [ -n "$CLEANUP" ]; then + cleanup +fi + +validate +generateChangelog +updateVersionGo +createReleasePR + +cleanup + +success "Pull Request for a new release {$NEW_PACKAGE_VERSION} created successfully." diff --git a/state/execution.go b/state/execution.go index ed658ef7c8..38f749c482 100644 --- a/state/execution.go +++ b/state/execution.go @@ -7,10 +7,12 @@ import ( "time" "github.com/tendermint/tendermint/crypto/bls12381" + dashtypes "github.com/tendermint/tendermint/dash/types" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" cryptoenc "github.com/tendermint/tendermint/crypto/encoding" + tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" @@ -88,7 +90,8 @@ func NewBlockExecutor( NextCoreChainLock: nextCoreChainLock, logger: logger, metrics: NopMetrics(), - appHashSize: crypto.DefaultAppHashSize, + // TODO: appHashSize should be read from config + appHashSize: crypto.DefaultAppHashSize, } for _, option := range options { @@ -303,7 +306,7 @@ func (blockExec *BlockExecutor) ApplyBlockWithLogger( // Events are fired after everything else. // NOTE: if we crash between Commit and Save, events wont be fired during replay - fireEvents(logger, blockExec.eventBus, block, abciResponses, validatorUpdates) + fireEvents(logger, blockExec.eventBus, block, abciResponses, validatorUpdates, quorumHash) return state, retainHeight, nil } @@ -526,6 +529,13 @@ func validateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, valUpdate.ProTxHash, ) } + + if valUpdate.NodeAddress != "" { + _, err := dashtypes.ParseValidatorAddress(valUpdate.NodeAddress) + if err != nil { + return fmt.Errorf("cannot parse validator address %s: %w", valUpdate.NodeAddress, err) + } + } } return nil } @@ -546,6 +556,9 @@ func updateState( // and update s.LastValidators and s.Validators. nValSet := state.NextValidators.Copy() + // We need to generate LastStateID before changing the state + lastStateID := state.StateID() + // Update the validator set with the latest abciResponses. lastHeightValsChanged := state.LastHeightValidatorsChanged if len(validatorUpdates) > 0 { @@ -597,7 +610,7 @@ func updateState( InitialHeight: state.InitialHeight, LastBlockHeight: header.Height, LastBlockID: blockID, - LastStateID: types.StateID{LastAppHash: state.AppHash}, + LastStateID: lastStateID, LastBlockTime: header.Time, LastCoreChainLockedBlockHeight: header.CoreChainLockedHeight, NextValidators: nValSet, @@ -620,6 +633,7 @@ func fireEvents( block *types.Block, abciResponses *tmstate.ABCIResponses, validatorUpdates []*types.Validator, + quorumHash tmbytes.HexBytes, ) { if err := eventBus.PublishEventNewBlock(types.EventDataNewBlock{ Block: block, @@ -662,7 +676,10 @@ func fireEvents( if len(validatorUpdates) > 0 { if err := eventBus.PublishEventValidatorSetUpdates( - types.EventDataValidatorSetUpdates{ValidatorUpdates: validatorUpdates}); err != nil { + types.EventDataValidatorSetUpdates{ + QuorumHash: append(tmbytes.HexBytes{}, quorumHash...), + ValidatorUpdates: validatorUpdates, + }); err != nil { logger.Error("failed publishing event", "err", err) } } diff --git a/state/execution_test.go b/state/execution_test.go index ad2c6fc3c0..4126a3f8d7 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -8,6 +8,7 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/bls12381" + dashtypes "github.com/tendermint/tendermint/dash/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -154,6 +155,8 @@ func TestValidateValidatorUpdates(t *testing.T) { PubKeyTypes: []string{types.ABCIPubKeyTypeBLS12381}, } + addr := dashtypes.RandValidatorAddress() + testCases := []struct { name string @@ -164,25 +167,30 @@ func TestValidateValidatorUpdates(t *testing.T) { }{ { "adding a validator is OK", + []abci.ValidatorUpdate{{PubKey: &pk2, Power: 100, ProTxHash: proTxHash2, NodeAddress: addr.String()}}, + defaultValidatorParams, + false, + }, + {"adding a validator without address is OK", []abci.ValidatorUpdate{{PubKey: &pk2, Power: 100, ProTxHash: proTxHash2}}, defaultValidatorParams, false, }, { "updating a validator is OK", - []abci.ValidatorUpdate{{PubKey: &pk1, Power: 100, ProTxHash: proTxHash1}}, + []abci.ValidatorUpdate{{PubKey: &pk1, Power: 100, ProTxHash: proTxHash1, NodeAddress: addr.String()}}, defaultValidatorParams, false, }, { "removing a validator is OK", - []abci.ValidatorUpdate{{Power: 0, ProTxHash: proTxHash2}}, + []abci.ValidatorUpdate{{Power: 0, ProTxHash: proTxHash2, NodeAddress: addr.String()}}, defaultValidatorParams, false, }, { "adding a validator with negative power results in error", - []abci.ValidatorUpdate{{PubKey: &pk2, Power: -100, ProTxHash: proTxHash2}}, + []abci.ValidatorUpdate{{PubKey: &pk2, Power: -100, ProTxHash: proTxHash2, NodeAddress: addr.String()}}, defaultValidatorParams, true, }, @@ -356,6 +364,11 @@ func TestEndBlockValidatorUpdates(t *testing.T) { } } + // Ensure new validators have some IP addresses set + for _, validator := range newVals.Validators { + validator.NodeAddress = dashtypes.RandValidatorAddress() + } + app.ValidatorSetUpdate = newVals.ABCIEquivalentValidatorUpdates() state, _, err = blockExec.ApplyBlock(state, nodeProTxHash, blockID, block) @@ -378,6 +391,7 @@ func TestEndBlockValidatorUpdates(t *testing.T) { "Expected event of type EventDataValidatorSetUpdates, got %T", msg.Data(), ) + assert.Len(t, event.QuorumHash, crypto.QuorumHashSize) if assert.NotEmpty(t, event.ValidatorUpdates) { assert.Equal(t, addProTxHash, event.ValidatorUpdates[pos].ProTxHash) assert.EqualValues( @@ -444,32 +458,3 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { assert.NotNil(t, err) assert.NotEmpty(t, state.NextValidators.Validators) } - -/* -func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.BlockID { - var ( - h = make([]byte, tmhash.Size) - psH = make([]byte, tmhash.Size) - ) - copy(h, hash) - copy(psH, partSetHash) - return types.BlockID{ - Hash: h, - PartSetHeader: types.PartSetHeader{ - Total: partSetSize, - Hash: psH, - }, - } -} -*/ - -/* -func makeStateID(lastAppHash []byte) types.StateID { - var ( - h = make([]byte, tmhash.Size) - ) - copy(h, lastAppHash) - return types.StateID{ - LastAppHash: h, - } -}*/ diff --git a/state/helpers_test.go b/state/helpers_test.go index 6d98897878..05db00d92b 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -29,33 +29,45 @@ func newTestApp() proxy.AppConns { return proxy.NewAppConns(cc) } -func makeAndCommitGoodBlock(state sm.State, nodeProTxHash *crypto.ProTxHash, height int64, lastCommit *types.Commit, proposerProTxHash crypto.ProTxHash, blockExec *sm.BlockExecutor, privVals map[string]types.PrivValidator, evidence []types.Evidence, proposedAppVersion uint64) (sm.State, types.BlockID, types.StateID, *types.Commit, error) { +func makeAndCommitGoodBlock( + state sm.State, + nodeProTxHash *crypto.ProTxHash, + height int64, + lastCommit *types.Commit, + proposerProTxHash crypto.ProTxHash, + blockExec *sm.BlockExecutor, + privVals map[string]types.PrivValidator, + evidence []types.Evidence, proposedAppVersion uint64, +) (sm.State, types.BlockID, *types.Commit, error) { + // A good block passes - state, blockID, stateID, err := makeAndApplyGoodBlock(state, nodeProTxHash, height, lastCommit, proposerProTxHash, blockExec, evidence, 0) + state, blockID, err := makeAndApplyGoodBlock(state, nodeProTxHash, height, lastCommit, proposerProTxHash, blockExec, evidence, proposedAppVersion) if err != nil { - return state, types.BlockID{}, types.StateID{}, nil, err + return state, types.BlockID{}, nil, err } // Simulate a lastCommit for this block from all validators for the next height - commit, err := makeValidCommit(height, blockID, stateID, state.Validators, privVals) + commit, err := makeValidCommit(height, blockID, state.LastStateID, state.Validators, privVals) if err != nil { - return state, types.BlockID{}, types.StateID{}, nil, err + return state, types.BlockID{}, nil, err } - return state, blockID, stateID, commit, nil + return state, blockID, commit, nil } -func makeAndApplyGoodBlock(state sm.State, nodeProTxHash *crypto.ProTxHash, height int64, lastCommit *types.Commit, proposerProTxHash []byte, blockExec *sm.BlockExecutor, evidence []types.Evidence, proposedAppVersion uint64) (sm.State, types.BlockID, types.StateID, error) { +func makeAndApplyGoodBlock(state sm.State, nodeProTxHash *crypto.ProTxHash, height int64, lastCommit *types.Commit, proposerProTxHash []byte, + blockExec *sm.BlockExecutor, evidence []types.Evidence, proposedAppVersion uint64) (sm.State, types.BlockID, error) { block, _ := state.MakeBlock(height, nil, makeTxs(height), lastCommit, evidence, proposerProTxHash, proposedAppVersion) if err := blockExec.ValidateBlock(state, block); err != nil { - return state, types.BlockID{}, types.StateID{}, err + return state, types.BlockID{}, err } blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: types.PartSetHeader{Total: 3, Hash: tmrand.Bytes(32)}} state, _, err := blockExec.ApplyBlock(state, nodeProTxHash, blockID, block) if err != nil { - return state, types.BlockID{}, types.StateID{}, err + return state, types.BlockID{}, err } - return state, blockID, types.StateID{LastAppHash: state.AppHash}, nil + + return state, blockID, nil } func makeValidCommit( @@ -93,7 +105,7 @@ func makeTxs(height int64) (txs []types.Tx) { return txs } -func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) { +func makeState(nVals int, height int64) (sm.State, dbm.DB, map[string]types.PrivValidator) { privValsByProTxHash := make(map[string]types.PrivValidator, nVals) vals, privVals, quorumHash, thresholdPublicKey := types.GenerateMockGenesisValidators(nVals) // vals and privals are sorted @@ -116,7 +128,7 @@ func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValida panic(err) } - for i := 1; i < height; i++ { + for i := int64(1); i < height; i++ { s.LastBlockHeight++ s.LastValidators = s.Validators.Copy() if err := stateStore.Save(s); err != nil { diff --git a/state/indexer/sink/psql/backport.go b/state/indexer/sink/psql/backport.go new file mode 100644 index 0000000000..3547911127 --- /dev/null +++ b/state/indexer/sink/psql/backport.go @@ -0,0 +1,88 @@ +package psql + +// This file adds code to the psql package that is needed for integration with +// v0.34, but which is not part of the original implementation. +// +// In v0.35, ADR 65 was implemented in which the TxIndexer and BlockIndexer +// interfaces were merged into a hybrid EventSink interface. The Backport* +// types defined here bridge the psql EventSink (which was built in terms of +// the v0.35 interface) to the old interfaces. +// +// We took this narrower approach to backporting to avoid pulling in a much +// wider-reaching set of changes in v0.35 that would have broken several of the +// v0.34.x APIs. The result is sufficient to work with the node plumbing as it +// exists in the v0.34 branch. + +import ( + "context" + "errors" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/pubsub/query" + "github.com/tendermint/tendermint/state/txindex" + "github.com/tendermint/tendermint/types" +) + +const ( + eventTypeBeginBlock = "begin_block" + eventTypeEndBlock = "end_block" +) + +// TxIndexer returns a bridge from es to the Tendermint v0.34 transaction indexer. +func (es *EventSink) TxIndexer() BackportTxIndexer { + return BackportTxIndexer{psql: es} +} + +// BackportTxIndexer implements the txindex.TxIndexer interface by delegating +// indexing operations to an underlying PostgreSQL event sink. +type BackportTxIndexer struct{ psql *EventSink } + +// AddBatch indexes a batch of transactions in Postgres, as part of TxIndexer. +func (b BackportTxIndexer) AddBatch(batch *txindex.Batch) error { + return b.psql.IndexTxEvents(batch.Ops) +} + +// Index indexes a single transaction result in Postgres, as part of TxIndexer. +func (b BackportTxIndexer) Index(txr *abci.TxResult) error { + return b.psql.IndexTxEvents([]*abci.TxResult{txr}) +} + +// Get is implemented to satisfy the TxIndexer interface, but is not supported +// by the psql event sink and reports an error for all inputs. +func (BackportTxIndexer) Get([]byte) (*abci.TxResult, error) { + return nil, errors.New("the TxIndexer.Get method is not supported") +} + +// Search is implemented to satisfy the TxIndexer interface, but it is not +// supported by the psql event sink and reports an error for all inputs. +func (BackportTxIndexer) Search(context.Context, *query.Query) ([]*abci.TxResult, error) { + return nil, errors.New("the TxIndexer.Search method is not supported") +} + +// BlockIndexer returns a bridge that implements the Tendermint v0.34 block +// indexer interface, using the Postgres event sink as a backing store. +func (es *EventSink) BlockIndexer() BackportBlockIndexer { + return BackportBlockIndexer{psql: es} +} + +// BackportBlockIndexer implements the indexer.BlockIndexer interface by +// delegating indexing operations to an underlying PostgreSQL event sink. +type BackportBlockIndexer struct{ psql *EventSink } + +// Has is implemented to satisfy the BlockIndexer interface, but it is not +// supported by the psql event sink and reports an error for all inputs. +func (BackportBlockIndexer) Has(height int64) (bool, error) { + return false, errors.New("the BlockIndexer.Has method is not supported") +} + +// Index indexes block begin and end events for the specified block. It is +// part of the BlockIndexer interface. +func (b BackportBlockIndexer) Index(block types.EventDataNewBlockHeader) error { + return b.psql.IndexBlockEvents(block) +} + +// Search is implemented to satisfy the BlockIndexer interface, but it is not +// supported by the psql event sink and reports an error for all inputs. +func (BackportBlockIndexer) Search(context.Context, *query.Query) ([]int64, error) { + return nil, errors.New("the BlockIndexer.Search method is not supported") +} diff --git a/state/indexer/sink/psql/backport_test.go b/state/indexer/sink/psql/backport_test.go new file mode 100644 index 0000000000..d85022247d --- /dev/null +++ b/state/indexer/sink/psql/backport_test.go @@ -0,0 +1,11 @@ +package psql + +import ( + "github.com/tendermint/tendermint/state/indexer" + "github.com/tendermint/tendermint/state/txindex" +) + +var ( + _ indexer.BlockIndexer = BackportBlockIndexer{} + _ txindex.TxIndexer = BackportTxIndexer{} +) diff --git a/state/indexer/sink/psql/psql.go b/state/indexer/sink/psql/psql.go new file mode 100644 index 0000000000..fb9e3190dd --- /dev/null +++ b/state/indexer/sink/psql/psql.go @@ -0,0 +1,253 @@ +// Package psql implements an event sink backed by a PostgreSQL database. +package psql + +import ( + "context" + "database/sql" + "errors" + "fmt" + "strings" + "time" + + "github.com/gogo/protobuf/proto" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/pubsub/query" + "github.com/tendermint/tendermint/types" +) + +const ( + tableBlocks = "blocks" + tableTxResults = "tx_results" + tableEvents = "events" + tableAttributes = "attributes" + driverName = "postgres" +) + +// EventSink is an indexer backend providing the tx/block index services. This +// implementation stores records in a PostgreSQL database using the schema +// defined in state/indexer/sink/psql/schema.sql. +type EventSink struct { + store *sql.DB + chainID string +} + +// NewEventSink constructs an event sink associated with the PostgreSQL +// database specified by connStr. Events written to the sink are attributed to +// the specified chainID. +func NewEventSink(connStr, chainID string) (*EventSink, error) { + db, err := sql.Open(driverName, connStr) + if err != nil { + return nil, err + } + + return &EventSink{ + store: db, + chainID: chainID, + }, nil +} + +// DB returns the underlying Postgres connection used by the sink. +// This is exported to support testing. +func (es *EventSink) DB() *sql.DB { return es.store } + +// runInTransaction executes query in a fresh database transaction. +// If query reports an error, the transaction is rolled back and the +// error from query is reported to the caller. +// Otherwise, the result of committing the transaction is returned. +func runInTransaction(db *sql.DB, query func(*sql.Tx) error) error { + dbtx, err := db.Begin() + if err != nil { + return err + } + if err := query(dbtx); err != nil { + _ = dbtx.Rollback() // report the initial error, not the rollback + return err + } + return dbtx.Commit() +} + +// queryWithID executes the specified SQL query with the given arguments, +// expecting a single-row, single-column result containing an ID. If the query +// succeeds, the ID from the result is returned. +func queryWithID(tx *sql.Tx, query string, args ...interface{}) (uint32, error) { + var id uint32 + if err := tx.QueryRow(query, args...).Scan(&id); err != nil { + return 0, err + } + return id, nil +} + +// insertEvents inserts a slice of events and any indexed attributes of those +// events into the database associated with dbtx. +// +// If txID > 0, the event is attributed to the Tendermint transaction with that +// ID; otherwise it is recorded as a block event. +func insertEvents(dbtx *sql.Tx, blockID, txID uint32, evts []abci.Event) error { + // Populate the transaction ID field iff one is defined (> 0). + var txIDArg interface{} + if txID > 0 { + txIDArg = txID + } + + // Add each event to the events table, and retrieve its row ID to use when + // adding any attributes the event provides. + for _, evt := range evts { + // Skip events with an empty type. + if evt.Type == "" { + continue + } + + eid, err := queryWithID(dbtx, ` +INSERT INTO `+tableEvents+` (block_id, tx_id, type) VALUES ($1, $2, $3) + RETURNING rowid; +`, blockID, txIDArg, evt.Type) + if err != nil { + return err + } + + // Add any attributes flagged for indexing. + for _, attr := range evt.Attributes { + if !attr.Index { + continue + } + compositeKey := evt.Type + "." + string(attr.Key) + if _, err := dbtx.Exec(` +INSERT INTO `+tableAttributes+` (event_id, key, composite_key, value) + VALUES ($1, $2, $3, $4); +`, eid, attr.Key, compositeKey, attr.Value); err != nil { + return err + } + } + } + return nil +} + +// makeIndexedEvent constructs an event from the specified composite key and +// value. If the key has the form "type.name", the event will have a single +// attribute with that name and the value; otherwise the event will have only +// a type and no attributes. +func makeIndexedEvent(compositeKey, value string) abci.Event { + i := strings.Index(compositeKey, ".") + if i < 0 { + return abci.Event{Type: compositeKey} + } + return abci.Event{Type: compositeKey[:i], Attributes: []abci.EventAttribute{ + {Key: []byte(compositeKey[i+1:]), Value: []byte(value), Index: true}, + }} +} + +// IndexBlockEvents indexes the specified block header, part of the +// indexer.EventSink interface. +func (es *EventSink) IndexBlockEvents(h types.EventDataNewBlockHeader) error { + ts := time.Now().UTC() + + return runInTransaction(es.store, func(dbtx *sql.Tx) error { + // Add the block to the blocks table and report back its row ID for use + // in indexing the events for the block. + blockID, err := queryWithID(dbtx, ` +INSERT INTO `+tableBlocks+` (height, chain_id, created_at) + VALUES ($1, $2, $3) + ON CONFLICT DO NOTHING + RETURNING rowid; +`, h.Header.Height, es.chainID, ts) + if err == sql.ErrNoRows { + return nil // we already saw this block; quietly succeed + } else if err != nil { + return fmt.Errorf("indexing block header: %w", err) + } + + // Insert the special block meta-event for height. + if err := insertEvents(dbtx, blockID, 0, []abci.Event{ + makeIndexedEvent(types.BlockHeightKey, fmt.Sprint(h.Header.Height)), + }); err != nil { + return fmt.Errorf("block meta-events: %w", err) + } + // Insert all the block events. Order is important here, + if err := insertEvents(dbtx, blockID, 0, h.ResultBeginBlock.Events); err != nil { + return fmt.Errorf("begin-block events: %w", err) + } + if err := insertEvents(dbtx, blockID, 0, h.ResultEndBlock.Events); err != nil { + return fmt.Errorf("end-block events: %w", err) + } + return nil + }) +} + +func (es *EventSink) IndexTxEvents(txrs []*abci.TxResult) error { + ts := time.Now().UTC() + + for _, txr := range txrs { + // Encode the result message in protobuf wire format for indexing. + resultData, err := proto.Marshal(txr) + if err != nil { + return fmt.Errorf("marshaling tx_result: %w", err) + } + + // Index the hash of the underlying transaction as a hex string. + txHash := fmt.Sprintf("%X", types.Tx(txr.Tx).Hash()) + + if err := runInTransaction(es.store, func(dbtx *sql.Tx) error { + // Find the block associated with this transaction. The block header + // must have been indexed prior to the transactions belonging to it. + blockID, err := queryWithID(dbtx, ` +SELECT rowid FROM `+tableBlocks+` WHERE height = $1 AND chain_id = $2; +`, txr.Height, es.chainID) + if err != nil { + return fmt.Errorf("finding block ID: %w", err) + } + + // Insert a record for this tx_result and capture its ID for indexing events. + txID, err := queryWithID(dbtx, ` +INSERT INTO `+tableTxResults+` (block_id, index, created_at, tx_hash, tx_result) + VALUES ($1, $2, $3, $4, $5) + ON CONFLICT DO NOTHING + RETURNING rowid; +`, blockID, txr.Index, ts, txHash, resultData) + if err == sql.ErrNoRows { + return nil // we already saw this transaction; quietly succeed + } else if err != nil { + return fmt.Errorf("indexing tx_result: %w", err) + } + + // Insert the special transaction meta-events for hash and height. + if err := insertEvents(dbtx, blockID, txID, []abci.Event{ + makeIndexedEvent(types.TxHashKey, txHash), + makeIndexedEvent(types.TxHeightKey, fmt.Sprint(txr.Height)), + }); err != nil { + return fmt.Errorf("indexing transaction meta-events: %w", err) + } + // Index any events packaged with the transaction. + if err := insertEvents(dbtx, blockID, txID, txr.Result.Events); err != nil { + return fmt.Errorf("indexing transaction events: %w", err) + } + return nil + + }); err != nil { + return err + } + } + return nil +} + +// SearchBlockEvents is not implemented by this sink, and reports an error for all queries. +func (es *EventSink) SearchBlockEvents(ctx context.Context, q *query.Query) ([]int64, error) { + return nil, errors.New("block search is not supported via the postgres event sink") +} + +// SearchTxEvents is not implemented by this sink, and reports an error for all queries. +func (es *EventSink) SearchTxEvents(ctx context.Context, q *query.Query) ([]*abci.TxResult, error) { + return nil, errors.New("tx search is not supported via the postgres event sink") +} + +// GetTxByHash is not implemented by this sink, and reports an error for all queries. +func (es *EventSink) GetTxByHash(hash []byte) (*abci.TxResult, error) { + return nil, errors.New("getTxByHash is not supported via the postgres event sink") +} + +// HasBlock is not implemented by this sink, and reports an error for all queries. +func (es *EventSink) HasBlock(h int64) (bool, error) { + return false, errors.New("hasBlock is not supported via the postgres event sink") +} + +// Stop closes the underlying PostgreSQL database. +func (es *EventSink) Stop() error { return es.store.Close() } diff --git a/state/indexer/sink/psql/psql_test.go b/state/indexer/sink/psql/psql_test.go new file mode 100644 index 0000000000..875a72a576 --- /dev/null +++ b/state/indexer/sink/psql/psql_test.go @@ -0,0 +1,341 @@ +package psql + +import ( + "context" + "database/sql" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "os/signal" + "testing" + "time" + + "github.com/adlio/schema" + "github.com/gogo/protobuf/proto" + "github.com/ory/dockertest" + "github.com/ory/dockertest/docker" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/types" + + // Register the Postgres database driver. + _ "github.com/lib/pq" +) + +var ( + doPauseAtExit = flag.Bool("pause-at-exit", false, + "If true, pause the test until interrupted at shutdown, to allow debugging") + + // A hook that test cases can call to obtain the shared database instance + // used for testing the sink. This is initialized in TestMain (see below). + testDB func() *sql.DB +) + +const ( + user = "postgres" + password = "secret" + port = "5432" + dsn = "postgres://%s:%s@localhost:%s/%s?sslmode=disable" + dbName = "postgres" + chainID = "test-chainID" + + viewBlockEvents = "block_events" + viewTxEvents = "tx_events" +) + +func TestMain(m *testing.M) { + flag.Parse() + + // Set up docker and start a container running PostgreSQL. + pool, err := dockertest.NewPool(os.Getenv("DOCKER_URL")) + if err != nil { + log.Fatalf("Creating docker pool: %v", err) + } + + resource, err := pool.RunWithOptions(&dockertest.RunOptions{ + Repository: "postgres", + Tag: "13", + Env: []string{ + "POSTGRES_USER=" + user, + "POSTGRES_PASSWORD=" + password, + "POSTGRES_DB=" + dbName, + "listen_addresses = '*'", + }, + ExposedPorts: []string{port}, + }, func(config *docker.HostConfig) { + // set AutoRemove to true so that stopped container goes away by itself + config.AutoRemove = true + config.RestartPolicy = docker.RestartPolicy{ + Name: "no", + } + }) + if err != nil { + log.Fatalf("Starting docker pool: %v", err) + } + + if *doPauseAtExit { + log.Print("Pause at exit is enabled, containers will not expire") + } else { + const expireSeconds = 60 + _ = resource.Expire(expireSeconds) + log.Printf("Container expiration set to %d seconds", expireSeconds) + } + + // Connect to the database, clear any leftover data, and install the + // indexing schema. + conn := fmt.Sprintf(dsn, user, password, resource.GetPort(port+"/tcp"), dbName) + var db *sql.DB + + if err := pool.Retry(func() error { + sink, err := NewEventSink(conn, chainID) + if err != nil { + return err + } + db = sink.DB() // set global for test use + return db.Ping() + }); err != nil { + log.Fatalf("Connecting to database: %v", err) + } + + if err := resetDatabase(db); err != nil { + log.Fatalf("Flushing database: %v", err) + } + + sm, err := readSchema() + if err != nil { + log.Fatalf("Reading schema: %v", err) + } else if err := schema.NewMigrator().Apply(db, sm); err != nil { + log.Fatalf("Applying schema: %v", err) + } + + // Set up the hook for tests to get the shared database handle. + testDB = func() *sql.DB { return db } + + // Run the selected test cases. + code := m.Run() + + // Clean up and shut down the database container. + if *doPauseAtExit { + log.Print("Testing complete, pausing for inspection. Send SIGINT to resume teardown") + waitForInterrupt() + log.Print("(resuming)") + } + log.Print("Shutting down database") + if err := pool.Purge(resource); err != nil { + log.Printf("WARNING: Purging pool failed: %v", err) + } + if err := db.Close(); err != nil { + log.Printf("WARNING: Closing database failed: %v", err) + } + + os.Exit(code) +} + +func TestIndexing(t *testing.T) { + t.Run("IndexBlockEvents", func(t *testing.T) { + indexer := &EventSink{store: testDB(), chainID: chainID} + require.NoError(t, indexer.IndexBlockEvents(newTestBlockHeader())) + + verifyBlock(t, 1) + verifyBlock(t, 2) + + verifyNotImplemented(t, "hasBlock", func() (bool, error) { return indexer.HasBlock(1) }) + verifyNotImplemented(t, "hasBlock", func() (bool, error) { return indexer.HasBlock(2) }) + + verifyNotImplemented(t, "block search", func() (bool, error) { + v, err := indexer.SearchBlockEvents(context.Background(), nil) + return v != nil, err + }) + + require.NoError(t, verifyTimeStamp(tableBlocks)) + + // Attempting to reindex the same events should gracefully succeed. + require.NoError(t, indexer.IndexBlockEvents(newTestBlockHeader())) + }) + + t.Run("IndexTxEvents", func(t *testing.T) { + indexer := &EventSink{store: testDB(), chainID: chainID} + + txResult := txResultWithEvents([]abci.Event{ + makeIndexedEvent("account.number", "1"), + makeIndexedEvent("account.owner", "Ivan"), + makeIndexedEvent("account.owner", "Yulieta"), + + {Type: "", Attributes: []abci.EventAttribute{ + { + Key: []byte("not_allowed"), + Value: []byte("Vlad"), + Index: true, + }, + }}, + }) + require.NoError(t, indexer.IndexTxEvents([]*abci.TxResult{txResult})) + + txr, err := loadTxResult(types.Tx(txResult.Tx).Hash()) + require.NoError(t, err) + assert.Equal(t, txResult, txr) + + require.NoError(t, verifyTimeStamp(tableTxResults)) + require.NoError(t, verifyTimeStamp(viewTxEvents)) + + verifyNotImplemented(t, "getTxByHash", func() (bool, error) { + txr, err := indexer.GetTxByHash(types.Tx(txResult.Tx).Hash()) + return txr != nil, err + }) + verifyNotImplemented(t, "tx search", func() (bool, error) { + txr, err := indexer.SearchTxEvents(context.Background(), nil) + return txr != nil, err + }) + + // try to insert the duplicate tx events. + err = indexer.IndexTxEvents([]*abci.TxResult{txResult}) + require.NoError(t, err) + }) +} + +func TestStop(t *testing.T) { + indexer := &EventSink{store: testDB()} + require.NoError(t, indexer.Stop()) +} + +// newTestBlockHeader constructs a fresh copy of a block header containing +// known test values to exercise the indexer. +func newTestBlockHeader() types.EventDataNewBlockHeader { + return types.EventDataNewBlockHeader{ + Header: types.Header{Height: 1}, + ResultBeginBlock: abci.ResponseBeginBlock{ + Events: []abci.Event{ + makeIndexedEvent("begin_event.proposer", "FCAA001"), + makeIndexedEvent("thingy.whatzit", "O.O"), + }, + }, + ResultEndBlock: abci.ResponseEndBlock{ + Events: []abci.Event{ + makeIndexedEvent("end_event.foo", "100"), + makeIndexedEvent("thingy.whatzit", "-.O"), + }, + }, + } +} + +// readSchema loads the indexing database schema file +func readSchema() ([]*schema.Migration, error) { + const filename = "schema.sql" + contents, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to read sql file from '%s': %w", filename, err) + } + + return []*schema.Migration{{ + ID: time.Now().Local().String() + " db schema", + Script: string(contents), + }}, nil +} + +// resetDB drops all the data from the test database. +func resetDatabase(db *sql.DB) error { + _, err := db.Exec(`DROP TABLE IF EXISTS blocks,tx_results,events,attributes CASCADE;`) + if err != nil { + return fmt.Errorf("dropping tables: %v", err) + } + _, err = db.Exec(`DROP VIEW IF EXISTS event_attributes,block_events,tx_events CASCADE;`) + if err != nil { + return fmt.Errorf("dropping views: %v", err) + } + return nil +} + +// txResultWithEvents constructs a fresh transaction result with fixed values +// for testing, that includes the specified events. +func txResultWithEvents(events []abci.Event) *abci.TxResult { + return &abci.TxResult{ + Height: 1, + Index: 0, + Tx: types.Tx("HELLO WORLD"), + Result: abci.ResponseDeliverTx{ + Data: []byte{0}, + Code: abci.CodeTypeOK, + Log: "", + Events: events, + }, + } +} + +func loadTxResult(hash []byte) (*abci.TxResult, error) { + hashString := fmt.Sprintf("%X", hash) + var resultData []byte + if err := testDB().QueryRow(` +SELECT tx_result FROM `+tableTxResults+` WHERE tx_hash = $1; +`, hashString).Scan(&resultData); err != nil { + return nil, fmt.Errorf("lookup transaction for hash %q failed: %v", hashString, err) + } + + txr := new(abci.TxResult) + if err := proto.Unmarshal(resultData, txr); err != nil { + return nil, fmt.Errorf("unmarshaling txr: %v", err) + } + + return txr, nil +} + +func verifyTimeStamp(tableName string) error { + return testDB().QueryRow(fmt.Sprintf(` +SELECT DISTINCT %[1]s.created_at + FROM %[1]s + WHERE %[1]s.created_at >= $1; +`, tableName), time.Now().Add(-2*time.Second)).Err() +} + +func verifyBlock(t *testing.T, height int64) { + // Check that the blocks table contains an entry for this height. + if err := testDB().QueryRow(` +SELECT height FROM `+tableBlocks+` WHERE height = $1; +`, height).Err(); err == sql.ErrNoRows { + t.Errorf("No block found for height=%d", height) + } else if err != nil { + t.Fatalf("Database query failed: %v", err) + } + + // Verify the presence of begin_block and end_block events. + if err := testDB().QueryRow(` +SELECT type, height, chain_id FROM `+viewBlockEvents+` + WHERE height = $1 AND type = $2 AND chain_id = $3; +`, height, eventTypeBeginBlock, chainID).Err(); err == sql.ErrNoRows { + t.Errorf("No %q event found for height=%d", eventTypeBeginBlock, height) + } else if err != nil { + t.Fatalf("Database query failed: %v", err) + } + + if err := testDB().QueryRow(` +SELECT type, height, chain_id FROM `+viewBlockEvents+` + WHERE height = $1 AND type = $2 AND chain_id = $3; +`, height, eventTypeEndBlock, chainID).Err(); err == sql.ErrNoRows { + t.Errorf("No %q event found for height=%d", eventTypeEndBlock, height) + } else if err != nil { + t.Fatalf("Database query failed: %v", err) + } +} + +// verifyNotImplemented calls f and verifies that it returns both a +// false-valued flag and a non-nil error whose string matching the expected +// "not supported" message with label prefixed. +func verifyNotImplemented(t *testing.T, label string, f func() (bool, error)) { + t.Helper() + t.Logf("Verifying that %q reports it is not implemented", label) + + want := label + " is not supported via the postgres event sink" + ok, err := f() + assert.False(t, ok) + require.NotNil(t, err) + assert.Equal(t, want, err.Error()) +} + +// waitForInterrupt blocks until a SIGINT is received by the process. +func waitForInterrupt() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, os.Interrupt) + <-ch +} diff --git a/state/indexer/sink/psql/schema.sql b/state/indexer/sink/psql/schema.sql new file mode 100644 index 0000000000..1091cd4c37 --- /dev/null +++ b/state/indexer/sink/psql/schema.sql @@ -0,0 +1,85 @@ +/* + This file defines the database schema for the PostgresQL ("psql") event sink + implementation in Tendermint. The operator must create a database and install + this schema before using the database to index events. + */ + +-- The blocks table records metadata about each block. +-- The block record does not include its events or transactions (see tx_results). +CREATE TABLE blocks ( + rowid BIGSERIAL PRIMARY KEY, + + height BIGINT NOT NULL, + chain_id VARCHAR NOT NULL, + + -- When this block header was logged into the sink, in UTC. + created_at TIMESTAMPTZ NOT NULL, + + UNIQUE (height, chain_id) +); + +-- Index blocks by height and chain, since we need to resolve block IDs when +-- indexing transaction records and transaction events. +CREATE INDEX idx_blocks_height_chain ON blocks(height, chain_id); + +-- The tx_results table records metadata about transaction results. Note that +-- the events from a transaction are stored separately. +CREATE TABLE tx_results ( + rowid BIGSERIAL PRIMARY KEY, + + -- The block to which this transaction belongs. + block_id BIGINT NOT NULL REFERENCES blocks(rowid), + -- The sequential index of the transaction within the block. + index INTEGER NOT NULL, + -- When this result record was logged into the sink, in UTC. + created_at TIMESTAMPTZ NOT NULL, + -- The hex-encoded hash of the transaction. + tx_hash VARCHAR NOT NULL, + -- The protobuf wire encoding of the TxResult message. + tx_result BYTEA NOT NULL, + + UNIQUE (block_id, index) +); + +-- The events table records events. All events (both block and transaction) are +-- associated with a block ID; transaction events also have a transaction ID. +CREATE TABLE events ( + rowid BIGSERIAL PRIMARY KEY, + + -- The block and transaction this event belongs to. + -- If tx_id is NULL, this is a block event. + block_id BIGINT NOT NULL REFERENCES blocks(rowid), + tx_id BIGINT NULL REFERENCES tx_results(rowid), + + -- The application-defined type label for the event. + type VARCHAR NOT NULL +); + +-- The attributes table records event attributes. +CREATE TABLE attributes ( + event_id BIGINT NOT NULL REFERENCES events(rowid), + key VARCHAR NOT NULL, -- bare key + composite_key VARCHAR NOT NULL, -- composed type.key + value VARCHAR NULL, + + UNIQUE (event_id, key) +); + +-- A joined view of events and their attributes. Events that do not have any +-- attributes are represented as a single row with empty key and value fields. +CREATE VIEW event_attributes AS + SELECT block_id, tx_id, type, key, composite_key, value + FROM events LEFT JOIN attributes ON (events.rowid = attributes.event_id); + +-- A joined view of all block events (those having tx_id NULL). +CREATE VIEW block_events AS + SELECT blocks.rowid as block_id, height, chain_id, type, key, composite_key, value + FROM blocks JOIN event_attributes ON (blocks.rowid = event_attributes.block_id) + WHERE event_attributes.tx_id IS NULL; + +-- A joined view of all transaction events. +CREATE VIEW tx_events AS + SELECT height, index, chain_id, type, key, composite_key, value, tx_results.created_at + FROM blocks JOIN tx_results ON (blocks.rowid = tx_results.block_id) + JOIN event_attributes ON (tx_results.rowid = event_attributes.tx_id) + WHERE event_attributes.tx_id IS NOT NULL; diff --git a/state/state.go b/state/state.go index 78a1df2e13..2348e64d6d 100644 --- a/state/state.go +++ b/state/state.go @@ -57,7 +57,7 @@ type State struct { LastBlockID types.BlockID LastBlockTime time.Time - // The Last StateID is actually the previous App Hash + // LastStateID contains App Hash and Height from previous state (at height-1) LastStateID types.StateID // Last Chain Lock is the last known chain locked height in consensus @@ -99,7 +99,7 @@ func (state State) Copy() State { LastBlockID: state.LastBlockID, LastBlockTime: state.LastBlockTime, - LastStateID: state.LastStateID, + LastStateID: state.LastStateID.Copy(), LastCoreChainLockedBlockHeight: state.LastCoreChainLockedBlockHeight, @@ -142,6 +142,22 @@ func (state State) IsEmpty() bool { return state.Validators == nil // XXX can't compare to Empty } +// StateID() generates new state ID based on current `state` +func (state State) StateID() types.StateID { + lastAppHash := make([]byte, len(state.AppHash)) + copy(lastAppHash, state.AppHash) + + height := state.LastBlockHeight + if height == 0 { + height = state.InitialHeight - 1 + } + + return types.StateID{ + Height: height, + LastAppHash: lastAppHash, + } +} + // ToProto takes the local state type and returns the equivalent proto type func (state *State) ToProto() (*tmstate.State, error) { if state == nil { @@ -369,6 +385,11 @@ func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) { ).CopyIncrementProposerPriority(1) } + stateID := types.StateID{ + Height: genDoc.InitialHeight - 1, + LastAppHash: genDoc.AppHash, + } + return State{ Version: InitStateVersion, ChainID: genDoc.ChainID, @@ -376,7 +397,7 @@ func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) { LastBlockHeight: 0, LastBlockID: types.BlockID{}, - LastStateID: types.StateID{}, + LastStateID: stateID, LastBlockTime: genDoc.GenesisTime, LastCoreChainLockedBlockHeight: genDoc.InitialCoreChainLockedHeight, diff --git a/state/state_test.go b/state/state_test.go index 877d531f20..3a840271ee 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -9,22 +9,20 @@ import ( "testing" "github.com/dashevo/dashd-go/btcjson" - - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/bls12381" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - dbm "github.com/tendermint/tm-db" - abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/bls12381" cryptoenc "github.com/tendermint/tendermint/crypto/encoding" + dashtypes "github.com/tendermint/tendermint/dash/types" + tmrand "github.com/tendermint/tendermint/libs/rand" tmstate "github.com/tendermint/tendermint/proto/tendermint/state" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" ) // setupTestCase does setup common to all test cases. @@ -115,8 +113,10 @@ func TestABCIResponsesSaveLoad1(t *testing.T) { pubKey := bls12381.GenPrivKey().PubKey() abciPubKey, err := cryptoenc.PubKeyToProto(pubKey) require.NoError(t, err) + + vu := types.TM2PB.NewValidatorUpdate(pubKey, 100, crypto.RandProTxHash(), dashtypes.RandValidatorAddress()) abciResponses.EndBlock = &abci.ResponseEndBlock{ValidatorSetUpdate: &abci.ValidatorSetUpdate{ - ValidatorUpdates: []abci.ValidatorUpdate{types.TM2PB.NewValidatorUpdate(pubKey, 100, crypto.RandProTxHash())}, + ValidatorUpdates: []abci.ValidatorUpdate{vu}, ThresholdPublicKey: abciPubKey, }} @@ -845,7 +845,8 @@ func TestFourAddFourMinusOneGenesisValidators(t *testing.T) { proTxHashes, privateKeys3, thresholdPublicKey3 := bls12381.CreatePrivLLMQDataOnProTxHashesDefaultThreshold(proTxHashes) abciValidatorUpdates := make([]abci.ValidatorUpdate, len(proTxHashes)) for j, proTxHash := range proTxHashes { - abciValidatorUpdates[j] = abci.UpdateValidator(proTxHash, privateKeys3[j].PubKey().Bytes(), types.DefaultDashVotingPower) + abciValidatorUpdates[j] = abci.UpdateValidator(proTxHash, privateKeys3[j].PubKey().Bytes(), + types.DefaultDashVotingPower, dashtypes.RandValidatorAddress().String()) } abciThresholdPublicKey3, err := cryptoenc.PubKeyToProto(thresholdPublicKey3) assert.NoError(t, err) @@ -1168,3 +1169,20 @@ func TestStateProto(t *testing.T) { } } } + +func TestState_StateID(t *testing.T) { + + state := sm.State{ + LastBlockHeight: 2, + } + state.AppHash = make([]byte, crypto.DefaultAppHashSize) + want := tmrand.Bytes(32) + copy(state.AppHash, want) + + stateID := state.StateID() + assert.Equal(t, int64(2), stateID.Height) + assert.EqualValues(t, want, stateID.LastAppHash) + + err := stateID.ValidateBasic() + assert.NoError(t, err, "StateID validation failed") +} diff --git a/state/validation_test.go b/state/validation_test.go index 9fc1509cd5..71606619bb 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -129,7 +129,7 @@ func TestValidateBlockHeader(t *testing.T) { A good block passes */ var err error - state, _, _, lastCommit, err = makeAndCommitGoodBlock( + state, _, lastCommit, err = makeAndCommitGoodBlock( state, nodeProTxHash, height, @@ -138,9 +138,8 @@ func TestValidateBlockHeader(t *testing.T) { blockExec, privVals, nil, - 3, - ) - require.NoError(t, err, "height %d", height) + 3) + require.NoError(t, err, "height: %d\nstate:\n%+v\n", height, state) } } @@ -181,6 +180,7 @@ func TestValidateBlockCommit(t *testing.T) { } for height := int64(1); height < validationTestsStopHeight; height++ { + stateID := state.StateID() proTxHash := state.Validators.GetProposer().ProTxHash if height > 1 { /* @@ -190,7 +190,7 @@ func TestValidateBlockCommit(t *testing.T) { wrongHeightVote, err := types.MakeVote( height, state.LastBlockID, - types.StateID{LastAppHash: state.AppHash}, + stateID, state.Validators, privVals[proTxHash.String()], chainID, @@ -200,7 +200,7 @@ func TestValidateBlockCommit(t *testing.T) { wrongHeightVote.Height, wrongHeightVote.Round, state.LastBlockID, - types.StateID{LastAppHash: state.AppHash}, + stateID, state.Validators.QuorumHash, wrongHeightVote.BlockSignature, wrongHeightVote.StateSignature, @@ -281,8 +281,7 @@ func TestValidateBlockCommit(t *testing.T) { */ var err error var blockID types.BlockID - var stateID types.StateID - state, blockID, stateID, lastCommit, err = makeAndCommitGoodBlock( + state, blockID, lastCommit, err = makeAndCommitGoodBlock( state, nodeProTxHash, height, @@ -318,7 +317,6 @@ func TestValidateBlockCommit(t *testing.T) { Round: 0, Type: tmproto.PrecommitType, BlockID: blockID, - StateID: stateID, } g := goodVote.ToProto() @@ -329,6 +327,7 @@ func TestValidateBlockCommit(t *testing.T) { state.Validators.QuorumType, badPrivValQuorumHash, g, + stateID, nil, ) require.NoError(t, err, "height %d", height) @@ -337,6 +336,7 @@ func TestValidateBlockCommit(t *testing.T) { state.Validators.QuorumType, badPrivValQuorumHash, b, + stateID, nil, ) require.NoError(t, err, "height %d", height) @@ -447,7 +447,7 @@ func TestValidateBlockEvidence(t *testing.T) { } var err error - state, _, _, lastCommit, err = makeAndCommitGoodBlock( + state, _, lastCommit, err = makeAndCommitGoodBlock( state, nodeProTxHash, height, diff --git a/statesync/reactor.go b/statesync/reactor.go index 27bad974e3..29fe8d1119 100644 --- a/statesync/reactor.go +++ b/statesync/reactor.go @@ -154,6 +154,7 @@ func (r *Reactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { Hash: msg.Hash, Metadata: msg.Metadata, }) + // TODO: We may want to consider punishing the peer for certain errors if err != nil { r.Logger.Error("Failed to add snapshot", "height", msg.Height, "format", msg.Format, "peer", src.ID(), "err", err) diff --git a/statesync/snapshots.go b/statesync/snapshots.go index 0f47145cd8..af8e72ae96 100644 --- a/statesync/snapshots.go +++ b/statesync/snapshots.go @@ -1,12 +1,10 @@ package statesync import ( - "context" "crypto/sha256" "fmt" "math/rand" "sort" - "time" tmsync "github.com/tendermint/tendermint/libs/sync" "github.com/tendermint/tendermint/p2p" @@ -46,8 +44,6 @@ func (s *snapshot) Key() snapshotKey { // snapshotPool discovers and aggregates snapshots across peers. type snapshotPool struct { - stateProvider StateProvider - tmsync.Mutex snapshots map[snapshotKey]*snapshot snapshotPeers map[snapshotKey]map[p2p.ID]p2p.Peer @@ -64,9 +60,8 @@ type snapshotPool struct { } // newSnapshotPool creates a new snapshot pool. The state source is used for -func newSnapshotPool(stateProvider StateProvider) *snapshotPool { +func newSnapshotPool() *snapshotPool { return &snapshotPool{ - stateProvider: stateProvider, snapshots: make(map[snapshotKey]*snapshot), snapshotPeers: make(map[snapshotKey]map[p2p.ID]p2p.Peer), formatIndex: make(map[uint32]map[snapshotKey]bool), @@ -82,14 +77,6 @@ func newSnapshotPool(stateProvider StateProvider) *snapshotPool { // returns true if this was a new, non-blacklisted snapshot. The snapshot height is verified using // the light client, and the expected app hash is set for the snapshot. func (p *snapshotPool) Add(peer p2p.Peer, snapshot *snapshot) (bool, error) { - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - appHash, err := p.stateProvider.AppHash(ctx, snapshot.Height) - if err != nil { - return false, err - } - snapshot.trustedAppHash = appHash key := snapshot.Key() p.Lock() diff --git a/statesync/snapshots_test.go b/statesync/snapshots_test.go index 588c0ac31a..28ed30eee9 100644 --- a/statesync/snapshots_test.go +++ b/statesync/snapshots_test.go @@ -4,12 +4,10 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/p2p" p2pmocks "github.com/tendermint/tendermint/p2p/mocks" - "github.com/tendermint/tendermint/statesync/mocks" ) func TestSnapshot_Key(t *testing.T) { @@ -41,14 +39,11 @@ func TestSnapshot_Key(t *testing.T) { } func TestSnapshotPool_Add(t *testing.T) { - stateProvider := &mocks.StateProvider{} - stateProvider.On("AppHash", mock.Anything, uint64(1)).Return([]byte("app_hash"), nil) - peer := &p2pmocks.Peer{} peer.On("ID").Return(p2p.ID("id")) // Adding to the pool should work - pool := newSnapshotPool(stateProvider) + pool := newSnapshotPool() added, err := pool.Add(peer, &snapshot{ Height: 1, Format: 1, @@ -73,15 +68,10 @@ func TestSnapshotPool_Add(t *testing.T) { // The pool should have populated the snapshot with the trusted app hash snapshot := pool.Best() require.NotNil(t, snapshot) - assert.Equal(t, []byte("app_hash"), snapshot.trustedAppHash) - - stateProvider.AssertExpectations(t) } func TestSnapshotPool_GetPeer(t *testing.T) { - stateProvider := &mocks.StateProvider{} - stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil) - pool := newSnapshotPool(stateProvider) + pool := newSnapshotPool() s := &snapshot{Height: 1, Format: 1, Chunks: 1, Hash: []byte{1}} peerA := &p2pmocks.Peer{} @@ -115,9 +105,7 @@ func TestSnapshotPool_GetPeer(t *testing.T) { } func TestSnapshotPool_GetPeers(t *testing.T) { - stateProvider := &mocks.StateProvider{} - stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil) - pool := newSnapshotPool(stateProvider) + pool := newSnapshotPool() s := &snapshot{Height: 1, Format: 1, Chunks: 1, Hash: []byte{1}} peerA := &p2pmocks.Peer{} @@ -139,9 +127,7 @@ func TestSnapshotPool_GetPeers(t *testing.T) { } func TestSnapshotPool_Ranked_Best(t *testing.T) { - stateProvider := &mocks.StateProvider{} - stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil) - pool := newSnapshotPool(stateProvider) + pool := newSnapshotPool() // snapshots in expected order (best to worst). Highest height wins, then highest format. // Snapshots with different chunk hashes are considered different, and the most peers is @@ -184,9 +170,7 @@ func TestSnapshotPool_Ranked_Best(t *testing.T) { } func TestSnapshotPool_Reject(t *testing.T) { - stateProvider := &mocks.StateProvider{} - stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil) - pool := newSnapshotPool(stateProvider) + pool := newSnapshotPool() peer := &p2pmocks.Peer{} peer.On("ID").Return(p2p.ID("id")) @@ -214,9 +198,7 @@ func TestSnapshotPool_Reject(t *testing.T) { } func TestSnapshotPool_RejectFormat(t *testing.T) { - stateProvider := &mocks.StateProvider{} - stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil) - pool := newSnapshotPool(stateProvider) + pool := newSnapshotPool() peer := &p2pmocks.Peer{} peer.On("ID").Return(p2p.ID("id")) @@ -245,9 +227,7 @@ func TestSnapshotPool_RejectFormat(t *testing.T) { } func TestSnapshotPool_RejectPeer(t *testing.T) { - stateProvider := &mocks.StateProvider{} - stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil) - pool := newSnapshotPool(stateProvider) + pool := newSnapshotPool() peerA := &p2pmocks.Peer{} peerA.On("ID").Return(p2p.ID("a")) @@ -287,9 +267,7 @@ func TestSnapshotPool_RejectPeer(t *testing.T) { } func TestSnapshotPool_RemovePeer(t *testing.T) { - stateProvider := &mocks.StateProvider{} - stateProvider.On("AppHash", mock.Anything, mock.Anything).Return([]byte("app_hash"), nil) - pool := newSnapshotPool(stateProvider) + pool := newSnapshotPool() peerA := &p2pmocks.Peer{} peerA.On("ID").Return(p2p.ID("a")) diff --git a/statesync/stateprovider.go b/statesync/stateprovider.go index f1a89e4c7f..eee271f4e4 100644 --- a/statesync/stateprovider.go +++ b/statesync/stateprovider.go @@ -116,10 +116,6 @@ func (s *clientStateProvider) AppHash(ctx context.Context, height uint64) ([]byt if err != nil { return nil, err } - _, err = s.lc.VerifyLightBlockAtHeight(ctx, int64(height), time.Now()) - if err != nil { - return nil, err - } return header.AppHash, nil } diff --git a/statesync/syncer.go b/statesync/syncer.go index 7582238bf1..c4ef514fab 100644 --- a/statesync/syncer.go +++ b/statesync/syncer.go @@ -11,6 +11,7 @@ import ( "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" tmsync "github.com/tendermint/tendermint/libs/sync" + "github.com/tendermint/tendermint/light" "github.com/tendermint/tendermint/p2p" ssproto "github.com/tendermint/tendermint/proto/tendermint/statesync" "github.com/tendermint/tendermint/proxy" @@ -78,7 +79,7 @@ func newSyncer( stateProvider: stateProvider, conn: conn, connQuery: connQuery, - snapshots: newSnapshotPool(stateProvider), + snapshots: newSnapshotPool(), tempDir: tempDir, chunkFetchers: cfg.ChunkFetchers, retryTimeout: cfg.ChunkRequestTimeout, @@ -230,6 +231,10 @@ func (s *syncer) SyncAny( s.logger.Info("Snapshot sender rejected", "peer", peer.ID()) } + case errors.Is(err, context.DeadlineExceeded): + s.logger.Info("Timed out validating snapshot, rejecting", "height", snapshot.Height, "err", err) + s.snapshots.Reject(snapshot) + default: return sm.State{}, nil, fmt.Errorf("snapshot restoration failed: %w", err) } @@ -260,30 +265,51 @@ func (s *syncer) Sync(snapshot *snapshot, chunks *chunkQueue) (sm.State, *types. s.mtx.Unlock() }() + hctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second) + defer cancel() + + appHash, err := s.stateProvider.AppHash(hctx, snapshot.Height) + if err != nil { + s.logger.Info("failed to fetch and verify app hash", "err", err) + if err == light.ErrNoWitnesses { + return sm.State{}, nil, err + } + return sm.State{}, nil, errRejectSnapshot + } + snapshot.trustedAppHash = appHash + // Offer snapshot to ABCI app. - err := s.offerSnapshot(snapshot) + err = s.offerSnapshot(snapshot) if err != nil { return sm.State{}, nil, err } // Spawn chunk fetchers. They will terminate when the chunk queue is closed or context cancelled. - ctx, cancel := context.WithCancel(context.Background()) + fetchCtx, cancel := context.WithCancel(context.TODO()) defer cancel() for i := int32(0); i < s.chunkFetchers; i++ { - go s.fetchChunks(ctx, snapshot, chunks) + go s.fetchChunks(fetchCtx, snapshot, chunks) } - pctx, pcancel := context.WithTimeout(context.Background(), 10*time.Second) + pctx, pcancel := context.WithTimeout(context.TODO(), 30*time.Second) defer pcancel() // Optimistically build new state, so we don't discover any light client failures at the end. state, err := s.stateProvider.State(pctx, snapshot.Height) if err != nil { - return sm.State{}, nil, fmt.Errorf("failed to build new state: %w", err) + s.logger.Info("failed to fetch and verify tendermint state", "err", err) + if err == light.ErrNoWitnesses { + return sm.State{}, nil, err + } + return sm.State{}, nil, errRejectSnapshot } commit, err := s.stateProvider.Commit(pctx, snapshot.Height) if err != nil { - return sm.State{}, nil, fmt.Errorf("failed to fetch commit: %w", err) + s.logger.Info("failed to fetch and verify commit", "err", err) + if err == light.ErrNoWitnesses { + return sm.State{}, nil, err + } + return sm.State{}, nil, errRejectSnapshot } // Restore snapshot @@ -486,12 +512,6 @@ func (s *syncer) verifyApp(snapshot *snapshot) (uint64, error) { "expected", snapshot.Height, "actual", resp.LastBlockHeight) return 0, errVerifyFailed } - if snapshot.CoreChainLockedHeight != resp.LastCoreChainLockedHeight { - s.logger.Error("last core chain locked height verification failed", - "expected", fmt.Sprintf("%d", snapshot.CoreChainLockedHeight), - "actual", fmt.Sprintf("%d", resp.LastCoreChainLockedHeight)) - return 0, errVerifyFailed - } s.logger.Info( "Verified ABCI app", diff --git a/statesync/syncer_test.go b/statesync/syncer_test.go index 020ecc46e7..3a9014cb21 100644 --- a/statesync/syncer_test.go +++ b/statesync/syncer_test.go @@ -196,10 +196,9 @@ func TestSyncer_SyncAny(t *testing.T) { Index: 2, Chunk: []byte{1, 1, 2}, }).Once().Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil) connQuery.On("InfoSync", proxy.RequestInfo).Return(&abci.ResponseInfo{ - AppVersion: 9, - LastBlockHeight: 1, - LastCoreChainLockedHeight: 1, - LastBlockAppHash: []byte("app_hash"), + AppVersion: 9, + LastBlockHeight: 1, + LastBlockAppHash: []byte("app_hash"), }, nil) newState, lastCommit, err := syncer.SyncAny(0, func() {}) @@ -682,10 +681,9 @@ func TestSyncer_verifyApp(t *testing.T) { expectErr error }{ "verified": {&abci.ResponseInfo{ - LastBlockHeight: 3, - LastCoreChainLockedHeight: 10, - LastBlockAppHash: []byte("app_hash"), - AppVersion: 9, + LastBlockHeight: 3, + LastBlockAppHash: []byte("app_hash"), + AppVersion: 9, }, nil, nil}, "invalid height": {&abci.ResponseInfo{ LastBlockHeight: 5, diff --git a/store/store_test.go b/store/store_test.go index 1630b61816..9f4906c04e 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -36,7 +36,7 @@ type cleanupFunc func() // make a Commit with a single vote containing just the height and a timestamp func makeTestCommit(height int64, timestamp time.Time) *types.Commit { blockID := types.BlockID{Hash: []byte(""), PartSetHeader: types.PartSetHeader{Hash: []byte(""), Total: 2}} - stateID := types.StateID{LastAppHash: make([]byte, 32)} + stateID := types.RandStateID().WithHeight(height - 1) goodVote := &types.Vote{ ValidatorProTxHash: crypto.RandProTxHash(), ValidatorIndex: 0, @@ -44,7 +44,6 @@ func makeTestCommit(height int64, timestamp time.Time) *types.Commit { Round: 0, Type: tmproto.PrecommitType, BlockID: blockID, - StateID: stateID, } g := goodVote.ToProto() @@ -53,7 +52,7 @@ func makeTestCommit(height int64, timestamp time.Time) *types.Commit { privVal := types.NewMockPVWithParams(privKey, crypto.RandProTxHash(), state.Validators.QuorumHash, state.Validators.ThresholdPublicKey, false, false) - _ = privVal.SignVote("chainID", state.Validators.QuorumType, state.Validators.QuorumHash, g, nil) + _ = privVal.SignVote("chainID", state.Validators.QuorumType, state.Validators.QuorumHash, g, stateID, nil) goodVote.BlockSignature = g.BlockSignature goodVote.StateSignature = g.StateSignature diff --git a/test/e2e/Makefile b/test/e2e/Makefile index d2b22fd686..7b9184d7c7 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -20,6 +20,9 @@ runner/dashcore: runner e2e/app/compile runner/rotate: runner e2e/app/compile ./build/runner -f networks/rotate.toml +runner/island: runner e2e/app/compile + ./build/runner -f networks/island.toml + # We need to build support for database backends into the app in # order to build a binary with a Tenderdash node in it (for built-in # ABCI testing). diff --git a/test/e2e/README.md b/test/e2e/README.md index b0eb84d24b..33e6f04caf 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -30,6 +30,12 @@ Multiple testnets can be run with the `run-multiple.sh` script: ./run-multiple.sh networks/generated/gen-group3-*.toml ``` +In order to generate network configurations with settings for dash, you have to override a default preset with `dash`. + +```sh +./build/generator -d networks/generated/ -p dash +``` + ## Test Stages The test runner has the following stages, which can also be executed explicitly by running `./build/runner -f `: @@ -82,6 +88,18 @@ func init() { } ``` +### Full-node keys + +Since Full nodes do not participate in the consensus process, then keeping validators' public keys also is not required. +By default, public keys are reset during network generation. However, if you need to keep its, then use this env +parameter `FULLNODE_PUBKEY_KEEP=true`. + +For instance: + +```sh +FULLNODE_PUBKEY_KEEP=true make runner/dashcore +``` + ### Speed up running e2e tests Running the e2e tests using `make runner {network}` takes time because the @@ -146,7 +164,12 @@ To enable Delve, set the `DEBUG` environment variable when setting up the runner DEBUG=1 ./build/runner -f networks/ci.toml setup ``` -NOTE: Right now, only built-in app is supported (the one using `entrypoint-builtin` script) +If you set DEBUG to `stop`, the app won't start automatically. +You'll need to connect to each app (each container) with your debugger +and start it manually. + +NOTE: Right now, only the built-in app is supported(the one using +`entrypoint-builtin` script) Containers expose DLV on ports starting from 40001 upwards. @@ -169,6 +192,34 @@ For more details, see: * [JetBrains configuration](https://blog.jetbrains.com/go/2020/05/06/debugging-a-go-application-inside-a-docker-container/) * [Visual Studio Code configuration](https://medium.com/@kaperys/delve-into-docker-d6c92be2f823) +#### Core dumps + +To analyze core dumps: + +1. Examine [Dockerfile](docker/Dockerfile) to ensure `ENV TENDERMINT_BUILD_OPTIONS` contains `nostrip` option AND `GOTRACEBACK` is set to `crash`, for example: + + ```docker + ENV TENDERMINT_BUILD_OPTIONS badgerdb,boltdb,cleveldb,rocksdb,nostrip + ENV GOTRACEBACK=crash + ``` + +2. Build the container with `make` +3. On the **host** machine, set the location of core files: + + ```bash + echo /core.%p | sudo tee /proc/sys/kernel/core_pattern + ``` + +4. After the container stops due to panic, you can export its contents and run delve debugger: + + ```bash + CONTAINER= + docker export -o ${CONTAINER}.tar ${CONTAINER} + mkdir ${CONTAINER} + cd ${CONTAINER} + tar -xf ../${CONTAINER}.tar + dlv core ./usr/bin/tenderdash ./core.* + ``` ## Enabling IPv6 diff --git a/test/e2e/app/app.go b/test/e2e/app/app.go index a819e19c01..65cf856c82 100644 --- a/test/e2e/app/app.go +++ b/test/e2e/app/app.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strconv" + "github.com/gogo/protobuf/proto" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/types" @@ -58,15 +59,14 @@ func NewApplication(cfg *Config) (*Application, error) { // Info implements ABCI. func (app *Application) Info(req abci.RequestInfo) abci.ResponseInfo { return abci.ResponseInfo{ - Version: version.ABCIVersion, - AppVersion: 1, - LastBlockHeight: int64(app.state.Height), - LastBlockAppHash: app.state.Hash, - LastCoreChainLockedHeight: app.state.CoreHeight, + Version: version.ABCIVersion, + AppVersion: 1, + LastBlockHeight: int64(app.state.Height), + LastBlockAppHash: app.state.Hash, } } -// Info implements ABCI. +// InitChain implements ABCI. func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { var err error app.state.initialHeight = uint64(req.InitialHeight) @@ -85,7 +85,7 @@ func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitCh panic(err) } resp.ValidatorSetUpdate = *validatorSetUpdate - + resp.InitialCoreHeight = app.cfg.InitAppInitialCoreHeight if resp.NextCoreChainLockUpdate, err = app.chainLockUpdate(0); err != nil { panic(err) } @@ -225,7 +225,7 @@ func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) a return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT} } -// validatorUpdates generates a validator set update. +// validatorSetUpdates generates a validator set update. func (app *Application) validatorSetUpdates(height uint64) (*abci.ValidatorSetUpdate, error) { updates := app.cfg.ValidatorUpdates[fmt.Sprintf("%v", height)] if len(updates) == 0 { @@ -259,17 +259,21 @@ func (app *Application) validatorSetUpdates(height uint64) (*abci.ValidatorSetUp valSetUpdates := abci.ValidatorSetUpdate{} valUpdates := abci.ValidatorUpdates{} - for proTxHashString, keyString := range updates { - keyBytes, err := base64.StdEncoding.DecodeString(keyString) + for proTxHashString, updateBase64 := range updates { + validator, err := parseValidatorUpdate(updateBase64) if err != nil { - return nil, fmt.Errorf("invalid base64 pubkey value %q: %w", keyString, err) + return nil, err } proTxHashBytes, err := hex.DecodeString(proTxHashString) if err != nil { return nil, fmt.Errorf("invalid hex proTxHash value %q: %w", proTxHashBytes, err) } - publicKeyUpdate := bls12381.PubKey(keyBytes) - valUpdates = append(valUpdates, abci.UpdateValidator(proTxHashBytes, publicKeyUpdate, types.DefaultDashVotingPower)) + if !bytes.Equal(proTxHashBytes, validator.ProTxHash) { + return nil, fmt.Errorf("proTxHash mismatch for key %s: %x != %x", + proTxHashString, proTxHashBytes, validator.ProTxHash) + } + + valUpdates = append(valUpdates, validator) } valSetUpdates.ValidatorUpdates = valUpdates valSetUpdates.ThresholdPublicKey = abciThresholdPublicKeyUpdate @@ -277,6 +281,22 @@ func (app *Application) validatorSetUpdates(height uint64) (*abci.ValidatorSetUp return &valSetUpdates, nil } +func parseValidatorUpdate(validatorUpdateBase64 string) (abci.ValidatorUpdate, error) { + validator := abci.ValidatorUpdate{} + + validatorBytes, err := base64.StdEncoding.DecodeString(validatorUpdateBase64) + if err != nil { + return validator, fmt.Errorf("invalid base64 validator update %q: %w", validatorUpdateBase64, err) + } + + err = proto.Unmarshal(validatorBytes, &validator) + if err != nil { + return validator, fmt.Errorf("cannot parse validator update protobuf %q: %w", validatorBytes, err) + } + + return validator, nil +} + // validatorUpdates generates a validator set update. func (app *Application) chainLockUpdate(height uint64) (*types1.CoreChainLock, error) { updates := app.cfg.ChainLockUpdates[fmt.Sprintf("%v", height)] diff --git a/test/e2e/app/config.go b/test/e2e/app/config.go index 9a7f1d925e..5b114c7228 100644 --- a/test/e2e/app/config.go +++ b/test/e2e/app/config.go @@ -10,24 +10,25 @@ import ( // Config is the application configuration. type Config struct { - ChainID string `toml:"chain_id"` - Listen string - Protocol string - Dir string - Mode string `toml:"mode"` - PersistInterval uint64 `toml:"persist_interval"` - SnapshotInterval uint64 `toml:"snapshot_interval"` - RetainBlocks uint64 `toml:"retain_blocks"` - ValidatorUpdates map[string]map[string]string `toml:"validator_update"` - ThesholdPublicKeyUpdate map[string]string `toml:"threshold_public_key_update"` - QuorumHashUpdate map[string]string `toml:"quorum_hash_update"` - ChainLockUpdates map[string]string `toml:"chainlock_updates"` - PrivValServerType string `toml:"privval_server_type"` - PrivValServer string `toml:"privval_server"` - PrivValKey string `toml:"privval_key"` - PrivValState string `toml:"privval_state"` - Misbehaviors map[string]string `toml:"misbehaviors"` - KeyType string `toml:"key_type"` + ChainID string `toml:"chain_id"` + Listen string + Protocol string + Dir string + Mode string `toml:"mode"` + PersistInterval uint64 `toml:"persist_interval"` + SnapshotInterval uint64 `toml:"snapshot_interval"` + RetainBlocks uint64 `toml:"retain_blocks"` + ValidatorUpdates map[string]map[string]string `toml:"validator_update"` + ThesholdPublicKeyUpdate map[string]string `toml:"threshold_public_key_update"` + QuorumHashUpdate map[string]string `toml:"quorum_hash_update"` + InitAppInitialCoreHeight uint32 `toml:"init_app_core_chain_locked_height"` + ChainLockUpdates map[string]string `toml:"chainlock_updates"` + PrivValServerType string `toml:"privval_server_type"` + PrivValServer string `toml:"privval_server"` + PrivValKey string `toml:"privval_key"` + PrivValState string `toml:"privval_state"` + Misbehaviors map[string]string `toml:"misbehaviors"` + KeyType string `toml:"key_type"` } // LoadConfig loads the configuration from disk. diff --git a/test/e2e/docker/Dockerfile b/test/e2e/docker/Dockerfile index a53db01a15..6362098d4a 100644 --- a/test/e2e/docker/Dockerfile +++ b/test/e2e/docker/Dockerfile @@ -7,7 +7,7 @@ RUN apt-get -qq update -y && apt-get -qq upgrade -y >/dev/null RUN apt-get -qq install -y cmake sudo libgmp-dev libleveldb-dev librocksdb-dev >/dev/null # Set up build directory /src/tenderdash -ENV TENDERMINT_BUILD_OPTIONS badgerdb,boltdb,cleveldb,rocksdb +ENV TENDERMINT_BUILD_OPTIONS badgerdb,boltdb,cleveldb,rocksdb,nostrip WORKDIR /src/tenderdash # Install DLV debugger @@ -30,14 +30,15 @@ COPY . . RUN make build && cp build/tenderdash /usr/bin/tenderdash COPY test/e2e/docker/entrypoint* /usr/bin/ -RUN cd test/e2e && GOFLAGS='-gcflags="all=-N' make maverick && cp build/maverick /usr/bin/maverick -RUN cd test/e2e && GOFLAGS='-gcflags="all=-N' make app && cp build/app /usr/bin/app +RUN cd test/e2e && make maverick && cp build/maverick /usr/bin/maverick +RUN cd test/e2e && make app && cp build/app /usr/bin/app # Set up runtime directory. We don't use a separate runtime image since we need # e.g. leveldb and rocksdb which are already installed in the build image. WORKDIR /tenderdash VOLUME /tenderdash ENV TMHOME=/tenderdash +ENV GOTRACEBACK=crash EXPOSE 26656 26657 26660 6060 ENTRYPOINT ["/usr/bin/entrypoint"] diff --git a/test/e2e/docker/entrypoint-builtin b/test/e2e/docker/entrypoint-builtin index 609a7fc2c8..c13c05d266 100755 --- a/test/e2e/docker/entrypoint-builtin +++ b/test/e2e/docker/entrypoint-builtin @@ -1,6 +1,15 @@ #!/usr/bin/env bash + if [ ! -z "$DEBUG" ] ; then - DLV="/go/bin/dlv --listen=:40000 --headless=true --api-version=2 --accept-multiclient exec --continue" + if [ -z "$DEBUG_PORT" ] ; then + echo DEBUG_PORT must be set when DEBUG is set + exit 1 + fi + if [ "$DEBUG" != "stop" ] ; then + CONTINUE="--continue" + fi + + DLV="/go/bin/dlv --listen=:${DEBUG_PORT} --headless=true --api-version=2 --accept-multiclient exec ${CONTINUE} " SEP="--" fi diff --git a/test/e2e/generator/generate.go b/test/e2e/generator/generate.go index 894b47af7d..67e3ef47f0 100644 --- a/test/e2e/generator/generate.go +++ b/test/e2e/generator/generate.go @@ -30,7 +30,8 @@ func generateTestnet(r *rand.Rand, opt map[string]interface{}) (e2e.Manifest, er manifest := e2e.Manifest{ IPv6: opt["ipv6"].(bool), InitialHeight: int64(opt["initialHeight"].(int)), - InitialCoreChainLockedHeight: uint32(opt["initialCoreChainLockedHeight"].(int)), + GenesisCoreChainLockedHeight: uint32(opt["initialCoreChainLockedHeight"].(int)), + InitAppCoreChainLockedHeight: uint32(opt["initAppCoreChainLockedHeight"].(int)), InitialState: opt["initialState"].(map[string]string), Validators: &map[string]int64{}, ValidatorUpdates: map[string]map[string]int64{}, diff --git a/test/e2e/networks/island.toml b/test/e2e/networks/island.toml new file mode 100644 index 0000000000..68312a7909 --- /dev/null +++ b/test/e2e/networks/island.toml @@ -0,0 +1,81 @@ +initial_height = 1000 +initial_state = { initial01 = "a", initial02 = "b", initial03 = "c" } +initial_core_chain_locked_height = 3400 + +[chainlock_updates] +1000 = 3450 +1004 = 3451 +1009 = 3452 +1020 = 3454 +1040 = 3500 + +[validator_update.0] +validator01 = 100 +validator02 = 100 +validator03 = 100 +validator04 = 100 + +[validator_update.1010] +validator02 = 100 +validator04 = 100 +validator06 = 100 +validator07 = 100 + +[validator_update.1020] +validator01 = 100 +validator03 = 100 +validator05 = 100 +validator07 = 100 + +[validator_update.1030] +validator01 = 100 +validator02 = 100 +validator03 = 100 +validator04 = 100 +validator05 = 100 +validator06 = 100 +validator07 = 100 + +[node.validator01] +snapshot_interval = 5 +perturb = ["disconnect"] +privval_protocol = "dashcore" + +[node.validator02] +database = "boltdb" +abci_protocol = "tcp" +privval_protocol = "dashcore" +persist_interval = 0 +perturb = ["restart"] + +[node.validator03] +database = "badgerdb" +privval_protocol = "dashcore" +persist_interval = 3 +retain_blocks = 3 +perturb = ["kill"] + +[node.validator04] +database = "rocksdb" +abci_protocol = "builtin" +privval_protocol = "dashcore" +perturb = ["pause"] + +[node.validator05] +start_at = 1005 +database = "cleveldb" +fast_sync = "v0" +privval_protocol = "dashcore" +perturb = ["kill", "pause", "disconnect", "restart"] + +[node.validator06] +database = "rocksdb" +fast_sync = "v0" +privval_protocol = "dashcore" + +[node.validator07] +start_at = 1005 +database = "cleveldb" +fast_sync = "v0" +privval_protocol = "dashcore" +perturb = ["pause"] diff --git a/test/e2e/networks/rotate.toml b/test/e2e/networks/rotate.toml index af07b6f50e..a7cd2a49e0 100644 --- a/test/e2e/networks/rotate.toml +++ b/test/e2e/networks/rotate.toml @@ -4,6 +4,7 @@ initial_height = 1000 initial_state = { initial01 = "a", initial02 = "b", initial03 = "c" } initial_core_chain_locked_height = 3400 +init_app_core_chain_locked_height = 2308 # should override initial_core_chain_locked_height [chainlock_updates] 1000 = 3450 @@ -34,6 +35,12 @@ initial_core_chain_locked_height = 3400 validator11 = 100 validator12 = 100 + [validator_update.1030] + validator02 = 100 + validator03 = 100 + validator04 = 100 + validator05 = 100 + validator09 = 100 [node.seed01] mode = "seed" seeds = ["seed02"] diff --git a/test/e2e/pkg/manifest.go b/test/e2e/pkg/manifest.go index 321ce000f8..1b63f9fe23 100644 --- a/test/e2e/pkg/manifest.go +++ b/test/e2e/pkg/manifest.go @@ -18,8 +18,12 @@ type Manifest struct { // InitialHeight specifies the initial block height, set in genesis. Defaults to 1. InitialHeight int64 `toml:"initial_height"` - // InitialCoreChainLockedHeight specifies the initial core chain locked block height, set in genesis. Defaults to 1. - InitialCoreChainLockedHeight uint32 `toml:"initial_core_chain_locked_height"` + // GenesisCoreChainLockedHeight specifies the initial core chain locked block height, set in genesis. Defaults to 1. + GenesisCoreChainLockedHeight uint32 `toml:"initial_core_chain_locked_height"` + + // InitAppCoreChainLockedHeight specifies initial core chain locked block height, returned by the App + // in response to InitApp ABCI request. It overrides value set in InitialCoreChainLockedHeight. + InitAppCoreChainLockedHeight uint32 `toml:"init_app_core_chain_locked_height"` // InitialState is an initial set of key/value pairs for the application, // set in genesis. Defaults to nothing. diff --git a/test/e2e/pkg/mockcoreserver/server_test.go b/test/e2e/pkg/mockcoreserver/server_test.go index 7d09945023..f3828fd8e3 100644 --- a/test/e2e/pkg/mockcoreserver/server_test.go +++ b/test/e2e/pkg/mockcoreserver/server_test.go @@ -8,11 +8,13 @@ import ( "net/http" "net/url" "testing" + "time" dashcore "github.com/tendermint/tendermint/dashcore/rpc" "github.com/dashevo/dashd-go/btcjson" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/bls12381" "github.com/tendermint/tendermint/privval" @@ -24,6 +26,7 @@ func TestServer(t *testing.T) { go func() { srv.Start() }() + time.Sleep(10 * time.Microsecond) testCases := []struct { url string e string @@ -133,6 +136,7 @@ func TestGetPubKey(t *testing.T) { b, _ := hex.DecodeString( "83349BA8363E5C03E9D6318B0491E38305CF59D9D57CEA2295A86ECFA696622571F266C28BACC78666E8B9B0FB2B3123", ) + require.NotNil(t, pubKey) assert.True(t, pubKey.Equals(bls12381.PubKey(b))) srv.Stop(ctx) } diff --git a/test/e2e/pkg/testnet.go b/test/e2e/pkg/testnet.go index 7eb9c11248..bfa9672cdc 100644 --- a/test/e2e/pkg/testnet.go +++ b/test/e2e/pkg/testnet.go @@ -14,13 +14,14 @@ import ( "strings" "github.com/dashevo/dashd-go/btcjson" - + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/bls12381" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" rpchttp "github.com/tendermint/tendermint/rpc/client/http" mcs "github.com/tendermint/tendermint/test/maverick/consensus" + "github.com/tendermint/tendermint/types" ) const ( @@ -28,6 +29,7 @@ const ( proxyPortFirst uint32 = 5701 networkIPv4 = "10.186.73.0/24" networkIPv6 = "fd80:b10c::/48" + networkPortP2P uint16 = 26656 ) type Mode string @@ -54,16 +56,23 @@ const ( ) // Testnet represents a single testnet. + +type ValidatorConfig struct { + abci.ValidatorUpdate +} + +type ValidatorsMap map[*Node]ValidatorConfig type Testnet struct { Name string File string Dir string IP *net.IPNet InitialHeight int64 - InitialCoreHeight uint32 + GenesisCoreHeight uint32 // InitialCoreHeight is a core height put into genesis file + InitAppCoreHeight uint32 // InitAppCoreHeight returned in InitApp response InitialState map[string]string - Validators map[*Node]crypto.PubKey - ValidatorUpdates map[int64]map[*Node]crypto.PubKey + Validators ValidatorsMap + ValidatorUpdates map[int64]ValidatorsMap ChainLockUpdates map[int64]int64 Nodes []*Node KeyType string @@ -182,10 +191,11 @@ func LoadTestnet(file string) (*Testnet, error) { Dir: dir, IP: ipGen.Network(), InitialHeight: 1, - InitialCoreHeight: 1, + GenesisCoreHeight: 1, + InitAppCoreHeight: 0, InitialState: manifest.InitialState, - Validators: map[*Node]crypto.PubKey{}, - ValidatorUpdates: map[int64]map[*Node]crypto.PubKey{}, + Validators: ValidatorsMap{}, + ValidatorUpdates: map[int64]ValidatorsMap{}, ChainLockUpdates: map[int64]int64{}, Nodes: []*Node{}, ThresholdPublicKey: thresholdPublicKey, @@ -197,8 +207,11 @@ func LoadTestnet(file string) (*Testnet, error) { if manifest.InitialHeight > 0 { testnet.InitialHeight = manifest.InitialHeight } - if manifest.InitialCoreChainLockedHeight > 0 { - testnet.InitialCoreHeight = manifest.InitialCoreChainLockedHeight + if manifest.GenesisCoreChainLockedHeight > 0 { + testnet.GenesisCoreHeight = manifest.GenesisCoreChainLockedHeight + } + if manifest.InitAppCoreChainLockedHeight > 0 { + testnet.InitAppCoreHeight = manifest.InitAppCoreChainLockedHeight } for _, name := range nodeNames { @@ -317,7 +330,13 @@ func LoadTestnet(file string) (*Testnet, error) { return nil, fmt.Errorf("unknown validator %q", validatorName) } pubKey := privateKeys[i].PubKey() - testnet.Validators[validator] = pubKey + + vu, err := validator.ValidatorUpdate(pubKey.Bytes()) + if err != nil { + return nil, err + } + + testnet.Validators[validator] = ValidatorConfig{vu} validator.ProTxHash = proTxHashes[i] quorumKeys := crypto.QuorumKeys{ @@ -337,7 +356,11 @@ func LoadTestnet(file string) (*Testnet, error) { var i = 0 for _, node := range testnet.Nodes { if node.Mode == ModeValidator { - testnet.Validators[node] = privateKeys[i].PubKey() + vu, err := node.ValidatorUpdate(privateKeys[i].PubKey().Bytes()) + if err != nil { + return nil, err + } + testnet.Validators[node] = ValidatorConfig{vu} // privateKeys[i].PubKey()} node.ProTxHash = proTxHashes[i] quorumKeys := crypto.QuorumKeys{ PrivKey: privateKeys[i], @@ -380,7 +403,8 @@ func LoadTestnet(file string) (*Testnet, error) { for _, height := range heights { heightStr := strconv.FormatInt(int64(height), 10) validators := manifest.ValidatorUpdates[heightStr] - valUpdate := map[*Node]crypto.PubKey{} + valUpdate := ValidatorsMap{} + proTxHashesInUpdate := make([]crypto.ProTxHash, len(validators)) i := 0 for name := range validators { @@ -406,10 +430,16 @@ func LoadTestnet(file string) (*Testnet, error) { for i, proTxHash := range proTxHashes { node := testnet.LookupNodeByProTxHash(proTxHash) - valUpdate[node] = privateKeys[i].PubKey() if node == nil { return nil, fmt.Errorf("unknown validator with protxHash %X for update at height %v", proTxHash, height) } + + vu, err := node.ValidatorUpdate(privateKeys[i].PubKey().Bytes()) + if err != nil { + return nil, err + } + valUpdate[node] = ValidatorConfig{vu} // privateKeys[i].PubKey()} + if height == 0 { pubKey := privateKeys[i].PubKey() quorumKeys := crypto.QuorumKeys{ @@ -697,6 +727,18 @@ func (t Testnet) LastMisbehaviorHeight() int64 { return lastHeight } +// ValidatorUpdate() creates an abci.ValidatorUpdate struct from the current node +func (n *Node) ValidatorUpdate(publicKey []byte) (abci.ValidatorUpdate, error) { + proTxHash := n.ProTxHash.Bytes() + + // TODO TD-10 find real power + power := types.DefaultDashVotingPower + + address := n.AddressP2P(false) + validatorUpdate := abci.UpdateValidator(proTxHash, publicKey, power, address) + return validatorUpdate, nil +} + // Address returns a P2P endpoint address for the node. func (n Node) AddressP2P(withID bool) string { ip := n.IP.String() @@ -704,7 +746,7 @@ func (n Node) AddressP2P(withID bool) string { // IPv6 addresses must be wrapped in [] to avoid conflict with : port separator ip = fmt.Sprintf("[%v]", ip) } - addr := fmt.Sprintf("%v:26656", ip) + addr := fmt.Sprintf("%v:%d", ip, networkPortP2P) if withID { addr = fmt.Sprintf("%x@%v", n.NodeKey.PubKey().Address().Bytes(), addr) } diff --git a/test/e2e/runner/rpc.go b/test/e2e/runner/rpc.go index 31c319972f..d45ece4084 100644 --- a/test/e2e/runner/rpc.go +++ b/test/e2e/runner/rpc.go @@ -12,6 +12,10 @@ import ( "github.com/tendermint/tendermint/types" ) +const ( + WaitForHeightTimeout = 20 * time.Second +) + // waitForHeight waits for the network to reach a certain height (or above), // returning the highest height seen. Errors if the network is not making // progress at all. @@ -55,7 +59,7 @@ func waitForHeight(testnet *e2e.Testnet, height int64) (*types.Block, *types.Blo if len(clients) == 0 { return nil, nil, errors.New("unable to connect to any network nodes") } - if time.Since(lastIncrease) >= 20*time.Second { + if time.Since(lastIncrease) >= WaitForHeightTimeout { if maxResult == nil { return nil, nil, errors.New("chain stalled at unknown height") } diff --git a/test/e2e/runner/setup.go b/test/e2e/runner/setup.go index 79acc28bf7..34d754bbb9 100644 --- a/test/e2e/runner/setup.go +++ b/test/e2e/runner/setup.go @@ -19,9 +19,11 @@ import ( "time" "github.com/BurntSushi/toml" + "github.com/gogo/protobuf/proto" "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/bls12381" + cryptoenc "github.com/tendermint/tendermint/crypto/encoding" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/privval" e2e "github.com/tendermint/tendermint/test/e2e/pkg" @@ -195,13 +197,14 @@ services: {{- if $.Debug }} environment: - DEBUG=1 + - DEBUG_PORT={{ debugPort $index }} {{- end }} ports: - 26656 - {{ if .ProxyPort }}{{ .ProxyPort }}:{{ end }}26657 - 6060 {{- if $.Debug }} - - {{ debugPort $index }}:40000 + - {{ debugPort $index }}:{{ debugPort $index }} {{- end }} volumes: - ./{{ .Name }}:/tenderdash @@ -240,12 +243,20 @@ func MakeGenesis(testnet *e2e.Testnet, genesisTime time.Time) (types.GenesisDoc, ChainID: testnet.Name, ConsensusParams: types.DefaultConsensusParams(), InitialHeight: testnet.InitialHeight, - InitialCoreChainLockedHeight: testnet.InitialCoreHeight, + InitialCoreChainLockedHeight: testnet.GenesisCoreHeight, ThresholdPublicKey: testnet.ThresholdPublicKey, QuorumType: testnet.QuorumType, QuorumHash: testnet.QuorumHash, } - for validator, pubkey := range testnet.Validators { + for validator, validatorUpdate := range testnet.Validators { + if validatorUpdate.PubKey == nil { + return genesis, fmt.Errorf("public key for validator %s is nil", validator.Name) + } + pubkey, err := cryptoenc.PubKeyFromProto(*validatorUpdate.PubKey) + if err != nil { + return genesis, err + } + genesis.Validators = append(genesis.Validators, types.GenesisValidator{ Name: validator.Name, PubKey: pubkey, @@ -279,7 +290,7 @@ func MakeConfig(node *e2e.Node) (*config.Config, error) { cfg.P2P.AddrBookStrict = false cfg.DBBackend = node.Database cfg.StateSync.DiscoveryTime = 5 * time.Second - cfg.Consensus.AppHashSize = crypto.DefaultHashSize + cfg.Consensus.AppHashSize = crypto.DefaultAppHashSize cfg.BaseConfig.LogLevel = "debug" switch node.ABCIProtocol { @@ -435,8 +446,15 @@ func MakeAppConfig(node *e2e.Node) ([]byte, error) { validatorUpdates := map[string]map[string]string{} for height, validators := range node.Testnet.ValidatorUpdates { updateVals := map[string]string{} - for node, pubkey := range validators { - updateVals[hex.EncodeToString(node.ProTxHash.Bytes())] = base64.StdEncoding.EncodeToString(pubkey.Bytes()) + for node, validatorUpdate := range validators { + key := hex.EncodeToString(node.ProTxHash.Bytes()) + update := validatorUpdate // avoid getting address of a range variable to make linter happy + value, err := proto.Marshal(&update) + if err != nil { + return nil, err + } + valueBase64 := base64.StdEncoding.EncodeToString(value) + updateVals[key] = valueBase64 } validatorUpdates[fmt.Sprintf("%v", height)] = updateVals } @@ -456,6 +474,10 @@ func MakeAppConfig(node *e2e.Node) ([]byte, error) { } cfg["quorum_hash_update"] = quorumHashUpdates } + + if node.Testnet.InitAppCoreHeight > 0 { + cfg["init_app_core_chain_locked_height"] = node.Testnet.InitAppCoreHeight + } if len(node.Testnet.ChainLockUpdates) > 0 { chainLockUpdates := map[string]string{} for height, coreHeight := range node.Testnet.ChainLockUpdates { diff --git a/test/e2e/runner/start.go b/test/e2e/runner/start.go index 4fb985b16d..9a612d1479 100644 --- a/test/e2e/runner/start.go +++ b/test/e2e/runner/start.go @@ -9,6 +9,9 @@ import ( ) func Start(testnet *e2e.Testnet) error { + if len(testnet.Nodes) == 0 { + return fmt.Errorf("no nodes in testnet") + } // Nodes are already sorted by name. Sort them by name then startAt, // which gives the overall order startAt, mode, name. @@ -25,12 +28,11 @@ func Start(testnet *e2e.Testnet) error { } return false }) + sort.SliceStable(nodeQueue, func(i, j int) bool { return nodeQueue[i].StartAt < nodeQueue[j].StartAt }) - if len(nodeQueue) == 0 { - return fmt.Errorf("no nodes in testnet") - } + if nodeQueue[0].StartAt > 0 { return fmt.Errorf("no initial nodes in testnet") } @@ -49,9 +51,15 @@ func Start(testnet *e2e.Testnet) error { logger.Info(fmt.Sprintf("Node %v up on http://127.0.0.1:%v", node.Name, node.ProxyPort)) } + networkHeight := testnet.InitialHeight + // Wait for initial height - logger.Info(fmt.Sprintf("Waiting for initial height %v...", testnet.InitialHeight)) - block, blockID, err := waitForHeight(testnet, testnet.InitialHeight) + logger.Info("Waiting for initial height", + "height", networkHeight, + "nodes", len(testnet.Nodes)-len(nodeQueue), + "pending", len(nodeQueue)) + + block, blockID, err := waitForHeight(testnet, networkHeight) if err != nil { return err } @@ -66,12 +74,28 @@ func Start(testnet *e2e.Testnet) error { } } - // Start up remaining nodes for _, node := range nodeQueue { - logger.Info(fmt.Sprintf("Starting node %v at height %v...", node.Name, node.StartAt)) - if _, _, err := waitForHeight(testnet, node.StartAt); err != nil { - return err + if node.StartAt > networkHeight { + // if we're starting a node that's ahead of + // the last known height of the network, then + // we should make sure that the rest of the + // network has reached at least the height + // that this node will start at before we + // start the node. + + networkHeight = node.StartAt + + logger.Info("Waiting for network to advance before starting catch up node", + "node", node.Name, + "height", networkHeight) + + if _, _, err := waitForHeight(testnet, networkHeight); err != nil { + return err + } } + + logger.Info("Starting catch up node", "node", node.Name, "height", node.StartAt) + if err := execCompose(testnet.Dir, "up", "-d", node.Name); err != nil { return err } diff --git a/test/e2e/tests/app_test.go b/test/e2e/tests/app_test.go index a86a9c9ab4..2b6d2f5e87 100644 --- a/test/e2e/tests/app_test.go +++ b/test/e2e/tests/app_test.go @@ -39,7 +39,9 @@ func TestApp_InitialState(t *testing.T) { // block and the node sync status. func TestApp_Hash(t *testing.T) { testNode(t, func(t *testing.T, node e2e.Node) { - if node.Mode == e2e.ModeSeed { + // disables tests for light clients + // see https://github.com/tendermint/tendermint/issues/6671 + if node.Mode == e2e.ModeSeed || node.Mode == e2e.ModeLight { return } @@ -64,7 +66,9 @@ func TestApp_Hash(t *testing.T) { // Tests that we can set a value and retrieve it. func TestApp_Tx(t *testing.T) { testNode(t, func(t *testing.T, node e2e.Node) { - if node.Mode == e2e.ModeSeed { + // disables tests for light clients + // see https://github.com/tendermint/tendermint/issues/6671 + if node.Mode == e2e.ModeSeed || node.Mode == e2e.ModeLight { return } diff --git a/test/e2e/tests/block_test.go b/test/e2e/tests/block_test.go index b3f4e9139f..c99b99dce5 100644 --- a/test/e2e/tests/block_test.go +++ b/test/e2e/tests/block_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" e2e "github.com/tendermint/tendermint/test/e2e/pkg" + "github.com/tendermint/tendermint/types" ) // Tests that block headers are identical across nodes where present. @@ -27,7 +28,7 @@ func TestBlock_Header(t *testing.T) { if node.RetainBlocks > 0 { first++ // avoid race conditions with block pruning } - + var prevBlock *types.Block for _, block := range blocks { if block.Header.Height < first { continue @@ -43,6 +44,14 @@ func TestBlock_Header(t *testing.T) { require.NoError(t, resp.Block.ValidateBasic(), "block at height %d is invalid", block.Header.Height) + + // validate StateID from last commit + if prevBlock != nil { + assert.EqualValues(t, block.Height-2, block.LastCommit.StateID.Height) + assert.EqualValues(t, prevBlock.Header.AppHash, block.LastCommit.StateID.LastAppHash) + } + + prevBlock = resp.Block } }) } diff --git a/test/e2e/tests/validator_test.go b/test/e2e/tests/validator_test.go index 0225d66e03..4017caa055 100644 --- a/test/e2e/tests/validator_test.go +++ b/test/e2e/tests/validator_test.go @@ -7,9 +7,11 @@ import ( "github.com/dashevo/dashd-go/btcjson" "github.com/tendermint/tendermint/crypto" + dashtypes "github.com/tendermint/tendermint/dash/types" "github.com/stretchr/testify/require" + cryptoenc "github.com/tendermint/tendermint/crypto/encoding" e2e "github.com/tendermint/tendermint/test/e2e/pkg" "github.com/tendermint/tendermint/types" ) @@ -65,6 +67,9 @@ func TestValidator_Sets(t *testing.T) { require.Equal(t, valScheduleValidator.PubKey.Bytes(), validator.PubKey.Bytes(), "mismatching validator %X publicKey at height %v (%X <=> %X", valScheduleValidator.ProTxHash, h, valScheduleValidator.PubKey.Bytes(), validator.PubKey.Bytes()) + + // Validators in the schedule don't contain addresses + validator.NodeAddress = dashtypes.ValidatorAddress{} } require.Equal(t, valSchedule.Set.Validators, validators, "incorrect validator set at height %v", h) @@ -111,7 +116,7 @@ func TestValidator_Propose(t *testing.T) { type validatorSchedule struct { Set *types.ValidatorSet height int64 - updates map[int64]map[*e2e.Node]crypto.PubKey + updates map[int64]e2e.ValidatorsMap thresholdPublicKeyUpdates map[int64]crypto.PubKey quorumHashUpdates map[int64]crypto.QuorumHash } @@ -172,10 +177,15 @@ func (s *validatorSchedule) Increment(heights int64) { } } -func makeVals(valMap map[*e2e.Node]crypto.PubKey) []*types.Validator { +func makeVals(valMap e2e.ValidatorsMap) []*types.Validator { vals := make([]*types.Validator, 0, len(valMap)) - for node, pubkey := range valMap { - vals = append(vals, types.NewValidatorDefaultVotingPower(pubkey, node.ProTxHash)) + for node, valUpdate := range valMap { + pubkey := valUpdate.PubKey + pk, err := cryptoenc.PubKeyFromProto(*pubkey) + if err != nil { + panic(err) + } + vals = append(vals, types.NewValidatorDefaultVotingPower(pk, node.ProTxHash)) } return vals } diff --git a/test/maverick/consensus/replay.go b/test/maverick/consensus/replay.go index 9ce73cb136..0edd5c4f38 100644 --- a/test/maverick/consensus/replay.go +++ b/test/maverick/consensus/replay.go @@ -414,6 +414,13 @@ func (h *Handshaker) ReplayBlocks( ) state.Version.Consensus.App = state.ConsensusParams.Version.AppVersion } + + // If we received non-zero initial core height, we set it here + if res.InitialCoreHeight > 0 && res.InitialCoreHeight != req.InitialCoreHeight { + state.LastCoreChainLockedBlockHeight = res.InitialCoreHeight + h.initialState.LastCoreChainLockedBlockHeight = res.InitialCoreHeight + } + // We update the last results hash with the empty hash, to conform with RFC-6962. state.LastResultsHash = merkle.HashFromByteSlices(nil) if err := h.stateStore.Save(state); err != nil { diff --git a/test/maverick/consensus/replay_file.go b/test/maverick/consensus/replay_file.go index 9466339ee1..f3624321a7 100644 --- a/test/maverick/consensus/replay_file.go +++ b/test/maverick/consensus/replay_file.go @@ -319,7 +319,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo } // Create proxyAppConn connection (consensus, mempool, query) - clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) + clientCreator, _ := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) proxyApp := proxy.NewAppConns(clientCreator) err = proxyApp.Start() if err != nil { diff --git a/test/maverick/consensus/state.go b/test/maverick/consensus/state.go index 16945aa0f3..fe4d47001c 100644 --- a/test/maverick/consensus/state.go +++ b/test/maverick/consensus/state.go @@ -896,6 +896,8 @@ func (cs *State) updateToState(state sm.State) { cs.StartTime = cs.config.Commit(cs.CommitTime) } + stateID := cs.state.StateID() + cs.Validators = validators cs.Proposal = nil cs.ProposalBlock = nil @@ -906,7 +908,7 @@ func (cs *State) updateToState(state sm.State) { cs.ValidRound = -1 cs.ValidBlock = nil cs.ValidBlockParts = nil - cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators) + cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, stateID, validators) cs.CommitRound = -1 cs.LastValidators = state.LastValidators cs.TriggeredTimeoutPrecommit = false @@ -1260,7 +1262,7 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa case cs.Height == cs.state.InitialHeight: // We're creating a proposal for the first block. // The commit is empty, but not nil. - commit = types.NewCommit(0, 0, types.BlockID{}, types.StateID{}, nil, nil, nil) + commit = types.NewCommit(0, 0, types.BlockID{}, cs.state.StateID(), nil, nil, nil) case cs.LastPrecommits.HasTwoThirdsMajority(): // Make the commit from LastPrecommits commit = cs.LastPrecommits.MakeCommit() @@ -1795,17 +1797,13 @@ func (cs *State) signVote( Round: cs.Round, Type: msgType, BlockID: types.BlockID{Hash: hash, PartSetHeader: header}, - StateID: types.StateID{LastAppHash: cs.state.AppHash}, - } - - // if hash is nil no need to send the state id - if hash == nil { - vote.StateID.LastAppHash = nil } v := vote.ToProto() + stateID := cs.state.StateID() + err := cs.privValidator.SignVote( - cs.state.ChainID, cs.state.Validators.QuorumType, cs.state.Validators.QuorumHash, v, nil) + cs.state.ChainID, cs.state.Validators.QuorumType, cs.state.Validators.QuorumHash, v, stateID, nil) vote.BlockSignature = v.BlockSignature vote.StateSignature = v.StateSignature diff --git a/test/maverick/node/node.go b/test/maverick/node/node.go index fa9e3f569b..466a176ebc 100644 --- a/test/maverick/node/node.go +++ b/test/maverick/node/node.go @@ -139,9 +139,10 @@ func DefaultNewNode( ) } + appClient, _ := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()) return NewNode(config, nodeKey, - proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), + appClient, DefaultGenesisDocProviderFunc(config), DefaultDBProvider, DefaultMetricsProvider(config.Instrumentation), diff --git a/tests.mk b/tests.mk index b0cacc0b33..3622cb4652 100644 --- a/tests.mk +++ b/tests.mk @@ -37,6 +37,7 @@ test_integrations: make build_docker_test_image make tools make install + make install_abci make test_cover make test_apps make test_abci_apps diff --git a/tools/tm-signer-harness/internal/test_harness.go b/tools/tm-signer-harness/internal/test_harness.go index 8754f69c56..79557be3ac 100644 --- a/tools/tm-signer-harness/internal/test_harness.go +++ b/tools/tm-signer-harness/internal/test_harness.go @@ -271,7 +271,6 @@ func (th *TestHarness) TestSignVote() error { for _, voteType := range voteTypes { th.logger.Info("Testing vote type", "type", voteType) hash := tmhash.Sum([]byte("hash")) - lastAppHash := tmhash.Sum([]byte("hash")) vote := &types.Vote{ Type: voteType, Height: 101, @@ -283,17 +282,18 @@ func (th *TestHarness) TestSignVote() error { Total: 1000000, }, }, - StateID: types.StateID{ - LastAppHash: lastAppHash, - }, ValidatorIndex: 0, ValidatorProTxHash: tmhash.Sum([]byte("pro_tx_hash")), } + + stateID := types.RandStateID().WithHeight(vote.Height - 1) + v := vote.ToProto() + voteBlockID := types.VoteBlockSignID(th.chainID, v, btcjson.LLMQType_5_60, th.quorumHash) - voteStateID := types.VoteStateSignID(th.chainID, v, btcjson.LLMQType_5_60, th.quorumHash) + stateIDSignID := stateID.SignID(th.chainID, btcjson.LLMQType_5_60, th.quorumHash) // sign the vote - if err := th.signerClient.SignVote(th.chainID, btcjson.LLMQType_5_60, th.quorumHash, v, nil); err != nil { + if err := th.signerClient.SignVote(th.chainID, btcjson.LLMQType_5_60, th.quorumHash, v, stateID, nil); err != nil { th.logger.Error("FAILED: Signing of vote", "err", err) return newTestHarnessError(ErrTestSignVoteFailed, err, fmt.Sprintf("voteType=%d", voteType)) } @@ -318,7 +318,7 @@ func (th *TestHarness) TestSignVote() error { return newTestHarnessError(ErrTestSignVoteFailed, nil, "signature validation failed") } - if sck.VerifySignatureDigest(voteStateID, vote.StateSignature) { + if sck.VerifySignatureDigest(stateIDSignID, vote.StateSignature) { th.logger.Info("Successfully validated vote signature", "type", voteType) } else { th.logger.Error("FAILED: Vote signature validation failed", "type", voteType) diff --git a/types/block.go b/types/block.go index 99320aac9c..dc790ae445 100644 --- a/types/block.go +++ b/types/block.go @@ -673,7 +673,6 @@ func (commit *Commit) GetCanonicalVote() *Vote { Height: commit.Height, Round: commit.Round, BlockID: commit.BlockID, - StateID: commit.StateID, } } @@ -711,50 +710,6 @@ func (commit *Commit) CanonicalVoteVerifySignID(chainID string, quorumType btcjs return VoteBlockSignID(chainID, vCanonical, quorumType, quorumHash) } -// VoteStateSignID returns the signID bytes of the state for the Vote corresponding to valIdx for -// signing. -// -// Panics if valIdx >= commit.Size(). -// -func (commit *Commit) VoteStateSignID(chainID string, quorumType btcjson.LLMQType, quorumHash []byte) []byte { - v := commit.GetCanonicalVote() - return VoteStateSignID(chainID, v.ToProto(), quorumType, quorumHash) -} - -// VoteStateRequestId returns the requestId Hash of the Vote corresponding to valIdx for -// signing. -// -// Panics if valIdx >= commit.Size(). -// -func (commit *Commit) VoteStateRequestID() []byte { - requestIDMessage := []byte("dpsvote") - heightByteArray := make([]byte, 8) - binary.LittleEndian.PutUint64(heightByteArray, uint64(commit.Height)) - roundByteArray := make([]byte, 4) - binary.LittleEndian.PutUint32(roundByteArray, uint32(commit.Round)) - - requestIDMessage = append(requestIDMessage, heightByteArray...) - requestIDMessage = append(requestIDMessage, roundByteArray...) - - return crypto.Sha256(requestIDMessage) -} - -// CanonicalVoteStateSignBytes returns the bytes of the State corresponding to valIdx for -// signing. -// -// Panics if valIdx >= commit.Size(). -// -// See VoteSignBytes -func (commit *Commit) CanonicalVoteStateSignBytes(chainID string) []byte { - v := commit.GetCanonicalVote() - return VoteStateSignBytes(chainID, v.ToProto()) -} - -func (commit *Commit) CanonicalVoteStateSignID(chainID string, quorumType btcjson.LLMQType, quorumHash []byte) []byte { - v := commit.GetCanonicalVote() - return VoteStateSignID(chainID, v.ToProto(), quorumType, quorumHash) -} - // Type returns the vote type of the commit, which is always VoteTypePrecommit // Implements VoteSetReader. func (commit *Commit) Type() byte { @@ -1185,72 +1140,3 @@ func BlockIDFromProto(bID *tmproto.BlockID) (*BlockID, error) { return blockID, blockID.ValidateBasic() } - -//-------------------------------------------------------------------------------- - -// StateID -type StateID struct { - LastAppHash tmbytes.HexBytes `json:"last_app_hash"` -} - -// Equals returns true if the StateID matches the given StateID -func (stateID StateID) Equals(other StateID) bool { - return bytes.Equal(stateID.LastAppHash, other.LastAppHash) -} - -// Key returns a machine-readable string representation of the StateID -func (stateID StateID) Key() string { - return string(stateID.LastAppHash) -} - -// ValidateBasic performs basic validation. -func (stateID StateID) ValidateBasic() error { - // LastAppHash can be empty in case of genesis block. - if err := ValidateAppHash(stateID.LastAppHash); err != nil { - return fmt.Errorf("wrong app Hash") - } - return nil -} - -// IsZero returns true if this is the StateID of a nil block. -func (stateID StateID) IsZero() bool { - return len(stateID.LastAppHash) == 0 -} - -// IsComplete returns true if this is a valid StateID of a non-nil block. -func (stateID StateID) IsComplete() bool { - return len(stateID.LastAppHash) == tmhash.Size -} - -// String returns a human readable string representation of the StateID. -// -// 1. hash -// -func (stateID StateID) String() string { - return fmt.Sprintf(`%v`, stateID.LastAppHash) -} - -// ToProto converts BlockID to protobuf -func (stateID *StateID) ToProto() tmproto.StateID { - if stateID == nil { - return tmproto.StateID{} - } - - return tmproto.StateID{ - LastAppHash: stateID.LastAppHash, - } -} - -// FromProto sets a protobuf BlockID to the given pointer. -// It returns an error if the block id is invalid. -func StateIDFromProto(sID *tmproto.StateID) (*StateID, error) { - if sID == nil { - return nil, errors.New("nil StateID") - } - - stateID := new(StateID) - - stateID.LastAppHash = sID.LastAppHash - - return stateID, stateID.ValidateBasic() -} diff --git a/types/block_meta.go b/types/block_meta.go index 4b21cfff21..0c503e750f 100644 --- a/types/block_meta.go +++ b/types/block_meta.go @@ -11,7 +11,6 @@ import ( // BlockMeta contains meta information. type BlockMeta struct { BlockID BlockID `json:"block_id"` - StateID StateID `json:"state_id"` BlockSize int `json:"block_size"` Header Header `json:"header"` HasCoreChainLock bool `json:"has_core_chain_lock"` @@ -22,7 +21,6 @@ type BlockMeta struct { func NewBlockMeta(block *Block, blockParts *PartSet) *BlockMeta { return &BlockMeta{ BlockID: BlockID{block.Hash(), blockParts.Header()}, - StateID: StateID{LastAppHash: block.Header.AppHash}, BlockSize: block.Size(), Header: block.Header, HasCoreChainLock: block.CoreChainLock != nil, @@ -37,7 +35,6 @@ func (bm *BlockMeta) ToProto() *tmproto.BlockMeta { pb := &tmproto.BlockMeta{ BlockID: bm.BlockID.ToProto(), - StateID: bm.StateID.ToProto(), BlockSize: int64(bm.BlockSize), Header: *bm.Header.ToProto(), HasCoreChainLock: bm.HasCoreChainLock, @@ -58,18 +55,12 @@ func BlockMetaFromProto(pb *tmproto.BlockMeta) (*BlockMeta, error) { return nil, err } - si, err := StateIDFromProto(&pb.StateID) - if err != nil { - return nil, err - } - h, err := HeaderFromProto(&pb.Header) if err != nil { return nil, err } bm.BlockID = *bi - bm.StateID = *si bm.BlockSize = int(pb.BlockSize) bm.Header = h bm.HasCoreChainLock = pb.HasCoreChainLock @@ -83,9 +74,6 @@ func (bm *BlockMeta) ValidateBasic() error { if err := bm.BlockID.ValidateBasic(); err != nil { return err } - if err := bm.StateID.ValidateBasic(); err != nil { - return err - } if !bytes.Equal(bm.BlockID.Hash, bm.Header.Hash()) { return fmt.Errorf("expected BlockID#Hash and Header#Hash to be the same, got %X != %X", bm.BlockID.Hash, bm.Header.Hash()) diff --git a/types/block_test.go b/types/block_test.go index 33535d2e09..606114dffd 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -1,27 +1,25 @@ package types import ( - // it is ok to use math/rand here: we do not need a cryptographically secure random - // number generator here and we can run the tests a bit faster "crypto/rand" "encoding/hex" "fmt" "math" "os" "reflect" + "strconv" "testing" "time" "github.com/dashevo/dashd-go/btcjson" - gogotypes "github.com/gogo/protobuf/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/tmhash" - "github.com/tendermint/tendermint/libs/bytes" + + tmbytes "github.com/tendermint/tendermint/libs/bytes" tmrand "github.com/tendermint/tendermint/libs/rand" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmversion "github.com/tendermint/tendermint/proto/tendermint/version" @@ -36,13 +34,14 @@ func TestMain(m *testing.M) { func TestBlockAddEvidence(t *testing.T) { txs := []Tx{Tx("foo"), Tx("bar")} lastID := makeBlockIDRandom() - lastStateID := makeStateIDRandom() + h := int64(3) + stateID := RandStateID().WithHeight(h - 2) coreChainLock := NewMockChainLock(1) - voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10) - commit, err := MakeCommit(lastID, lastStateID, h-1, 1, voteSet, vals) + voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, stateID) + commit, err := MakeCommit(lastID, stateID, h-1, 1, voteSet, vals) require.NoError(t, err) ev := NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain", valSet.QuorumType, @@ -60,11 +59,12 @@ func TestBlockValidateBasic(t *testing.T) { txs := []Tx{Tx("foo"), Tx("bar")} lastID := makeBlockIDRandom() - lastStateID := makeStateIDRandom() h := int64(3) - voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10) - commit, err := MakeCommit(lastID, lastStateID, h-1, 1, voteSet, vals) + stateID := RandStateID().WithHeight(h - 2) + + voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, stateID) + commit, err := MakeCommit(lastID, stateID, h-1, 1, voteSet, vals) require.NoError(t, err) ev := NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain", valSet.QuorumType, @@ -135,11 +135,11 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) { assert.Nil(t, (*Block)(nil).MakePartSet(2)) lastID := makeBlockIDRandom() - lastStateID := makeStateIDRandom() h := int64(3) + stateID := RandStateID().WithHeight(h - 2) - voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10) - commit, err := MakeCommit(lastID, lastStateID, h-1, 1, voteSet, vals) + voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, stateID) + commit, err := MakeCommit(lastID, stateID, h-1, 1, voteSet, vals) require.NoError(t, err) ev := NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain", valSet.QuorumType, @@ -158,11 +158,11 @@ func TestBlockHashesTo(t *testing.T) { assert.False(t, (*Block)(nil).HashesTo(nil)) lastID := makeBlockIDRandom() - lastStateID := makeStateIDRandom() h := int64(3) + stateID := RandStateID().WithHeight(h - 2) - voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10) - commit, err := MakeCommit(lastID, lastStateID, h-1, 1, voteSet, vals) + voteSet, valSet, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, stateID) + commit, err := MakeCommit(lastID, stateID, h-1, 1, voteSet, vals) require.NoError(t, err) ev := NewMockDuplicateVoteEvidenceWithValidator(h, time.Now(), vals[0], "block-test-chain", valSet.QuorumType, @@ -220,24 +220,6 @@ func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) BlockID { } } -func makeStateIDRandom() StateID { - var ( - lastAppHash = make([]byte, tmhash.Size) - ) - rand.Read(lastAppHash) //nolint: errcheck // ignore errcheck for read - return StateID{lastAppHash} -} - -func makeStateID(lastAppHash []byte) StateID { - var ( - lap = make([]byte, tmhash.Size) - ) - copy(lap, lastAppHash) - return StateID{ - LastAppHash: lap, - } -} - var nilBytes []byte // This follows RFC-6962, i.e. `echo -n '' | sha256sum` @@ -257,10 +239,11 @@ func TestNilDataHashDoesntCrash(t *testing.T) { func TestCommit(t *testing.T) { lastID := makeBlockIDRandom() - lastStateID := makeStateIDRandom() h := int64(3) - voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10) - commit, err := MakeCommit(lastID, lastStateID, h-1, 1, voteSet, vals) + stateID := RandStateID().WithHeight(h - 2) + + voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, stateID) + commit, err := MakeCommit(lastID, stateID, h-1, 1, voteSet, vals) require.NoError(t, err) assert.Equal(t, h-1, commit.Height) @@ -273,6 +256,7 @@ func TestCommit(t *testing.T) { } func TestCommitValidateBasic(t *testing.T) { + const height int64 = 5 testCases := []struct { testName string malleateCommit func(*Commit) @@ -287,7 +271,7 @@ func TestCommitValidateBasic(t *testing.T) { for _, tc := range testCases { tcRun := tc t.Run(tcRun.testName, func(t *testing.T) { - com := randCommit() + com := randCommit(RandStateID().WithHeight(height - 1)) tcRun.malleateCommit(com) assert.Equal(t, tcRun.expectErr, com.ValidateBasic() != nil, "Validate Basic had an unexpected result") }) @@ -328,7 +312,7 @@ func TestHeaderHash(t *testing.T) { testCases := []struct { desc string header *Header - expectHash bytes.HexBytes + expectHash tmbytes.HexBytes }{ {"Generates expected hash", &Header{ Version: tmversion.Consensus{Block: 1, App: 2}, @@ -386,7 +370,7 @@ func TestHeaderHash(t *testing.T) { s.Type().Field(i).Name) switch f := f.Interface().(type) { - case int64, uint32, uint64, bytes.HexBytes, string: + case int64, uint32, uint64, tmbytes.HexBytes, string: byteSlices = append(byteSlices, cdcEncode(f)) case time.Time: bz, err := gogotypes.StdTimeMarshal(f) @@ -406,7 +390,7 @@ func TestHeaderHash(t *testing.T) { } } assert.Equal(t, - bytes.HexBytes(merkle.HashFromByteSlices(byteSlices)), tcRun.header.Hash()) + tmbytes.HexBytes(merkle.HashFromByteSlices(byteSlices)), tcRun.header.Hash()) } }) } @@ -450,24 +434,24 @@ func TestMaxHeaderBytes(t *testing.T) { assert.EqualValues(t, MaxHeaderBytes, int64(len(bz))) } -func randCommit() *Commit { +func randCommit(stateID StateID) *Commit { lastID := makeBlockIDRandom() - lastStateID := makeStateIDRandom() - h := int64(3) - voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10) - commit, err := MakeCommit(lastID, lastStateID, h-1, 1, voteSet, vals) + height := stateID.Height + 1 + + voteSet, _, vals := randVoteSet(height, 1, tmproto.PrecommitType, 10, stateID) + commit, err := MakeCommit(lastID, stateID, height, 1, voteSet, vals) if err != nil { panic(err) } return commit } -func hexBytesFromString(s string) bytes.HexBytes { +func hexBytesFromString(s string) tmbytes.HexBytes { b, err := hex.DecodeString(s) if err != nil { panic(err) } - return bytes.HexBytes(b) + return tmbytes.HexBytes(b) } func TestBlockMaxDataBytes(t *testing.T) { @@ -543,27 +527,28 @@ func TestBlockMaxDataBytesNoEvidence(t *testing.T) { func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) - stateID := makeStateID([]byte("lastapphash")) const ( height = int64(3) round = 0 ) + // all votes below use height - 1, so state is at height - 2 + stateID := RandStateID().WithHeight(height - 2) + type commitVoteTest struct { blockIDs []BlockID - stateIDs []StateID numVotes []int // must sum to numValidators numValidators int valid bool } testCases := []commitVoteTest{ - {[]BlockID{blockID, {}}, []StateID{stateID, {}}, []int{67, 33}, 100, true}, + {[]BlockID{blockID, {}}, []int{67, 33}, 100, true}, } for _, tc := range testCases { - voteSet, valSet, vals := randVoteSet(height-1, round, tmproto.PrecommitType, tc.numValidators) + voteSet, valSet, vals := randVoteSet(height-1, round, tmproto.PrecommitType, tc.numValidators, stateID) vi := int32(0) for n := range tc.blockIDs { @@ -577,7 +562,6 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { Round: round, Type: tmproto.PrecommitType, BlockID: tc.blockIDs[n], - StateID: tc.stateIDs[n], } added, err := signAddVote(vals[vi], vote, voteSet) @@ -601,10 +585,10 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { func TestBlockIDValidateBasic(t *testing.T) { validBlockID := BlockID{ - Hash: bytes.HexBytes{}, + Hash: tmbytes.HexBytes{}, PartSetHeader: PartSetHeader{ Total: 1, - Hash: bytes.HexBytes{}, + Hash: tmbytes.HexBytes{}, }, } @@ -618,7 +602,7 @@ func TestBlockIDValidateBasic(t *testing.T) { testCases := []struct { testName string - blockIDHash bytes.HexBytes + blockIDHash tmbytes.HexBytes blockIDPartSetHeader PartSetHeader expectErr bool }{ @@ -641,7 +625,8 @@ func TestBlockIDValidateBasic(t *testing.T) { func TestBlockProtoBuf(t *testing.T) { h := tmrand.Int63() - c1 := randCommit() + stateID := RandStateID().WithHeight(h - 1) + c1 := randCommit(stateID) b1 := MakeBlock(h, 0, nil, []Tx{Tx([]byte{1})}, &Commit{}, []Evidence{}, 0) b1.ProposerProTxHash = tmrand.Bytes(crypto.DefaultHashSize) @@ -831,7 +816,9 @@ func TestBlockIDProtoBuf(t *testing.T) { } func TestSignedHeaderProtoBuf(t *testing.T) { - commit := randCommit() + stateID := RandStateID() + + commit := randCommit(stateID) h := makeRandHeader() sh := SignedHeader{Header: &h, Commit: commit} @@ -873,3 +860,90 @@ func TestBlockIDEquals(t *testing.T) { assert.True(t, blockIDEmpty.Equals(blockIDEmpty)) assert.False(t, blockIDEmpty.Equals(blockIDDifferent)) } + +// StateID tests + +// TODO: Move to separate file + +func TestStateID_Copy(t *testing.T) { + state1 := RandStateID() + state2 := state1.Copy() + assert.Equal(t, state1, state2) + + state2.LastAppHash[5] = 0x12 + assert.NotEqual(t, state1, state2) +} + +func TestStateID_Equals(t *testing.T) { + tests := []struct { + state1 StateID + state2 StateID + equal bool + }{ + {RandStateID(), RandStateID(), false}, + { + StateID{12, []byte("12345678901234567890123456789012")}, + StateID{12, []byte("12345678901234567890123456789012")}, + true, + }, + { + StateID{11, []byte("12345678901234567890123456789012")}, + StateID{12, []byte("12345678901234567890123456789012")}, + false, + }, + { + StateID{12, []byte("12345678901234567890123456789012")}, + StateID{12, []byte("1234567890123456789012345678901")}, + false, + }, + } + //nolint:scopelint + for tcID, tc := range tests { + t.Run(strconv.Itoa(tcID), func(t *testing.T) { + assert.Equal(t, tc.equal, tc.state1.Equals(tc.state2)) + }) + } +} + +func TestStateID_ValidateBasic(t *testing.T) { + type fields struct { + Height int64 + LastAppHash tmbytes.HexBytes + } + tests := []struct { + name string + fields fields + wantErr bool + }{ + {name: "negative height", fields: fields{-1, nil}, wantErr: true}, + {name: "zero height - allowed for genesis block", fields: fields{0, nil}, wantErr: false}, + {name: "nil apphash", fields: fields{12, nil}, wantErr: false}, + {name: "empty apphash", fields: fields{12, []byte{}}, wantErr: false}, + {name: "apphash too short", fields: fields{12, []byte{0x1, 0x2, 0x3}}, wantErr: true}, + {name: "apphash too short 2", fields: fields{12, tmrand.Bytes(crypto.SmallAppHashSize - 1)}, wantErr: true}, + {name: "apphash small", fields: fields{12, tmrand.Bytes(crypto.SmallAppHashSize)}, wantErr: false}, + {name: "apphash default", fields: fields{12, tmrand.Bytes(crypto.DefaultAppHashSize)}, wantErr: false}, + {name: "apphash large", fields: fields{12, tmrand.Bytes(crypto.LargeAppHashSize)}, wantErr: false}, + {name: "apphash too large", fields: fields{12, tmrand.Bytes(crypto.LargeAppHashSize + 1)}, wantErr: true}, + } + //nolint:scopelint + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stateID := StateID{ + Height: tt.fields.Height, + LastAppHash: tt.fields.LastAppHash, + } + if err := stateID.ValidateBasic(); (err != nil) != tt.wantErr { + t.Errorf("StateID.ValidateBasic() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestStateID_WithHeight(t *testing.T) { + stateID := RandStateID() + height := stateID.Height + stateIDWithHeight := stateID.WithHeight(height + 1) + + assert.Equal(t, height, stateIDWithHeight.Height-1) +} diff --git a/types/canonical.go b/types/canonical.go index dac07c1c0c..4c64f96e01 100644 --- a/types/canonical.go +++ b/types/canonical.go @@ -33,23 +33,6 @@ func CanonicalizeBlockID(bid tmproto.BlockID) *tmproto.CanonicalBlockID { return cbid } -func CanonicalizeStateID(sid tmproto.StateID) *tmproto.CanonicalStateID { - rsid, err := StateIDFromProto(&sid) - if err != nil { - panic(err) - } - var csid *tmproto.CanonicalStateID - if rsid == nil || rsid.IsZero() { - csid = nil - } else { - csid = &tmproto.CanonicalStateID{ - LastAppHash: sid.LastAppHash, - } - } - - return csid -} - // CanonicalizeVote transforms the given PartSetHeader to a CanonicalPartSetHeader. func CanonicalizePartSetHeader(psh tmproto.PartSetHeader) tmproto.CanonicalPartSetHeader { return tmproto.CanonicalPartSetHeader(psh) @@ -76,20 +59,10 @@ func CanonicalizeVote(chainID string, vote *tmproto.Vote) tmproto.CanonicalVote Height: vote.Height, // encoded as sfixed64 Round: int64(vote.Round), // encoded as sfixed64 BlockID: CanonicalizeBlockID(vote.BlockID), - StateID: CanonicalizeStateID(vote.StateID), ChainID: chainID, } } -// CanonicalizeStateVote transforms the given Vote to a CanonicalStateVote, which does -// not contain ValidatorIndex and ValidatorProTxHash fields. -func CanonicalizeStateVote(vote *tmproto.Vote) tmproto.CanonicalStateVote { - return tmproto.CanonicalStateVote{ - Height: vote.Height, // encoded as sfixed64 - StateID: CanonicalizeStateID(vote.StateID), - } -} - // CanonicalTime can be used to stringify time in a canonical way. func CanonicalTime(t time.Time) string { // Note that sending time over amino resets it to diff --git a/types/errors.go b/types/errors.go index ab00c447dd..1b07048bad 100644 --- a/types/errors.go +++ b/types/errors.go @@ -9,6 +9,8 @@ type ( Expected int64 Actual int64 } + + ErrInvalidVoteSignature error ) func NewErrInvalidCommitHeight(expected, actual int64) ErrInvalidCommitHeight { diff --git a/types/events.go b/types/events.go index 40cf1e43a0..75d9afeace 100644 --- a/types/events.go +++ b/types/events.go @@ -4,6 +4,7 @@ import ( "fmt" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/bytes" tmjson "github.com/tendermint/tendermint/libs/json" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" tmquery "github.com/tendermint/tendermint/libs/pubsub/query" @@ -128,6 +129,7 @@ type EventDataString string type EventDataValidatorSetUpdates struct { ValidatorUpdates []*Validator `json:"validator_updates"` + QuorumHash bytes.HexBytes } // PUBSUB diff --git a/types/evidence.go b/types/evidence.go index 32d022e083..4dc8f01e0d 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -313,6 +313,7 @@ func NewMockDuplicateVoteEvidence( // NewMockDuplicateVoteEvidenceWithValidator assumes voting power to be DefaultDashVotingPower and // validator to be the only one in the set +// TODO: discuss if this might be moved to some *_test.go file func NewMockDuplicateVoteEvidenceWithValidator( height int64, time time.Time, @@ -325,17 +326,20 @@ func NewMockDuplicateVoteEvidenceWithValidator( if err != nil { panic(err) } + + stateID := RandStateID().WithHeight(height - 1) + proTxHash, _ := pv.GetProTxHash() - val := NewValidator(pubKey, DefaultDashVotingPower, proTxHash) + val := NewValidator(pubKey, DefaultDashVotingPower, proTxHash, "") - voteA := makeMockVote(height, 0, 0, proTxHash, randBlockID(), randStateID()) + voteA := makeMockVote(height, 0, 0, proTxHash, randBlockID()) vA := voteA.ToProto() - _ = pv.SignVote(chainID, quorumType, quorumHash, vA, nil) + _ = pv.SignVote(chainID, quorumType, quorumHash, vA, stateID, nil) voteA.BlockSignature = vA.BlockSignature voteA.StateSignature = vA.StateSignature - voteB := makeMockVote(height, 0, 0, proTxHash, randBlockID(), randStateID()) + voteB := makeMockVote(height, 0, 0, proTxHash, randBlockID()) vB := voteB.ToProto() - _ = pv.SignVote(chainID, quorumType, quorumHash, vB, nil) + _ = pv.SignVote(chainID, quorumType, quorumHash, vB, stateID, nil) voteB.BlockSignature = vB.BlockSignature voteB.StateSignature = vB.StateSignature return NewDuplicateVoteEvidence( @@ -352,21 +356,23 @@ func NewMockDuplicateVoteEvidenceWithPrivValInValidatorSet(height int64, time ti quorumHash crypto.QuorumHash) *DuplicateVoteEvidence { proTxHash, _ := pv.GetProTxHash() - voteA := makeMockVote(height, 0, 0, proTxHash, randBlockID(), randStateID()) + stateID := RandStateID().WithHeight(height - 1) + + voteA := makeMockVote(height, 0, 0, proTxHash, randBlockID()) vA := voteA.ToProto() - _ = pv.SignVote(chainID, quorumType, quorumHash, vA, nil) + _ = pv.SignVote(chainID, quorumType, quorumHash, vA, stateID, nil) voteA.BlockSignature = vA.BlockSignature voteA.StateSignature = vA.StateSignature - voteB := makeMockVote(height, 0, 0, proTxHash, randBlockID(), randStateID()) + voteB := makeMockVote(height, 0, 0, proTxHash, randBlockID()) vB := voteB.ToProto() - _ = pv.SignVote(chainID, quorumType, quorumHash, vB, nil) + _ = pv.SignVote(chainID, quorumType, quorumHash, vB, stateID, nil) voteB.BlockSignature = vB.BlockSignature voteB.StateSignature = vB.StateSignature return NewDuplicateVoteEvidence(voteA, voteB, time, valSet) } func makeMockVote(height int64, round, index int32, proTxHash crypto.ProTxHash, - blockID BlockID, stateID StateID) *Vote { + blockID BlockID) *Vote { return &Vote{ Type: tmproto.SignedMsgType(2), Height: height, @@ -374,7 +380,6 @@ func makeMockVote(height int64, round, index int32, proTxHash crypto.ProTxHash, BlockID: blockID, ValidatorProTxHash: proTxHash, ValidatorIndex: index, - StateID: stateID, } } @@ -387,9 +392,3 @@ func randBlockID() BlockID { }, } } - -func randStateID() StateID { - return StateID{ - LastAppHash: tmrand.Bytes(tmhash.Size), - } -} diff --git a/types/evidence_test.go b/types/evidence_test.go index 0ae1ed79c1..f2cc3054fd 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -34,13 +34,14 @@ func randomDuplicateVoteEvidence(t *testing.T) *DuplicateVoteEvidence { val := NewMockPVForQuorum(quorumHash) blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) - stateID := makeStateID(tmhash.Sum([]byte("statehash"))) quorumType := btcjson.LLMQType_5_60 const chainID = "mychain" + const height = int64(10) + stateID := RandStateID().WithHeight(height - 1) return &DuplicateVoteEvidence{ - VoteA: makeVote(t, val, chainID, 0, 10, quorumType, quorumHash, 2, 1, blockID, stateID), - VoteB: makeVote(t, val, chainID, 0, 10, quorumType, quorumHash, 2, 1, blockID2, stateID), + VoteA: makeVote(t, val, chainID, 0, height, quorumType, quorumHash, 2, 1, blockID, stateID), + VoteB: makeVote(t, val, chainID, 0, height, quorumType, quorumHash, 2, 1, blockID2, stateID), TotalVotingPower: 3 * DefaultDashVotingPower, ValidatorPower: DefaultDashVotingPower, Timestamp: defaultVoteTime, @@ -61,7 +62,6 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) { val := NewMockPVForQuorum(quorumHash) blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) - stateID := makeStateID(tmhash.Sum([]byte("statehash"))) quorumType := btcjson.LLMQType_5_60 const chainID = "mychain" @@ -80,7 +80,7 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) { {"Invalid vote type", func(ev *DuplicateVoteEvidence) { ev.VoteA = makeVote( t, val, chainID, math.MaxInt32, math.MaxInt64, quorumType, - quorumHash, math.MaxInt32, 0, blockID2, stateID, + quorumHash, math.MaxInt32, 0, blockID2, RandStateID().WithHeight(math.MaxInt64-1), ) }, true}, {"Invalid vote order", func(ev *DuplicateVoteEvidence) { @@ -92,10 +92,11 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { - vote1 := makeVote( - t, val, chainID, math.MaxInt32, math.MaxInt64, quorumType, + const height int64 = math.MaxInt64 + stateID := RandStateID().WithHeight(height - 1) + vote1 := makeVote(t, val, chainID, math.MaxInt32, height, quorumType, quorumHash, math.MaxInt32, 0x02, blockID, stateID) - vote2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, quorumType, + vote2 := makeVote(t, val, chainID, math.MaxInt32, height, quorumType, quorumHash, math.MaxInt32, 0x02, blockID2, stateID) thresholdPublicKey, err := val.GetThresholdPublicKey(quorumHash) assert.NoError(t, err) @@ -129,11 +130,10 @@ func makeVote( Round: round, Type: tmproto.SignedMsgType(step), BlockID: blockID, - StateID: stateID, } vpb := v.ToProto() - err = val.SignVote(chainID, quorumType, quorumHash, vpb, nil) + err = val.SignVote(chainID, quorumType, quorumHash, vpb, stateID, nil) if err != nil { panic(err) } @@ -167,14 +167,16 @@ func TestEvidenceProto(t *testing.T) { val := NewMockPVForQuorum(quorumHash) blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), math.MaxInt32, tmhash.Sum([]byte("partshash"))) - stateID := makeStateID(tmhash.Sum([]byte("statehash"))) quorumType := btcjson.LLMQType_5_60 const chainID = "mychain" - v := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, quorumType, quorumHash, 1, 0x01, blockID, stateID) - v2 := makeVote(t, val, chainID, math.MaxInt32, math.MaxInt64, quorumType, quorumHash, 2, 0x01, blockID2, stateID) + var height int64 = math.MaxInt64 + stateID := RandStateID().WithHeight(height - 1) + + v := makeVote(t, val, chainID, math.MaxInt32, height, quorumType, quorumHash, 1, 0x01, blockID, stateID) + v2 := makeVote(t, val, chainID, math.MaxInt32, height, quorumType, quorumHash, 2, 0x01, blockID2, stateID) // -------- SignedHeaders -------- - const height int64 = 37 + height = int64(37) var ( header1 = makeHeaderRandom() diff --git a/types/genesis.go b/types/genesis.go index d7038cd459..a8cbd7e6ae 100644 --- a/types/genesis.go +++ b/types/genesis.go @@ -121,7 +121,9 @@ func (genDoc *GenesisDoc) ValidateAndComplete() error { return fmt.Errorf("the threshold public key must be 48 bytes for BLS") } if genDoc.Validators != nil && len(genDoc.QuorumHash.Bytes()) < crypto.SmallAppHashSize { - return fmt.Errorf("the quorum hash must be at least 20 bytes long (%d Validator(s))", len(genDoc.Validators)) + return fmt.Errorf("the quorum hash must be at least %d bytes long (%d Validator(s))", + crypto.SmallAppHashSize, + len(genDoc.Validators)) } if genDoc.QuorumType == 0 { diff --git a/types/light.go b/types/light.go index c882e252bc..d70ed88094 100644 --- a/types/light.go +++ b/types/light.go @@ -32,6 +32,11 @@ func (lb LightBlock) ValidateBasic(chainID string) error { if err := lb.ValidatorSet.ValidateBasic(); err != nil { return fmt.Errorf("invalid validator set: %w", err) } + // Validate StateID height + if lb.Commit.StateID.Height != lb.Height-1 { + return fmt.Errorf("invalid commit stateID height %d for light block height %d", + lb.Commit.StateID.Height, lb.Height) + } // make sure the validator set is consistent with the header if valSetHash := lb.ValidatorSet.Hash(); !bytes.Equal(lb.SignedHeader.ValidatorsHash, valSetHash) { @@ -43,6 +48,15 @@ func (lb LightBlock) ValidateBasic(chainID string) error { return nil } +// StateID() returns StateID for a given light block +func (lb LightBlock) StateID() StateID { + if lb.Commit == nil { + panic("Cannot read state of a block without commit") + } + + return lb.Commit.StateID.Copy() +} + // String returns a string representation of the LightBlock func (lb LightBlock) String() string { return lb.StringIndented("") diff --git a/types/light_test.go b/types/light_test.go index 0cad6fc6a0..29d86ee9b1 100644 --- a/types/light_test.go +++ b/types/light_test.go @@ -2,11 +2,11 @@ package types import ( "math" + "reflect" "testing" "time" "github.com/stretchr/testify/assert" - "github.com/tendermint/tendermint/crypto" tmversion "github.com/tendermint/tendermint/proto/tendermint/version" "github.com/tendermint/tendermint/version" @@ -14,7 +14,8 @@ import ( func TestLightBlockValidateBasic(t *testing.T) { header := makeRandHeader() - commit := randCommit() + stateID := RandStateID() + commit := randCommit(stateID) vals, _ := GenerateValidatorSet(5) header.Height = commit.Height header.LastBlockID = commit.BlockID @@ -39,7 +40,7 @@ func TestLightBlockValidateBasic(t *testing.T) { {"valid light block", sh, vals, false}, {"hashes don't match", sh, vals2, true}, {"invalid validator set", sh, vals3, true}, - {"invalid signed header", &SignedHeader{Header: &header, Commit: randCommit()}, vals, true}, + {"invalid signed header", &SignedHeader{Header: &header, Commit: randCommit(stateID)}, vals, true}, } for _, tc := range testCases { @@ -59,7 +60,7 @@ func TestLightBlockValidateBasic(t *testing.T) { func TestLightBlockProtobuf(t *testing.T) { header := makeRandHeader() - commit := randCommit() + commit := randCommit(RandStateID()) vals, _ := GenerateValidatorSet(5) header.Height = commit.Height header.LastBlockID = commit.BlockID @@ -112,7 +113,7 @@ func TestLightBlockProtobuf(t *testing.T) { } func TestSignedHeaderValidateBasic(t *testing.T) { - commit := randCommit() + commit := randCommit(RandStateID()) chainID := "𠜎" timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) h := Header{ @@ -165,3 +166,47 @@ func TestSignedHeaderValidateBasic(t *testing.T) { }) } } + +func TestLightBlock_StateID(t *testing.T) { + + tests := []struct { + name string + commit *Commit + want StateID + shouldPanic bool + }{ + { + "State ID OK", + randCommit(StateID{12, []byte("12345678901234567890123456789012")}), + StateID{12, []byte("12345678901234567890123456789012")}, + false, + }, + { + "Short app hash", + randCommit(StateID{12, []byte("12345678901234567890")}), + StateID{12, []byte("12345678901234567890")}, + false, + }, + { + "Nil app hash", + randCommit(StateID{12, nil}), + StateID{12, []byte{}}, + false, + }, + } + // nolint:scopelint + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + lb := LightBlock{ + SignedHeader: &SignedHeader{Commit: tt.commit}, + } + if got := lb.StateID(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("LightBlock.StateID() = %v, want %v", got, tt.want) + } + }) + } +} +func TestLightBlock_StateID_nocommit(t *testing.T) { + lb := LightBlock{} + assert.Panics(t, func() { lb.StateID() }) +} diff --git a/types/priv_validator.go b/types/priv_validator.go index fb0fd9c845..7a330e6520 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -37,7 +37,7 @@ type PrivValidator interface { SignVote( chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, - vote *tmproto.Vote, logger log.Logger) error + vote *tmproto.Vote, stateID StateID, logger log.Logger) error SignProposal( chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, proposal *tmproto.Proposal) ([]byte, error) @@ -213,7 +213,7 @@ func (pv *MockPV) GetHeight(quorumHash crypto.QuorumHash) (int64, error) { // SignVote implements PrivValidator. func (pv *MockPV) SignVote( chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, - vote *tmproto.Vote, logger log.Logger) error { + vote *tmproto.Vote, stateID StateID, logger log.Logger) error { useChainID := chainID if pv.breakVoteSigning { useChainID = "incorrect-chain-id" @@ -239,7 +239,7 @@ func (pv *MockPV) SignVote( vote.BlockSignature = blockSignature if vote.BlockID.Hash != nil { - stateSignID := VoteStateSignID(useChainID, vote, quorumType, quorumHash) + stateSignID := stateID.SignID(useChainID, quorumType, quorumHash) stateSignature, err := privKey.SignDigest(stateSignID) if err != nil { return err @@ -344,7 +344,7 @@ var ErroringMockPVErr = errors.New("erroringMockPV always returns an error") // SignVote Implements PrivValidator. func (pv *ErroringMockPV) SignVote( chainID string, quorumType btcjson.LLMQType, quorumHash crypto.QuorumHash, - vote *tmproto.Vote, logger log.Logger) error { + vote *tmproto.Vote, stateID StateID, logger log.Logger) error { return ErroringMockPVErr } diff --git a/types/protobuf.go b/types/protobuf.go index aac1263c38..1a84183fdf 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -5,6 +5,7 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/bls12381" + dashtypes "github.com/tendermint/tendermint/dash/types" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" @@ -91,8 +92,9 @@ func (tm2pb) PartSetHeader(header PartSetHeader) tmproto.PartSetHeader { // ValidatorUpdate panics on unknown pubkey type func (tm2pb) ValidatorUpdate(val *Validator) abci.ValidatorUpdate { valUpdate := abci.ValidatorUpdate{ - Power: val.VotingPower, - ProTxHash: val.ProTxHash, + Power: val.VotingPower, + ProTxHash: val.ProTxHash, + NodeAddress: val.NodeAddress.String(), } if val.PubKey != nil { pk, err := cryptoenc.PubKeyToProto(val.PubKey) @@ -133,7 +135,12 @@ func (tm2pb) ConsensusParams(params *tmproto.ConsensusParams) *abci.ConsensusPar } // XXX: panics on nil or unknown pubkey type -func (tm2pb) NewValidatorUpdate(pubkey crypto.PubKey, power int64, proTxHash []byte) abci.ValidatorUpdate { +func (tm2pb) NewValidatorUpdate( + pubkey crypto.PubKey, + power int64, + proTxHash []byte, + address dashtypes.ValidatorAddress, +) abci.ValidatorUpdate { var pubkeyABCI *crypto2.PublicKey if pubkey != nil { pubkeyProto, err := cryptoenc.PubKeyToProto(pubkey) @@ -146,9 +153,10 @@ func (tm2pb) NewValidatorUpdate(pubkey crypto.PubKey, power int64, proTxHash []b } return abci.ValidatorUpdate{ - PubKey: pubkeyABCI, - Power: power, - ProTxHash: proTxHash, + PubKey: pubkeyABCI, + Power: power, + ProTxHash: proTxHash, + NodeAddress: address.String(), } } @@ -171,7 +179,7 @@ func (pb2tm) ValidatorUpdates(vals []abci.ValidatorUpdate) ([]*Validator, error) return nil, err } } - tmVals[i] = NewValidator(pub, v.Power, v.ProTxHash) + tmVals[i] = NewValidator(pub, v.Power, v.ProTxHash, v.NodeAddress) } return tmVals, nil } @@ -191,7 +199,7 @@ func (pb2tm) ValidatorUpdatesFromValidatorSet(valSetUpdate *abci.ValidatorSetUpd return nil, nil, nil, err } } - tmVals[i] = NewValidator(pub, v.Power, v.ProTxHash) + tmVals[i] = NewValidator(pub, v.Power, v.ProTxHash, v.NodeAddress) err = tmVals[i].ValidateBasic() if err != nil { return nil, nil, nil, fmt.Errorf("validator updates from validator set error when validating validator: %s", err) diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 15829e82ae..0eb275861e 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -6,6 +6,7 @@ import ( "github.com/dashevo/dashd-go/btcjson" "github.com/tendermint/tendermint/crypto/bls12381" + dashtypes "github.com/tendermint/tendermint/dash/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -84,12 +85,22 @@ func (pubKeyBLS) TypeValue() crypto.KeyType { retu func TestABCIValidatorFromPubKeyAndPower(t *testing.T) { pubkey := bls12381.GenPrivKey().PubKey() - - abciVal := TM2PB.NewValidatorUpdate(pubkey, DefaultDashVotingPower, crypto.RandProTxHash()) + address := dashtypes.RandValidatorAddress() + abciVal := TM2PB.NewValidatorUpdate(pubkey, DefaultDashVotingPower, crypto.RandProTxHash(), address) assert.Equal(t, DefaultDashVotingPower, abciVal.Power) - - assert.NotPanics(t, func() { TM2PB.NewValidatorUpdate(nil, DefaultDashVotingPower, crypto.RandProTxHash()) }) - assert.Panics(t, func() { TM2PB.NewValidatorUpdate(pubKeyBLS{}, DefaultDashVotingPower, crypto.RandProTxHash()) }) + assert.Equal(t, address.String(), abciVal.NodeAddress) + + assert.NotPanics(t, func() { + TM2PB.NewValidatorUpdate(nil, DefaultDashVotingPower, crypto.RandProTxHash(), dashtypes.RandValidatorAddress()) + }) + assert.Panics(t, func() { + TM2PB.NewValidatorUpdate( + pubKeyBLS{}, + DefaultDashVotingPower, + crypto.RandProTxHash(), + dashtypes.RandValidatorAddress(), + ) + }) } func TestABCIValidatorWithoutPubKey(t *testing.T) { diff --git a/types/signable.go b/types/signable.go index f21029ae8a..41c4da1eb6 100644 --- a/types/signable.go +++ b/types/signable.go @@ -1,6 +1,7 @@ package types import ( + "github.com/dashevo/dashd-go/btcjson" "github.com/tendermint/tendermint/crypto/bls12381" ) @@ -10,10 +11,17 @@ var ( // Signable is an interface for all signable things. // It typically removes signatures before serializing. -// SignBytes returns the bytes to be signed // NOTE: chainIDs are part of the SignBytes but not // necessarily the object themselves. // NOTE: Expected to panic if there is an error marshalling. type Signable interface { + // SignBytes returns the bytes to be signed. SignBytes(chainID string) []byte + // SignRequestID returns a deterministically calculable, unique id of a signing request. + // See DIP-0007 for more details. + SignRequestID() []byte + + // SignID returns signing session data that will be signed to get threshold signature share. + // See DIP-0007 for more details. + SignID(chainID string, quorumType btcjson.LLMQType, quorumHash []byte) []byte } diff --git a/types/stateid.go b/types/stateid.go new file mode 100644 index 0000000000..254b2ff4d0 --- /dev/null +++ b/types/stateid.go @@ -0,0 +1,160 @@ +package types + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "strconv" + + "github.com/dashevo/dashd-go/btcjson" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/bls12381" + tmbytes "github.com/tendermint/tendermint/libs/bytes" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +// This file contains implementation of StateID logic. + +//-------------------------------------------------------------------------------- + +// StateID +// TODO: Move to separate file +type StateID struct { + + // Height of last committed block + Height int64 `json:"height"` + // LastAppHash used in last committed block + LastAppHash tmbytes.HexBytes `json:"last_app_hash"` +} + +// Copy returns new StateID that is equal to this one +func (stateID StateID) Copy() StateID { + appHash := make([]byte, len(stateID.LastAppHash)) + if copy(appHash, stateID.LastAppHash) != len(stateID.LastAppHash) { + panic("Cannot copy LastAppHash, this should never happen. Out of memory???") + } + + return StateID{ + Height: stateID.Height, + LastAppHash: appHash, + } +} + +// Equals returns true if the StateID matches the given StateID +func (stateID StateID) Equals(other StateID) bool { + return stateID.Height == other.Height && + bytes.Equal(stateID.LastAppHash, other.LastAppHash) +} + +// Key returns a machine-readable string representation of the StateID +func (stateID StateID) Key() string { + return strconv.FormatInt(stateID.Height, 36) + string(stateID.LastAppHash) +} + +// ValidateBasic performs basic validation. +func (stateID StateID) ValidateBasic() error { + // LastAppHash can be empty in case of genesis block. + if err := ValidateAppHash(stateID.LastAppHash); err != nil { + return fmt.Errorf("wrong app Hash") + } + + if stateID.Height < 0 { + return fmt.Errorf("stateID height is not valid: %d < 0", stateID.Height) + } + + return nil +} + +func (stateID StateID) Signable() Signable { + return stateID +} + +// SignBytes returns bytes that should be signed +// TODO why we don't simply use stateID.Marshal() ? +func (stateID StateID) SignBytes(chainID string) []byte { + bz := make([]byte, 8) + binary.LittleEndian.PutUint64(bz, uint64(stateID.Height)) + + lastAppHash := make([]byte, len(stateID.LastAppHash)) + copy(lastAppHash, stateID.LastAppHash) + bz = append(bz, lastAppHash...) + return bz +} + +// SignID returns signing session data that will be signed to get threshold signature share. +// See DIP-0007 +func (stateID StateID) SignID(chainID string, quorumType btcjson.LLMQType, quorumHash []byte) []byte { + + stateSignBytes := stateID.SignBytes(chainID) + + if stateSignBytes == nil { + return nil + } + + stateMessageHash := crypto.Sha256(stateSignBytes) + + stateRequestID := stateID.SignRequestID() + + stateSignID := crypto.SignID( + quorumType, + bls12381.ReverseBytes(quorumHash), + bls12381.ReverseBytes(stateRequestID), + bls12381.ReverseBytes(stateMessageHash), + ) + + return stateSignID + +} + +func (stateID StateID) SignRequestID() []byte { + requestIDMessage := []byte("dpsvote") + heightByteArray := make([]byte, 8) + + binary.LittleEndian.PutUint64(heightByteArray, uint64(stateID.Height)) + + requestIDMessage = append(requestIDMessage, heightByteArray...) + + return crypto.Sha256(requestIDMessage) +} + +// String returns a human readable string representation of the StateID. +// +// 1. hash +// +func (stateID StateID) String() string { + return fmt.Sprintf(`%d:%v`, stateID.Height, stateID.LastAppHash) +} + +// ToProto converts StateID to protobuf +func (stateID StateID) ToProto() tmproto.StateID { + return tmproto.StateID{ + LastAppHash: stateID.LastAppHash, + Height: stateID.Height, + } +} + +// WithHeight returns new copy of stateID with height set to provided value. +// It is a convenience method used in tests. +// Note that this is Last Height from state, so it will be (height-1) for Vote. +func (stateID StateID) WithHeight(height int64) StateID { + ret := stateID.Copy() + ret.Height = height + + return ret +} + +// FromProto sets a protobuf BlockID to the given pointer. +// It returns an error if the block id is invalid. +func StateIDFromProto(sID *tmproto.StateID) (*StateID, error) { + if sID == nil { + return nil, errors.New("nil StateID") + } + + stateID := new(StateID) + + stateID.LastAppHash = sID.LastAppHash + stateID.Height = sID.Height + + return stateID, stateID.ValidateBasic() +} diff --git a/types/test_util.go b/types/test_util.go index f1713f719d..0b5d02b975 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -3,6 +3,8 @@ package types import ( "fmt" + "github.com/tendermint/tendermint/crypto/tmhash" + tmrand "github.com/tendermint/tendermint/libs/rand" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmversion "github.com/tendermint/tendermint/proto/tendermint/version" "github.com/tendermint/tendermint/version" @@ -24,7 +26,6 @@ func MakeCommit(blockID BlockID, stateID StateID, height int64, round int32, Round: round, Type: tmproto.PrecommitType, BlockID: blockID, - StateID: stateID, } _, err = signAddVote(validators[i], vote, voteSet) @@ -36,9 +37,18 @@ func MakeCommit(blockID BlockID, stateID StateID, height int64, round int32, return voteSet.MakeCommit(), nil } +// signAddVote signs a vote using StateID configured inside voteSet, and adds it to that voteSet func signAddVote(privVal PrivValidator, vote *Vote, voteSet *VoteSet) (signed bool, err error) { + return signAddVoteForStateID(privVal, vote, voteSet, voteSet.stateID) +} + +// signAddVoteForStateID signs a vote using specific StateID and adds it to voteSet +func signAddVoteForStateID(privVal PrivValidator, vote *Vote, voteSet *VoteSet, + stateID StateID) (signed bool, err error) { v := vote.ToProto() - err = privVal.SignVote(voteSet.ChainID(), voteSet.valSet.QuorumType, voteSet.valSet.QuorumHash, v, nil) + + err = privVal.SignVote(voteSet.ChainID(), voteSet.valSet.QuorumType, voteSet.valSet.QuorumHash, + v, stateID, nil) if err != nil { return false, err } @@ -70,11 +80,10 @@ func MakeVote( Round: 0, Type: tmproto.PrecommitType, BlockID: blockID, - StateID: stateID, } v := vote.ToProto() - if err := privVal.SignVote(chainID, valSet.QuorumType, valSet.QuorumHash, v, nil); err != nil { + if err := privVal.SignVote(chainID, valSet.QuorumType, valSet.QuorumHash, v, stateID, nil); err != nil { return nil, err } @@ -113,3 +122,10 @@ func MakeBlock( block.fillHeader() return block } + +func RandStateID() StateID { + return StateID{ + Height: tmrand.Int63(), + LastAppHash: tmrand.Bytes(tmhash.Size), + } +} diff --git a/types/validation.go b/types/validation.go index ff0df00cda..e95b46e992 100644 --- a/types/validation.go +++ b/types/validation.go @@ -46,9 +46,10 @@ func ValidateHash(h []byte) error { // ValidateAppHash returns an error if the hash is not empty, but its // size != tmhash.Size. func ValidateAppHash(h []byte) error { - if len(h) > 0 && len(h) < crypto.SmallAppHashSize { - return fmt.Errorf("expected size to be at least %d bytes, got %d bytes", + if len(h) > 0 && (len(h) < crypto.SmallAppHashSize || len(h) > crypto.LargeAppHashSize) { + return fmt.Errorf("expected size to be at between %d and %d bytes, got %d bytes", crypto.SmallAppHashSize, + crypto.LargeAppHashSize, len(h), ) } diff --git a/types/validator.go b/types/validator.go index 158d088d9a..4ed0d7bd47 100644 --- a/types/validator.go +++ b/types/validator.go @@ -6,10 +6,10 @@ import ( "fmt" "strings" - "github.com/tendermint/tendermint/crypto/bls12381" - "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/bls12381" ce "github.com/tendermint/tendermint/crypto/encoding" + dashtypes "github.com/tendermint/tendermint/dash/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) @@ -18,9 +18,10 @@ import ( // make sure to update that method if changes are made here // The ProTxHash is part of Dash additions required for BLS threshold signatures type Validator struct { - PubKey crypto.PubKey `json:"pub_key"` - VotingPower int64 `json:"voting_power"` - ProTxHash ProTxHash `json:"pro_tx_hash"` + PubKey crypto.PubKey `json:"pub_key"` + VotingPower int64 `json:"voting_power"` + ProTxHash ProTxHash `json:"pro_tx_hash"` + NodeAddress dashtypes.ValidatorAddress `json:"address"` ProposerPriority int64 `json:"proposer_priority"` } @@ -42,18 +43,28 @@ func NewTestRemoveValidatorGeneratedFromProTxHash(proTxHash crypto.ProTxHash) *V } func NewValidatorDefaultVotingPower(pubKey crypto.PubKey, proTxHash []byte) *Validator { - return NewValidator(pubKey, DefaultDashVotingPower, proTxHash) + return NewValidator(pubKey, DefaultDashVotingPower, proTxHash, "") } // NewValidator returns a new validator with the given pubkey and voting power. -func NewValidator(pubKey crypto.PubKey, votingPower int64, proTxHash []byte) *Validator { - val := &Validator{ +func NewValidator(pubKey crypto.PubKey, votingPower int64, proTxHash []byte, address string) *Validator { + var ( + addr dashtypes.ValidatorAddress + err error + ) + if address != "" { + addr, err = dashtypes.ParseValidatorAddress(address) + if err != nil { + panic(err.Error()) + } + } + return &Validator{ PubKey: pubKey, VotingPower: votingPower, ProposerPriority: 0, ProTxHash: proTxHash, + NodeAddress: addr, } - return val } // ValidateBasic performs basic validation. @@ -74,6 +85,12 @@ func (v *Validator) ValidateBasic() error { return fmt.Errorf("validator proTxHash is the wrong size: %v", len(v.ProTxHash)) } + if !v.NodeAddress.Zero() { + if err := v.NodeAddress.Validate(); err != nil { + return fmt.Errorf("validator node address is invalid: %w", err) + } + } + return nil } @@ -125,15 +142,17 @@ func (v *Validator) CompareProposerPriority(other *Validator) *Validator { // 2. public key // 3. voting power // 4. proposer priority +// 5. node address func (v *Validator) String() string { if v == nil { return "nil-Validator" } - return fmt.Sprintf("Validator{%v %v VP:%v A:%v}", + return fmt.Sprintf("Validator{%v %v VP:%v A:%v N:%s}", v.ProTxHash, v.PubKey, v.VotingPower, - v.ProposerPriority) + v.ProposerPriority, + v.NodeAddress.String()) } func (v *Validator) ShortStringBasic() string { @@ -200,6 +219,7 @@ func (v *Validator) ToProto() (*tmproto.Validator, error) { } vp.PubKey = &pk } + vp.NodeAddress = v.NodeAddress.String() return &vp, nil } @@ -216,12 +236,17 @@ func ValidatorFromProto(vp *tmproto.Validator) (*Validator, error) { v.ProposerPriority = vp.GetProposerPriority() v.ProTxHash = vp.ProTxHash + var err error if vp.PubKey != nil && vp.PubKey.Sum != nil { - pk, err := ce.PubKeyFromProto(*vp.PubKey) - if err != nil { + if v.PubKey, err = ce.PubKeyFromProto(*vp.PubKey); err != nil { + return nil, err + } + } + + if vp.NodeAddress != "" { + if v.NodeAddress, err = dashtypes.ParseValidatorAddress(vp.NodeAddress); err != nil { return nil, err } - v.PubKey = pk } return v, nil diff --git a/types/validator_set.go b/types/validator_set.go index cad382eac6..585074ac56 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -10,13 +10,12 @@ import ( "strings" "github.com/dashevo/dashd-go/btcjson" - "github.com/tendermint/tendermint/crypto/merkle" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/bls12381" cryptoenc "github.com/tendermint/tendermint/crypto/encoding" - + "github.com/tendermint/tendermint/crypto/merkle" + dashtypes "github.com/tendermint/tendermint/dash/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) @@ -524,6 +523,7 @@ func (vals *ValidatorSet) QuorumVotingPower() int64 { } // QuorumVotingThresholdPower returns the threshold power of the voting power of the quorum if all the members existed. +// Voting is considered successful when voting power is at or above this threshold. func (vals *ValidatorSet) QuorumVotingThresholdPower() int64 { return int64(vals.QuorumTypeThresholdCount()) * DefaultDashVotingPower } @@ -921,7 +921,7 @@ func (vals *ValidatorSet) CommitSignIds(chainID string, commit *Commit) ([]byte, blockSignID := commit.CanonicalVoteVerifySignID(chainID, vals.QuorumType, vals.QuorumHash) - stateSignID := commit.CanonicalVoteStateSignID(chainID, vals.QuorumType, vals.QuorumHash) + stateSignID := commit.StateID.SignID(chainID, vals.QuorumType, vals.QuorumHash) return blockSignID, stateSignID } @@ -959,12 +959,13 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, stateID canonicalVoteBlockSignBytes, blockSignID, commit, vals.QuorumType, vals.QuorumHash, vals.ThresholdPublicKey) } - stateSignID := commit.CanonicalVoteStateSignID(chainID, vals.QuorumType, vals.QuorumHash) + stateSignID := commit.StateID.SignID(chainID, vals.QuorumType, vals.QuorumHash) if !vals.ThresholdPublicKey.VerifySignatureDigest(stateSignID, commit.ThresholdStateSignature) { - canonicalVoteStateSignBytes := commit.CanonicalVoteStateSignBytes(chainID) + commit.StateID.SignBytes(chainID) + stateSignBytes := commit.StateID.SignBytes(chainID) return fmt.Errorf("incorrect threshold state signature bytes: %X commit: %v valQuorumHash %X", - canonicalVoteStateSignBytes, commit, vals.QuorumHash) + stateSignBytes, commit, vals.QuorumHash) } return nil @@ -1010,8 +1011,9 @@ func (e ErrNotEnoughVotingPowerSigned) Error() string { func (vals *ValidatorSet) ABCIEquivalentValidatorUpdates() *abci.ValidatorSetUpdate { var valUpdates []abci.ValidatorUpdate for i := 0; i < len(vals.Validators); i++ { + valUpdate := TM2PB.NewValidatorUpdate(vals.Validators[i].PubKey, DefaultDashVotingPower, - vals.Validators[i].ProTxHash) + vals.Validators[i].ProTxHash, vals.Validators[i].NodeAddress) valUpdates = append(valUpdates, valUpdate) } abciThresholdPublicKey, err := cryptoenc.PubKeyToProto(vals.ThresholdPublicKey) @@ -1319,6 +1321,7 @@ func GenerateTestValidatorSetWithProTxHashes( privateKeys[i].PubKey(), originalPowerMap[string(orderedProTxHashes[i])], orderedProTxHashes[i], + "", ) } @@ -1534,6 +1537,7 @@ func ValidatorUpdatesRegenerateOnProTxHashes(proTxHashes []crypto.ProTxHash) abc privateKeys[i].PubKey(), DefaultDashVotingPower, orderedProTxHashes[i], + dashtypes.ValidatorAddress{}, ) valUpdates = append(valUpdates, valUpdate) } diff --git a/types/validator_set_test.go b/types/validator_set_test.go index ae93e8d1c6..e2477f8931 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -558,9 +558,12 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) { vote := examplePrecommit() vote.ValidatorProTxHash = proTxHash v := vote.ToProto() + + stateID := RandStateID().WithHeight(v.Height - 1) + blockSig, err := privKey.SignDigest(VoteBlockSignID(chainID, v, btcjson.LLMQType_5_60, quorumHash)) require.NoError(t, err) - stateSig, err := privKey.SignDigest(VoteStateSignID(chainID, v, btcjson.LLMQType_5_60, quorumHash)) + stateSig, err := privKey.SignDigest(stateID.SignID(chainID, btcjson.LLMQType_5_60, quorumHash)) require.NoError(t, err) vote.BlockSignature = blockSig vote.StateSignature = stateSig @@ -568,7 +571,7 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) { commit := NewCommit(vote.Height, vote.Round, vote.BlockID, - vote.StateID, + stateID, quorumHash, vote.BlockSignature, vote.StateSignature, @@ -577,7 +580,7 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) { vote2 := *vote blockSig2, err := privKey.SignDigest(VoteBlockSignBytes("EpsilonEridani", v)) require.NoError(t, err) - stateSig2, err := privKey.SignDigest(VoteStateSignBytes("EpsilonEridani", v)) + stateSig2, err := privKey.SignDigest(stateID.SignBytes("EpsilonEridani")) require.NoError(t, err) vote2.BlockSignature = blockSig2 vote2.StateSignature = stateSig2 @@ -591,21 +594,21 @@ func TestValidatorSet_VerifyCommit_All(t *testing.T) { commit *Commit expErr bool }{ - {"good", chainID, vote.BlockID, vote.StateID, vote.Height, commit, false}, + {"good", chainID, vote.BlockID, stateID, vote.Height, commit, false}, - {"incorrect threshold block signature", "EpsilonEridani", vote.BlockID, vote.StateID, vote.Height, commit, true}, - {"wrong block ID", chainID, makeBlockIDRandom(), vote.StateID, vote.Height, commit, true}, - {"wrong height", chainID, vote.BlockID, vote.StateID, vote.Height - 1, commit, true}, + {"incorrect threshold block signature", "EpsilonEridani", vote.BlockID, stateID, vote.Height, commit, true}, + {"wrong block ID", chainID, makeBlockIDRandom(), stateID, vote.Height, commit, true}, + {"wrong height", chainID, vote.BlockID, stateID, vote.Height - 1, commit, true}, - {"incorrect threshold block signature", chainID, vote.BlockID, vote.StateID, vote.Height, - NewCommit(vote.Height, vote.Round, vote.BlockID, vote.StateID, quorumHash, nil, nil), true}, + {"incorrect threshold block signature", chainID, vote.BlockID, stateID, vote.Height, + NewCommit(vote.Height, vote.Round, vote.BlockID, stateID, quorumHash, nil, nil), true}, - {"incorrect threshold state signature", chainID, vote.BlockID, vote.StateID, vote.Height, - NewCommit(vote.Height, vote.Round, vote.BlockID, vote.StateID, + {"incorrect threshold state signature", chainID, vote.BlockID, stateID, vote.Height, + NewCommit(vote.Height, vote.Round, vote.BlockID, stateID, quorumHash, vote.BlockSignature, nil), true}, - {"incorrect threshold block signature", chainID, vote.BlockID, vote.StateID, vote.Height, - NewCommit(vote.Height, vote.Round, vote.BlockID, vote.StateID, quorumHash, vote2.BlockSignature, vote2.StateSignature), true}, + {"incorrect threshold block signature", chainID, vote.BlockID, stateID, vote.Height, + NewCommit(vote.Height, vote.Round, vote.BlockID, stateID, quorumHash, vote2.BlockSignature, vote2.StateSignature), true}, } for _, tc := range testCases { @@ -628,17 +631,17 @@ func TestValidatorSet_VerifyCommit_CheckThresholdSignatures(t *testing.T) { chainID = "test_chain_id" h = int64(3) blockID = makeBlockIDRandom() - stateID = makeStateIDRandom() + stateID = RandStateID().WithHeight(h - 1) ) - voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4) + voteSet, valSet, vals := randVoteSet(h, 0, tmproto.PrecommitType, 4, stateID) commit, err := MakeCommit(blockID, stateID, h, 0, voteSet, vals) require.NoError(t, err) // malleate threshold sigs signature vote := voteSet.GetByIndex(3) v := vote.ToProto() - err = vals[3].SignVote("CentaurusA", valSet.QuorumType, valSet.QuorumHash, v, nil) + err = vals[3].SignVote("CentaurusA", valSet.QuorumType, valSet.QuorumHash, v, stateID, nil) require.NoError(t, err) commit.ThresholdBlockSignature = v.BlockSignature commit.ThresholdStateSignature = v.StateSignature diff --git a/types/validator_test.go b/types/validator_test.go index 08ea35811d..b323e545e2 100644 --- a/types/validator_test.go +++ b/types/validator_test.go @@ -1,9 +1,12 @@ package types import ( + "fmt" "testing" "github.com/tendermint/tendermint/crypto" + dashtypes "github.com/tendermint/tendermint/dash/types" + "github.com/tendermint/tendermint/p2p" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -71,7 +74,7 @@ func TestValidatorValidateBasic(t *testing.T) { msg: "no error", }, { - val: NewValidator(pubKey, -1, priv.ProTxHash), + val: NewValidator(pubKey, -1, priv.ProTxHash, ""), err: true, msg: "validator has negative voting power", }, @@ -107,3 +110,27 @@ func TestValidatorValidateBasic(t *testing.T) { }) } } + +func TestNewValidator(t *testing.T) { + quorumHash := crypto.RandQuorumHash() + priv := NewMockPVForQuorum(quorumHash) + pubKey, err := priv.GetPubKey(quorumHash) + nodeID := p2p.PubKeyToID(pubKey) + proTxHash := crypto.RandProTxHash() + require.NoError(t, err) + + validator := NewValidator(pubKey, DefaultDashVotingPower, proTxHash, + fmt.Sprintf("tcp://%s@127.0.0.1:12345", nodeID)) + require.NotNil(t, validator) + newNodeID := validator.NodeAddress.NodeID + assert.Equal(t, nodeID, newNodeID) + + validator = NewValidator(pubKey, DefaultDashVotingPower, proTxHash, "127.0.0.1:23456") + require.NotNil(t, validator) + assert.EqualValues(t, "127.0.0.1", validator.NodeAddress.Hostname) + assert.EqualValues(t, 23456, validator.NodeAddress.Port) + assert.EqualValues(t, "tcp", validator.NodeAddress.Protocol) + newNodeID, err = dashtypes.NewTCPNodeIDResolver().Resolve(validator.NodeAddress) + assert.Contains(t, err.Error(), "connection refused") + assert.Zero(t, newNodeID) +} diff --git a/types/vote.go b/types/vote.go index 496f5fb676..07d64a894f 100644 --- a/types/vote.go +++ b/types/vote.go @@ -74,7 +74,6 @@ type Vote struct { Height int64 `json:"height"` Round int32 `json:"round"` // assume there will not be greater than 2^32 rounds BlockID BlockID `json:"block_id"` // zero if vote is nil. - StateID StateID `json:"state_id"` ValidatorProTxHash ProTxHash `json:"validator_pro_tx_hash"` ValidatorIndex int32 `json:"validator_index"` BlockSignature []byte `json:"block_signature"` @@ -117,36 +116,6 @@ func VoteBlockSignID(chainID string, vote *tmproto.Vote, quorumType btcjson.LLMQ return blockSignID } -// VoteStateSignBytes returns the 40 bytes of the height + last state app hash. -func VoteStateSignBytes(chainID string, vote *tmproto.Vote) []byte { - bz := make([]byte, 8) - binary.LittleEndian.PutUint64(bz, uint64(vote.Height-1)) - bz = append(bz, vote.StateID.LastAppHash...) - return bz -} - -// VoteStateSignID returns signID that should be signed for the state -func VoteStateSignID(chainID string, vote *tmproto.Vote, quorumType btcjson.LLMQType, quorumHash []byte) []byte { - stateSignBytes := VoteStateSignBytes(chainID, vote) - - if stateSignBytes == nil { - return nil - } - - stateMessageHash := crypto.Sha256(stateSignBytes) - - stateRequestID := VoteStateRequestIDProto(vote) - - stateSignID := crypto.SignID( - quorumType, - bls12381.ReverseBytes(quorumHash), - bls12381.ReverseBytes(stateRequestID), - bls12381.ReverseBytes(stateMessageHash), - ) - - return stateSignID -} - func (vote *Vote) Copy() *Vote { voteCopy := *vote return &voteCopy @@ -178,7 +147,7 @@ func (vote *Vote) String() string { panic("Unknown vote type") } - return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X %X %X}", + return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X %X}", vote.ValidatorIndex, tmbytes.Fingerprint(vote.ValidatorProTxHash), vote.Height, @@ -187,7 +156,6 @@ func (vote *Vote) String() string { typeString, tmbytes.Fingerprint(vote.BlockID.Hash), tmbytes.Fingerprint(vote.BlockSignature), - tmbytes.Fingerprint(vote.StateID.LastAppHash), tmbytes.Fingerprint(vote.StateSignature), ) } @@ -218,30 +186,9 @@ func VoteBlockRequestIDProto(vote *tmproto.Vote) []byte { return crypto.Sha256(requestIDMessage) } -func VoteStateRequestID(vote *Vote) []byte { - requestIDMessage := []byte("dpsvote") - heightByteArray := make([]byte, 8) - // We use height - 1 because we are signing the state at the end of the execution of the previous block - binary.LittleEndian.PutUint64(heightByteArray, uint64(vote.Height)-1) - - requestIDMessage = append(requestIDMessage, heightByteArray...) - - return crypto.Sha256(requestIDMessage) -} - -func VoteStateRequestIDProto(vote *tmproto.Vote) []byte { - requestIDMessage := []byte("dpsvote") - heightByteArray := make([]byte, 8) - binary.LittleEndian.PutUint64(heightByteArray, uint64(vote.Height)-1) - - requestIDMessage = append(requestIDMessage, heightByteArray...) - - return crypto.Sha256(requestIDMessage) -} - func (vote *Vote) Verify( chainID string, quorumType btcjson.LLMQType, quorumHash []byte, - pubKey crypto.PubKey, proTxHash crypto.ProTxHash) ([]byte, []byte, error) { + pubKey crypto.PubKey, proTxHash crypto.ProTxHash, stateID StateID) ([]byte, []byte, error) { if !bytes.Equal(proTxHash, vote.ValidatorProTxHash) { return nil, nil, ErrVoteInvalidValidatorProTxHash } @@ -274,10 +221,10 @@ func (vote *Vote) Verify( stateSignID := []byte(nil) // we must verify the stateID but only if the blockID isn't nil if vote.BlockID.Hash != nil { - voteStateSignBytes := VoteStateSignBytes(chainID, v) + voteStateSignBytes := stateID.SignBytes(chainID) stateMessageHash := crypto.Sha256(voteStateSignBytes) - stateRequestID := VoteStateRequestID(vote) + stateRequestID := stateID.SignRequestID() stateSignID = crypto.SignID( quorumType, bls12381.ReverseBytes(quorumHash), bls12381.ReverseBytes(stateRequestID), @@ -316,10 +263,6 @@ func (vote *Vote) ValidateBasic() error { return fmt.Errorf("wrong BlockID: %v", err) } - if err := vote.StateID.ValidateBasic(); err != nil { - return fmt.Errorf("wrong StateID: %v", err) - } - // BlockID.ValidateBasic would not err if we for instance have an empty hash but a // non-empty PartsSetHeader: if !vote.BlockID.IsZero() && !vote.BlockID.IsComplete() { @@ -367,7 +310,6 @@ func (vote *Vote) ToProto() *tmproto.Vote { Height: vote.Height, Round: vote.Round, BlockID: vote.BlockID.ToProto(), - StateID: vote.StateID.ToProto(), ValidatorProTxHash: vote.ValidatorProTxHash, ValidatorIndex: vote.ValidatorIndex, BlockSignature: vote.BlockSignature, @@ -387,17 +329,11 @@ func VoteFromProto(pv *tmproto.Vote) (*Vote, error) { return nil, err } - stateID, err := StateIDFromProto(&pv.StateID) - if err != nil { - return nil, err - } - vote := new(Vote) vote.Type = pv.Type vote.Height = pv.Height vote.Round = pv.Round vote.BlockID = *blockID - vote.StateID = *stateID vote.ValidatorProTxHash = pv.ValidatorProTxHash vote.ValidatorIndex = pv.ValidatorIndex vote.BlockSignature = pv.BlockSignature diff --git a/types/vote_set.go b/types/vote_set.go index 421383cc85..6d9086398f 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -64,6 +64,7 @@ type P2PID string type VoteSet struct { chainID string height int64 + stateID StateID // ID of state for which this voting is executed round int32 signedMsgType tmproto.SignedMsgType valSet *ValidatorSet @@ -73,7 +74,6 @@ type VoteSet struct { votes []*Vote // Primary votes to share sum int64 // Sum of voting power for seen votes, discounting conflicts maj23 *BlockID // First 2/3 majority seen - stateMaj23 *StateID // If a 2/3 majority is seen, this is the stateID thresholdBlockSig []byte // If a 2/3 majority is seen, recover the block sig thresholdStateSig []byte // If a 2/3 majority is seen, recover the state sig votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes @@ -82,16 +82,18 @@ type VoteSet struct { // NewVoteSet constructs a new VoteSet struct used to accumulate votes for given height/round. func NewVoteSet(chainID string, height int64, round int32, - signedMsgType tmproto.SignedMsgType, valSet *ValidatorSet) *VoteSet { + signedMsgType tmproto.SignedMsgType, valSet *ValidatorSet, stateID StateID) *VoteSet { if height == 0 { panic("Cannot make VoteSet for height == 0, doesn't make sense.") } if !valSet.HasPublicKeys { panic("Cannot make VoteSet when the validator set doesn't have public keys.") } + return &VoteSet{ chainID: chainID, height: height, + stateID: stateID, round: round, signedMsgType: signedMsgType, valSet: valSet, @@ -99,7 +101,6 @@ func NewVoteSet(chainID string, height int64, round int32, votes: make([]*Vote, valSet.Size()), sum: 0, maj23: nil, - stateMaj23: nil, votesByBlock: make(map[string]*blockVotes, valSet.Size()), peerMaj23s: make(map[P2PID]BlockID), } @@ -214,11 +215,13 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { } // Check signature. + signID, stateSignID, err := vote.Verify( - voteSet.chainID, voteSet.valSet.QuorumType, voteSet.valSet.QuorumHash, val.PubKey, val.ProTxHash) + voteSet.chainID, voteSet.valSet.QuorumType, voteSet.valSet.QuorumHash, val.PubKey, val.ProTxHash, voteSet.stateID) if err != nil { - return false, fmt.Errorf("failed to verify vote with ChainID %s and PubKey %s ProTxHash %s: %w", - voteSet.chainID, val.PubKey, val.ProTxHash, err) + return false, ErrInvalidVoteSignature( + fmt.Errorf("failed to verify vote with ChainID %s and PubKey %s ProTxHash %s: %w", + voteSet.chainID, val.PubKey, val.ProTxHash, err)) } // Add vote and get conflicting vote if any. @@ -311,11 +314,9 @@ func (voteSet *VoteSet) addVerifiedVote( // Only consider the first quorum reached if voteSet.maj23 == nil { maj23BlockID := vote.BlockID - stateMaj23StateID := vote.StateID // fmt.Printf("vote majority reached at height %d (%d/%d) quorum size %d\n", // voteSet.height, voteSet.round, voteSet.signedMsgType, quorum) voteSet.maj23 = &maj23BlockID - voteSet.stateMaj23 = &stateMaj23StateID if voteSet.signedMsgType == tmproto.PrecommitType { if len(votesByBlock.votes) > 1 { err := voteSet.recoverThresholdSigsAndVerify(votesByBlock, signID, stateSignID) @@ -506,13 +507,14 @@ func (voteSet *VoteSet) IsCommit() bool { return voteSet.maj23 != nil } +// HasTwoThirdsAny returns true if we are above voting threshold, regardless of the block id voted func (voteSet *VoteSet) HasTwoThirdsAny() bool { if voteSet == nil { return false } voteSet.mtx.Lock() defer voteSet.mtx.Unlock() - return voteSet.sum > voteSet.valSet.TotalVotingPower()*2/3 + return voteSet.sum >= voteSet.valSet.QuorumVotingThresholdPower() } func (voteSet *VoteSet) HasAll() bool { @@ -701,10 +703,6 @@ func (voteSet *VoteSet) MakeCommit() *Commit { panic("Cannot MakeCommit() unless a blockhash has +2/3") } - if voteSet.stateMaj23 == nil { - panic("Cannot MakeCommit() unless a stateMaj23 has been set") - } - if voteSet.thresholdBlockSig == nil { panic("Cannot MakeCommit() unless a thresholdBlockSig has been created") } @@ -717,7 +715,7 @@ func (voteSet *VoteSet) MakeCommit() *Commit { voteSet.GetHeight(), voteSet.GetRound(), *voteSet.maj23, - *voteSet.stateMaj23, + voteSet.stateID, voteSet.valSet.QuorumHash, voteSet.thresholdBlockSig, voteSet.thresholdStateSig, diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 0c78baf2d3..982245319b 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -2,19 +2,24 @@ package types import ( "bytes" + "math" + "sort" + "strconv" "testing" + "github.com/dashevo/dashd-go/btcjson" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/bls12381" tmrand "github.com/tendermint/tendermint/libs/rand" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" ) func TestVoteSet_AddVote_Good(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10) + voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, + RandStateID().WithHeight(height-1)) val0 := privValidators[0] val0ProTxHash, err := val0.GetProTxHash() @@ -32,7 +37,6 @@ func TestVoteSet_AddVote_Good(t *testing.T) { Round: round, Type: tmproto.PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, - StateID: StateID{LastAppHash: nil}, } _, err = signAddVote(val0, vote, voteSet) require.NoError(t, err) @@ -45,7 +49,8 @@ func TestVoteSet_AddVote_Good(t *testing.T) { func TestVoteSet_AddVote_Bad(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10) + voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, + RandStateID().WithHeight(height-1)) voteProto := &Vote{ ValidatorProTxHash: nil, @@ -54,7 +59,6 @@ func TestVoteSet_AddVote_Bad(t *testing.T) { Round: round, Type: tmproto.PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, - StateID: StateID{LastAppHash: nil}, } // val0 votes for nil. @@ -113,9 +117,82 @@ func TestVoteSet_AddVote_Bad(t *testing.T) { } } +// TestVoteSet_AddVote_StateID checks if state signature is verified correctly when adding votes to voteSet +func TestVoteSet_AddVote_StateID(t *testing.T) { + height, round := int64(10), int32(0) + + randStateID1 := RandStateID().WithHeight(height - 1) + randStateID2 := RandStateID().WithHeight(height - 1) + + testCases := []struct { + name string + voteSetStateID StateID + wrongStateID StateID + shouldFail bool + }{ + {"correct", randStateID1, randStateID1, false}, + {"wrong apphash", randStateID1, randStateID2, true}, + {"too low height", randStateID1, randStateID1.WithHeight(height - 5), true}, + {"too high height", randStateID1, randStateID1.WithHeight(height + 5), true}, + } + //nolint:scopelint + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, + tc.voteSetStateID) + + val0 := privValidators[0] + val0ProTxHash, err := val0.GetProTxHash() + require.NoError(t, err) + + val1 := privValidators[1] + val1ProTxHash, err := val1.GetProTxHash() + require.NoError(t, err) + + assert.Nil(t, voteSet.GetByProTxHash(val0ProTxHash)) + assert.False(t, voteSet.BitArray().GetIndex(0)) + majorityBlockID, ok := voteSet.TwoThirdsMajority() + assert.False(t, ok || !majorityBlockID.IsZero(), "there should be no 2/3 majority") + blockID := randBlockID() + vote1 := &Vote{ + ValidatorProTxHash: val0ProTxHash, + ValidatorIndex: 0, // since privValidators are in order + Height: height, + Round: round, + Type: tmproto.PrevoteType, + BlockID: blockID, + } + _, err = signAddVote(val0, vote1, voteSet) + require.NoError(t, err) + + vote2 := &Vote{ + ValidatorProTxHash: val1ProTxHash, + ValidatorIndex: 1, // since privValidators are in order + Height: height, + Round: round, + Type: tmproto.PrevoteType, + BlockID: blockID, + } + _, err = signAddVoteForStateID(val1, vote2, voteSet, tc.wrongStateID) + if tc.shouldFail { + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid state signature") + } else { + require.NoError(t, err) + } + + assert.NotNil(t, voteSet.GetByProTxHash(val0ProTxHash)) + assert.True(t, voteSet.BitArray().GetIndex(0)) + majorityBlockID, ok = voteSet.TwoThirdsMajority() + assert.False(t, ok || !majorityBlockID.IsZero(), "there should be no 2/3 majority") + }) + } +} + func TestVoteSet_2_3Majority(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10) + voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 10, + RandStateID().WithHeight(height-1)) voteProto := &Vote{ ValidatorProTxHash: nil, // NOTE: must fill in @@ -124,7 +201,6 @@ func TestVoteSet_2_3Majority(t *testing.T) { Round: round, Type: tmproto.PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, - StateID: StateID{LastAppHash: nil}, } // 6 out of 10 voted for nil. for i := int32(0); i < 6; i++ { @@ -162,10 +238,10 @@ func TestVoteSet_2_3Majority(t *testing.T) { func TestVoteSet_2_3MajorityRedux(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 100) + voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 100, + RandStateID().WithHeight(height-1)) blockHash := crypto.CRandBytes(32) - stateHash := crypto.CRandBytes(32) blockPartsTotal := uint32(123) blockPartSetHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)} @@ -176,7 +252,6 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) { Round: round, Type: tmproto.PrevoteType, BlockID: BlockID{blockHash, blockPartSetHeader}, - StateID: StateID{stateHash}, } // 66 out of 100 voted for nil. @@ -256,7 +331,8 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) { func TestVoteSet_Conflicts(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 4) + voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrevoteType, 4, + RandStateID().WithHeight(height-1)) blockHash1 := tmrand.Bytes(32) blockHash2 := tmrand.Bytes(32) @@ -267,7 +343,6 @@ func TestVoteSet_Conflicts(t *testing.T) { Round: round, Type: tmproto.PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, - StateID: StateID{nil}, } val0ProTxHash, err := privValidators[0].GetProTxHash() @@ -381,9 +456,9 @@ func TestVoteSet_Conflicts(t *testing.T) { func TestVoteSet_MakeCommit(t *testing.T) { height, round := int64(1), int32(0) - voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrecommitType, 10) + voteSet, _, privValidators := randVoteSet(height, round, tmproto.PrecommitType, 10, + RandStateID().WithHeight(height-1)) blockHash, blockPartSetHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)} - stateHash := crypto.CRandBytes(32) voteProto := &Vote{ ValidatorProTxHash: nil, @@ -392,7 +467,6 @@ func TestVoteSet_MakeCommit(t *testing.T) { Round: round, Type: tmproto.PrecommitType, BlockID: BlockID{blockHash, blockPartSetHeader}, - StateID: StateID{stateHash}, } // 6 out of 10 voted for some block. @@ -449,15 +523,153 @@ func TestVoteSet_MakeCommit(t *testing.T) { } } +func TestVoteSet_LLMQType_50_60(t *testing.T) { + const ( + height = int64(1) + round = int32(0) + ) + testCases := []struct { + llmqType btcjson.LLMQType + numValidators int + threshold int + }{ + { + llmqType: btcjson.LLMQType(0), // "tendermint" algorithm + numValidators: 40, + threshold: int(math.Floor(2.0/3.0*40)) + 1, + }, + { + llmqType: btcjson.LLMQType_50_60, + numValidators: 35, + threshold: 30, + }, + { + llmqType: btcjson.LLMQType(0), + numValidators: 50, + threshold: 34, + }, + { + llmqType: btcjson.LLMQType_50_60, + numValidators: 50, + threshold: 30, + }, + } + + for ti, tt := range testCases { + name := strconv.Itoa(ti) + t.Run(name, func(t *testing.T) { + voteSet, valSet, privValidators := randVoteSetWithLLMQType( + height, + round, + tmproto.PrevoteType, + tt.numValidators, + RandStateID().WithHeight(height-1), + tt.llmqType, + tt.threshold, + ) + assert.EqualValues(t, tt.threshold, valSet.QuorumTypeThresholdCount()) + assert.GreaterOrEqual(t, len(privValidators), tt.threshold+3, + "need at least %d validators", tt.threshold+3) + + blockHash := crypto.CRandBytes(32) + blockPartSetHeader := PartSetHeader{uint32(123), crypto.CRandBytes(32)} + votedBlock := BlockID{blockHash, blockPartSetHeader} + + // below threshold + for i := 0; i < tt.threshold-1; i++ { + blockMaj, anyMaj := castVote(t, votedBlock, height, round, privValidators, int32(i), voteSet) + assert.False(t, blockMaj, "no block majority expected here: i=%d, threshold=%d", i, tt.threshold) + assert.False(t, anyMaj, "no 'any' majority expected here: i=%d, threshold=%d", i, tt.threshold) + } + + // we add null vote + blockMaj, anyMaj := castVote(t, BlockID{}, height, round, privValidators, int32(tt.threshold), voteSet) + assert.False(t, blockMaj, "no block majority expected after nil vote") + assert.True(t, anyMaj, "'any' majority expected after nil vote at threshold") + + // at threshold + blockMaj, anyMaj = castVote(t, votedBlock, height, round, privValidators, int32(tt.threshold+1), voteSet) + assert.True(t, blockMaj, "block majority expected") + assert.True(t, anyMaj, "'any' majority expected") + + // above threshold + blockMaj, anyMaj = castVote(t, votedBlock, height, round, privValidators, int32(tt.threshold+2), voteSet) + assert.True(t, blockMaj, "block majority expected") + assert.True(t, anyMaj, "'any' majority expected") + }) + } +} + +func castVote( + t *testing.T, + blockID BlockID, + height int64, + round int32, + privValidators []PrivValidator, + validatorID int32, + voteSet *VoteSet, +) (twoThirdsMajority, hasTwoThirdsAny bool) { + voteProto := &Vote{ + ValidatorProTxHash: nil, // NOTE: must fill in + ValidatorIndex: -1, // NOTE: must fill in + Height: height, + Round: round, + Type: tmproto.PrevoteType, + BlockID: blockID, + } + proTxHash, err := privValidators[validatorID].GetProTxHash() + require.NoError(t, err) + vote := withValidator(voteProto, proTxHash, validatorID) + signed, err := signAddVote(privValidators[validatorID], vote, voteSet) + require.True(t, signed) + require.NoError(t, err) + + majorityBlock, twoThirdsMajority := voteSet.TwoThirdsMajority() + assert.EqualValues(t, twoThirdsMajority, !majorityBlock.IsZero()) + return twoThirdsMajority, voteSet.HasTwoThirdsAny() +} + // NOTE: privValidators are in order func randVoteSet( height int64, round int32, signedMsgType tmproto.SignedMsgType, numValidators int, + stateID StateID, ) (*VoteSet, *ValidatorSet, []PrivValidator) { valSet, privValidators := GenerateValidatorSet(numValidators) - return NewVoteSet("test_chain_id", height, round, signedMsgType, valSet), valSet, privValidators + return NewVoteSet("test_chain_id", height, round, signedMsgType, valSet, stateID), valSet, privValidators +} + +func randVoteSetWithLLMQType( + height int64, + round int32, + signedMsgType tmproto.SignedMsgType, + numValidators int, + stateID StateID, + llmqType btcjson.LLMQType, + threshold int, +) (*VoteSet, *ValidatorSet, []PrivValidator) { + var ( + valz = make([]*Validator, numValidators) + privValidators = make([]PrivValidator, numValidators) + ) + privateKeys, proTxHashes, thresholdPublicKey := bls12381.CreatePrivLLMQData(numValidators, threshold) + quorumHash := crypto.RandQuorumHash() + + for i := 0; i < numValidators; i++ { + privValidators[i] = NewMockPVWithParams(privateKeys[i], proTxHashes[i], quorumHash, + thresholdPublicKey, false, false) + valz[i] = NewValidatorDefaultVotingPower(privateKeys[i].PubKey(), proTxHashes[i]) + } + + sort.Sort(PrivValidatorsByProTxHash(privValidators)) + + valSet := NewValidatorSet(valz, thresholdPublicKey, llmqType, quorumHash, true) + voteSet := NewVoteSet("test_chain_id", height, round, tmproto.PrevoteType, + valSet, stateID) + + return voteSet, valSet, privValidators } // Convenience: Return new vote with different validator address/index diff --git a/types/vote_test.go b/types/vote_test.go index e03b880672..6cf2b154f9 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -37,9 +37,6 @@ func exampleVote(t byte) *Vote { Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")), }, }, - StateID: StateID{ - LastAppHash: tmhash.Sum([]byte("lastAppState_hash")), - }, ValidatorProTxHash: crypto.ProTxHashFromSeedBytes([]byte("validator_pro_tx_hash")), ValidatorIndex: 56789, } @@ -130,22 +127,24 @@ func TestVoteSignBytesTestVectors(t *testing.T) { func TestVoteStateSignBytesTestVectors(t *testing.T) { tests := []struct { chainID string - vote *Vote + height int64 + apphash []byte want []byte }{ 0: { - "", &Vote{Height: 1, StateID: StateID{ - LastAppHash: crypto.Sha256([]byte("hello")), - }}, + "", 1, []byte("12345678901234567890123456789012"), // NOTE: Height and Round are skipped here. This case needs to be considered while parsing. - []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0xf2, 0x4d, 0xba, 0x5f, 0xb0, 0xa3, 0xe, 0x26, 0xe8, - 0x3b, 0x2a, 0xc5, 0xb9, 0xe2, 0x9e, 0x1b, 0x16, 0x1e, 0x5c, 0x1f, 0xa7, 0x42, 0x5e, 0x73, 0x4, 0x33, 0x62, - 0x93, 0x8b, 0x98, 0x24}, + []byte{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32}, }, } for i, tc := range tests { - v := tc.vote.ToProto() - got := VoteStateSignBytes(tc.chainID, v) + sid := StateID{ + Height: tc.height, + LastAppHash: tc.apphash, + } + got := sid.SignBytes(tc.chainID) assert.Equal(t, len(tc.want), len(got), "test case #%v: got unexpected sign bytes length for Vote.", i) assert.Equal(t, tc.want, got, "test case #%v: got unexpected sign bytes for Vote.", i) } @@ -169,12 +168,13 @@ func TestVoteVerifySignature(t *testing.T) { vote := examplePrecommit() v := vote.ToProto() + stateID := RandStateID().WithHeight(vote.Height - 1) quorumType := btcjson.LLMQType_5_60 signID := VoteBlockSignID("test_chain_id", v, quorumType, quorumHash) - signStateID := VoteStateSignID("test_chain_id", v, quorumType, quorumHash) + signStateID := stateID.SignID("test_chain_id", quorumType, quorumHash) // sign it - err = privVal.SignVote("test_chain_id", quorumType, quorumHash, v, nil) + err = privVal.SignVote("test_chain_id", quorumType, quorumHash, v, stateID, nil) require.NoError(t, err) // verify the same vote @@ -194,7 +194,7 @@ func TestVoteVerifySignature(t *testing.T) { // verify the transmitted vote newSignID := VoteBlockSignID("test_chain_id", precommit, quorumType, quorumHash) - newSignStateID := VoteStateSignID("test_chain_id", precommit, quorumType, quorumHash) + newSignStateID := stateID.SignID("test_chain_id", quorumType, quorumHash) require.Equal(t, string(signID), string(newSignID)) require.Equal(t, string(signStateID), string(newSignStateID)) valid = pubkey.VerifySignatureDigest(newSignID, precommit.BlockSignature) @@ -238,13 +238,15 @@ func TestVoteVerify(t *testing.T) { vote := examplePrevote() vote.ValidatorProTxHash = proTxHash - _, _, err = vote.Verify( - "test_chain_id", quorumType, quorumHash, bls12381.GenPrivKey().PubKey(), crypto.RandProTxHash()) + stateID := RandStateID().WithHeight(vote.Height - 1) + _, _, err = vote.Verify("test_chain_id", quorumType, quorumHash, bls12381.GenPrivKey().PubKey(), + crypto.RandProTxHash(), stateID) + if assert.Error(t, err) { assert.Equal(t, ErrVoteInvalidValidatorProTxHash, err) } - _, _, err = vote.Verify("test_chain_id", quorumType, quorumHash, pubkey, proTxHash) + _, _, err = vote.Verify("test_chain_id", quorumType, quorumHash, pubkey, proTxHash, stateID) if assert.Error(t, err) { assert.True( t, strings.HasPrefix(err.Error(), ErrVoteInvalidBlockSignature.Error()), @@ -254,13 +256,13 @@ func TestVoteVerify(t *testing.T) { func TestVoteString(t *testing.T) { str := examplePrecommit().String() - expected := `Vote{56789:959A8F5EF2BE 12345/02/SIGNED_MSG_TYPE_PRECOMMIT(Precommit) 8B01023386C3 000000000000 46E3D8DC8536 000000000000}` //nolint:lll //ignore line length for tests + expected := `Vote{56789:959A8F5EF2BE 12345/02/SIGNED_MSG_TYPE_PRECOMMIT(Precommit) 8B01023386C3 000000000000 000000000000}` //nolint:lll //ignore line length for tests if str != expected { t.Errorf("got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str) } str2 := examplePrevote().String() - expected = `Vote{56789:959A8F5EF2BE 12345/02/SIGNED_MSG_TYPE_PREVOTE(Prevote) 8B01023386C3 000000000000 46E3D8DC8536 000000000000}` //nolint:lll //ignore line length for tests + expected = `Vote{56789:959A8F5EF2BE 12345/02/SIGNED_MSG_TYPE_PREVOTE(Prevote) 8B01023386C3 000000000000 000000000000}` //nolint:lll //ignore line length for tests if str2 != expected { t.Errorf("got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str2) } @@ -291,7 +293,8 @@ func TestVoteValidateBasic(t *testing.T) { t.Run(tc.testName, func(t *testing.T) { vote := examplePrecommit() v := vote.ToProto() - err := privVal.SignVote("test_chain_id", 0, quorumHash, v, nil) + stateID := RandStateID().WithHeight(v.Height - 1) + err := privVal.SignVote("test_chain_id", 0, quorumHash, v, stateID, nil) vote.BlockSignature = v.BlockSignature vote.StateSignature = v.StateSignature require.NoError(t, err) @@ -306,7 +309,8 @@ func TestVoteProtobuf(t *testing.T) { privVal := NewMockPVForQuorum(quorumHash) vote := examplePrecommit() v := vote.ToProto() - err := privVal.SignVote("test_chain_id", 0, quorumHash, v, nil) + stateID := RandStateID().WithHeight(v.Height - 1) + err := privVal.SignVote("test_chain_id", 0, quorumHash, v, stateID, nil) vote.BlockSignature = v.BlockSignature vote.StateSignature = v.StateSignature require.NoError(t, err) diff --git a/version/version.go b/version/version.go index 8a61f29d9e..935e8bb75b 100644 --- a/version/version.go +++ b/version/version.go @@ -7,9 +7,9 @@ var ( const ( // TMVersionDefault is the used as the fallback version for Tenderdash // when not using git describe. It is formatted with semantic versioning. - TMVersionDefault = "0.6.0" + TMVersionDefault = "0.7.0" // ABCISemVer is the semantic version of the ABCI library - ABCISemVer = "0.17.0" + ABCISemVer = "0.18.0" ABCIVersion = ABCISemVer )