diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb5f765e0a4..734c6d5af5d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,10 +24,10 @@ jobs: goos: ["linux"] timeout-minutes: 5 steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - id: filter - uses: dorny/paths-filter@v4 + uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 with: filters: | code: @@ -38,7 +38,7 @@ jobs: - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV if: steps.filter.outputs.code == 'true' - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 if: steps.filter.outputs.code == 'true' with: go-version: ${{ env.GO_VERSION }} diff --git a/.github/workflows/check-generated.yml b/.github/workflows/check-generated.yml index e298717fd5c..7482961fc44 100644 --- a/.github/workflows/check-generated.yml +++ b/.github/workflows/check-generated.yml @@ -20,11 +20,11 @@ jobs: check-mocks-metrics: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ env.GO_VERSION }} @@ -48,13 +48,13 @@ jobs: check-proto: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 1 # we need a .git directory to run git diff - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ env.GO_VERSION }} diff --git a/.github/workflows/compat-rolling-upgrade.yml b/.github/workflows/compat-rolling-upgrade.yml new file mode 100644 index 00000000000..5ed2c87444d --- /dev/null +++ b/.github/workflows/compat-rolling-upgrade.yml @@ -0,0 +1,103 @@ +# Runs test/compat/rolling_upgrade.sh: a live bera-v1.x <-> bera-v0.40.x +# data-dir reuse and 4-validator rolling-upgrade test (built-in kvstore app, +# no beacon-kit). See test/compat/README.md. +# +# Runs on every pull request that touches Go code (like tests.yml and e2e.yml) +# and on pushes to the release branches. The bera-v1.x side is the commit +# pinned in test/compat/gen-v1x/go.mod; the bera-v0.40.x side is this checkout. + +name: compat-rolling-upgrade +on: + workflow_dispatch: + inputs: + soak_blocks: + description: "Blocks to soak at each stage (SOAK_BLOCKS)" + required: false + default: "25" + pull_request: + merge_group: + push: + branches: + - main + - bera-v[0-9]*.x + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + rolling-upgrade: + runs-on: ubuntu-latest + timeout-minutes: 25 + env: + WORKDIR: ${{ github.workspace }}/build/compat + steps: + # Check out the PR/branch head, which is the bera-v0.40.x side of the test. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + # Skip the whole job unless Go code, the compat suite, or this file changed. + - id: filter + uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 + with: + filters: | + code: + - '**/*.go' + - 'Makefile' + - 'go.*' + - 'test/compat/**' + - '.github/workflows/compat-rolling-upgrade.yml' + + # Read the Go version from the shared env file used by the other workflows. + - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV + if: steps.filter.outputs.code == 'true' + + # Install Go and cache modules for both this checkout and the bera-v1.x pin. + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + if: steps.filter.outputs.code == 'true' + with: + go-version: ${{ env.GO_VERSION }} + cache-dependency-path: | + go.sum + test/compat/gen-v1x/go.sum + + # Reverse direction: write this fork's outputs, check they match the committed file, verify them with the bera-v1.x code. + - name: Verify bera-v1.x reads bera-v0.40.x outputs + if: steps.filter.outputs.code == 'true' + run: | + mkdir -p "$WORKDIR" + BERA_COMPAT_WRITE_OUTPUTS="$WORKDIR/bera_v040_outputs.json" go test -tags bls12381 -count=1 -run TestWriteV040Outputs ./test/compat/ + cmp "$WORKDIR/bera_v040_outputs.json" test/compat/vectors/bera_v040_outputs.json \ + || { echo "::error::test/compat/vectors/bera_v040_outputs.json is stale, regenerate it (see test/compat/README.md)"; exit 1; } + cd test/compat/gen-v1x + go vet . + go run . -verify ../vectors/bera_v040_outputs.json + + # Build both binaries and run Phase A (data-dir reuse) and Phase B (rolling upgrade). + - name: Run rolling upgrade + if: steps.filter.outputs.code == 'true' + env: + SOAK_BLOCKS: ${{ github.event.inputs.soak_blocks || '25' }} + run: test/compat/rolling_upgrade.sh "$WORKDIR" + + # Always keep the per-stage report, pass or fail. + - name: Archive report + if: ${{ always() && steps.filter.outputs.code == 'true' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: compat-report + path: ${{ env.WORKDIR }}/report.md + if-no-files-found: ignore + retention-days: 14 + + # On failure also keep node logs and configs (binaries and data dirs excluded). + - name: Archive node logs and configs + if: ${{ failure() && steps.filter.outputs.code == 'true' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: compat-workdir + path: | + ${{ env.WORKDIR }}/**/*.log + ${{ env.WORKDIR }}/**/config/*.toml + ${{ env.WORKDIR }}/**/config/genesis.json + if-no-files-found: ignore + retention-days: 7 diff --git a/.github/workflows/docker-build-cometbft.yml b/.github/workflows/docker-build-cometbft.yml index 462832e76b7..c3252a4dd7a 100644 --- a/.github/workflows/docker-build-cometbft.yml +++ b/.github/workflows/docker-build-cometbft.yml @@ -60,13 +60,13 @@ jobs: TAGS=$(echo "${{ needs.vars.outputs.tags }}" | sed "s/[^,]*/&-${{ matrix.arch }}/g") echo "tags=${TAGS}" >> $GITHUB_OUTPUT - - uses: actions/checkout@v7 - - uses: docker/login-action@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - uses: docker/setup-buildx-action@v4 - - uses: docker/build-push-action@v7 + - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 id: build with: platforms: linux/${{ matrix.arch }} @@ -84,7 +84,7 @@ jobs: - vars - build-images steps: - - uses: docker/login-action@v4 + - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/docker-build-e2e-node.yml b/.github/workflows/docker-build-e2e-node.yml index b50ea0abd51..1963f418b13 100644 --- a/.github/workflows/docker-build-e2e-node.yml +++ b/.github/workflows/docker-build-e2e-node.yml @@ -60,13 +60,13 @@ jobs: TAGS=$(echo "${{ needs.vars.outputs.tags }}" | sed "s/[^,]*/&-${{ matrix.arch }}/g") echo "tags=${TAGS}" >> $GITHUB_OUTPUT - - uses: actions/checkout@v7 - - uses: docker/login-action@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - uses: docker/setup-buildx-action@v4 - - uses: docker/build-push-action@v7 + - uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 id: build with: platforms: linux/${{ matrix.arch }} @@ -84,7 +84,7 @@ jobs: - vars - build-images steps: - - uses: docker/login-action@v4 + - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} diff --git a/.github/workflows/docs-toc.yml b/.github/workflows/docs-toc.yml index 91f6ac10815..cbbae3660b7 100644 --- a/.github/workflows/docs-toc.yml +++ b/.github/workflows/docs-toc.yml @@ -10,8 +10,8 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 - - uses: technote-space/get-diff-action@v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: technote-space/get-diff-action@f27caffdd0fb9b13f4fc191c016bb4e0632844af # v6.1.2 with: PATTERNS: | docs/architecture/** diff --git a/.github/workflows/e2e-manual-multiversion.yml b/.github/workflows/e2e-manual-multiversion.yml index 24bdcb6cdc2..01e73153c9e 100644 --- a/.github/workflows/e2e-manual-multiversion.yml +++ b/.github/workflows/e2e-manual-multiversion.yml @@ -12,14 +12,14 @@ jobs: fail-fast: false matrix: group: ['00', '01', '02', '03', '04', '05'] - runs-on: depot-ubuntu-24.04-4 + runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: '1.22' - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Build working-directory: test/e2e diff --git a/.github/workflows/e2e-manual.yml b/.github/workflows/e2e-manual.yml index d4c7479e025..c7fd75ad9ce 100644 --- a/.github/workflows/e2e-manual.yml +++ b/.github/workflows/e2e-manual.yml @@ -12,14 +12,14 @@ jobs: fail-fast: false matrix: group: ['00', '01', '02', '03', '04', '05'] - runs-on: depot-ubuntu-24.04-4 + runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: '1.22' - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Build working-directory: test/e2e diff --git a/.github/workflows/e2e-nightly-main.yml b/.github/workflows/e2e-nightly-main.yml index f6e88edf94f..e428b16b947 100644 --- a/.github/workflows/e2e-nightly-main.yml +++ b/.github/workflows/e2e-nightly-main.yml @@ -17,14 +17,14 @@ jobs: fail-fast: false matrix: group: ['00', '01', '02', '03', '04', '05'] - runs-on: depot-ubuntu-24.04-4 + runs-on: ubuntu-latest timeout-minutes: 60 steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ env.GO_VERSION }} @@ -55,7 +55,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Notify Slack on failure - uses: slackapi/slack-github-action@v4.0.0 + uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 env: BRANCH: ${{ github.ref_name }} RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 00cfb0581b6..f69837f0938 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -22,13 +22,13 @@ jobs: fail-fast: false matrix: group: ['networks/ci', 'networks/libp2p-ci', 'networks_regressions/*'] - runs-on: depot-ubuntu-24.04-4 + runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - id: filter - uses: dorny/paths-filter@v4 + uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 with: filters: | code: @@ -39,7 +39,7 @@ jobs: - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV if: steps.filter.outputs.code == 'true' - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 if: steps.filter.outputs.code == 'true' with: go-version: ${{ env.GO_VERSION }} diff --git a/.github/workflows/enforce_signed.yml b/.github/workflows/enforce_signed.yml index 0e8b5ecb9d3..e0cd4c330c3 100644 --- a/.github/workflows/enforce_signed.yml +++ b/.github/workflows/enforce_signed.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Verify all PR commits are signed - uses: actions/github-script@v9 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const { owner, repo } = context.repo; diff --git a/.github/workflows/fuzz-nightly.yml b/.github/workflows/fuzz-nightly.yml index 69d7d73672b..121c55af1ab 100644 --- a/.github/workflows/fuzz-nightly.yml +++ b/.github/workflows/fuzz-nightly.yml @@ -16,12 +16,12 @@ concurrency: jobs: fuzz-nightly-test: - runs-on: depot-ubuntu-24.04-4 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ env.GO_VERSION }} @@ -55,14 +55,14 @@ jobs: continue-on-error: true - name: Archive crashers - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: crashers path: test/fuzz/**/crashers retention-days: 3 - name: Archive suppressions - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: suppressions path: test/fuzz/**/suppressions @@ -82,7 +82,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Notify Slack on failure - uses: slackapi/slack-github-action@v4.0.0 + uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 env: BRANCH: ${{ github.ref_name }} CRASHERS: ${{ needs.fuzz-nightly-test.outputs.crashers-count }} diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 0697d86d5ba..69f9a3478c9 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -16,10 +16,10 @@ jobs: integration_tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - id: filter - uses: dorny/paths-filter@v4 + uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 with: filters: | code: @@ -30,7 +30,7 @@ jobs: - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV if: steps.filter.outputs.code == 'true' - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 if: steps.filter.outputs.code == 'true' with: go-version: ${{ env.GO_VERSION }} diff --git a/.github/workflows/janitor.yml b/.github/workflows/janitor.yml index cd09b79374d..f214a9f7434 100644 --- a/.github/workflows/janitor.yml +++ b/.github/workflows/janitor.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 3 steps: - - uses: styfle/cancel-workflow-action@0.13.1 + - uses: styfle/cancel-workflow-action@d07a454dad7609a92316b57b23c9ccfd4f59af66 # 0.13.1 with: workflow_id: 1041851,1401230,2837803 access_token: ${{ github.token }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ae11b580325..f95048f5c9a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,14 +22,14 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v7 - - uses: actions/setup-python@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: '3.x' - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ env.GO_VERSION }} diff --git a/.github/workflows/markdown-linter.yml b/.github/workflows/markdown-linter.yml index 962a0b2f781..aae0ba77bbb 100644 --- a/.github/workflows/markdown-linter.yml +++ b/.github/workflows/markdown-linter.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Lint Code Base uses: docker://github/super-linter:v4 env: diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 95b8697be04..eee4eb93a7f 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -12,11 +12,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: '1.22' @@ -44,7 +44,7 @@ jobs: echo "See the [CHANGELOG](${CHANGELOG_URL}) for changes available in this pre-release, but not yet officially released." > ../release_notes.md - name: Release - uses: goreleaser/goreleaser-action@v7 + uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3 with: version: latest args: release --clean --release-notes ../release_notes.md @@ -57,7 +57,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Notify Slack upon pre-release - uses: slackapi/slack-github-action@v4.0.0 + uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 env: RELEASE_URL: "${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.ref_name }}" with: diff --git a/.github/workflows/proto-lint.yml b/.github/workflows/proto-lint.yml index b9735a866bd..2be6253e7de 100644 --- a/.github/workflows/proto-lint.yml +++ b/.github/workflows/proto-lint.yml @@ -18,8 +18,8 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@v7 - - uses: bufbuild/buf-action@v1 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: bufbuild/buf-action@8c6a16e16f12ba20b6470afa9c2ba9b5ba8c97c3 # v1.5.0 with: input: 'proto' version: '1.66.0' diff --git a/.github/workflows/release-version.yml b/.github/workflows/release-version.yml index 8c1961a6170..6d1562868ba 100644 --- a/.github/workflows/release-version.yml +++ b/.github/workflows/release-version.yml @@ -11,11 +11,11 @@ jobs: check-version: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ env.GO_VERSION }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 63f477966ae..e2a6fbfb1fc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,13 +10,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ env.GO_VERSION }} @@ -45,7 +45,7 @@ jobs: echo "See the [CHANGELOG](${CHANGELOG_URL}) for this release." > ../release_notes.md - name: Release - uses: goreleaser/goreleaser-action@v7 + uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3 with: version: latest args: release --clean --release-notes ../release_notes.md @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Notify Slack upon release - uses: slackapi/slack-github-action@v4.0.0 + uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0 env: RELEASE_URL: "${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.ref_name }}" with: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 8a972eb11d0..4fa1ea3f779 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -10,7 +10,7 @@ jobs: pull-requests: write # For reading the PR and adding the label runs-on: ubuntu-latest steps: - - uses: actions/stale@v10 + - uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-pr-message: "This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days-before-close if no further activity occurs." diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8604ed92e29..63d4ae70cef 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,10 +18,10 @@ jobs: matrix: part: ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"] steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - id: filter - uses: dorny/paths-filter@v4 + uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 with: filters: | code: @@ -32,7 +32,7 @@ jobs: - run: echo "GO_VERSION=$(cat .github/workflows/go-version.env | grep GO_VERSION | cut -d '=' -f2)" >> $GITHUB_ENV if: steps.filter.outputs.code == 'true' - - uses: actions/setup-go@v7 + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 if: steps.filter.outputs.code == 'true' with: go-version: ${{ env.GO_VERSION }} diff --git a/.gitignore b/.gitignore index b6cad1188fe..948d7e22e67 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ proto/spec/**/*.pb.go # Python virtual environments .venv go.work.sum +test/compat/gen-v1x/gen-v1x diff --git a/.golangci.yml b/.golangci.yml index e9e69dcea68..1bf51822630 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -51,6 +51,7 @@ linters: - github.com/stretchr/testify/require - github.com/syndtr/goleveldb - github.com/decred/dcrd/dcrec/secp256k1/v4 + - github.com/supranational/blst/bindings/go - google.golang.org/grpc - google.golang.org/protobuf/proto - golang.org/x/sync @@ -83,6 +84,7 @@ linters: - github.com/spf13 - github.com/stretchr/testify - github.com/decred/dcrd/dcrec/secp256k1/v4 + - github.com/supranational/blst/bindings/go - google.golang.org/grpc - google.golang.org/protobuf/proto - google.golang.org/protobuf/types/known/timestampp diff --git a/CHANGELOG.md b/CHANGELOG.md index b7a588909a1..565301a0971 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,51 @@ ### API-BREAKING +## Berachain fork (unreleased) + +_Berachain-specific changes layered on top of upstream CometBFT, based on upstream cometbft +v0.40.0 (`0880b4d37`). These ports keep consensus sign/state bytes byte-compatible with the +bera-v1.x line, so an existing bera-v1.x network can restart on this binary (consensus-compatible +with bera-v1.x networks). Upstream backports are tracked in the sections above. At release, this +section becomes `## v0.40.0-bera.N`._ + +### FEATURES + +- `[consensus,types,state]` Port Proposer-Based Timestamps (PBTS) from the bera-v1.x line. + Block timestamps are set by the proposer and validated against synchrony parameters instead of + the BFT-median of vote times; per-vote timestamps are removed. PBTS is always enabled on this fork. + ([\#51](https://github.com/berachain/cometbft/pull/51)) +- `[consensus,crypto,types]` Port BLS12-381 signature aggregation. For all-BLS validator sets, + precommit signatures are aggregated into a single commit that is gossiped whole and used for + fast catch-up of lagging nodes. + ([\#51](https://github.com/berachain/cometbft/pull/51)) +- `[state,abci]` Add `NextBlockDelay` (ADR-115): the application controls the delay before the next + height starts, replacing the static `timeout_commit`. Also adds `next_proposer_address` to + `ProcessProposal`. + ([\#51](https://github.com/berachain/cometbft/pull/51)) +- `[consensus]` The WAL persists `MsgInfo.receive_time` (same field as the bera-v1.x line), so a + proposal replayed from the WAL keeps its PBTS timeliness verdict instead of being prevoted nil + on restart. +- `[abci,state,types]` `ValidatorUpdate` accepts and persists both public key encodings: `pub_key` + (upstream) and `pub_key_bytes` + `pub_key_type` (bera-v1.x). FinalizeBlock responses persisted + by either line are readable by the other (crash recovery, `/block_results`). +- `[abci,state]` Add `syncing_to_height` (field 9) to `RequestFinalizeBlock`, the upstream v1 / + bera-v1.x field the application uses to tell block sync and replay from live consensus + (cometbft [\#3134](https://github.com/cometbft/cometbft/pull/3134)). Block sync passes the + highest peer height, consensus and replay pass the block height. +- `[test]` Add `test/compat`: golden vectors generated by the bera-v1.x code (sign bytes, + signatures, hashes, persisted state, p2p/WAL wire format, key and genesis files) asserted + byte-for-byte against this fork in both directions, plus a live data-dir reuse and + mixed-validator rolling-upgrade script against a bera-v1.x binary. + +### STATE-BREAKING + +- `[types,state]` New consensus parameters (`SynchronyParams`, `FeatureParams`) and the removal of + per-vote timestamps change consensus sign bytes and persisted-state encoding. These match the + bera-v1.x line byte-for-byte, so an existing bera-v1.x network can restart on this binary; the + encoding is not compatible with pristine upstream v0.40.x state. + ([\#51](https://github.com/berachain/cometbft/pull/51)) + ## v0.40.0 *July 27, 2026* diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index f4314f82f3e..eade5208843 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -13,6 +13,7 @@ import ( proto "github.com/cosmos/gogoproto/proto" _ "github.com/cosmos/gogoproto/types" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + _ "github.com/golang/protobuf/ptypes/duration" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -1325,6 +1326,10 @@ type RequestProcessProposal struct { NextValidatorsHash []byte `protobuf:"bytes,7,opt,name=next_validators_hash,json=nextValidatorsHash,proto3" json:"next_validators_hash,omitempty"` // address of the public key of the original proposer of the block. ProposerAddress []byte `protobuf:"bytes,8,opt,name=proposer_address,json=proposerAddress,proto3" json:"proposer_address,omitempty"` + // address of the public key of the validator that will propose the block at the + // next height (also the proposer at round 1 of the current height if consensus + // goes into multiple rounds). + NextProposerAddress []byte `protobuf:"bytes,9,opt,name=next_proposer_address,json=nextProposerAddress,proto3" json:"next_proposer_address,omitempty"` } func (m *RequestProcessProposal) Reset() { *m = RequestProcessProposal{} } @@ -1416,6 +1421,13 @@ func (m *RequestProcessProposal) GetProposerAddress() []byte { return nil } +func (m *RequestProcessProposal) GetNextProposerAddress() []byte { + if m != nil { + return m.NextProposerAddress + } + return nil +} + // Extends a vote with application-injected data type RequestExtendVote struct { // the hash of the block that this vote may be referring to @@ -1603,6 +1615,8 @@ type RequestFinalizeBlock struct { NextValidatorsHash []byte `protobuf:"bytes,7,opt,name=next_validators_hash,json=nextValidatorsHash,proto3" json:"next_validators_hash,omitempty"` // proposer_address is the address of the public key of the original proposer of the block. ProposerAddress []byte `protobuf:"bytes,8,opt,name=proposer_address,json=proposerAddress,proto3" json:"proposer_address,omitempty"` + // If the node is syncing/replaying blocks - target height. If not, syncing_to == height. + SyncingToHeight int64 `protobuf:"varint,9,opt,name=syncing_to_height,json=syncingToHeight,proto3" json:"syncing_to_height,omitempty"` } func (m *RequestFinalizeBlock) Reset() { *m = RequestFinalizeBlock{} } @@ -1694,6 +1708,13 @@ func (m *RequestFinalizeBlock) GetProposerAddress() []byte { return nil } +func (m *RequestFinalizeBlock) GetSyncingToHeight() int64 { + if m != nil { + return m.SyncingToHeight + } + return 0 +} + type Response struct { // Types that are valid to be assigned to Value: // @@ -2984,6 +3005,9 @@ type ResponseFinalizeBlock struct { ConsensusParamUpdates *types1.ConsensusParams `protobuf:"bytes,4,opt,name=consensus_param_updates,json=consensusParamUpdates,proto3" json:"consensus_param_updates,omitempty"` // app_hash is the hash of the applications' state which is used to confirm that execution of the transactions was deterministic. It is up to the application to decide which algorithm to use. AppHash []byte `protobuf:"bytes,5,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` + // delay between the time when this block is committed and the next height is started. + // previously `timeout_commit` in config.toml + NextBlockDelay time.Duration `protobuf:"bytes,6,opt,name=next_block_delay,json=nextBlockDelay,proto3,stdduration" json:"next_block_delay"` } func (m *ResponseFinalizeBlock) Reset() { *m = ResponseFinalizeBlock{} } @@ -3054,6 +3078,13 @@ func (m *ResponseFinalizeBlock) GetAppHash() []byte { return nil } +func (m *ResponseFinalizeBlock) GetNextBlockDelay() time.Duration { + if m != nil { + return m.NextBlockDelay + } + return 0 +} + type CommitInfo struct { Round int32 `protobuf:"varint,1,opt,name=round,proto3" json:"round,omitempty"` Votes []VoteInfo `protobuf:"bytes,2,rep,name=votes,proto3" json:"votes"` @@ -3510,6 +3541,14 @@ func (m *Validator) GetPower() int64 { type ValidatorUpdate struct { PubKey crypto.PublicKey `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key"` Power int64 `protobuf:"varint,2,opt,name=power,proto3" json:"power,omitempty"` + // Berachain: the bera-v1.x line encodes the public key as raw bytes plus a + // key type (pub_key instead being reserved there). Both forms are accepted + // when decoding, and both are written to the state store, so that + // FinalizeBlock responses persisted by either line can be read by the other + // (e.g. a rolling upgrade or rollback while the last block's response is + // needed for crash recovery, or /block_results for historical heights). + PubKeyBytes []byte `protobuf:"bytes,3,opt,name=pub_key_bytes,json=pubKeyBytes,proto3" json:"pub_key_bytes,omitempty"` + PubKeyType string `protobuf:"bytes,4,opt,name=pub_key_type,json=pubKeyType,proto3" json:"pub_key_type,omitempty"` } func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} } @@ -3559,6 +3598,20 @@ func (m *ValidatorUpdate) GetPower() int64 { return 0 } +func (m *ValidatorUpdate) GetPubKeyBytes() []byte { + if m != nil { + return m.PubKeyBytes + } + return nil +} + +func (m *ValidatorUpdate) GetPubKeyType() string { + if m != nil { + return m.PubKeyType + } + return "" +} + type VoteInfo struct { Validator Validator `protobuf:"bytes,1,opt,name=validator,proto3" json:"validator"` BlockIdFlag types1.BlockIDFlag `protobuf:"varint,3,opt,name=block_id_flag,json=blockIdFlag,proto3,enum=tendermint.types.BlockIDFlag" json:"block_id_flag,omitempty"` @@ -3904,215 +3957,222 @@ func init() { func init() { proto.RegisterFile("tendermint/abci/types.proto", fileDescriptor_252557cfdd89a31a) } var fileDescriptor_252557cfdd89a31a = []byte{ - // 3321 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xc9, 0x73, 0x1b, 0xc7, - 0xd5, 0xc7, 0x60, 0xc7, 0xc3, 0xc2, 0x61, 0x93, 0xa2, 0x20, 0x48, 0x26, 0xa9, 0x51, 0xd9, 0x96, - 0x65, 0x9b, 0xf4, 0x27, 0x7d, 0xf2, 0x52, 0xb2, 0xbf, 0x2f, 0x20, 0x04, 0x1a, 0xa4, 0x64, 0x92, - 0x1e, 0x42, 0x72, 0x39, 0x8b, 0xc7, 0x43, 0xa0, 0x41, 0x8c, 0x05, 0x60, 0xc6, 0x33, 0x03, 0x1a, - 0xf4, 0x29, 0x15, 0x27, 0x55, 0x29, 0x9f, 0x5c, 0x95, 0x4a, 0x95, 0x0f, 0xf1, 0x21, 0x87, 0x5c, - 0xf2, 0x17, 0xe4, 0x90, 0xf2, 0x29, 0x07, 0x1f, 0x72, 0xf0, 0x31, 0x27, 0x27, 0x65, 0xdf, 0x7c, - 0xcd, 0x21, 0xd7, 0x54, 0x2f, 0xb3, 0x01, 0x33, 0x58, 0x64, 0xe7, 0x90, 0x4a, 0x6e, 0xbd, 0xbc, - 0xf7, 0xba, 0xfb, 0x75, 0xf7, 0x5b, 0x7e, 0xdd, 0x70, 0xd9, 0xc6, 0x83, 0x36, 0x36, 0xfb, 0xda, - 0xc0, 0xde, 0x56, 0x4f, 0x5a, 0xda, 0xb6, 0x7d, 0x6e, 0x60, 0x6b, 0xcb, 0x30, 0x75, 0x5b, 0x47, - 0x4b, 0x5e, 0xe7, 0x16, 0xe9, 0xac, 0xac, 0x9e, 0xea, 0xa7, 0x3a, 0xed, 0xdb, 0x26, 0x25, 0x46, - 0x56, 0xd9, 0x38, 0xd5, 0xf5, 0xd3, 0x1e, 0xde, 0xa6, 0xb5, 0x93, 0x61, 0x67, 0xdb, 0xd6, 0xfa, - 0xd8, 0xb2, 0xd5, 0xbe, 0xc1, 0x09, 0xae, 0xf8, 0x06, 0x69, 0x99, 0xe7, 0x86, 0xad, 0x6f, 0x3f, - 0xc2, 0xe7, 0x7c, 0x94, 0xca, 0x13, 0x93, 0xbd, 0x86, 0xa9, 0xeb, 0x9d, 0x90, 0x6e, 0x3a, 0xb9, - 0x6d, 0x43, 0x35, 0xd5, 0xbe, 0xc3, 0xbd, 0x39, 0xd1, 0x7d, 0xa6, 0xf6, 0xb4, 0xb6, 0x6a, 0xeb, - 0x26, 0xa3, 0x90, 0x3e, 0x07, 0xc8, 0xc8, 0xf8, 0xfd, 0x21, 0xb6, 0x6c, 0x74, 0x13, 0x92, 0xb8, - 0xd5, 0xd5, 0xcb, 0xc2, 0xa6, 0x70, 0x3d, 0x7f, 0xf3, 0xca, 0xd6, 0xd8, 0x02, 0xb7, 0x38, 0x5d, - 0xbd, 0xd5, 0xd5, 0x1b, 0x31, 0x99, 0xd2, 0xa2, 0xdb, 0x90, 0xea, 0xf4, 0x86, 0x56, 0xb7, 0x1c, - 0xa7, 0x4c, 0x4f, 0x44, 0x31, 0xed, 0x12, 0xa2, 0x46, 0x4c, 0x66, 0xd4, 0x64, 0x28, 0x6d, 0xd0, - 0xd1, 0xcb, 0x89, 0xe9, 0x43, 0xed, 0x0d, 0x3a, 0x74, 0x28, 0x42, 0x8b, 0x76, 0x00, 0xb4, 0x81, - 0x66, 0x2b, 0xad, 0xae, 0xaa, 0x0d, 0xca, 0x29, 0xca, 0x79, 0x35, 0x9a, 0x53, 0xb3, 0x6b, 0x84, - 0xb0, 0x11, 0x93, 0x73, 0x9a, 0x53, 0x21, 0xd3, 0x7d, 0x7f, 0x88, 0xcd, 0xf3, 0x72, 0x7a, 0xfa, - 0x74, 0xdf, 0x24, 0x44, 0x64, 0xba, 0x94, 0x1a, 0xbd, 0x0a, 0xd9, 0x56, 0x17, 0xb7, 0x1e, 0x29, - 0xf6, 0xa8, 0x9c, 0xa5, 0x9c, 0x1b, 0x51, 0x9c, 0x35, 0x42, 0xd7, 0x1c, 0x35, 0x62, 0x72, 0xa6, - 0xc5, 0x8a, 0xe8, 0x65, 0x48, 0xb7, 0xf4, 0x7e, 0x5f, 0xb3, 0xcb, 0x79, 0xca, 0xbb, 0x1e, 0xc9, - 0x4b, 0xa9, 0x1a, 0x31, 0x99, 0xd3, 0xa3, 0x03, 0x28, 0xf5, 0x34, 0xcb, 0x56, 0xac, 0x81, 0x6a, - 0x58, 0x5d, 0xdd, 0xb6, 0xca, 0x05, 0x2a, 0xe1, 0xc9, 0x28, 0x09, 0xf7, 0x35, 0xcb, 0x3e, 0x76, - 0x88, 0x1b, 0x31, 0xb9, 0xd8, 0xf3, 0x37, 0x10, 0x79, 0x7a, 0xa7, 0x83, 0x4d, 0x57, 0x60, 0xb9, - 0x38, 0x5d, 0xde, 0x21, 0xa1, 0x76, 0xf8, 0x89, 0x3c, 0xdd, 0xdf, 0x80, 0x7e, 0x04, 0x2b, 0x3d, - 0x5d, 0x6d, 0xbb, 0xe2, 0x94, 0x56, 0x77, 0x38, 0x78, 0x54, 0x2e, 0x51, 0xa1, 0xcf, 0x44, 0x4e, - 0x52, 0x57, 0xdb, 0x8e, 0x88, 0x1a, 0x61, 0x68, 0xc4, 0xe4, 0xe5, 0xde, 0x78, 0x23, 0x7a, 0x07, - 0x56, 0x55, 0xc3, 0xe8, 0x9d, 0x8f, 0x4b, 0x5f, 0xa2, 0xd2, 0x6f, 0x44, 0x49, 0xaf, 0x12, 0x9e, - 0x71, 0xf1, 0x48, 0x9d, 0x68, 0x45, 0x4d, 0x10, 0x0d, 0x13, 0x1b, 0xaa, 0x89, 0x15, 0xc3, 0xd4, - 0x0d, 0xdd, 0x52, 0x7b, 0x65, 0x91, 0xca, 0x7e, 0x3a, 0x4a, 0xf6, 0x11, 0xa3, 0x3f, 0xe2, 0xe4, - 0x8d, 0x98, 0xbc, 0x64, 0x04, 0x9b, 0x98, 0x54, 0xbd, 0x85, 0x2d, 0xcb, 0x93, 0xba, 0x3c, 0x4b, - 0x2a, 0xa5, 0x0f, 0x4a, 0x0d, 0x34, 0xa1, 0x3a, 0xe4, 0xf1, 0x88, 0xb0, 0x2b, 0x67, 0xba, 0x8d, - 0xcb, 0x88, 0x0a, 0x94, 0x22, 0x6f, 0x28, 0x25, 0x7d, 0xa8, 0xdb, 0xb8, 0x11, 0x93, 0x01, 0xbb, - 0x35, 0xa4, 0xc2, 0x85, 0x33, 0x6c, 0x6a, 0x9d, 0x73, 0x2a, 0x46, 0xa1, 0x3d, 0x96, 0xa6, 0x0f, - 0xca, 0x2b, 0x54, 0xe0, 0xb3, 0x51, 0x02, 0x1f, 0x52, 0x26, 0x22, 0xa2, 0xee, 0xb0, 0x34, 0x62, - 0xf2, 0xca, 0xd9, 0x64, 0x33, 0x39, 0x62, 0x1d, 0x6d, 0xa0, 0xf6, 0xb4, 0x0f, 0xb1, 0x72, 0xd2, - 0xd3, 0x5b, 0x8f, 0xca, 0xab, 0xd3, 0x8f, 0xd8, 0x2e, 0xa7, 0xde, 0x21, 0xc4, 0xe4, 0x88, 0x75, - 0xfc, 0x0d, 0xe8, 0xff, 0x21, 0xa7, 0x0d, 0x2c, 0x6c, 0xda, 0xe4, 0xee, 0x5d, 0xa0, 0xa2, 0x36, - 0xa3, 0x2f, 0x3d, 0x21, 0xa4, 0x97, 0x2f, 0xab, 0xf1, 0x32, 0xb9, 0xbb, 0x26, 0x56, 0x0d, 0xc5, - 0x1e, 0x59, 0xe5, 0xb5, 0xe9, 0x77, 0x57, 0xc6, 0xaa, 0xd1, 0x1c, 0x91, 0x7b, 0x93, 0x31, 0x59, - 0x71, 0x27, 0x03, 0xa9, 0x33, 0xb5, 0x37, 0xc4, 0xfb, 0xc9, 0x6c, 0x52, 0x4c, 0xed, 0x27, 0xb3, - 0x19, 0x31, 0xbb, 0x9f, 0xcc, 0xe6, 0x44, 0xd8, 0x4f, 0x66, 0x41, 0xcc, 0x4b, 0x4f, 0x43, 0xde, - 0x67, 0x17, 0x51, 0x19, 0x32, 0x7d, 0x6c, 0x59, 0xea, 0x29, 0xa6, 0x66, 0x34, 0x27, 0x3b, 0x55, - 0xa9, 0x04, 0x05, 0xbf, 0x2d, 0x94, 0x3e, 0x11, 0x5c, 0x4e, 0x62, 0xe6, 0x08, 0xe7, 0x19, 0x36, - 0xe9, 0x6e, 0x70, 0x4e, 0x5e, 0x45, 0xd7, 0xa0, 0x48, 0x35, 0xa9, 0x38, 0xfd, 0xc4, 0xd6, 0x26, - 0xe5, 0x02, 0x6d, 0x7c, 0xc8, 0x89, 0x36, 0x20, 0x6f, 0xdc, 0x34, 0x5c, 0x92, 0x04, 0x25, 0x01, - 0xe3, 0xa6, 0xe1, 0x10, 0x5c, 0x85, 0x02, 0x59, 0xab, 0x4b, 0x91, 0xa4, 0x83, 0xe4, 0x49, 0x1b, - 0x27, 0x91, 0xfe, 0x1c, 0x07, 0x71, 0xdc, 0x7e, 0xa2, 0x97, 0x21, 0x49, 0x5c, 0x16, 0xf7, 0x0a, - 0x95, 0x2d, 0xe6, 0xcf, 0xb6, 0x1c, 0x7f, 0xb6, 0xd5, 0x74, 0xfc, 0xd9, 0x4e, 0xf6, 0x8b, 0xaf, - 0x36, 0x62, 0x9f, 0xfc, 0x75, 0x43, 0x90, 0x29, 0x07, 0xba, 0x44, 0xac, 0xa6, 0xaa, 0x0d, 0x14, - 0xad, 0x4d, 0xa7, 0x9c, 0x23, 0x26, 0x51, 0xd5, 0x06, 0x7b, 0x6d, 0x74, 0x1f, 0xc4, 0x96, 0x3e, - 0xb0, 0xf0, 0xc0, 0x1a, 0x5a, 0x0a, 0x73, 0x59, 0xdc, 0x17, 0x04, 0x2c, 0x3a, 0xf3, 0xb7, 0x35, - 0x87, 0xf2, 0x88, 0x12, 0xca, 0x4b, 0xad, 0x60, 0x03, 0xda, 0x05, 0x70, 0xfd, 0x9a, 0x55, 0x4e, - 0x6e, 0x26, 0x42, 0x0f, 0xc9, 0x43, 0x87, 0xe4, 0x81, 0xd1, 0x56, 0x6d, 0xbc, 0x93, 0x24, 0xd3, - 0x95, 0x7d, 0x9c, 0xe8, 0x29, 0x58, 0x52, 0x0d, 0x43, 0xb1, 0x6c, 0xd5, 0xc6, 0xca, 0xc9, 0xb9, - 0x8d, 0x2d, 0xea, 0x66, 0x0a, 0x72, 0x51, 0x35, 0x8c, 0x63, 0xd2, 0xba, 0x43, 0x1a, 0xd1, 0x93, - 0x50, 0x22, 0x2e, 0x45, 0x53, 0x7b, 0x4a, 0x17, 0x6b, 0xa7, 0x5d, 0x9b, 0xba, 0x93, 0x84, 0x5c, - 0xe4, 0xad, 0x0d, 0xda, 0x28, 0xb5, 0xdd, 0x1d, 0xa7, 0xee, 0x04, 0x21, 0x48, 0xb6, 0x55, 0x5b, - 0xa5, 0x9a, 0x2c, 0xc8, 0xb4, 0x4c, 0xda, 0x0c, 0xd5, 0xee, 0x72, 0xfd, 0xd0, 0x32, 0x5a, 0x83, - 0x34, 0x17, 0x9b, 0xa0, 0x62, 0x79, 0x0d, 0xad, 0x42, 0xca, 0x30, 0xf5, 0x33, 0x4c, 0xb7, 0x2e, - 0x2b, 0xb3, 0x8a, 0x24, 0x43, 0x29, 0xe8, 0x7a, 0x50, 0x09, 0xe2, 0xf6, 0x88, 0x8f, 0x12, 0xb7, - 0x47, 0xe8, 0x05, 0x48, 0x12, 0x45, 0xd2, 0x31, 0x4a, 0x21, 0xce, 0x96, 0xf3, 0x35, 0xcf, 0x0d, - 0x2c, 0x53, 0x4a, 0xe9, 0x2a, 0x2c, 0x8d, 0x5d, 0xa9, 0x71, 0xa1, 0xd2, 0xae, 0x3b, 0x2c, 0xbf, - 0x35, 0xe8, 0x32, 0xe4, 0xfa, 0xea, 0x88, 0xeb, 0x4d, 0xa0, 0xe7, 0x2f, 0xdb, 0x57, 0x47, 0x4c, - 0x65, 0x17, 0x21, 0x43, 0x3a, 0x4f, 0x55, 0x8b, 0x9f, 0xde, 0x74, 0x5f, 0x1d, 0xbd, 0xae, 0x5a, - 0xd2, 0x12, 0x14, 0x03, 0xde, 0x4f, 0x5a, 0x83, 0xd5, 0x30, 0x67, 0x26, 0x75, 0xdd, 0xf6, 0x80, - 0x53, 0x42, 0xb7, 0x21, 0xeb, 0x7a, 0x33, 0x76, 0x46, 0x2f, 0x4d, 0xac, 0xd0, 0x21, 0x96, 0x5d, - 0x52, 0x72, 0x38, 0xc9, 0x5e, 0x77, 0x55, 0x1e, 0xbb, 0x14, 0xe4, 0x8c, 0x6a, 0x18, 0x0d, 0xd5, - 0xea, 0x4a, 0xef, 0x42, 0x39, 0xca, 0x53, 0xf9, 0xf6, 0x86, 0xad, 0xd0, 0xd9, 0x9b, 0x35, 0x48, - 0x77, 0x74, 0xb3, 0xaf, 0xda, 0x54, 0x58, 0x51, 0xe6, 0x35, 0xb2, 0x67, 0xcc, 0x6b, 0x25, 0x68, - 0x33, 0xab, 0x48, 0x0a, 0x5c, 0x8a, 0xf4, 0x56, 0x84, 0x45, 0x1b, 0xb4, 0x31, 0x53, 0x76, 0x51, - 0x66, 0x15, 0x4f, 0x10, 0x9b, 0x2c, 0xab, 0x90, 0x61, 0x2d, 0xba, 0x56, 0x2a, 0x3f, 0x27, 0xf3, - 0x9a, 0xf4, 0x69, 0x02, 0xd6, 0xc2, 0x7d, 0x16, 0xda, 0x84, 0x02, 0xd9, 0x09, 0xdb, 0xbf, 0x53, - 0x09, 0x19, 0xfa, 0xea, 0xa8, 0xc9, 0xf7, 0x4a, 0x84, 0x04, 0x31, 0x96, 0xf1, 0xcd, 0xc4, 0xf5, - 0x82, 0x4c, 0x8a, 0xe8, 0x01, 0x2c, 0xf7, 0xf4, 0x96, 0xda, 0x53, 0x7a, 0xaa, 0x65, 0x2b, 0x3c, - 0x98, 0x61, 0xf7, 0xf5, 0xda, 0x84, 0xb2, 0x99, 0xf7, 0xc1, 0x6d, 0xb6, 0x9f, 0xc4, 0xb6, 0xf1, - 0xab, 0xb6, 0x44, 0x65, 0xdc, 0x57, 0x9d, 0xad, 0x46, 0x77, 0x21, 0xdf, 0xd7, 0xac, 0x13, 0xdc, - 0x55, 0xcf, 0x34, 0xdd, 0xe4, 0x17, 0x77, 0xf2, 0x7c, 0xbe, 0xe1, 0xd1, 0x70, 0x49, 0x7e, 0x36, - 0xdf, 0x96, 0xa4, 0x02, 0xd7, 0xc5, 0x31, 0x5c, 0xe9, 0x85, 0x0d, 0xd7, 0x0b, 0xb0, 0x3a, 0xc0, - 0x23, 0x5b, 0xf1, 0x4c, 0x03, 0x3b, 0x27, 0x19, 0xaa, 0x7a, 0x44, 0xfa, 0x5c, 0x63, 0x62, 0x91, - 0x23, 0x83, 0x9e, 0xa1, 0x5e, 0xdf, 0xd0, 0x2d, 0x6c, 0x2a, 0x6a, 0xbb, 0x6d, 0x62, 0xcb, 0xa2, - 0x81, 0x62, 0x81, 0xba, 0x72, 0xda, 0x5e, 0x65, 0xcd, 0xd2, 0x2f, 0xfd, 0x5b, 0x13, 0xf4, 0xf2, - 0x5c, 0xf1, 0x82, 0xa7, 0xf8, 0x63, 0x58, 0xe5, 0xfc, 0xed, 0x80, 0xee, 0x59, 0xb4, 0x7d, 0x79, - 0xf2, 0x2a, 0x8f, 0xeb, 0x1c, 0x39, 0xec, 0xd1, 0x6a, 0x4f, 0x3c, 0x9e, 0xda, 0x11, 0x24, 0xa9, - 0x52, 0x92, 0xcc, 0x9a, 0x91, 0xf2, 0xbf, 0xdb, 0x56, 0x7c, 0x94, 0x80, 0xe5, 0x89, 0x90, 0xc9, - 0x5d, 0x98, 0x10, 0xba, 0xb0, 0x78, 0xe8, 0xc2, 0x12, 0x0b, 0x2f, 0x8c, 0xef, 0x75, 0x72, 0xf6, - 0x5e, 0xa7, 0xbe, 0xc7, 0xbd, 0x4e, 0x3f, 0xde, 0x5e, 0xff, 0x4b, 0x77, 0xe1, 0x37, 0x02, 0x54, - 0xa2, 0xe3, 0xcc, 0xd0, 0xed, 0x78, 0x16, 0x96, 0xdd, 0xa9, 0xb8, 0xe2, 0x99, 0x61, 0x14, 0xdd, - 0x0e, 0x2e, 0x3f, 0xd2, 0x9d, 0x3e, 0x09, 0xa5, 0xb1, 0x28, 0x98, 0x1d, 0xe5, 0xe2, 0x99, 0x7f, - 0x7c, 0xe9, 0xe7, 0x09, 0xd7, 0xf1, 0x04, 0x42, 0xd5, 0x90, 0xdb, 0xfa, 0x26, 0xac, 0xb4, 0x71, - 0x4b, 0x6b, 0x3f, 0xee, 0x65, 0x5d, 0xe6, 0xdc, 0xff, 0xbd, 0xab, 0x93, 0xa7, 0xe4, 0xd7, 0x79, - 0xc8, 0xca, 0xd8, 0x32, 0x48, 0xe8, 0x87, 0x76, 0x20, 0x87, 0x47, 0x2d, 0x6c, 0xd8, 0x4e, 0xb4, - 0x1c, 0x9e, 0x0c, 0x31, 0xea, 0xba, 0x43, 0xd9, 0x88, 0xc9, 0x1e, 0x1b, 0xba, 0xc5, 0xd1, 0x8e, - 0x68, 0xe0, 0x82, 0xb3, 0xfb, 0xe1, 0x8e, 0x17, 0x1d, 0xb8, 0x23, 0x11, 0x99, 0xc9, 0x33, 0xae, - 0x31, 0xbc, 0xe3, 0x16, 0xc7, 0x3b, 0x92, 0x33, 0x06, 0x0b, 0x00, 0x1e, 0xb5, 0x00, 0xe0, 0x91, - 0x9e, 0xb1, 0xcc, 0x08, 0xc4, 0xe3, 0x45, 0x07, 0xf1, 0xc8, 0xcc, 0x98, 0xf1, 0x18, 0xe4, 0xf1, - 0x9a, 0x0f, 0xf2, 0xc8, 0x45, 0xa6, 0x5d, 0x8c, 0x35, 0x04, 0xf3, 0x78, 0xc5, 0xc5, 0x3c, 0x0a, - 0x91, 0x39, 0x17, 0x67, 0x1e, 0x07, 0x3d, 0x0e, 0x27, 0x40, 0x0f, 0x06, 0x52, 0x3c, 0x15, 0x29, - 0x62, 0x06, 0xea, 0x71, 0x38, 0x81, 0x7a, 0x94, 0x66, 0x08, 0x9c, 0x01, 0x7b, 0xfc, 0x38, 0x1c, - 0xf6, 0x88, 0x06, 0x26, 0xf8, 0x34, 0xe7, 0xc3, 0x3d, 0x94, 0x08, 0xdc, 0x43, 0x8c, 0xcc, 0xd1, - 0x99, 0xf8, 0xb9, 0x81, 0x8f, 0x07, 0x21, 0xc0, 0x07, 0x83, 0x28, 0xae, 0x47, 0x0a, 0x9f, 0x03, - 0xf9, 0x78, 0x10, 0x82, 0x7c, 0xa0, 0x99, 0x62, 0x67, 0x42, 0x1f, 0xbb, 0x41, 0xe8, 0x63, 0x25, - 0x22, 0xea, 0xf4, 0x6e, 0x7b, 0x04, 0xf6, 0x71, 0x12, 0x85, 0x7d, 0x30, 0x7c, 0xe2, 0xb9, 0x48, - 0x89, 0x0b, 0x80, 0x1f, 0x87, 0x13, 0xe0, 0xc7, 0x85, 0x19, 0x27, 0x6d, 0x06, 0xfa, 0xf1, 0x03, - 0x3f, 0xfa, 0xb1, 0x16, 0x09, 0x79, 0x3a, 0x16, 0x20, 0x04, 0xfe, 0x78, 0xcd, 0x07, 0x7f, 0x5c, - 0x9c, 0x71, 0x8f, 0xa7, 0xe3, 0x1f, 0x29, 0x31, 0xbd, 0x9f, 0xcc, 0x66, 0xc5, 0x1c, 0x43, 0x3e, - 0xf6, 0x93, 0xd9, 0xbc, 0x58, 0x90, 0x9e, 0x21, 0x21, 0xd4, 0x98, 0xa1, 0x25, 0xc9, 0x0a, 0x36, - 0x4d, 0xdd, 0xe4, 0x48, 0x06, 0xab, 0x48, 0xd7, 0x49, 0x3e, 0xec, 0x19, 0xd5, 0x29, 0x58, 0x09, - 0x4d, 0x0a, 0x7d, 0x86, 0x54, 0xfa, 0x83, 0xe0, 0xf1, 0x52, 0xb4, 0xc4, 0x9f, 0x4b, 0xe7, 0x78, - 0x2e, 0xed, 0x43, 0x50, 0xe2, 0x41, 0x04, 0x65, 0x03, 0xf2, 0x24, 0xd9, 0x1b, 0x03, 0x47, 0x54, - 0xc3, 0x05, 0x47, 0x6e, 0xc0, 0x32, 0xf5, 0xd8, 0x0c, 0x67, 0xe1, 0x7e, 0x31, 0x49, 0xfd, 0xe2, - 0x12, 0xe9, 0x60, 0xdb, 0xc3, 0x1c, 0xe4, 0xf3, 0xb0, 0xe2, 0xa3, 0x75, 0x93, 0x48, 0x86, 0x14, - 0x88, 0x2e, 0x75, 0x95, 0x67, 0x93, 0x7f, 0x12, 0x3c, 0x0d, 0x79, 0xa8, 0x4a, 0x18, 0x00, 0x22, - 0x7c, 0x4f, 0x00, 0x48, 0xfc, 0xb1, 0x01, 0x10, 0x7f, 0x52, 0x9c, 0x08, 0x26, 0xc5, 0xff, 0x10, - 0xbc, 0x3d, 0x71, 0xe1, 0x8c, 0x96, 0xde, 0xc6, 0x3c, 0x4d, 0xa5, 0x65, 0x12, 0x13, 0xf5, 0xf4, - 0x53, 0x9e, 0x8c, 0x92, 0x22, 0xa1, 0x72, 0x3d, 0x5f, 0x8e, 0x3b, 0x36, 0x37, 0xc3, 0x65, 0x91, - 0x07, 0xcf, 0x70, 0x45, 0x48, 0x3c, 0xc2, 0x0c, 0x99, 0x2f, 0xc8, 0xa4, 0x48, 0xe8, 0xe8, 0xe1, - 0xe3, 0x11, 0x04, 0xab, 0xa0, 0x97, 0x21, 0x47, 0x9f, 0x40, 0x14, 0xdd, 0xb0, 0x38, 0x1a, 0x1f, - 0x88, 0xad, 0xd8, 0x33, 0xc9, 0xd6, 0x11, 0xa1, 0x39, 0x34, 0x2c, 0x39, 0x6b, 0xf0, 0x92, 0x2f, - 0xe4, 0xc9, 0x05, 0x42, 0x9e, 0x2b, 0x90, 0x23, 0xb3, 0xb7, 0x0c, 0xb5, 0x85, 0xcb, 0x40, 0x27, - 0xea, 0x35, 0x48, 0xbf, 0x8f, 0xc3, 0xd2, 0x98, 0xa7, 0x0b, 0x5d, 0xbb, 0x73, 0x24, 0xe3, 0x3e, - 0x78, 0x67, 0x3e, 0x7d, 0xac, 0x03, 0x9c, 0xaa, 0x96, 0xf2, 0x81, 0x3a, 0xb0, 0x71, 0x9b, 0x2b, - 0xc5, 0xd7, 0x82, 0x2a, 0x90, 0x25, 0xb5, 0xa1, 0x85, 0xdb, 0x1c, 0x69, 0x72, 0xeb, 0xa8, 0x01, - 0x69, 0x7c, 0x86, 0x07, 0xb6, 0x55, 0xce, 0xd0, 0x6d, 0x5f, 0x9b, 0xcc, 0xc7, 0x49, 0xf7, 0x4e, - 0x99, 0x6c, 0xf6, 0xb7, 0x5f, 0x6d, 0x88, 0x8c, 0xfa, 0x39, 0xbd, 0xaf, 0xd9, 0xb8, 0x6f, 0xd8, - 0xe7, 0x32, 0xe7, 0x0f, 0x6a, 0x21, 0x3b, 0xa6, 0x05, 0x8a, 0x79, 0x16, 0x1c, 0x7c, 0x81, 0xe8, - 0x54, 0xd3, 0x4d, 0xcd, 0x3e, 0x97, 0x8b, 0x7d, 0xdc, 0x37, 0x74, 0xbd, 0xa7, 0xb0, 0x3b, 0xfe, - 0x14, 0x88, 0xe3, 0xe6, 0x28, 0x4c, 0x59, 0xd2, 0x35, 0x4f, 0xa7, 0x0e, 0x7e, 0x34, 0x11, 0x4f, - 0x4b, 0x55, 0x28, 0x05, 0xa3, 0x04, 0x74, 0x0d, 0x8a, 0x26, 0xb6, 0x55, 0x6d, 0xa0, 0x04, 0x42, - 0xfa, 0x02, 0x6b, 0x64, 0x17, 0x74, 0x3f, 0x99, 0x15, 0xc4, 0xf8, 0x7e, 0x32, 0x1b, 0x17, 0x13, - 0xd2, 0x11, 0x5c, 0x08, 0x8d, 0x12, 0xd0, 0x4b, 0x90, 0xf3, 0x02, 0x0c, 0x81, 0xaa, 0x6e, 0x0a, - 0x6e, 0xe4, 0xd1, 0x4a, 0x9f, 0x0b, 0x9e, 0xc8, 0x20, 0x12, 0x55, 0x87, 0xb4, 0x89, 0xad, 0x61, - 0x8f, 0x61, 0x43, 0xa5, 0x9b, 0xcf, 0xcf, 0x17, 0x5f, 0x90, 0xd6, 0x61, 0xcf, 0x96, 0x39, 0xb3, - 0xf4, 0x0e, 0xa4, 0x59, 0x0b, 0xca, 0x43, 0xe6, 0xc1, 0xc1, 0xbd, 0x83, 0xc3, 0xb7, 0x0e, 0xc4, - 0x18, 0x02, 0x48, 0x57, 0x6b, 0xb5, 0xfa, 0x51, 0x53, 0x14, 0x50, 0x0e, 0x52, 0xd5, 0x9d, 0x43, - 0xb9, 0x29, 0xc6, 0x49, 0xb3, 0x5c, 0xdf, 0xaf, 0xd7, 0x9a, 0x62, 0x02, 0x2d, 0x43, 0x91, 0x95, - 0x95, 0xdd, 0x43, 0xf9, 0x8d, 0x6a, 0x53, 0x4c, 0xfa, 0x9a, 0x8e, 0xeb, 0x07, 0x77, 0xeb, 0xb2, - 0x98, 0x92, 0xfe, 0x07, 0x2e, 0x45, 0x46, 0x24, 0x1e, 0xcc, 0x24, 0xf8, 0x60, 0x26, 0xe9, 0xd3, - 0x38, 0x49, 0xd1, 0xa2, 0xc2, 0x0c, 0xb4, 0x3f, 0xb6, 0xf0, 0x9b, 0x0b, 0xc4, 0x28, 0x63, 0xab, - 0x27, 0x59, 0x99, 0x89, 0x3b, 0xd8, 0x6e, 0x75, 0x59, 0xd8, 0xc3, 0xcc, 0x59, 0x51, 0x2e, 0xf2, - 0x56, 0xca, 0x64, 0x31, 0xb2, 0xf7, 0x70, 0xcb, 0x56, 0xd8, 0x89, 0xb4, 0x68, 0x6a, 0x94, 0x23, - 0x64, 0xa4, 0xf5, 0x98, 0x35, 0x4a, 0xef, 0x2e, 0xa4, 0xcb, 0x1c, 0xa4, 0xe4, 0x7a, 0x53, 0x7e, - 0x5b, 0x4c, 0x20, 0x04, 0x25, 0x5a, 0x54, 0x8e, 0x0f, 0xaa, 0x47, 0xc7, 0x8d, 0x43, 0xa2, 0xcb, - 0x15, 0x58, 0x72, 0x74, 0xe9, 0x34, 0xa6, 0xa4, 0x67, 0xe1, 0x62, 0x44, 0x8c, 0x14, 0x72, 0xa0, - 0x7f, 0x2b, 0xf8, 0xa9, 0x83, 0x71, 0xce, 0x21, 0xa4, 0x2d, 0x5b, 0xb5, 0x87, 0x16, 0x57, 0xe2, - 0x4b, 0xf3, 0x06, 0x4d, 0x5b, 0x4e, 0xe1, 0x98, 0xb2, 0xcb, 0x5c, 0x8c, 0x74, 0x1b, 0x4a, 0xc1, - 0x9e, 0x68, 0x1d, 0x78, 0x87, 0x28, 0x2e, 0xdd, 0x01, 0x34, 0x19, 0x4b, 0x85, 0x24, 0xcb, 0x42, - 0x58, 0xb2, 0xfc, 0x3b, 0x01, 0x2e, 0x4f, 0x89, 0x9b, 0xd0, 0x9b, 0x63, 0x8b, 0x7c, 0x65, 0x91, - 0xa8, 0x6b, 0x8b, 0xb5, 0x8d, 0x2d, 0xf3, 0x16, 0x14, 0xfc, 0xed, 0xf3, 0x2d, 0xf2, 0xdb, 0xb8, - 0x77, 0x89, 0x83, 0x59, 0xbd, 0x67, 0x4f, 0x85, 0xef, 0x68, 0x4f, 0x5f, 0x05, 0xb0, 0x47, 0x0a, - 0x3b, 0xd6, 0x8e, 0x53, 0x7e, 0x22, 0x04, 0x2d, 0xc5, 0xad, 0xe6, 0x88, 0x5f, 0x82, 0x9c, 0xcd, - 0x4b, 0x16, 0x3a, 0xf6, 0x43, 0x1c, 0x43, 0xea, 0xb0, 0x2d, 0x9e, 0xfe, 0xcf, 0xeb, 0xd9, 0x3d, - 0x28, 0x84, 0x35, 0x5b, 0xe8, 0x6d, 0xb8, 0x38, 0x16, 0x75, 0xb8, 0xa2, 0x93, 0xf3, 0x06, 0x1f, - 0x17, 0x82, 0xc1, 0x87, 0x23, 0xda, 0x1f, 0x3a, 0xa4, 0x82, 0xa1, 0xc3, 0xdb, 0x00, 0x1e, 0xd4, - 0x41, 0x2c, 0x8c, 0xa9, 0x0f, 0x07, 0x6d, 0x7a, 0x02, 0x52, 0x32, 0xab, 0xa0, 0xdb, 0x90, 0x22, - 0x27, 0xc9, 0xd1, 0xd3, 0xa4, 0x29, 0x26, 0x27, 0xc1, 0x07, 0x95, 0x30, 0x6a, 0x49, 0x03, 0x34, - 0x09, 0x37, 0x47, 0x0c, 0xf1, 0x5a, 0x70, 0x88, 0xab, 0x91, 0xc0, 0x75, 0xf8, 0x50, 0x1f, 0x42, - 0x8a, 0xee, 0x3c, 0x71, 0x67, 0xf4, 0x39, 0x85, 0x87, 0x9e, 0xa4, 0x8c, 0x7e, 0x02, 0xa0, 0xda, - 0xb6, 0xa9, 0x9d, 0x0c, 0xbd, 0x01, 0x36, 0xc2, 0x4f, 0x4e, 0xd5, 0xa1, 0xdb, 0xb9, 0xc2, 0x8f, - 0xd0, 0xaa, 0xc7, 0xea, 0x3b, 0x46, 0x3e, 0x81, 0xd2, 0x01, 0x94, 0x82, 0xbc, 0x4e, 0xb0, 0xc4, - 0xe6, 0x10, 0x0c, 0x96, 0x58, 0xec, 0xcb, 0x83, 0x25, 0x37, 0xd4, 0x4a, 0xb0, 0x37, 0x23, 0x5a, - 0x91, 0x7e, 0x1a, 0x87, 0x82, 0xff, 0xe0, 0xfd, 0xe7, 0xc5, 0x33, 0xd2, 0x2f, 0x04, 0xc8, 0xba, - 0xcb, 0x0f, 0xbe, 0xea, 0x04, 0x5e, 0xdc, 0x98, 0xf6, 0xe2, 0xfe, 0xa7, 0x18, 0xf6, 0x14, 0x96, - 0x70, 0xdf, 0xd7, 0xee, 0xb8, 0xee, 0x2f, 0x0a, 0xde, 0xf1, 0xeb, 0x9a, 0x9f, 0x2a, 0xc7, 0xdb, - 0xdf, 0x81, 0x9c, 0x7b, 0x7b, 0x49, 0x06, 0xe3, 0xc0, 0x60, 0x02, 0xbf, 0x43, 0x1c, 0xc4, 0x5c, - 0x85, 0x94, 0xa1, 0x7f, 0xc0, 0xdf, 0x79, 0x12, 0x32, 0xab, 0x48, 0x6d, 0x58, 0x1a, 0xbb, 0xfa, - 0xe8, 0x0e, 0x64, 0x8c, 0xe1, 0x89, 0xe2, 0x1c, 0x8e, 0x31, 0xb0, 0xd0, 0x89, 0x8d, 0x87, 0x27, - 0x3d, 0xad, 0x75, 0x0f, 0x9f, 0x3b, 0x93, 0x31, 0x86, 0x27, 0xf7, 0xd8, 0x19, 0x62, 0xa3, 0xc4, - 0xfd, 0xa3, 0xfc, 0x4a, 0x80, 0xac, 0x73, 0x27, 0xd0, 0xff, 0x41, 0xce, 0x35, 0x2b, 0xee, 0x9b, - 0x70, 0xa4, 0x3d, 0xe2, 0xf2, 0x3d, 0x16, 0x54, 0x75, 0x1e, 0xb3, 0xb5, 0xb6, 0xd2, 0xe9, 0xa9, - 0xec, 0x2c, 0x95, 0x82, 0x3a, 0x63, 0x86, 0x87, 0xda, 0xe3, 0xbd, 0xbb, 0xbb, 0x3d, 0xf5, 0x54, - 0xce, 0x53, 0x9e, 0xbd, 0x36, 0xa9, 0xf0, 0xc8, 0xee, 0xef, 0x02, 0x88, 0xe3, 0x37, 0xf6, 0x3b, - 0xcf, 0x6e, 0xd2, 0xcd, 0x25, 0x42, 0xdc, 0x1c, 0xda, 0x86, 0x15, 0x97, 0x42, 0xb1, 0xb4, 0xd3, - 0x81, 0x6a, 0x0f, 0x4d, 0xcc, 0xe1, 0x55, 0xe4, 0x76, 0x1d, 0x3b, 0x3d, 0x93, 0xab, 0x4e, 0x3d, - 0xe6, 0xaa, 0x3f, 0x8a, 0x43, 0xde, 0x07, 0xf6, 0xa2, 0xff, 0xf5, 0x19, 0xa3, 0x52, 0x88, 0x67, - 0xf0, 0xd1, 0x7a, 0xef, 0xbb, 0x41, 0x35, 0xc5, 0x17, 0x57, 0x53, 0x14, 0xa4, 0xee, 0x60, 0xc7, - 0xc9, 0x85, 0xb1, 0xe3, 0xe7, 0x00, 0xd9, 0xba, 0xad, 0xf6, 0x94, 0x33, 0xdd, 0xd6, 0x06, 0xa7, - 0x0a, 0x3b, 0x86, 0xcc, 0x74, 0x88, 0xb4, 0xe7, 0x21, 0xed, 0x38, 0xa2, 0x27, 0xf2, 0x67, 0x02, - 0x64, 0xdd, 0xb0, 0x7b, 0xd1, 0x27, 0xd9, 0x35, 0x48, 0xf3, 0xc8, 0x92, 0xbd, 0xc9, 0xf2, 0x5a, - 0x28, 0x48, 0x5e, 0x81, 0x6c, 0x1f, 0xdb, 0x2a, 0xb5, 0x83, 0xcc, 0xab, 0xb9, 0xf5, 0x1b, 0xaf, - 0x40, 0xde, 0xf7, 0x72, 0x4e, 0x4c, 0xe3, 0x41, 0xfd, 0x2d, 0x31, 0x56, 0xc9, 0x7c, 0xfc, 0xd9, - 0x66, 0xe2, 0x00, 0x7f, 0x40, 0x6e, 0xb3, 0x5c, 0xaf, 0x35, 0xea, 0xb5, 0x7b, 0xa2, 0x50, 0xc9, - 0x7f, 0xfc, 0xd9, 0x66, 0x46, 0xc6, 0x14, 0x1f, 0xbd, 0x71, 0x0f, 0x96, 0xc6, 0x36, 0x26, 0x18, - 0xb6, 0x20, 0x28, 0xdd, 0x7d, 0x70, 0x74, 0x7f, 0xaf, 0x56, 0x6d, 0xd6, 0x95, 0x87, 0x87, 0xcd, - 0xba, 0x28, 0xa0, 0x8b, 0xb0, 0x72, 0x7f, 0xef, 0xf5, 0x46, 0x53, 0xa9, 0xdd, 0xdf, 0xab, 0x1f, - 0x34, 0x95, 0x6a, 0xb3, 0x59, 0xad, 0xdd, 0x13, 0xe3, 0x37, 0xff, 0x58, 0x80, 0x64, 0x75, 0xa7, - 0xb6, 0x87, 0x6a, 0x90, 0xa4, 0xb8, 0xca, 0xd4, 0x9f, 0x7b, 0x95, 0xe9, 0x48, 0x37, 0xda, 0x85, - 0x14, 0x85, 0x5c, 0xd0, 0xf4, 0xaf, 0x7c, 0x95, 0x19, 0xd0, 0x37, 0x99, 0x0c, 0xbd, 0x91, 0x53, - 0xff, 0xf6, 0x55, 0xa6, 0x23, 0xe1, 0xe8, 0x3e, 0x64, 0x9c, 0x8c, 0x7b, 0xd6, 0x87, 0xbb, 0xca, - 0x4c, 0x78, 0x1a, 0x1d, 0x42, 0xd6, 0xcd, 0x49, 0x67, 0xfe, 0x21, 0xaa, 0xcc, 0xc6, 0xd9, 0xc8, - 0xf4, 0x9c, 0xe4, 0x75, 0xd6, 0x9f, 0xa2, 0xca, 0x4c, 0xd4, 0x8d, 0x68, 0x9e, 0x01, 0x2b, 0xd3, - 0x7f, 0x25, 0x56, 0x66, 0x40, 0xf8, 0x68, 0x0f, 0xd2, 0x3c, 0x5b, 0x9e, 0xf1, 0xd1, 0xb0, 0x32, - 0x0b, 0x94, 0x47, 0x32, 0xe4, 0x3c, 0xc8, 0x6a, 0xf6, 0x5f, 0xcb, 0xca, 0x1c, 0xaf, 0x13, 0xe8, - 0x1d, 0x28, 0x06, 0x33, 0xf1, 0xf9, 0x3e, 0x33, 0x56, 0xe6, 0x84, 0xff, 0x89, 0xfc, 0x60, 0x5a, - 0x3e, 0xdf, 0xe7, 0xc6, 0xca, 0x9c, 0xaf, 0x01, 0xe8, 0x3d, 0x58, 0x9e, 0x4c, 0x9b, 0xe7, 0xff, - 0xeb, 0x58, 0x59, 0xe0, 0x7d, 0x00, 0xf5, 0x01, 0x85, 0xa4, 0xdb, 0x0b, 0x7c, 0x7d, 0xac, 0x2c, - 0xf2, 0x5c, 0x80, 0xda, 0xb0, 0x34, 0x9e, 0xc3, 0xce, 0xfb, 0x15, 0xb2, 0x32, 0xf7, 0xd3, 0x01, - 0x1b, 0x25, 0x98, 0xfb, 0xce, 0xfb, 0x35, 0xb2, 0x32, 0xf7, 0x4b, 0x02, 0x7a, 0x00, 0xe0, 0x4b, - 0x5f, 0xe7, 0xf8, 0x2a, 0x59, 0x99, 0xe7, 0x4d, 0x01, 0x19, 0xb0, 0x12, 0x96, 0xd7, 0x2e, 0xf2, - 0x73, 0xb2, 0xb2, 0xd0, 0x53, 0x03, 0x39, 0xcf, 0xc1, 0x0c, 0x75, 0xbe, 0x9f, 0x94, 0x95, 0x39, - 0xdf, 0x1c, 0x76, 0xaa, 0x5f, 0x7c, 0xbd, 0x2e, 0x7c, 0xf9, 0xf5, 0xba, 0xf0, 0xb7, 0xaf, 0xd7, - 0x85, 0x4f, 0xbe, 0x59, 0x8f, 0x7d, 0xf9, 0xcd, 0x7a, 0xec, 0x2f, 0xdf, 0xac, 0xc7, 0x7e, 0xf8, - 0xf4, 0xa9, 0x66, 0x77, 0x87, 0x27, 0x5b, 0x2d, 0xbd, 0xbf, 0xdd, 0xd2, 0xfb, 0xd8, 0x3e, 0xe9, - 0xd8, 0x5e, 0xc1, 0xfb, 0x10, 0x7f, 0x92, 0xa6, 0x0e, 0xfe, 0xd6, 0x3f, 0x03, 0x00, 0x00, 0xff, - 0xff, 0x0d, 0x83, 0x0c, 0x75, 0x30, 0x2f, 0x00, 0x00, + // 3440 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5b, 0x3b, 0x70, 0x1b, 0xc7, + 0x19, 0xc6, 0xe1, 0x8d, 0x1f, 0x0f, 0x1e, 0x97, 0x14, 0x05, 0x41, 0x32, 0x49, 0x9d, 0xc6, 0xb6, + 0x2c, 0xdb, 0xa4, 0x43, 0x45, 0x7e, 0x8c, 0xec, 0x24, 0x24, 0x05, 0x1a, 0xa4, 0x64, 0x92, 0x3e, + 0x42, 0xf2, 0x38, 0x0f, 0x9f, 0x8f, 0xc0, 0x92, 0x38, 0x0b, 0xc0, 0x9d, 0xef, 0x16, 0x34, 0xe9, + 0x2a, 0x13, 0x4f, 0x1a, 0x57, 0x9e, 0xc9, 0x64, 0xc6, 0x45, 0x5c, 0xa4, 0x48, 0xe3, 0x2a, 0x65, + 0x32, 0xe3, 0x71, 0x95, 0xc2, 0x45, 0x0a, 0x97, 0xa9, 0xec, 0x8c, 0xdd, 0xa5, 0x4d, 0x91, 0x36, + 0xb3, 0x8f, 0x7b, 0x01, 0x77, 0x00, 0x28, 0x3b, 0x45, 0x26, 0xe9, 0xf6, 0xf1, 0xff, 0xff, 0x2d, + 0xfe, 0xfd, 0xf7, 0x7f, 0x7c, 0xbb, 0x80, 0xcb, 0x04, 0xf7, 0xdb, 0xd8, 0xee, 0x19, 0x7d, 0xb2, + 0xaa, 0x1f, 0xb6, 0x8c, 0x55, 0x72, 0x66, 0x61, 0x67, 0xc5, 0xb2, 0x4d, 0x62, 0xa2, 0x19, 0x7f, + 0x72, 0x85, 0x4e, 0xd6, 0xe6, 0x8f, 0xcd, 0x63, 0x93, 0xcd, 0xad, 0xd2, 0x16, 0x27, 0xab, 0x2d, + 0x1e, 0x9b, 0xe6, 0x71, 0x17, 0xaf, 0xb2, 0xde, 0xe1, 0xe0, 0x68, 0xb5, 0x3d, 0xb0, 0x75, 0x62, + 0x98, 0x7d, 0x31, 0xbf, 0x34, 0x3c, 0x4f, 0x8c, 0x1e, 0x76, 0x88, 0xde, 0xb3, 0x04, 0xc1, 0x95, + 0xc0, 0x22, 0x5a, 0xf6, 0x99, 0x45, 0xcc, 0xd5, 0x87, 0xf8, 0x4c, 0xac, 0xa2, 0xf6, 0xd8, 0xe8, + 0xac, 0x65, 0x9b, 0xe6, 0x51, 0xc4, 0x34, 0x5b, 0xfc, 0xaa, 0xa5, 0xdb, 0x7a, 0xcf, 0xe5, 0x5e, + 0x1e, 0x99, 0x3e, 0xd1, 0xbb, 0x46, 0x5b, 0x27, 0xa6, 0xcd, 0x29, 0x94, 0xcf, 0x01, 0x72, 0x2a, + 0x7e, 0x77, 0x80, 0x1d, 0x82, 0xd6, 0x20, 0x8d, 0x5b, 0x1d, 0xb3, 0x2a, 0x2d, 0x4b, 0xd7, 0x8b, + 0x6b, 0x57, 0x56, 0x86, 0x14, 0xb0, 0x22, 0xe8, 0xea, 0xad, 0x8e, 0xd9, 0x48, 0xa8, 0x8c, 0x16, + 0xdd, 0x82, 0xcc, 0x51, 0x77, 0xe0, 0x74, 0xaa, 0x49, 0xc6, 0xf4, 0x58, 0x1c, 0xd3, 0x16, 0x25, + 0x6a, 0x24, 0x54, 0x4e, 0x4d, 0x3f, 0x65, 0xf4, 0x8f, 0xcc, 0x6a, 0x6a, 0xfc, 0xa7, 0xb6, 0xfb, + 0x47, 0xec, 0x53, 0x94, 0x16, 0x6d, 0x00, 0x18, 0x7d, 0x83, 0x68, 0xad, 0x8e, 0x6e, 0xf4, 0xab, + 0x19, 0xc6, 0x79, 0x35, 0x9e, 0xd3, 0x20, 0x9b, 0x94, 0xb0, 0x91, 0x50, 0x0b, 0x86, 0xdb, 0xa1, + 0xcb, 0x7d, 0x77, 0x80, 0xed, 0xb3, 0x6a, 0x76, 0xfc, 0x72, 0x5f, 0xa7, 0x44, 0x74, 0xb9, 0x8c, + 0x1a, 0xbd, 0x0c, 0xf9, 0x56, 0x07, 0xb7, 0x1e, 0x6a, 0xe4, 0xb4, 0x9a, 0x67, 0x9c, 0x4b, 0x71, + 0x9c, 0x9b, 0x94, 0xae, 0x79, 0xda, 0x48, 0xa8, 0xb9, 0x16, 0x6f, 0xa2, 0x17, 0x21, 0xdb, 0x32, + 0x7b, 0x3d, 0x83, 0x54, 0x8b, 0x8c, 0x77, 0x31, 0x96, 0x97, 0x51, 0x35, 0x12, 0xaa, 0xa0, 0x47, + 0xbb, 0x50, 0xe9, 0x1a, 0x0e, 0xd1, 0x9c, 0xbe, 0x6e, 0x39, 0x1d, 0x93, 0x38, 0xd5, 0x12, 0x93, + 0xf0, 0x78, 0x9c, 0x84, 0x7b, 0x86, 0x43, 0x0e, 0x5c, 0xe2, 0x46, 0x42, 0x2d, 0x77, 0x83, 0x03, + 0x54, 0x9e, 0x79, 0x74, 0x84, 0x6d, 0x4f, 0x60, 0xb5, 0x3c, 0x5e, 0xde, 0x1e, 0xa5, 0x76, 0xf9, + 0xa9, 0x3c, 0x33, 0x38, 0x80, 0x7e, 0x06, 0x73, 0x5d, 0x53, 0x6f, 0x7b, 0xe2, 0xb4, 0x56, 0x67, + 0xd0, 0x7f, 0x58, 0xad, 0x30, 0xa1, 0x4f, 0xc5, 0x2e, 0xd2, 0xd4, 0xdb, 0xae, 0x88, 0x4d, 0xca, + 0xd0, 0x48, 0xa8, 0xb3, 0xdd, 0xe1, 0x41, 0xf4, 0x16, 0xcc, 0xeb, 0x96, 0xd5, 0x3d, 0x1b, 0x96, + 0x3e, 0xc3, 0xa4, 0xdf, 0x88, 0x93, 0xbe, 0x4e, 0x79, 0x86, 0xc5, 0x23, 0x7d, 0x64, 0x14, 0x35, + 0x41, 0xb6, 0x6c, 0x6c, 0xe9, 0x36, 0xd6, 0x2c, 0xdb, 0xb4, 0x4c, 0x47, 0xef, 0x56, 0x65, 0x26, + 0xfb, 0xc9, 0x38, 0xd9, 0xfb, 0x9c, 0x7e, 0x5f, 0x90, 0x37, 0x12, 0xea, 0x8c, 0x15, 0x1e, 0xe2, + 0x52, 0xcd, 0x16, 0x76, 0x1c, 0x5f, 0xea, 0xec, 0x24, 0xa9, 0x8c, 0x3e, 0x2c, 0x35, 0x34, 0x84, + 0xea, 0x50, 0xc4, 0xa7, 0x94, 0x5d, 0x3b, 0x31, 0x09, 0xae, 0x22, 0x26, 0x50, 0x89, 0x3d, 0xa1, + 0x8c, 0xf4, 0x81, 0x49, 0x70, 0x23, 0xa1, 0x02, 0xf6, 0x7a, 0x48, 0x87, 0x0b, 0x27, 0xd8, 0x36, + 0x8e, 0xce, 0x98, 0x18, 0x8d, 0xcd, 0x38, 0x86, 0xd9, 0xaf, 0xce, 0x31, 0x81, 0x4f, 0xc7, 0x09, + 0x7c, 0xc0, 0x98, 0xa8, 0x88, 0xba, 0xcb, 0xd2, 0x48, 0xa8, 0x73, 0x27, 0xa3, 0xc3, 0xd4, 0xc4, + 0x8e, 0x8c, 0xbe, 0xde, 0x35, 0xde, 0xc7, 0xda, 0x61, 0xd7, 0x6c, 0x3d, 0xac, 0xce, 0x8f, 0x37, + 0xb1, 0x2d, 0x41, 0xbd, 0x41, 0x89, 0xa9, 0x89, 0x1d, 0x05, 0x07, 0xd0, 0x8f, 0xa1, 0x60, 0xf4, + 0x1d, 0x6c, 0x13, 0x7a, 0xf6, 0x2e, 0x30, 0x51, 0xcb, 0xf1, 0x87, 0x9e, 0x12, 0xb2, 0xc3, 0x97, + 0x37, 0x44, 0x9b, 0x9e, 0x5d, 0x1b, 0xeb, 0x96, 0x46, 0x4e, 0x9d, 0xea, 0xc2, 0xf8, 0xb3, 0xab, + 0x62, 0xdd, 0x6a, 0x9e, 0xd2, 0x73, 0x93, 0xb3, 0x79, 0x73, 0x23, 0x07, 0x99, 0x13, 0xbd, 0x3b, + 0xc0, 0x3b, 0xe9, 0x7c, 0x5a, 0xce, 0xec, 0xa4, 0xf3, 0x39, 0x39, 0xbf, 0x93, 0xce, 0x17, 0x64, + 0xd8, 0x49, 0xe7, 0x41, 0x2e, 0x2a, 0x4f, 0x42, 0x31, 0xe0, 0x17, 0x51, 0x15, 0x72, 0x3d, 0xec, + 0x38, 0xfa, 0x31, 0x66, 0x6e, 0xb4, 0xa0, 0xba, 0x5d, 0xa5, 0x02, 0xa5, 0xa0, 0x2f, 0x54, 0x3e, + 0x92, 0x3c, 0x4e, 0xea, 0xe6, 0x28, 0xe7, 0x09, 0xb6, 0xd9, 0x6e, 0x08, 0x4e, 0xd1, 0x45, 0xd7, + 0xa0, 0xcc, 0x34, 0xa9, 0xb9, 0xf3, 0xd4, 0xd7, 0xa6, 0xd5, 0x12, 0x1b, 0x7c, 0x20, 0x88, 0x96, + 0xa0, 0x68, 0xad, 0x59, 0x1e, 0x49, 0x8a, 0x91, 0x80, 0xb5, 0x66, 0xb9, 0x04, 0x57, 0xa1, 0x44, + 0x7f, 0xab, 0x47, 0x91, 0x66, 0x1f, 0x29, 0xd2, 0x31, 0x41, 0xa2, 0xfc, 0x35, 0x09, 0xf2, 0xb0, + 0xff, 0x44, 0x2f, 0x42, 0x9a, 0x86, 0x2c, 0x11, 0x15, 0x6a, 0x2b, 0x3c, 0x9e, 0xad, 0xb8, 0xf1, + 0x6c, 0xa5, 0xe9, 0xc6, 0xb3, 0x8d, 0xfc, 0x17, 0x5f, 0x2d, 0x25, 0x3e, 0xfa, 0x7a, 0x49, 0x52, + 0x19, 0x07, 0xba, 0x44, 0xbd, 0xa6, 0x6e, 0xf4, 0x35, 0xa3, 0xcd, 0x96, 0x5c, 0xa0, 0x2e, 0x51, + 0x37, 0xfa, 0xdb, 0x6d, 0x74, 0x0f, 0xe4, 0x96, 0xd9, 0x77, 0x70, 0xdf, 0x19, 0x38, 0x1a, 0x0f, + 0x59, 0x22, 0x16, 0x84, 0x3c, 0x3a, 0x8f, 0xc7, 0x9b, 0x2e, 0xe5, 0x3e, 0x23, 0x54, 0x67, 0x5a, + 0xe1, 0x01, 0xb4, 0x05, 0xe0, 0xc5, 0x35, 0xa7, 0x9a, 0x5e, 0x4e, 0x45, 0x1a, 0xc9, 0x03, 0x97, + 0xe4, 0xbe, 0xd5, 0xd6, 0x09, 0xde, 0x48, 0xd3, 0xe5, 0xaa, 0x01, 0x4e, 0xf4, 0x04, 0xcc, 0xe8, + 0x96, 0xa5, 0x39, 0x44, 0x27, 0x58, 0x3b, 0x3c, 0x23, 0xd8, 0x61, 0x61, 0xa6, 0xa4, 0x96, 0x75, + 0xcb, 0x3a, 0xa0, 0xa3, 0x1b, 0x74, 0x10, 0x3d, 0x0e, 0x15, 0x1a, 0x52, 0x0c, 0xbd, 0xab, 0x75, + 0xb0, 0x71, 0xdc, 0x21, 0x2c, 0x9c, 0xa4, 0xd4, 0xb2, 0x18, 0x6d, 0xb0, 0x41, 0xa5, 0xed, 0xed, + 0x38, 0x0b, 0x27, 0x08, 0x41, 0xba, 0xad, 0x13, 0x9d, 0x69, 0xb2, 0xa4, 0xb2, 0x36, 0x1d, 0xb3, + 0x74, 0xd2, 0x11, 0xfa, 0x61, 0x6d, 0xb4, 0x00, 0x59, 0x21, 0x36, 0xc5, 0xc4, 0x8a, 0x1e, 0x9a, + 0x87, 0x8c, 0x65, 0x9b, 0x27, 0x98, 0x6d, 0x5d, 0x5e, 0xe5, 0x1d, 0x45, 0x85, 0x4a, 0x38, 0xf4, + 0xa0, 0x0a, 0x24, 0xc9, 0xa9, 0xf8, 0x4a, 0x92, 0x9c, 0xa2, 0xe7, 0x20, 0x4d, 0x15, 0xc9, 0xbe, + 0x51, 0x89, 0x08, 0xb6, 0x82, 0xaf, 0x79, 0x66, 0x61, 0x95, 0x51, 0x2a, 0x57, 0x61, 0x66, 0xe8, + 0x48, 0x0d, 0x0b, 0x55, 0xb6, 0xbc, 0xcf, 0x8a, 0x53, 0x83, 0x2e, 0x43, 0xa1, 0xa7, 0x9f, 0x0a, + 0xbd, 0x49, 0xcc, 0xfe, 0xf2, 0x3d, 0xfd, 0x94, 0xab, 0xec, 0x22, 0xe4, 0xe8, 0xe4, 0xb1, 0xee, + 0x08, 0xeb, 0xcd, 0xf6, 0xf4, 0xd3, 0x57, 0x75, 0x47, 0x99, 0x81, 0x72, 0x28, 0xfa, 0x29, 0x0b, + 0x30, 0x1f, 0x15, 0xcc, 0x94, 0x8e, 0x37, 0x1e, 0x0a, 0x4a, 0xe8, 0x16, 0xe4, 0xbd, 0x68, 0xc6, + 0x6d, 0xf4, 0xd2, 0xc8, 0x2f, 0x74, 0x89, 0x55, 0x8f, 0x94, 0x1a, 0x27, 0xdd, 0xeb, 0x8e, 0x2e, + 0x72, 0x97, 0x92, 0x9a, 0xd3, 0x2d, 0xab, 0xa1, 0x3b, 0x1d, 0xe5, 0x6d, 0xa8, 0xc6, 0x45, 0xaa, + 0xc0, 0xde, 0xf0, 0x5f, 0xe8, 0xee, 0xcd, 0x02, 0x64, 0x8f, 0x4c, 0xbb, 0xa7, 0x13, 0x26, 0xac, + 0xac, 0x8a, 0x1e, 0xdd, 0x33, 0x1e, 0xb5, 0x52, 0x6c, 0x98, 0x77, 0x14, 0x0d, 0x2e, 0xc5, 0x46, + 0x2b, 0xca, 0x62, 0xf4, 0xdb, 0x98, 0x2b, 0xbb, 0xac, 0xf2, 0x8e, 0x2f, 0x88, 0x2f, 0x96, 0x77, + 0xe8, 0x67, 0x1d, 0xf6, 0x5b, 0x99, 0xfc, 0x82, 0x2a, 0x7a, 0xca, 0xc7, 0x29, 0x58, 0x88, 0x8e, + 0x59, 0x68, 0x19, 0x4a, 0x74, 0x27, 0x48, 0x70, 0xa7, 0x52, 0x2a, 0xf4, 0xf4, 0xd3, 0xa6, 0xd8, + 0x2b, 0x19, 0x52, 0xd4, 0x59, 0x26, 0x97, 0x53, 0xd7, 0x4b, 0x2a, 0x6d, 0xa2, 0xfb, 0x30, 0xdb, + 0x35, 0x5b, 0x7a, 0x57, 0xeb, 0xea, 0x0e, 0xd1, 0x44, 0x32, 0xc3, 0xcf, 0xeb, 0xb5, 0x11, 0x65, + 0xf3, 0xe8, 0x83, 0xdb, 0x7c, 0x3f, 0xa9, 0x6f, 0x13, 0x47, 0x6d, 0x86, 0xc9, 0xb8, 0xa7, 0xbb, + 0x5b, 0x8d, 0xee, 0x40, 0xb1, 0x67, 0x38, 0x87, 0xb8, 0xa3, 0x9f, 0x18, 0xa6, 0x2d, 0x0e, 0xee, + 0xa8, 0x7d, 0xbe, 0xe6, 0xd3, 0x08, 0x49, 0x41, 0xb6, 0xc0, 0x96, 0x64, 0x42, 0xc7, 0xc5, 0x75, + 0x5c, 0xd9, 0x73, 0x3b, 0xae, 0xe7, 0x60, 0xbe, 0x8f, 0x4f, 0x89, 0xe6, 0xbb, 0x06, 0x6e, 0x27, + 0x39, 0xa6, 0x7a, 0x44, 0xe7, 0x3c, 0x67, 0xe2, 0x50, 0x93, 0x41, 0x4f, 0xb1, 0xa8, 0x6f, 0x99, + 0x0e, 0xb6, 0x35, 0xbd, 0xdd, 0xb6, 0xb1, 0xe3, 0xb0, 0x44, 0xb1, 0xc4, 0x42, 0x39, 0x1b, 0x5f, + 0xe7, 0xc3, 0xca, 0x67, 0xc1, 0xad, 0x09, 0x47, 0x79, 0xa1, 0x78, 0xc9, 0x57, 0xfc, 0x01, 0xcc, + 0x0b, 0xfe, 0x76, 0x48, 0xf7, 0x3c, 0xdb, 0xbe, 0x3c, 0x7a, 0x94, 0x87, 0x75, 0x8e, 0x5c, 0xf6, + 0x78, 0xb5, 0xa7, 0x1e, 0x4d, 0xed, 0x08, 0xd2, 0x4c, 0x29, 0x69, 0xee, 0xcd, 0x68, 0xfb, 0xbf, + 0x6c, 0x2b, 0xd0, 0x1a, 0x5c, 0x60, 0xc2, 0x47, 0xe8, 0x0b, 0x8c, 0x7e, 0x8e, 0x4e, 0xee, 0x0f, + 0x6d, 0xdf, 0x07, 0x29, 0x98, 0x1d, 0x49, 0xb3, 0x3c, 0x65, 0x48, 0x91, 0xca, 0x48, 0x46, 0x2a, + 0x23, 0x75, 0x6e, 0x65, 0x08, 0xfb, 0x48, 0x4f, 0xb6, 0x8f, 0xcc, 0xf7, 0x68, 0x1f, 0xd9, 0x47, + 0xb3, 0x8f, 0xff, 0xe8, 0x21, 0xfa, 0x9d, 0x04, 0xb5, 0xf8, 0xdc, 0x34, 0x72, 0x3b, 0x9e, 0x86, + 0x59, 0x6f, 0x29, 0x9e, 0x78, 0xee, 0x4c, 0x65, 0x6f, 0xc2, 0xb5, 0x8c, 0xb8, 0x10, 0xfc, 0x38, + 0x54, 0x86, 0x32, 0x67, 0x6e, 0xfe, 0xe5, 0x93, 0xe0, 0xf7, 0x95, 0x3f, 0xa6, 0xbc, 0x60, 0x15, + 0x4a, 0x6f, 0x23, 0x4e, 0xf8, 0xeb, 0x30, 0xd7, 0xc6, 0x2d, 0xa3, 0xfd, 0xa8, 0x07, 0x7c, 0x56, + 0x70, 0xff, 0xff, 0x7c, 0x8f, 0x9e, 0xef, 0x1b, 0x30, 0xeb, 0x9c, 0xf5, 0x5b, 0x46, 0xff, 0x58, + 0x23, 0xa6, 0x9b, 0xaa, 0x15, 0xd8, 0xca, 0x67, 0xc4, 0x44, 0xd3, 0x14, 0xc9, 0xda, 0x6f, 0x8b, + 0x90, 0x57, 0xb1, 0x63, 0xd1, 0xd4, 0x12, 0x6d, 0x40, 0x01, 0x9f, 0xb6, 0xb0, 0x45, 0xdc, 0x6c, + 0x3c, 0xba, 0xd8, 0xe2, 0xd4, 0x75, 0x97, 0xb2, 0x91, 0x50, 0x7d, 0x36, 0x74, 0x53, 0xa0, 0x29, + 0xf1, 0xc0, 0x88, 0x60, 0x0f, 0xc2, 0x29, 0xcf, 0xbb, 0x70, 0x4a, 0x2a, 0x16, 0x29, 0xe0, 0x5c, + 0x43, 0x78, 0xca, 0x4d, 0x81, 0xa7, 0xa4, 0x27, 0x7c, 0x2c, 0x04, 0xa8, 0x6c, 0x86, 0x00, 0x95, + 0xec, 0x84, 0x9f, 0x19, 0x83, 0xa8, 0x3c, 0xef, 0x22, 0x2a, 0xb9, 0x09, 0x2b, 0x1e, 0x82, 0x54, + 0x5e, 0x09, 0x40, 0x2a, 0x85, 0xd8, 0xb2, 0x8e, 0xb3, 0x46, 0x60, 0x2a, 0x2f, 0x79, 0x98, 0x4a, + 0x29, 0xb6, 0xa6, 0x13, 0xcc, 0xc3, 0xa0, 0xca, 0xde, 0x08, 0xa8, 0xc2, 0x41, 0x90, 0x27, 0x62, + 0x45, 0x4c, 0x40, 0x55, 0xf6, 0x46, 0x50, 0x95, 0xca, 0x04, 0x81, 0x13, 0x60, 0x95, 0x9f, 0x47, + 0xc3, 0x2a, 0xf1, 0xc0, 0x87, 0x58, 0xe6, 0x74, 0xb8, 0x8a, 0x16, 0x83, 0xab, 0xc8, 0xb1, 0x18, + 0x00, 0x17, 0x3f, 0x35, 0xb0, 0x72, 0x3f, 0x02, 0x58, 0xe1, 0x10, 0xc8, 0xf5, 0x58, 0xe1, 0x53, + 0x20, 0x2b, 0xf7, 0x23, 0x90, 0x15, 0x34, 0x51, 0xec, 0x44, 0x68, 0x65, 0x2b, 0x0c, 0xad, 0xcc, + 0xc5, 0x64, 0xb5, 0xfe, 0x69, 0x8f, 0xc1, 0x56, 0x0e, 0xe3, 0xb0, 0x15, 0x8e, 0x7f, 0x3c, 0x13, + 0x2b, 0xf1, 0x1c, 0xe0, 0xca, 0xde, 0x08, 0xb8, 0x72, 0x61, 0x82, 0xa5, 0x4d, 0x40, 0x57, 0x7e, + 0x12, 0x44, 0x57, 0x16, 0x62, 0x21, 0x55, 0xd7, 0x03, 0x44, 0xc0, 0x2b, 0xaf, 0x04, 0xe0, 0x95, + 0x8b, 0x13, 0xce, 0xf1, 0x78, 0x7c, 0x25, 0x23, 0x67, 0x77, 0xd2, 0xf9, 0xbc, 0x5c, 0xe0, 0xc8, + 0xca, 0x4e, 0x3a, 0x5f, 0x94, 0x4b, 0xca, 0x53, 0x34, 0xdd, 0x1a, 0x72, 0xb4, 0xb4, 0x18, 0xc2, + 0xb6, 0x6d, 0xda, 0x02, 0x29, 0xe1, 0x1d, 0xe5, 0x3a, 0xad, 0xb7, 0x7d, 0xa7, 0x3a, 0x06, 0x8b, + 0x61, 0x45, 0x67, 0xc0, 0x91, 0x2a, 0x7f, 0x92, 0x7c, 0x5e, 0x86, 0xc6, 0x04, 0x6b, 0xf5, 0x82, + 0xa8, 0xd5, 0x03, 0x08, 0x4d, 0x32, 0x8c, 0xd0, 0x2c, 0x41, 0x91, 0x16, 0x93, 0x43, 0xe0, 0x8b, + 0x6e, 0x79, 0xe0, 0xcb, 0x0d, 0x98, 0x65, 0xd1, 0x9d, 0xe3, 0x38, 0x22, 0x12, 0xa5, 0x79, 0x24, + 0xa2, 0x13, 0x7c, 0x7b, 0x78, 0x30, 0x7d, 0x16, 0xe6, 0x02, 0xb4, 0x5e, 0x91, 0xca, 0x91, 0x08, + 0xd9, 0xa3, 0x5e, 0x17, 0xd5, 0xea, 0x5f, 0x24, 0x5f, 0x43, 0x3e, 0x6a, 0x13, 0x05, 0xb0, 0x48, + 0xdf, 0x13, 0xc0, 0x92, 0x7c, 0x64, 0x80, 0x25, 0x58, 0x74, 0xa7, 0xc2, 0x45, 0xf7, 0xbf, 0x24, + 0x7f, 0x4f, 0x3c, 0xb8, 0xa4, 0x65, 0xb6, 0xb1, 0x28, 0x83, 0x59, 0x9b, 0xe6, 0x4f, 0x5d, 0xf3, + 0x58, 0x14, 0xbb, 0xb4, 0x49, 0xa9, 0xbc, 0xc8, 0x57, 0x10, 0x81, 0xcd, 0xab, 0xa0, 0x79, 0x96, + 0x22, 0x2a, 0x68, 0x19, 0x52, 0x0f, 0x31, 0x47, 0xfe, 0x4b, 0x2a, 0x6d, 0x52, 0x3a, 0x66, 0x7c, + 0x22, 0xdb, 0xe0, 0x1d, 0xf4, 0x22, 0x14, 0xd8, 0x15, 0x8b, 0x66, 0x5a, 0x8e, 0x40, 0xfb, 0x43, + 0x79, 0x18, 0xbf, 0x86, 0x59, 0xd9, 0xa7, 0x34, 0x7b, 0x96, 0xa3, 0xe6, 0x2d, 0xd1, 0x0a, 0xa4, + 0x47, 0x85, 0x50, 0x7a, 0x74, 0x05, 0x0a, 0x74, 0xf5, 0x8e, 0xa5, 0xb7, 0x70, 0x15, 0xd8, 0x42, + 0xfd, 0x01, 0xe5, 0xd3, 0x24, 0xcc, 0x0c, 0x45, 0xba, 0xc8, 0xdf, 0xee, 0x9a, 0x64, 0x32, 0x00, + 0x1f, 0x4d, 0xa7, 0x8f, 0x45, 0x80, 0x63, 0xdd, 0xd1, 0xde, 0xd3, 0xfb, 0x04, 0xb7, 0x85, 0x52, + 0x02, 0x23, 0xa8, 0x06, 0x79, 0xda, 0x1b, 0x38, 0xb8, 0x2d, 0x90, 0x2c, 0xaf, 0x8f, 0x1a, 0x90, + 0xc5, 0x27, 0xb8, 0x4f, 0x9c, 0x6a, 0x8e, 0x6d, 0xfb, 0xc2, 0x68, 0xbd, 0x4f, 0xa7, 0x37, 0xaa, + 0x74, 0xb3, 0xff, 0xf1, 0xd5, 0x92, 0xcc, 0xa9, 0x9f, 0x31, 0x7b, 0x06, 0xc1, 0x3d, 0x8b, 0x9c, + 0xa9, 0x82, 0x3f, 0xac, 0x85, 0xfc, 0x90, 0x16, 0x18, 0xa6, 0x5a, 0x72, 0xf1, 0x0b, 0xaa, 0x53, + 0xc3, 0xb4, 0x0d, 0x72, 0xa6, 0x96, 0x7b, 0xb8, 0x67, 0x99, 0x66, 0x57, 0xe3, 0x67, 0xfc, 0x09, + 0x90, 0x87, 0xdd, 0x51, 0x94, 0xb2, 0x94, 0x6b, 0xbe, 0x4e, 0x5d, 0x7c, 0x6a, 0x24, 0xf7, 0x56, + 0xd6, 0xa1, 0x12, 0xce, 0x12, 0xd0, 0x35, 0x28, 0xdb, 0x98, 0xe8, 0x46, 0x5f, 0x0b, 0xa5, 0xff, + 0x25, 0x3e, 0xc8, 0x0f, 0xe8, 0x4e, 0x3a, 0x2f, 0xc9, 0xc9, 0x9d, 0x74, 0x3e, 0x29, 0xa7, 0x94, + 0x7d, 0xb8, 0x10, 0x99, 0x25, 0xa0, 0x17, 0xa0, 0xe0, 0x27, 0x18, 0x12, 0x53, 0xdd, 0x18, 0x5c, + 0xca, 0xa7, 0x55, 0x3e, 0x97, 0x7c, 0x91, 0x61, 0xa4, 0xab, 0x0e, 0x59, 0x1b, 0x3b, 0x83, 0x2e, + 0xc7, 0x9e, 0x2a, 0x6b, 0xcf, 0x4e, 0x97, 0x5f, 0xd0, 0xd1, 0x41, 0x97, 0xa8, 0x82, 0x59, 0x79, + 0x0b, 0xb2, 0x7c, 0x04, 0x15, 0x21, 0x77, 0x7f, 0xf7, 0xee, 0xee, 0xde, 0x1b, 0xbb, 0x72, 0x02, + 0x01, 0x64, 0xd7, 0x37, 0x37, 0xeb, 0xfb, 0x4d, 0x59, 0x42, 0x05, 0xc8, 0xac, 0x6f, 0xec, 0xa9, + 0x4d, 0x39, 0x49, 0x87, 0xd5, 0xfa, 0x4e, 0x7d, 0xb3, 0x29, 0xa7, 0xd0, 0x2c, 0x94, 0x79, 0x5b, + 0xdb, 0xda, 0x53, 0x5f, 0x5b, 0x6f, 0xca, 0xe9, 0xc0, 0xd0, 0x41, 0x7d, 0xf7, 0x4e, 0x5d, 0x95, + 0x33, 0xca, 0x0f, 0xe0, 0x52, 0x6c, 0x46, 0xe2, 0xc3, 0x58, 0x52, 0x00, 0xc6, 0x52, 0x3e, 0x4e, + 0xd2, 0x72, 0x2e, 0x2e, 0xcd, 0x40, 0x3b, 0x43, 0x3f, 0x7c, 0xed, 0x1c, 0x39, 0xca, 0xd0, 0xaf, + 0xa7, 0x15, 0x9c, 0x8d, 0x8f, 0x30, 0x69, 0x75, 0x78, 0xda, 0xc3, 0xdd, 0x59, 0x59, 0x2d, 0x8b, + 0x51, 0xc6, 0xe4, 0x70, 0xb2, 0x77, 0x70, 0x8b, 0x68, 0xdc, 0x22, 0x1d, 0x56, 0x46, 0x15, 0x28, + 0x19, 0x1d, 0x3d, 0xe0, 0x83, 0xca, 0xdb, 0xe7, 0xd2, 0x65, 0x01, 0x32, 0x6a, 0xbd, 0xa9, 0xbe, + 0x29, 0xa7, 0x10, 0x82, 0x0a, 0x6b, 0x6a, 0x07, 0xbb, 0xeb, 0xfb, 0x07, 0x8d, 0x3d, 0xaa, 0xcb, + 0x39, 0x98, 0x71, 0x75, 0xe9, 0x0e, 0x66, 0x94, 0xa7, 0xe1, 0x62, 0x4c, 0x8e, 0x14, 0x61, 0xd0, + 0xbf, 0x97, 0x82, 0xd4, 0xe1, 0x3c, 0x67, 0x0f, 0xb2, 0x0e, 0xd1, 0xc9, 0xc0, 0x11, 0x4a, 0x7c, + 0x61, 0xda, 0xa4, 0x69, 0xc5, 0x6d, 0x1c, 0x30, 0x76, 0x55, 0x88, 0x51, 0x6e, 0x41, 0x25, 0x3c, + 0x13, 0xaf, 0x03, 0xdf, 0x88, 0x92, 0xca, 0x6d, 0x40, 0xa3, 0xb9, 0x54, 0x44, 0x61, 0x2d, 0x45, + 0x15, 0xd6, 0x7f, 0x90, 0xe0, 0xf2, 0x98, 0xbc, 0x09, 0xbd, 0x3e, 0xf4, 0x23, 0x5f, 0x3a, 0x4f, + 0xd6, 0xb5, 0xc2, 0xc7, 0x86, 0x7e, 0xe6, 0x4d, 0x28, 0x05, 0xc7, 0xa7, 0xfb, 0x91, 0x7f, 0x4e, + 0xf9, 0x87, 0x38, 0x8c, 0x00, 0xf8, 0xfe, 0x54, 0xfa, 0x8e, 0xfe, 0xf4, 0x65, 0x00, 0x72, 0xaa, + 0x71, 0xb3, 0x76, 0x83, 0xf2, 0x63, 0x11, 0x68, 0x2c, 0x6e, 0x35, 0x4f, 0xc5, 0x21, 0x28, 0x10, + 0xd1, 0x72, 0xd0, 0x41, 0x10, 0x0e, 0x19, 0xb0, 0x80, 0xed, 0x08, 0xa8, 0x60, 0xda, 0xc8, 0xee, + 0xc3, 0x26, 0x7c, 0xd8, 0x41, 0x6f, 0xc2, 0xc5, 0xa1, 0xac, 0xc3, 0x13, 0x9d, 0x9e, 0x36, 0xf9, + 0xb8, 0x10, 0x4e, 0x3e, 0x5c, 0xd1, 0xc1, 0xd4, 0x21, 0x13, 0x4a, 0x1d, 0xd0, 0x6b, 0x20, 0x33, + 0x0c, 0x81, 0x27, 0x4c, 0x6d, 0xdc, 0xd5, 0xdd, 0xfb, 0xfd, 0x4b, 0x23, 0x48, 0xc4, 0x1d, 0xf1, + 0x3a, 0x83, 0x03, 0x11, 0x1f, 0x7f, 0xbd, 0x24, 0xa9, 0x15, 0xca, 0xcc, 0x36, 0xe7, 0x0e, 0x65, + 0x55, 0xde, 0x04, 0xf0, 0x51, 0x16, 0xea, 0xb0, 0x6c, 0x73, 0xd0, 0x6f, 0x33, 0x83, 0xca, 0xa8, + 0xbc, 0x83, 0x6e, 0x41, 0x86, 0x1a, 0xa6, 0xab, 0xf6, 0x51, 0xcf, 0x4e, 0x0d, 0x2b, 0x80, 0xd2, + 0x70, 0x6a, 0xc5, 0x00, 0x34, 0x8a, 0x8e, 0xc7, 0x7c, 0xe2, 0x95, 0xf0, 0x27, 0xae, 0xc6, 0xe2, + 0xec, 0xd1, 0x9f, 0x7a, 0x1f, 0x32, 0xcc, 0x90, 0x68, 0x74, 0x64, 0xb7, 0x3f, 0x22, 0x93, 0xa5, + 0x6d, 0xf4, 0x0b, 0x00, 0x9d, 0x10, 0xdb, 0x38, 0x1c, 0xf8, 0x1f, 0x58, 0x8a, 0x36, 0xc4, 0x75, + 0x97, 0x6e, 0xe3, 0x8a, 0xb0, 0xc8, 0x79, 0x9f, 0x35, 0x60, 0x95, 0x01, 0x81, 0xca, 0x2e, 0x54, + 0xc2, 0xbc, 0x6e, 0xee, 0xc5, 0xd7, 0x10, 0xce, 0xbd, 0x78, 0x2a, 0x2d, 0x72, 0x2f, 0x2f, 0x73, + 0x4b, 0xf1, 0x2b, 0x2e, 0xd6, 0x51, 0x7e, 0x99, 0x84, 0x52, 0xd0, 0x8e, 0xff, 0xf7, 0xd2, 0x23, + 0xe5, 0xd7, 0x12, 0xe4, 0xbd, 0x9f, 0x1f, 0xbe, 0x84, 0x0a, 0x5d, 0x10, 0x72, 0xed, 0x25, 0x83, + 0x37, 0x47, 0xfc, 0xe6, 0x2e, 0xe5, 0x5d, 0x07, 0xde, 0xf6, 0xa2, 0x69, 0x1c, 0x5a, 0x14, 0xd4, + 0xb5, 0xb0, 0x2a, 0x37, 0x79, 0xb8, 0x0d, 0x05, 0xcf, 0x19, 0xd0, 0x82, 0xc8, 0x45, 0xe0, 0x24, + 0x71, 0x24, 0x05, 0xf2, 0x36, 0x0f, 0x19, 0xcb, 0x7c, 0x4f, 0x5c, 0x4b, 0xa5, 0x54, 0xde, 0x51, + 0x3e, 0x95, 0x60, 0x66, 0xc8, 0x95, 0xa0, 0xdb, 0x90, 0xb3, 0x06, 0x87, 0x9a, 0x6b, 0x1d, 0x43, + 0x40, 0xa5, 0x9b, 0x6b, 0x0f, 0x0e, 0xbb, 0x46, 0xeb, 0x2e, 0x3e, 0x73, 0x57, 0x63, 0x0d, 0x0e, + 0xef, 0x72, 0x23, 0xe2, 0x9f, 0x49, 0x06, 0x3e, 0x83, 0x14, 0x28, 0x0b, 0x91, 0xe2, 0x8a, 0x8b, + 0xff, 0xf6, 0x22, 0x67, 0xe2, 0x77, 0x5c, 0xcb, 0x50, 0x72, 0x69, 0xd8, 0xe9, 0xe0, 0xf6, 0x00, + 0x9c, 0xa4, 0x79, 0x66, 0x61, 0xe5, 0x37, 0x12, 0xe4, 0xdd, 0xa3, 0x85, 0x7e, 0x04, 0x05, 0xcf, + 0xd9, 0x79, 0x37, 0xe1, 0xb1, 0x5e, 0x52, 0xac, 0xd2, 0x67, 0x41, 0xeb, 0xee, 0x15, 0xbe, 0xd1, + 0xd6, 0x8e, 0xba, 0x3a, 0x37, 0xc9, 0x4a, 0x58, 0xf5, 0xdc, 0x1d, 0x32, 0x47, 0xb4, 0x7d, 0x67, + 0xab, 0xab, 0x1f, 0xab, 0x45, 0xc6, 0xb3, 0xdd, 0xa6, 0x1d, 0x91, 0x6f, 0xfe, 0x53, 0x02, 0x79, + 0xf8, 0xe0, 0x7f, 0xe7, 0xd5, 0x8d, 0x06, 0xdf, 0x54, 0x44, 0xf0, 0x45, 0xab, 0x30, 0xe7, 0x51, + 0x68, 0x8e, 0x71, 0xdc, 0xd7, 0xc9, 0xc0, 0xc6, 0x02, 0x20, 0x46, 0xde, 0xd4, 0x81, 0x3b, 0x33, + 0xfa, 0xab, 0x33, 0x8f, 0xf8, 0xab, 0x3f, 0x48, 0x42, 0x31, 0x00, 0x57, 0xa3, 0x1f, 0x06, 0x7c, + 0x5a, 0x25, 0x22, 0x5e, 0x05, 0x68, 0xfd, 0x5b, 0xed, 0xb0, 0x9a, 0x92, 0xe7, 0x57, 0x53, 0xdc, + 0xa5, 0x80, 0x8b, 0x7e, 0xa7, 0xcf, 0x8d, 0x7e, 0x3f, 0x03, 0x88, 0x98, 0x44, 0xef, 0x6a, 0x27, + 0x26, 0x31, 0xfa, 0xc7, 0x1a, 0x37, 0x66, 0xee, 0x81, 0x64, 0x36, 0xf3, 0x80, 0x4d, 0xec, 0xb3, + 0xe3, 0xf3, 0x2b, 0x09, 0xf2, 0x5e, 0x31, 0x70, 0xde, 0x8b, 0xe8, 0x05, 0xc8, 0x8a, 0x7c, 0x97, + 0xdf, 0x44, 0x8b, 0x5e, 0x24, 0xcc, 0x5f, 0x83, 0x7c, 0x0f, 0x13, 0x9d, 0xb9, 0x53, 0x1e, 0x6b, + 0xbd, 0xfe, 0x8d, 0x97, 0xa0, 0x18, 0x78, 0x2f, 0x40, 0x3d, 0xec, 0x6e, 0xfd, 0x0d, 0x39, 0x51, + 0xcb, 0x7d, 0xf8, 0xc9, 0x72, 0x6a, 0x17, 0xbf, 0x47, 0x9d, 0x82, 0x5a, 0xdf, 0x6c, 0xd4, 0x37, + 0xef, 0xca, 0x52, 0xad, 0xf8, 0xe1, 0x27, 0xcb, 0x39, 0x15, 0x33, 0xd4, 0xf6, 0xc6, 0x5d, 0x98, + 0x19, 0xda, 0x98, 0x70, 0x32, 0x85, 0xa0, 0x72, 0xe7, 0xfe, 0xfe, 0xbd, 0xed, 0xcd, 0xf5, 0x66, + 0x5d, 0x7b, 0xb0, 0xd7, 0xac, 0xcb, 0x12, 0xba, 0x08, 0x73, 0xf7, 0xb6, 0x5f, 0x6d, 0x34, 0xb5, + 0xcd, 0x7b, 0xdb, 0xf5, 0xdd, 0xa6, 0xb6, 0xde, 0x6c, 0xae, 0x6f, 0xde, 0x95, 0x93, 0x6b, 0x9f, + 0x95, 0x20, 0xbd, 0xbe, 0xb1, 0xb9, 0x8d, 0x36, 0x21, 0xcd, 0xd0, 0x9e, 0xb1, 0xef, 0x15, 0x6b, + 0xe3, 0xf1, 0x77, 0xb4, 0x05, 0x19, 0x06, 0x04, 0xa1, 0xf1, 0x0f, 0x18, 0x6b, 0x13, 0x00, 0x79, + 0xba, 0x18, 0x76, 0x22, 0xc7, 0xbe, 0x68, 0xac, 0x8d, 0xc7, 0xe7, 0xd1, 0x3d, 0xc8, 0xb9, 0x38, + 0xc0, 0xa4, 0x67, 0x86, 0xb5, 0x89, 0xa0, 0x39, 0xda, 0x83, 0xbc, 0x57, 0x29, 0x4f, 0x7c, 0x39, + 0x55, 0x9b, 0x8c, 0xfe, 0xd1, 0xe5, 0xb9, 0x25, 0xf5, 0xa4, 0x97, 0x54, 0xb5, 0x89, 0x58, 0x20, + 0xd5, 0x3c, 0x87, 0x7b, 0xc6, 0xbf, 0xc5, 0xac, 0x4d, 0xb8, 0x58, 0x40, 0xdb, 0x90, 0x15, 0x35, + 0xfc, 0x84, 0xe7, 0x95, 0xb5, 0x49, 0x57, 0x05, 0x48, 0x85, 0x82, 0x0f, 0xa4, 0x4d, 0x7e, 0x61, + 0x5a, 0x9b, 0xe2, 0xce, 0x04, 0xbd, 0x05, 0xe5, 0x30, 0x3e, 0x30, 0xdd, 0x13, 0xce, 0xda, 0x94, + 0x97, 0x12, 0x54, 0x7e, 0x18, 0x2c, 0x98, 0xee, 0x49, 0x67, 0x6d, 0xca, 0x3b, 0x0a, 0xf4, 0x0e, + 0xcc, 0x8e, 0x16, 0xf3, 0xd3, 0xbf, 0xf0, 0xac, 0x9d, 0xe3, 0xd6, 0x02, 0xf5, 0x00, 0x45, 0x80, + 0x00, 0xe7, 0x78, 0xf0, 0x59, 0x3b, 0xcf, 0x25, 0x06, 0x6a, 0xc3, 0xcc, 0x70, 0x65, 0x3d, 0xed, + 0x03, 0xd0, 0xda, 0xd4, 0x17, 0x1a, 0xfc, 0x2b, 0xe1, 0x8a, 0x7c, 0xda, 0x07, 0xa1, 0xb5, 0xa9, + 0xef, 0x37, 0xd0, 0x7d, 0x80, 0x40, 0x51, 0x3d, 0xc5, 0x03, 0xd1, 0xda, 0x34, 0x37, 0x1d, 0xc8, + 0x82, 0xb9, 0xa8, 0x6a, 0xfb, 0x3c, 0xef, 0x45, 0x6b, 0xe7, 0xba, 0x00, 0xa1, 0xf6, 0x1c, 0xae, + 0x9b, 0xa7, 0x7b, 0x3f, 0x5a, 0x9b, 0xf2, 0x26, 0x64, 0x63, 0xfd, 0x8b, 0x6f, 0x16, 0xa5, 0x2f, + 0xbf, 0x59, 0x94, 0xfe, 0xfe, 0xcd, 0xa2, 0xf4, 0xd1, 0xb7, 0x8b, 0x89, 0x2f, 0xbf, 0x5d, 0x4c, + 0xfc, 0xed, 0xdb, 0xc5, 0xc4, 0x4f, 0x9f, 0x3c, 0x36, 0x48, 0x67, 0x70, 0xb8, 0xd2, 0x32, 0x7b, + 0xab, 0x2d, 0xb3, 0x87, 0xc9, 0xe1, 0x11, 0xf1, 0x1b, 0xfe, 0xdf, 0x04, 0x0e, 0xb3, 0x2c, 0xc0, + 0xdf, 0xfc, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9b, 0x55, 0xf0, 0xfd, 0x46, 0x30, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -5834,6 +5894,13 @@ func (m *RequestProcessProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) _ = i var l int _ = l + if len(m.NextProposerAddress) > 0 { + i -= len(m.NextProposerAddress) + copy(dAtA[i:], m.NextProposerAddress) + i = encodeVarintTypes(dAtA, i, uint64(len(m.NextProposerAddress))) + i-- + dAtA[i] = 0x4a + } if len(m.ProposerAddress) > 0 { i -= len(m.ProposerAddress) copy(dAtA[i:], m.ProposerAddress) @@ -6063,6 +6130,11 @@ func (m *RequestFinalizeBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.SyncingToHeight != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.SyncingToHeight)) + i-- + dAtA[i] = 0x48 + } if len(m.ProposerAddress) > 0 { i -= len(m.ProposerAddress) copy(dAtA[i:], m.ProposerAddress) @@ -7314,6 +7386,14 @@ func (m *ResponseFinalizeBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + n53, err53 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.NextBlockDelay, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.NextBlockDelay):]) + if err53 != nil { + return 0, err53 + } + i -= n53 + i = encodeVarintTypes(dAtA, i, uint64(n53)) + i-- + dAtA[i] = 0x32 if len(m.AppHash) > 0 { i -= len(m.AppHash) copy(dAtA[i:], m.AppHash) @@ -7738,6 +7818,20 @@ func (m *ValidatorUpdate) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.PubKeyType) > 0 { + i -= len(m.PubKeyType) + copy(dAtA[i:], m.PubKeyType) + i = encodeVarintTypes(dAtA, i, uint64(len(m.PubKeyType))) + i-- + dAtA[i] = 0x22 + } + if len(m.PubKeyBytes) > 0 { + i -= len(m.PubKeyBytes) + copy(dAtA[i:], m.PubKeyBytes) + i = encodeVarintTypes(dAtA, i, uint64(len(m.PubKeyBytes))) + i-- + dAtA[i] = 0x1a + } if m.Power != 0 { i = encodeVarintTypes(dAtA, i, uint64(m.Power)) i-- @@ -7871,12 +7965,12 @@ func (m *Misbehavior) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x28 } - n58, err58 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err58 != nil { - return 0, err58 + n59, err59 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) + if err59 != nil { + return 0, err59 } - i -= n58 - i = encodeVarintTypes(dAtA, i, uint64(n58)) + i -= n59 + i = encodeVarintTypes(dAtA, i, uint64(n59)) i-- dAtA[i] = 0x22 if m.Height != 0 { @@ -8486,6 +8580,10 @@ func (m *RequestProcessProposal) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + l = len(m.NextProposerAddress) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -8590,6 +8688,9 @@ func (m *RequestFinalizeBlock) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + if m.SyncingToHeight != 0 { + n += 1 + sovTypes(uint64(m.SyncingToHeight)) + } return n } @@ -9188,6 +9289,8 @@ func (m *ResponseFinalizeBlock) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.NextBlockDelay) + n += 1 + l + sovTypes(uint64(l)) return n } @@ -9354,6 +9457,14 @@ func (m *ValidatorUpdate) Size() (n int) { if m.Power != 0 { n += 1 + sovTypes(uint64(m.Power)) } + l = len(m.PubKeyBytes) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.PubKeyType) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -12111,6 +12222,40 @@ func (m *RequestProcessProposal) Unmarshal(dAtA []byte) error { m.ProposerAddress = []byte{} } iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextProposerAddress", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NextProposerAddress = append(m.NextProposerAddress[:0], dAtA[iNdEx:postIndex]...) + if m.NextProposerAddress == nil { + m.NextProposerAddress = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -12888,6 +13033,25 @@ func (m *RequestFinalizeBlock) Unmarshal(dAtA []byte) error { m.ProposerAddress = []byte{} } iNdEx = postIndex + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field SyncingToHeight", wireType) + } + m.SyncingToHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.SyncingToHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -15895,6 +16059,39 @@ func (m *ResponseFinalizeBlock) Unmarshal(dAtA []byte) error { m.AppHash = []byte{} } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextBlockDelay", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.NextBlockDelay, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -16982,6 +17179,72 @@ func (m *ValidatorUpdate) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKeyBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PubKeyBytes = append(m.PubKeyBytes[:0], dAtA[iNdEx:postIndex]...) + if m.PubKeyBytes == nil { + m.PubKeyBytes = []byte{} + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKeyType", 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.PubKeyType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/blocksync/reactor.go b/blocksync/reactor.go index 510d7225df8..b7eef18e0e4 100644 --- a/blocksync/reactor.go +++ b/blocksync/reactor.go @@ -307,7 +307,7 @@ func (r *Reactor) respondToPeer(msg *bcproto.BlockRequest, src p2p.Peer) { } var extCommit *types.ExtendedCommit - if state.ConsensusParams.ABCI.VoteExtensionsEnabled(msg.Height) { + if state.ConsensusParams.Feature.VoteExtensionsEnabled(msg.Height) { extCommit = r.store.LoadBlockExtendedCommit(msg.Height) if extCommit == nil { r.Logger.Error("Found block in store with no extended commit", "height", msg.Height) @@ -529,7 +529,7 @@ FOR_LOOP: // missingExtension := true if state.LastBlockHeight == 0 || - !state.ConsensusParams.ABCI.VoteExtensionsEnabled(state.LastBlockHeight) || + !state.ConsensusParams.Feature.VoteExtensionsEnabled(state.LastBlockHeight) || blocksSynced > 0 || initialCommitHasExtensions { missingExtension = false @@ -624,7 +624,7 @@ FOR_LOOP: // vote extension validations presentExtCommit := extCommit != nil - extensionsEnabled := state.ConsensusParams.ABCI.VoteExtensionsEnabled(first.Height) + extensionsEnabled := state.ConsensusParams.Feature.VoteExtensionsEnabled(first.Height) if presentExtCommit != extensionsEnabled { err = fmt.Errorf("non-nil extended commit must be received iff vote extensions are enabled for its height "+ @@ -685,7 +685,7 @@ FOR_LOOP: // TODO: same thing for app - but we would need a way to // get the hash without persisting the state - state, err = r.blockExec.ApplyVerifiedBlock(state, firstID, first) + state, err = r.blockExec.ApplyVerifiedBlock(state, firstID, first, r.pool.MaxPeerHeight()) if err != nil { // TODO This is bad, are we zombie? panic(fmt.Sprintf("Failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err)) diff --git a/blocksync/reactor_test.go b/blocksync/reactor_test.go index 962e9723bfd..282182ac60c 100644 --- a/blocksync/reactor_test.go +++ b/blocksync/reactor_test.go @@ -54,7 +54,7 @@ func genesisDocWithValsPowers(powers []int64) (*types.GenesisDoc, []types.PrivVa sort.Sort(types.PrivValidatorsByAddress(privValidators)) consPar := types.DefaultConsensusParams() - consPar.ABCI.VoteExtensionsEnableHeight = 1 + consPar.Feature.VoteExtensionsEnableHeight = 1 return &types.GenesisDoc{ GenesisTime: cmttime.Now(), ChainID: test.DefaultTestChainID, @@ -165,12 +165,18 @@ func newReactor( // let's add some blocks in for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { - voteExtensionIsEnabled := genDoc.ConsensusParams.ABCI.VoteExtensionsEnabled(blockHeight) + voteExtensionIsEnabled := genDoc.ConsensusParams.Feature.VoteExtensionsEnabled(blockHeight) lastExtCommit := seenExtCommit.Clone() thisBlock, err := state.MakeBlock(blockHeight, nil, lastExtCommit.ToCommit(), nil, state.Validators.Proposer.Address) require.NoError(t, err) + if options.deterministicVoteTimes { + // Under PBTS the proposer stamps the block with its wall clock; + // pin it so independently constructed test chains with the same + // genesis produce identical block IDs and LastBlockID links. + thisBlock.Time = genDoc.GenesisTime.Add(time.Duration(blockHeight) * time.Second) + } thisParts, err := thisBlock.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) @@ -206,7 +212,7 @@ func newReactor( ExtendedSignatures: extCommit, } - state, err = blockExec.ApplyBlock(state, blockID, thisBlock) + state, err = blockExec.ApplyBlock(state, blockID, thisBlock, maxBlockHeight) if err != nil { panic(fmt.Errorf("error apply block: %w", err)) } @@ -436,7 +442,7 @@ func ExtendedCommitNetworkHelper(t *testing.T, maxBlockHeight int64, enableVoteE config = test.ResetTestRoot("blocksync_reactor_test") defer os.RemoveAll(config.RootDir) genDoc, privVals := genesisDocWithValsPowers(valPowers) - genDoc.ConsensusParams.ABCI.VoteExtensionsEnableHeight = enableVoteExtensionAt + genDoc.ConsensusParams.Feature.VoteExtensionsEnableHeight = enableVoteExtensionAt reactorPairs := make([]ReactorPair, 1, 2) reactorPairs[0] = newReactor(t, log.TestingLogger(), genDoc, privVals, 0) @@ -856,7 +862,7 @@ func (bcR *ByzantineReactor) respondToPeer(msg *bcproto.BlockRequest, src p2p.Pe return false } var extCommit *types.ExtendedCommit - voteExtensionEnabled := state.ConsensusParams.ABCI.VoteExtensionsEnabled(msg.Height) + voteExtensionEnabled := state.ConsensusParams.Feature.VoteExtensionsEnabled(msg.Height) incorrectBlock := bcR.corruptedBlock == msg.Height if voteExtensionEnabled && !incorrectBlock || !voteExtensionEnabled && incorrectBlock { extCommit = bcR.store.LoadBlockExtendedCommit(msg.Height) diff --git a/buf.gen.yaml b/buf.gen.yaml index a36032410ce..a05ed1c0895 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -5,5 +5,6 @@ plugins: opt: - Mgoogle/protobuf/timestamp.proto=github.com/cosmos/gogoproto/types - Mgoogle/protobuf/duration.proto=github.com/golang/protobuf/ptypes/duration + - Mgoogle/protobuf/wrappers.proto=github.com/cosmos/gogoproto/types - plugins=grpc - paths=source_relative diff --git a/consensus/aggregation_catchup_test.go b/consensus/aggregation_catchup_test.go new file mode 100644 index 00000000000..4556ace6c99 --- /dev/null +++ b/consensus/aggregation_catchup_test.go @@ -0,0 +1,328 @@ +//go:build bls12381 + +package consensus + +import ( + "context" + "fmt" + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + + dbm "github.com/cometbft/cometbft-db" + + abci "github.com/cometbft/cometbft/abci/types" + cstypes "github.com/cometbft/cometbft/consensus/types" + "github.com/cometbft/cometbft/crypto/bls12381" + "github.com/cometbft/cometbft/internal/test" + "github.com/cometbft/cometbft/libs/log" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" +) + +// blsConsensusNetFromGenesis is like blsConsensusNet but builds the network from +// a CALLER-SUPPLIED genesis doc + priv validators, so a separate late-joiner +// node can be bootstrapped from the very same genesis and catch up. +func blsConsensusNetFromGenesis( + t *testing.T, + genDoc *types.GenesisDoc, + privVals []types.PrivValidator, + testName string, +) ([]*State, cleanupFunc) { + t.Helper() + n := len(privVals) + css := make([]*State, n) + logger := consensusLogger() + configRootDirs := make([]string, 0, n) + for i := 0; i < n; i++ { + stateDB := dbm.NewMemDB() + stateStore := sm.NewStore(stateDB, sm.StoreOptions{DiscardABCIResponses: false}) + state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc) + require.NoError(t, err) + + thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) + configRootDirs = append(configRootDirs, thisConfig.RootDir) + ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0o700) + + app := newKVStore() + vals := types.TM2PB.ValidatorUpdates(state.Validators) + _, err = app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) + + css[i] = newStateWithConfigAndBlockStore(thisConfig, state, privVals[i], app, stateDB) + css[i].SetTimeoutTicker(newMockTickerFunc(false)()) + css[i].SetLogger(logger.With("validator", i, "module", "consensus")) + } + return css, func() { + for _, dir := range configRootDirs { + os.RemoveAll(dir) + } + } +} + +// TestAggregationCatchUpViaAddCommit proves the late-joiner / catch-up path for +// aggregated commits: a node that is behind ingests a whole aggregated Commit +// (the CommitMessage gossip payload) via AddCommit, fetches the committed block +// via block-part gossip, and finalizes the height with that aggregated commit in +// its store — all of which then verifies. +// +// It drives the exact load-bearing state-machine path the reactor's +// CommitMessage handler invokes (AddCommit -> enterCommit -> block parts -> +// tryFinalizeCommit -> finalizeCommit), feeding the messages a lagging node would +// receive over the wire. +func TestAggregationCatchUpViaAddCommit(t *testing.T) { + const N = 4 + + // One shared genesis for both the producers and the late joiner. + genDoc, privVals := blsGenesisDoc(N, 10, blsConsensusParams()) + require.Equal(t, test.DefaultTestChainID, genDoc.ChainID) + + // ---- Producer network: produce a real aggregated commit. ---- + prodCss, cleanup := blsConsensusNetFromGenesis(t, genDoc, privVals, "consensus_agg_catchup_prod") + defer cleanup() + require.True(t, prodCss[0].state.Validators.AllKeysHaveSameType()) + require.Equal(t, bls12381.KeyType, prodCss[0].state.Validators.Validators[0].PubKey.Type()) + + reactors, blocksSubs, eventBuses := startConsensusNet(t, prodCss, N) + for j := 0; j < N; j++ { + go func(j int) { + for range blocksSubs[j].Out() { + } + }(j) + } + + const producedHeight = int64(3) + deadline := time.Now().Add(60 * time.Second) + for prodCss[0].blockStore.Height() < producedHeight { + if time.Now().After(deadline) { + stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + t.Fatalf("producer network did not reach height %d", producedHeight) + } + time.Sleep(50 * time.Millisecond) + } + + const catchUpHeight = int64(1) + pbs := prodCss[0].blockStore + block := pbs.LoadBlock(catchUpHeight) + require.NotNil(t, block) + blockParts, err := block.MakePartSet(types.BlockPartSizeBytes) + require.NoError(t, err) + commit := pbs.LoadBlockCommit(catchUpHeight) + require.NotNil(t, commit) + require.True(t, commit.HasAggregatedSignature(), + "producer commit at height %d is not aggregated", catchUpHeight) + + stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + + // GetState copies under the lock, avoiding a race with any state updates + // still in flight on the receive routine. + prodState := prodCss[0].GetState() + chainID := prodState.ChainID + genValSet := prodState.Validators.Copy() + + // ---- Late joiner: a fresh node bootstrapped from the same genesis. ---- + lateDB := dbm.NewMemDB() + lateStateStore := sm.NewStore(lateDB, sm.StoreOptions{DiscardABCIResponses: false}) + lateState, err := lateStateStore.LoadFromDBOrGenesisDoc(genDoc) + require.NoError(t, err) + require.EqualValues(t, 0, lateState.LastBlockHeight, "late joiner must start at genesis") + + lateConfig := ResetConfig("consensus_agg_catchup_late") + t.Cleanup(func() { _ = os.RemoveAll(lateConfig.RootDir) }) + ensureDir(filepath.Dir(lateConfig.Consensus.WalFile()), 0o700) + + app := newKVStore() + vals := types.TM2PB.ValidatorUpdates(lateState.Validators) + _, err = app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) + + // The late joiner uses a fresh non-validator BLS key: it is catching up, not + // participating, so it must never propose (which would race the catch-up and + // try to finalize its own block). + nonValKey, err := bls12381.GenPrivKey() + require.NoError(t, err) + latePV := types.NewMockPVWithParams(nonValKey, false, false) + late := newStateWithConfigAndBlockStore(lateConfig, lateState, latePV, app, lateDB) + late.SetTimeoutTicker(newMockTickerFunc(true)()) + late.SetLogger(log.TestingLogger().With("module", "consensus", "node", "late")) + + require.NoError(t, late.Start()) + defer func() { _ = late.Stop() }() + + // Give the state machine a moment to settle at height 1. + require.Eventually(t, func() bool { + late.mtx.RLock() + defer late.mtx.RUnlock() + return late.Height == catchUpHeight + }, 5*time.Second, 20*time.Millisecond, "late joiner never reached height 1") + + // Before feeding the real commit, exercise AddCommit's rejection paths. A + // lagging node adopts a block on the strength of this check alone, so the + // gate must reject any commit it cannot verify. + + // Forged aggregated signature: flip a byte in the aggregate so BLS + // verification fails. Expect an error and no adoption. + forged := commit.Clone() + for i, s := range forged.Signatures { + if len(s.Signature) > 0 { + bad := make([]byte, len(s.Signature)) + copy(bad, s.Signature) + bad[0] ^= 0xff + forged.Signatures[i].Signature = bad + break + } + } + late.mtx.Lock() + rejAdded, rejErr := late.AddCommit(forged, "byzantine-peer") + late.mtx.Unlock() + require.Error(t, rejErr, "AddCommit accepted a forged aggregated signature") + require.False(t, rejAdded, "forged-signature commit must not be added") + + // Wrong block: the aggregate was signed over a different BlockID, so + // verification must fail. + wrongBlock := commit.Clone() + badHash := make([]byte, len(commit.BlockID.Hash)) + copy(badHash, commit.BlockID.Hash) + badHash[0] ^= 0xff + wrongBlock.BlockID.Hash = badHash + late.mtx.Lock() + rejAdded, rejErr = late.AddCommit(wrongBlock, "byzantine-peer") + late.mtx.Unlock() + require.Error(t, rejErr, "AddCommit accepted a commit for the wrong block") + require.False(t, rejAdded, "wrong-block commit must not be added") + + // Non-aggregated commit: this fork only adopts aggregated commits via catch-up, + // so a non-aggregated one is ignored (dropped, but not treated as an error). + nonAgg := &types.Commit{Height: catchUpHeight, Round: commit.Round, BlockID: commit.BlockID} + require.False(t, nonAgg.HasAggregatedSignature(), "test setup: nonAgg must not be aggregated") + late.mtx.Lock() + rejAdded, rejErr = late.AddCommit(nonAgg, "byzantine-peer") + late.mtx.Unlock() + require.NoError(t, rejErr, "non-aggregated commit should be ignored, not error") + require.False(t, rejAdded, "non-aggregated commit must not be added") + + // None of the rejected commits may have advanced the node or polluted round state. + late.mtx.RLock() + require.Nil(t, late.Votes.GetCommit(commit.Round), + "a rejected commit must not be stored in the HeightVoteSet") + require.Equal(t, catchUpHeight, late.Height, + "late joiner must still be at the catch-up height after rejections") + late.mtx.RUnlock() + + // Feed the aggregated commit through the real ingestion path, a + // CommitMessage on the peer message queue handled by the receive routine + // (handleMsg -> AddCommit). AddCommit is the load-bearing catch-up entry + // point: it + // (a) rejects non-aggregated commits, + // (b) VERIFIES the aggregated commit against the local validator set + // (routing through verifyAggregatedCommit), and + // (c) stores it in the HeightVoteSet and enters the commit step. + // A lagging validator uses this to adopt a block it never voted on. + // State-mutating calls must run on the receive routine's goroutine (the + // unlocked RoundState read in receiveRoutine assumes a single writer), so + // the test must not call AddCommit directly here. + late.peerMsgQueue <- msgInfo{&CommitMessage{commit}, "producer-peer", cmttime.Now()} + require.Eventually(t, func() bool { + late.mtx.RLock() + defer late.mtx.RUnlock() + return late.Votes.GetCommit(commit.Round) != nil + }, 5*time.Second, 20*time.Millisecond, + "aggregated commit was not added by the late joiner") + + // The aggregated commit is now adopted by the late joiner: it is stored in the + // round's HeightVoteSet, is aggregated, and verifies against the validator set. + late.mtx.RLock() + stored := late.Votes.GetCommit(commit.Round) + step := late.Step + late.mtx.RUnlock() + require.NotNil(t, stored, "late joiner did not adopt the aggregated commit via AddCommit") + require.True(t, stored.HasAggregatedSignature(), + "adopted commit is not aggregated: %v", stored) + require.Equal(t, commit.BlockID, stored.BlockID, "adopted commit has wrong BlockID") + require.NoError(t, + genValSet.VerifyCommit(chainID, stored.BlockID, catchUpHeight, stored), + "late joiner's adopted aggregated commit failed VerifyCommit") + + // AddCommit drove the node into the commit step for the caught-up height, + // waiting only for the committed block (delivered via block-part gossip). + require.GreaterOrEqual(t, step, cstypes.RoundStepCommit, + "AddCommit did not advance the late joiner into the commit step (step=%v)", step) + + t.Logf("late joiner adopted aggregated commit for height %d via AddCommit "+ + "(verified, commit step reached); blockParts available=%d", + catchUpHeight, blockParts.Total()) + + // ---- Deliver the committed block via block-part gossip and finalize. ---- + // AddCommit -> enterCommit set up cs.ProposalBlockParts from the commit's + // part-set header (the node has the commit but not yet the block). Feeding the + // parts completes the block, which fires handleCompleteProposal -> + // tryFinalizeCommit -> finalizeCommit. finalizeCommit's GetCommit branch saves + // the aggregated seen-commit, and updateToState sets LastCommit = *Commit + // (the aggregated commit; individual precommit votes cannot be reconstructed) + // and advances the node to the next height. This is the catch-up *advance* + // the VoteSetReader/LastCommit change unblocks. + // Deliver the parts on the peer message queue. The message loop runs + // addProposalBlockPart, then (once complete) handleCompleteProposal, which + // in the commit step calls tryFinalizeCommit -> finalizeCommit. + for i := 0; i < int(blockParts.Total()); i++ { + part := blockParts.GetPart(i) + late.peerMsgQueue <- msgInfo{ + &BlockPartMessage{Height: catchUpHeight, Round: commit.Round, Part: part}, + "producer-peer", + cmttime.Now(), + } + } + + // The caught-up node now finalizes the height and ADVANCES past it. This is + // the gap the inventory's round 4 left open: with LastCommit as a concrete + // *VoteSet the node could adopt the aggregated commit (reach commit step) but + // not advance, since votesFromSeenCommit cannot reconstruct the individual + // precommits of an aggregated commit. With LastCommit as a VoteSetReader and + // *Commit implementing it, updateToState keeps the *Commit and advances. + const nextHeight = catchUpHeight + 1 + require.Eventually(t, func() bool { + late.mtx.RLock() + defer late.mtx.RUnlock() + return late.Height == nextHeight + }, 10*time.Second, 20*time.Millisecond, + "caught-up node did not advance past the adopted aggregated commit "+ + "(still at height %d, expected %d)", func() int64 { + late.mtx.RLock() + defer late.mtx.RUnlock() + return late.Height + }(), nextHeight) + + // The committed block is now in the late joiner's own block store, and the + // stored seen-commit for the caught-up height is the same aggregated commit. + // (LoadBlockCommit(h) lives in block h+1's LastCommit, which does not exist + // yet; the locally-justified seen commit is what finalizeCommit saved.) + storedCommit := late.blockStore.LoadSeenCommit(catchUpHeight) + require.NotNil(t, storedCommit, "caught-up node did not store the committed block") + require.True(t, storedCommit.HasAggregatedSignature(), + "stored commit at caught-up height is not aggregated") + require.NoError(t, + genValSet.VerifyCommit(chainID, storedCommit.BlockID, catchUpHeight, storedCommit), + "caught-up node's stored aggregated commit failed VerifyCommit") + require.Equal(t, catchUpHeight, late.blockStore.Height(), + "caught-up node block store height mismatch") + + // And LastCommit is the adopted aggregated *Commit (the VoteSetReader path), + // not a reconstructed VoteSet. + late.mtx.RLock() + lastCommit := late.LastCommit + late.mtx.RUnlock() + asCommit, ok := lastCommit.(*types.Commit) + require.True(t, ok, + "LastCommit after aggregated catch-up should be a *types.Commit, got %T", lastCommit) + require.True(t, asCommit.HasAggregatedSignature(), + "LastCommit *Commit is not aggregated") + require.Equal(t, commit.BlockID, asCommit.BlockID, + "LastCommit *Commit has wrong BlockID") + + t.Logf("late joiner finalized height %d via aggregated commit and advanced to height %d "+ + "(LastCommit is the adopted aggregated *Commit)", catchUpHeight, nextHeight) +} diff --git a/consensus/aggregation_pbts_test.go b/consensus/aggregation_pbts_test.go new file mode 100644 index 00000000000..097e54e68d0 --- /dev/null +++ b/consensus/aggregation_pbts_test.go @@ -0,0 +1,188 @@ +//go:build bls12381 + +package consensus + +import ( + "context" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/cometbft/cometbft/abci/example/kvstore" + "github.com/cometbft/cometbft/crypto/bls12381" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" +) + +// blsConsensusParamsWithSynchrony returns BLS/PBTS consensus params with tight +// synchrony bounds, so the timeliness window is small enough that an +// intentionally-skewed proposal timestamp lands clearly out of window. +func blsConsensusParamsWithSynchrony(precision, msgDelay time.Duration) *types.ConsensusParams { + c := blsConsensusParams() + c.Synchrony = types.SynchronyParams{ + Precision: precision, + MessageDelay: msgDelay, + } + return c +} + +// blsConsensusState builds a single observed consensus State plus validator +// stubs, all using bls12_381 keys with PBTS enabled. The observed State is +// privVals[0]; the returned stubs cover ALL validators (index 0 is the observed +// validator itself), mirroring randState's contract so callers can drive the +// network by hand. +func blsConsensusState(t *testing.T, nValidators int, consensusParams *types.ConsensusParams) (*State, []*validatorStub) { + t.Helper() + genDoc, privVals := blsGenesisDoc(nValidators, 10, consensusParams) + state, err := sm.MakeGenesisState(genDoc) + require.NoError(t, err) + + thisConfig := ResetConfig("blsConsensusState") + t.Cleanup(func() { _ = os.RemoveAll(thisConfig.RootDir) }) + + app := kvstore.NewInMemoryApplication() + cs := newStateWithConfig(thisConfig, state, privVals[0], app) + cs.SetTimeoutTicker(newMockTickerFunc(true)()) + + vss := make([]*validatorStub, nValidators) + for i := 0; i < nValidators; i++ { + vss[i] = newValidatorStub(privVals[i], int32(i)) + } + return cs, vss +} + +// proposerStubForRound returns the validator stub whose privval is the proposer +// for the given round in cs's validator set, along with its index. It mirrors +// the increment that enterNewRound applies (IncrementProposerPriority by +// round-cs.Round on the live round-state valset) so the chosen proposer matches +// the one cs will verify the proposal signature against. +func proposerStubForRound(cs *State, vss []*validatorStub, round int32) (*validatorStub, int) { + base := cs.Validators.Copy() + if cs.Round < round { + base.IncrementProposerPriority(round - cs.Round) + } + propAddr := base.GetProposer().Address + for i, vs := range vss { + pub, err := vs.GetPubKey() + if err != nil { + continue + } + if pub.Address().String() == propAddr.String() { + return vs, i + } + } + return nil, -1 +} + +// TestAggregationPBTSRejectsUntimelyProposal proves the PBTS timeliness gate +// actually rejects: in a BLS+PBTS network, when the proposer delivers a block +// whose timestamp is far outside the synchrony window relative to the receiver's +// clock, the observed validator prevotes NIL rather than for the block. A +// control case with an in-window timestamp prevotes for the block, proving the +// gate is selective and not simply always-nil. +func TestAggregationPBTSRejectsUntimelyProposal(t *testing.T) { + testCases := []struct { + name string + // offset applied to BOTH the proposed block time and the proposal + // timestamp, relative to "now" (the receiver's clock at prevote time). + // Positive = future, negative = past. + tsOffset time.Duration + expectNilVote bool + }{ + { + name: "timely_proposal_prevotes_for_block", + tsOffset: 0, // proposal time == now, clearly inside window + expectNilVote: false, + }, + { + name: "future_proposal_prevotes_nil", + tsOffset: 1 * time.Hour, // way beyond MessageDelay+Precision + expectNilVote: true, + }, + { + name: "past_proposal_prevotes_nil", + tsOffset: -1 * time.Hour, // way before now-Precision + expectNilVote: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + + // Tight synchrony: 10ms precision, 50ms message delay. The 1h skew in + // the negative cases dwarfs this window; the 0 offset sits inside it. + cp := blsConsensusParamsWithSynchrony(10*time.Millisecond, 50*time.Millisecond) + cs, vss := blsConsensusState(t, 4, cp) + + // Sanity: the aggregation path is in play (all BLS keys). + require.True(t, cs.state.Validators.AllKeysHaveSameType()) + pk0, err := vss[0].GetPubKey() + require.NoError(t, err) + require.Equal(t, bls12381.KeyType, pk0.Type()) + + height, round := cs.Height, cs.Round + + proposalCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal) + voteCh := subscribe(cs.eventBus, types.EventQueryVote) + + // Build the proposal block from the observed state, then have a + // DIFFERENT validator (a stub) act as proposer so the observed + // validator must evaluate timeliness on a received proposal rather + // than proposing itself. + cs.mtx.Lock() + propBlock, err := cs.createProposalBlock(ctx) + cs.mtx.Unlock() + require.NoError(t, err) + + // Make a stub the proposer for round 1 by advancing the round. + round++ + incrementRound(vss[1:]...) + prop, propIdx := proposerStubForRound(cs, vss, round) + require.NotNil(t, prop, "could not find proposer stub for round %d", round) + require.NotEqual(t, 0, propIdx, "observed validator must not be the proposer") + + // Stamp the block time and proposal timestamp with the skew, relative + // to the genesis time. At InitialHeight, ValidateBlock requires the + // block time to equal genesis time, so the timely (offset 0) control + // uses genesis time exactly. The timeliness gate runs BEFORE + // ValidateBlock, so the out-of-window cases are rejected by PBTS first. + // Both the block time and the proposal timestamp must be equal (the + // gate first requires Proposal.Timestamp == block.Time), so we set both + // to the same skewed value and exercise the timeliness check. + genesisTime := cs.state.LastBlockTime // == genesis time at InitialHeight + skewed := cmttime.Canonical(genesisTime.Add(tc.tsOffset)) + propBlock.Time = skewed + + // Set the proposer address in the header to the chosen proposer. + ppk, err := prop.GetPubKey() + require.NoError(t, err) + propBlock.ProposerAddress = ppk.Address() + + propBlockParts, err := propBlock.MakePartSet(types.BlockPartSizeBytes) + require.NoError(t, err) + blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} + + proposal := types.NewProposal(height, round, -1, blockID, propBlock.Time) + pp := proposal.ToProto() + require.NoError(t, prop.SignProposal(cs.state.ChainID, pp)) + proposal.Signature = pp.Signature + + require.NoError(t, cs.SetProposalAndBlock(proposal, propBlock, propBlockParts, "peerID")) + + startTestRound(cs, height, round) + + ensureNewProposal(proposalCh, height, round) + ensurePrevote(voteCh, height, round) + + if tc.expectNilVote { + validatePrevote(t, cs, round, vss[0], nil) + } else { + validatePrevote(t, cs, round, vss[0], propBlock.Hash()) + } + }) + } +} diff --git a/consensus/aggregation_reactor_test.go b/consensus/aggregation_reactor_test.go new file mode 100644 index 00000000000..1aac88699bd --- /dev/null +++ b/consensus/aggregation_reactor_test.go @@ -0,0 +1,205 @@ +//go:build bls12381 + +package consensus + +import ( + "context" + "fmt" + "os" + "path/filepath" + "sort" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" + + dbm "github.com/cometbft/cometbft-db" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/crypto/bls12381" + "github.com/cometbft/cometbft/internal/test" + "github.com/cometbft/cometbft/libs/log" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" +) + +// blsConsensusParams returns consensus params suitable for BLS aggregation: +// PBTS enabled from height 1 (required, since aggregation strips the per-vote +// timestamp so every validator signs identical bytes) and vote extensions +// disabled (the aggregated-commit path does not support extensions). +func blsConsensusParams() *types.ConsensusParams { + c := types.DefaultConsensusParams() + c.Feature.VoteExtensionsEnableHeight = 0 // disabled + c.Feature.VoteExtensionsEnableHeight = 0 + c.Feature.PbtsEnableHeight = 1 // PBTS on from genesis + return c +} + +// blsGenesisDoc builds a genesis doc whose validators all use bls12_381 keys, +// returning the matching MockPV priv validators (sorted by address, as the +// harness expects). +func blsGenesisDoc(numValidators int, votingPower int64, consensusParams *types.ConsensusParams) (*types.GenesisDoc, []types.PrivValidator) { + validators := make([]types.GenesisValidator, numValidators) + privValidators := make([]types.PrivValidator, numValidators) + for i := 0; i < numValidators; i++ { + privKey, err := bls12381.GenPrivKey() + if err != nil { + panic(fmt.Errorf("could not generate BLS priv key: %w", err)) + } + privVal := types.NewMockPVWithParams(privKey, false, false) + pubKey, err := privVal.GetPubKey() + if err != nil { + panic(err) + } + validators[i] = types.GenesisValidator{ + PubKey: pubKey, + Power: votingPower, + } + privValidators[i] = privVal + } + sort.Sort(types.PrivValidatorsByAddress(privValidators)) + + return &types.GenesisDoc{ + GenesisTime: cmttime.Now(), + InitialHeight: 1, + ChainID: test.DefaultTestChainID, + Validators: validators, + ConsensusParams: consensusParams, + }, privValidators +} + +// blsConsensusNet spins up nValidators in-process consensus States, all using +// BLS keys, with PBTS enabled. Mirrors randConsensusNet but with a BLS genesis. +func blsConsensusNet(t *testing.T, nValidators int, testName string) ([]*State, cleanupFunc) { + t.Helper() + genDoc, privVals := blsGenesisDoc(nValidators, 10, blsConsensusParams()) + css := make([]*State, nValidators) + logger := consensusLogger() + configRootDirs := make([]string, 0, nValidators) + for i := 0; i < nValidators; i++ { + stateDB := dbm.NewMemDB() + stateStore := sm.NewStore(stateDB, sm.StoreOptions{DiscardABCIResponses: false}) + state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc) + require.NoError(t, err) + thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) + configRootDirs = append(configRootDirs, thisConfig.RootDir) + ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0o700) + app := newKVStore() + vals := types.TM2PB.ValidatorUpdates(state.Validators) + _, err = app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) + + css[i] = newStateWithConfigAndBlockStore(thisConfig, state, privVals[i], app, stateDB) + css[i].SetTimeoutTicker(newMockTickerFunc(false)()) + css[i].SetLogger(logger.With("validator", i, "module", "consensus")) + } + return css, func() { + for _, dir := range configRootDirs { + os.RemoveAll(dir) + } + } +} + +// TestReactorAggregatedCommits is the win-condition test: a multi-validator +// network using BLS keys + PBTS produces aggregated commits (BlockIDFlagAgg*) +// over the real network/consensus path, makes progress across several heights +// (liveness), and the aggregated commits verify via VerifyCommit (which routes +// through verifyAggregatedCommit). +func TestReactorAggregatedCommits(t *testing.T) { + N := 4 + css, cleanup := blsConsensusNet(t, N, "consensus_agg_reactor_test") + defer cleanup() + + // Sanity: all validators carry BLS keys, so the aggregation path is taken. + require.True(t, css[0].state.Validators.AllKeysHaveSameType()) + require.Equal(t, bls12381.KeyType, css[0].state.Validators.Validators[0].PubKey.Type(), + "validators must use bls12381 keys") + + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N) + defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + + // Wait until every validator commits several blocks (liveness). We drain the + // new-block subscriptions continuously in the background so the event bus + // never back-pressures and stalls consensus, while tracking the max height + // each validator has reached. + const targetHeight = 4 + heights := make([]int64, N) + var hmtx sync.Mutex + for j := 0; j < N; j++ { + go func(j int) { + for msg := range blocksSubs[j].Out() { + if nb, ok := msg.Data().(types.EventDataNewBlock); ok { + hmtx.Lock() + if nb.Block.Height > heights[j] { + heights[j] = nb.Block.Height + } + hmtx.Unlock() + } + } + }(j) + } + + deadline := time.Now().Add(60 * time.Second) + for { + hmtx.Lock() + min := int64(1 << 62) + for _, h := range heights { + if h < min { + min = h + } + } + hmtx.Unlock() + if min >= targetHeight { + break + } + if time.Now().After(deadline) { + t.Fatalf("liveness stall: heights=%v, did not all reach %d", heights, targetHeight) + } + time.Sleep(50 * time.Millisecond) + } + + // Give the last commit a moment to be persisted everywhere. + time.Sleep(500 * time.Millisecond) + + // Assert the real properties on every validator's block store. + // The network is still running, so read consensus state via GetState(), + // which copies under the lock. + chainID := css[0].GetState().ChainID + checkedAgg := 0 + for i := 0; i < N; i++ { + bs := css[i].blockStore + valSet := css[i].GetState().Validators // genesis valset (unchanged across these heights) + require.GreaterOrEqual(t, bs.Height(), int64(targetHeight), + "validator %d only reached height %d", i, bs.Height()) + + // For each committed height, the seen commit must be aggregated and verify. + for h := int64(1); h <= bs.Height()-1; h++ { + block := bs.LoadBlock(h) + require.NotNil(t, block, "validator %d missing block %d", i, h) + + // The commit stored at h+1's LastCommit / seen commit for h. + commit := bs.LoadBlockCommit(h) + if commit == nil { + commit = bs.LoadSeenCommit(h) + } + require.NotNil(t, commit, "validator %d missing commit for height %d", i, h) + + require.True(t, commit.HasAggregatedSignature(), + "validator %d commit at height %d is NOT aggregated: %v", i, h, commit) + + // The commit's BlockID must match the block it commits. + require.Equal(t, block.Hash().Bytes(), commit.BlockID.Hash.Bytes(), + "validator %d commit blockID mismatch at height %d", i, h) + + err := valSet.VerifyCommit(chainID, commit.BlockID, h, commit) + require.NoError(t, err, + "validator %d aggregated commit at height %d failed VerifyCommit", i, h) + checkedAgg++ + } + } + require.Positive(t, checkedAgg, "no aggregated commits were checked") + t.Logf("verified %d aggregated commits across %d validators up to height %d", + checkedAgg, N, css[0].blockStore.Height()) +} diff --git a/consensus/aggregation_replay_test.go b/consensus/aggregation_replay_test.go new file mode 100644 index 00000000000..db4015ef341 --- /dev/null +++ b/consensus/aggregation_replay_test.go @@ -0,0 +1,179 @@ +//go:build bls12381 + +package consensus + +import ( + "context" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/require" + + dbm "github.com/cometbft/cometbft-db" + + abci "github.com/cometbft/cometbft/abci/types" + cfg "github.com/cometbft/cometbft/config" + "github.com/cometbft/cometbft/crypto/bls12381" + "github.com/cometbft/cometbft/libs/log" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/store" + "github.com/cometbft/cometbft/types" +) + +// buildBLSState constructs a single consensus State backed by the supplied +// (shared, hence restart-surviving) block/state DB and application, using a BLS +// genesis with PBTS enabled. Returning the genesis doc lets a second invocation +// rebuild an identical State over the same persistent stores, simulating a node +// restart. +func buildBLSState( + t *testing.T, + thisConfig *cfg.Config, + genDoc *types.GenesisDoc, + privVal types.PrivValidator, + app abci.Application, + blockDB dbm.DB, +) *State { + t.Helper() + + stateDB := blockDB + stateStore := sm.NewStore(stateDB, sm.StoreOptions{DiscardABCIResponses: false}) + + state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc) + require.NoError(t, err) + + // InitChain so the app knows the validator set (idempotent for the kvstore). + vals := types.TM2PB.ValidatorUpdates(state.Validators) + _, err = app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals}) + require.NoError(t, err) + + cs := newStateWithConfigAndBlockStore(thisConfig, state, privVal, app, blockDB) + cs.SetLogger(log.TestingLogger().With("module", "consensus")) + return cs +} + +// TestAggregationWALReplayRestart proves a BLS+PBTS node can be restarted and +// replay its WAL cleanly, ending at the same height with the SAME aggregated +// commits in its store, all of which still verify. +// +// 1. A single BLS validator commits several heights to a persistent block/state +// DB and a real on-disk WAL. +// 2. The State is stopped. +// 3. A brand-new State is constructed over the very same DB, app and WAL file, +// and started. On start it replays the WAL (catchupReplay). +// 4. We assert the post-restart height matches and that every stored commit is +// aggregated (BlockIDFlagAgg*) and verifies via VerifyCommit. +func TestAggregationWALReplayRestart(t *testing.T) { + // Shared, restart-surviving resources. + cp := blsConsensusParams() + genDoc, privVals := blsGenesisDoc(1, 10, cp) + privVal := privVals[0] + + pk, err := privVal.GetPubKey() + require.NoError(t, err) + require.Equal(t, bls12381.KeyType, pk.Type(), "validator must use a bls12381 key") + + thisConfig := ResetConfig("aggregation_wal_replay") + t.Cleanup(func() { _ = thisConfig }) + ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0o700) + + blockDB := dbm.NewMemDB() // a single handle, shared across both State lifetimes + appPath := filepath.Join(thisConfig.DBDir(), "agg_replay_app") + app := newPersistentKVStoreWithPath(appPath) + + const targetHeight = int64(4) + + // ---- Phase 1: run to targetHeight, persisting to DB + WAL. ---- + func() { + cs := buildBLSState(t, thisConfig, genDoc, privVal, app, blockDB) + cs.SetTimeoutTicker(newMockTickerFunc(true)()) + + newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock) + + require.NoError(t, cs.Start()) + defer func() { _ = cs.Stop() }() + + deadline := time.Now().Add(30 * time.Second) + for { + select { + case msg := <-newBlockCh: + nb := msg.Data().(types.EventDataNewBlock) + if nb.Block.Height >= targetHeight { + goto done + } + case <-time.After(time.Until(deadline)): + t.Fatalf("phase 1 liveness stall before height %d", targetHeight) + } + } + done: + // Let the commit for the final height settle into the store/WAL. + time.Sleep(300 * time.Millisecond) + require.GreaterOrEqual(t, cs.blockStore.Height(), targetHeight, + "phase 1 did not reach target height") + }() + + // Capture what the store holds pre-restart for comparison. + preStore := store.NewBlockStore(blockDB) + preHeight := preStore.Height() + require.GreaterOrEqual(t, preHeight, targetHeight) + + chainID := genDoc.ChainID + valSet := types.NewValidatorSet(nil) + { + // Reconstruct the genesis validator set for verification. + s, err := sm.MakeGenesisState(genDoc) + require.NoError(t, err) + valSet = s.Validators + } + + assertAggregatedCommits := func(t *testing.T, bs sm.BlockStore, label string) { + t.Helper() + checked := 0 + for h := int64(1); h <= bs.Height()-1; h++ { + commit := bs.LoadBlockCommit(h) + if commit == nil { + commit = bs.LoadSeenCommit(h) + } + require.NotNil(t, commit, "%s: missing commit for height %d", label, h) + require.True(t, commit.HasAggregatedSignature(), + "%s: commit at height %d is NOT aggregated: %v", label, h, commit) + require.NoError(t, valSet.VerifyCommit(chainID, commit.BlockID, h, commit), + "%s: aggregated commit at height %d failed VerifyCommit", label, h) + checked++ + } + require.Positive(t, checked, "%s: no aggregated commits checked", label) + t.Logf("%s: verified %d aggregated commits up to height %d", label, checked, bs.Height()) + } + + // Pre-restart sanity: the persisted commits are aggregated and verify. + assertAggregatedCommits(t, preStore, "pre-restart") + + // ---- Phase 2: restart over the SAME DB + WAL and replay. ---- + cs2 := buildBLSState(t, thisConfig, genDoc, privVal, app, blockDB) + cs2.SetTimeoutTicker(newMockTickerFunc(true)()) + + // Starting the State triggers WAL load + catchupReplay over the persisted WAL. + require.NoError(t, cs2.Start()) + defer func() { _ = cs2.Stop() }() + + // Give replay a moment to run. + time.Sleep(500 * time.Millisecond) + + // The restarted node must be at least at the pre-restart height (replay must + // not lose committed state) and its store must still hold the aggregated + // commits, all verifying. + require.GreaterOrEqual(t, cs2.blockStore.Height(), preHeight, + "restarted node regressed below pre-restart height %d (got %d)", + preHeight, cs2.blockStore.Height()) + + // cs2 is running, so read its height via GetRoundState, which takes the + // consensus mutex; a direct cs2.Height read races with the state machine. + csHeight := cs2.GetRoundState().Height + require.GreaterOrEqual(t, csHeight, preHeight, + "restarted consensus height %d below pre-restart height %d", csHeight, preHeight) + + assertAggregatedCommits(t, cs2.blockStore, "post-restart") + + t.Logf("restart replay OK: pre-restart height=%d, post-restart store height=%d, cs height=%d", + preHeight, cs2.blockStore.Height(), csHeight) +} diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 00849058259..9437edacb22 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -22,6 +22,7 @@ import ( cmtsync "github.com/cometbft/cometbft/libs/sync" mempl "github.com/cometbft/cometbft/mempool" "github.com/cometbft/cometbft/proxy" + cmttime "github.com/cometbft/cometbft/types/time" "github.com/cometbft/cometbft/p2p" cmtcons "github.com/cometbft/cometbft/proto/tendermint/consensus" @@ -182,18 +183,20 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { } var extCommit *types.ExtendedCommit - switch { - case lazyProposer.Height == lazyProposer.state.InitialHeight: + switch lazyProposer.Height { + case lazyProposer.state.InitialHeight: // We're creating a proposal for the first block. // The commit is empty, but not nil. extCommit = &types.ExtendedCommit{} - case lazyProposer.LastCommit.HasTwoThirdsMajority(): + default: + lastCommitAsVs, ok := lazyProposer.LastCommit.(*types.VoteSet) + if !ok || !lastCommitAsVs.HasTwoThirdsMajority() { + lazyProposer.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block") + return + } // Make the commit from LastCommit - veHeightParam := types.ABCIParams{VoteExtensionsEnableHeight: height} - extCommit = lazyProposer.LastCommit.MakeExtendedCommit(veHeightParam) - default: // This shouldn't happen. - lazyProposer.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block") - return + veHeightParam := types.FeatureParams{VoteExtensionsEnableHeight: height} + extCommit = lastCommitAsVs.MakeExtendedCommit(veHeightParam) } // omit the last signature in the commit @@ -221,16 +224,16 @@ func TestByzantinePrevoteEquivocation(t *testing.T) { // Make proposal propBlockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} - proposal := types.NewProposal(height, round, lazyProposer.ValidRound, propBlockID) + proposal := types.NewProposal(height, round, lazyProposer.ValidRound, propBlockID, block.Time) p := proposal.ToProto() if err := lazyProposer.privValidator.SignProposal(lazyProposer.state.ChainID, p); err == nil { proposal.Signature = p.Signature // send proposal and block parts on internal msg queue - lazyProposer.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, ""}) + lazyProposer.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, "", cmttime.Now()}) for i := 0; i < int(blockParts.Total()); i++ { part := blockParts.GetPart(i) - lazyProposer.sendInternalMessage(msgInfo{&BlockPartMessage{lazyProposer.Height, lazyProposer.Round, part}, ""}) + lazyProposer.sendInternalMessage(msgInfo{&BlockPartMessage{lazyProposer.Height, lazyProposer.Round, part}, "", time.Time{}}) } lazyProposer.Logger.Info("Signed proposal", "height", height, "round", round, "proposal", proposal) lazyProposer.Logger.Debug(fmt.Sprintf("Signed proposal block: %v", block)) @@ -477,7 +480,7 @@ func byzantineDecideProposalFunc(ctx context.Context, t *testing.T, height int64 blockParts1, err := block1.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) polRound, propBlockID := cs.ValidRound, types.BlockID{Hash: block1.Hash(), PartSetHeader: blockParts1.Header()} - proposal1 := types.NewProposal(height, round, polRound, propBlockID) + proposal1 := types.NewProposal(height, round, polRound, propBlockID, block1.Time) p1 := proposal1.ToProto() if err := cs.privValidator.SignProposal(cs.state.ChainID, p1); err != nil { t.Error(err) @@ -494,7 +497,7 @@ func byzantineDecideProposalFunc(ctx context.Context, t *testing.T, height int64 blockParts2, err := block2.MakePartSet(types.BlockPartSizeBytes) require.NoError(t, err) polRound, propBlockID = cs.ValidRound, types.BlockID{Hash: block2.Hash(), PartSetHeader: blockParts2.Header()} - proposal2 := types.NewProposal(height, round, polRound, propBlockID) + proposal2 := types.NewProposal(height, round, polRound, propBlockID, block2.Time) p2 := proposal2.ToProto() if err := cs.privValidator.SignProposal(cs.state.ChainID, p2); err != nil { t.Error(err) @@ -658,7 +661,7 @@ func TestRejectOversizedProposals(t *testing.T) { propBlockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} propBlockID.PartSetHeader.Total = 4294967295 - proposal := types.NewProposal(height, round, -1, propBlockID) + proposal := types.NewProposal(height, round, -1, propBlockID, block.Time) p := proposal.ToProto() if err := cs.privValidator.SignProposal(cs.state.ChainID, p); err != nil { t.Error(err) diff --git a/consensus/common_test.go b/consensus/common_test.go index a9c43f96c6e..edd5608340d 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -87,6 +87,14 @@ func newValidatorStub(privValidator types.PrivValidator, valIndex int32) *valida } } +func signProposal(t *testing.T, proposal *types.Proposal, chainID string, vss *validatorStub) { + t.Helper() + p := proposal.ToProto() + err := vss.SignProposal(chainID, p) + require.NoError(t, err) + proposal.Signature = p.Signature +} + func (vs *validatorStub) signVote( voteType cmtproto.SignedMsgType, hash []byte, @@ -217,6 +225,19 @@ func startTestRound(cs *State, height int64, round int32) { cs.startRoutines(0) } +func createProposalBlockWithTime(t *testing.T, cs *State, time time.Time) (*types.Block, *types.PartSet, types.BlockID) { + t.Helper() + block, err := cs.createProposalBlock(context.Background()) + if !time.IsZero() { + block.Time = cmttime.Canonical(time) + } + assert.NoError(t, err) + blockParts, err := block.MakePartSet(types.BlockPartSizeBytes) + assert.NoError(t, err) + blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} + return block, blockParts, blockID +} + // Create proposal block from cs1 but sign it with vs. func decideProposal( ctx context.Context, @@ -240,7 +261,7 @@ func decideProposal( // Make proposal polRound, propBlockID := validRound, types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} - proposal := types.NewProposal(height, round, polRound, propBlockID) + proposal := types.NewProposal(height, round, polRound, propBlockID, block.Time) p := proposal.ToProto() if err := vs.SignProposal(chainID, p); err != nil { panic(err) @@ -295,11 +316,13 @@ func validateLastPrecommit(t *testing.T, cs *State, privVal *validatorStub, bloc require.NoError(t, err) address := pv.Address() var vote *types.Vote - if vote = votes.GetByAddress(address); vote == nil { - panic("Failed to find precommit from validator") - } - if !bytes.Equal(vote.BlockID.Hash, blockHash) { - panic(fmt.Sprintf("Expected precommit to be for %X, got %X", blockHash, vote.BlockID.Hash)) + if vs, ok := votes.(*types.VoteSet); ok { + if vote = vs.GetByAddress(address); vote == nil { + panic("Failed to find precommit from validator") + } + if !bytes.Equal(vote.BlockID.Hash, blockHash) { + panic(fmt.Sprintf("Expected precommit to be for %X, got %X", blockHash, vote.BlockID.Hash)) + } } } @@ -464,7 +487,7 @@ func randStateWithAppWithHeight( height int64, ) (*State, []*validatorStub) { c := test.ConsensusParams() - c.ABCI.VoteExtensionsEnableHeight = height + c.Feature.VoteExtensionsEnableHeight = height return randStateWithAppImpl(nValidators, app, c) } @@ -477,9 +500,18 @@ func randStateWithAppImpl( nValidators int, app abci.Application, consensusParams *types.ConsensusParams, +) (*State, []*validatorStub) { + return randStateWithAppImplGenesisTime(nValidators, app, consensusParams, cmttime.Now()) +} + +func randStateWithAppImplGenesisTime( + nValidators int, + app abci.Application, + consensusParams *types.ConsensusParams, + genesisTime time.Time, ) (*State, []*validatorStub) { // Get State - state, privVals := randGenesisState(nValidators, false, 10, consensusParams) + state, privVals := randGenesisStateWithTime(nValidators, consensusParams, genesisTime) vss := make([]*validatorStub, nValidators) @@ -916,6 +948,17 @@ func randGenesisState( return s0, privValidators } +func randGenesisStateWithTime( + numValidators int, + consensusParams *types.ConsensusParams, + genesisTime time.Time, +) (sm.State, []types.PrivValidator) { + genDoc, privValidators := randGenesisDoc(numValidators, false, 10, consensusParams) + genDoc.GenesisTime = genesisTime + s0, _ := sm.MakeGenesisState(genDoc) + return s0, privValidators +} + //------------------------------------ // mock ticker diff --git a/consensus/invalid_test.go b/consensus/invalid_test.go index c028afb3868..46d3a7ae97e 100644 --- a/consensus/invalid_test.go +++ b/consensus/invalid_test.go @@ -85,7 +85,6 @@ func invalidDoPrevoteFunc(t *testing.T, cs *State, sw p2p.Switcher, pv types.Pri ValidatorIndex: valIndex, Height: cs.Height, Round: cs.Round, - Timestamp: cs.voteTime(), Type: cmtproto.PrecommitType, BlockID: types.BlockID{ Hash: blockHash, diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index 405ba194b67..0f077980f16 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -81,14 +81,14 @@ func TestMempoolProgressInHigherRound(t *testing.T) { newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock) newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound) timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose) - cs.setProposal = func(proposal *types.Proposal) error { + cs.setProposal = func(proposal *types.Proposal, recvTime time.Time) error { if cs.Height == 2 && 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") return nil } - return cs.defaultSetProposal(proposal) + return cs.defaultSetProposal(proposal, recvTime) } startTestRound(cs, height, round) diff --git a/consensus/metrics.gen.go b/consensus/metrics.gen.go index aa05a97bdd0..6696ef010a2 100644 --- a/consensus/metrics.gen.go +++ b/consensus/metrics.gen.go @@ -228,6 +228,20 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { Name: "round_increment_total", Help: "RoundIncrementTotal is the number of times that the consensus reactor has incremented above the initial round in a step.", }, append(labels, "step")).With(labelsAndValues...), + NextBlockDelay: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "next_block_delay", + Help: "NextBlockDelay is the time to wait before proposing the next block, as instructed by the application via FinalizeBlockResponse (ADR-115).", + }, labels).With(labelsAndValues...), + ProposalTimestampDifference: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: MetricsSubsystem, + Name: "proposal_timestamp_difference", + Help: "Difference in seconds between the local time when a proposal message is received and the timestamp in the proposal message.", + + Buckets: []float64{-1.5, -1.0, -0.5, -0.2, 0, 0.2, 0.5, 1.0, 1.5, 2.0, 2.5, 4.0, 8.0}, + }, append(labels, "is_timely")).With(labelsAndValues...), } } @@ -268,5 +282,7 @@ func NopMetrics() *Metrics { LateVotes: discard.NewCounter(), PeerHeight: discard.NewGauge(), RoundIncrementTotal: discard.NewCounter(), + NextBlockDelay: discard.NewHistogram(), + ProposalTimestampDifference: discard.NewHistogram(), } } diff --git a/consensus/metrics.go b/consensus/metrics.go index e1840062657..746d57f6bff 100644 --- a/consensus/metrics.go +++ b/consensus/metrics.go @@ -152,6 +152,25 @@ type Metrics struct { // RoundIncrementTotal is the number of times that the consensus reactor // has incremented above the initial round in a step. RoundIncrementTotal metrics.Counter `metrics_labels:"step"` + + // NextBlockDelay is the time to wait before proposing the next block, + // as instructed by the application via FinalizeBlockResponse (ADR-115). + NextBlockDelay metrics.Histogram + + // ProposalTimestampDifference is the difference between the local time + // of the validator at the time it receives a proposal message, and the + // timestamp of the received proposal message. + // + // The value of this metric is not expected to be negative, as it would + // mean that the proposal's timestamp is in the future. This indicates + // that the proposer's and this node's clocks are desynchronized. + // + // A positive value of this metric reflects the message delay from the + // proposer to this node, for the delivery of a proposal message. This + // metric thus should drive the definition of values for the consensus + // parameter SynchronyParams.MessageDelay, used by the PBTS algorithm. + //metrics:Difference in seconds between the local time when a proposal message is received and the timestamp in the proposal message. + ProposalTimestampDifference metrics.Histogram `metrics_bucketsizes:"-1.5, -1.0, -0.5, -0.2, 0, 0.2, 0.5, 1.0, 1.5, 2.0, 2.5, 4.0, 8.0" metrics_labels:"is_timely"` } func (m *Metrics) MarkRoundIncremented(step cstypes.RoundStepType) { diff --git a/consensus/msgs.go b/consensus/msgs.go index 9d7583444be..652bbaa03cd 100644 --- a/consensus/msgs.go +++ b/consensus/msgs.go @@ -2,6 +2,7 @@ package consensus import ( "fmt" + "time" cmterrors "github.com/cometbft/cometbft/types/errors" "github.com/cosmos/gogoproto/proto" @@ -76,6 +77,12 @@ func MsgToProto(msg Message) (proto.Message, error) { Vote: vote, } + case *CommitMessage: + commit := msg.Commit.ToProto() + pb = &cmtcons.Commit{ + Commit: commit, + } + case *HasVoteMessage: pb = &cmtcons.HasVote{ Height: msg.Height, @@ -84,6 +91,13 @@ func MsgToProto(msg Message) (proto.Message, error) { Index: msg.Index, } + case *HasProposalBlockPartMessage: + pb = &cmtcons.HasProposalBlockPart{ + Height: msg.Height, + Round: msg.Round, + Index: msg.Index, + } + case *VoteSetMaj23Message: bi := msg.BlockID.ToProto() pb = &cmtcons.VoteSetMaj23{ @@ -181,6 +195,15 @@ func MsgFromProto(p proto.Message) (Message, error) { Round: msg.Round, Part: parts, } + case *cmtcons.Commit: + commit, err := types.CommitFromProto(msg.Commit) + if err != nil { + return nil, cmterrors.ErrMsgToProto{MessageName: "Commit", Err: err} + } + + pb = &CommitMessage{ + Commit: commit, + } case *cmtcons.Vote: // Vote validation will be handled in the vote message ValidateBasic // call below. @@ -199,6 +222,12 @@ func MsgFromProto(p proto.Message) (Message, error) { Type: msg.Type, Index: msg.Index, } + case *cmtcons.HasProposalBlockPart: + pb = &HasProposalBlockPartMessage{ + Height: msg.Height, + Round: msg.Round, + Index: msg.Index, + } case *cmtcons.VoteSetMaj23: bi, err := types.BlockIDFromProto(&msg.BlockID) if err != nil { @@ -260,11 +289,19 @@ func WALToProto(msg WALMessage) (*cmtcons.WALMessage, error) { consMsg = w.Wrap() } cm := consMsg.(*cmtcons.Message) + // Persist the receive time (as bera-v1.x does) so that a proposal + // replayed from the WAL keeps its PBTS timeliness verdict. + var rtp *time.Time + if !msg.ReceiveTime.IsZero() { + rt := msg.ReceiveTime + rtp = &rt + } pb = cmtcons.WALMessage{ Sum: &cmtcons.WALMessage_MsgInfo{ MsgInfo: &cmtcons.MsgInfo{ - Msg: *cm, - PeerID: string(msg.PeerID), + Msg: *cm, + PeerID: string(msg.PeerID), + ReceiveTime: rtp, }, }, } @@ -317,10 +354,14 @@ func WALFromProto(msg *cmtcons.WALMessage) (WALMessage, error) { if err != nil { return nil, cmterrors.ErrMsgFromProto{MessageName: "MsgInfo", Err: err} } - pb = msgInfo{ + mi := msgInfo{ Msg: walMsg, PeerID: p2p.ID(msg.MsgInfo.PeerID), } + if msg.MsgInfo.ReceiveTime != nil { + mi.ReceiveTime = *msg.MsgInfo.ReceiveTime + } + pb = mi case *cmtcons.WALMessage_TimeoutInfo: tis, err := cmtmath.SafeConvertUint8(int64(msg.TimeoutInfo.Step)) diff --git a/consensus/msgs_test.go b/consensus/msgs_test.go index d55b55d00ef..76f8da5c2d9 100644 --- a/consensus/msgs_test.go +++ b/consensus/msgs_test.go @@ -17,6 +17,7 @@ import ( cmtcons "github.com/cometbft/cometbft/proto/tendermint/consensus" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" ) func TestMsgToProto(t *testing.T) { @@ -49,10 +50,10 @@ func TestMsgToProto(t *testing.T) { proposal := types.Proposal{ Type: cmtproto.ProposalType, Height: 1, - Round: 1, + Round: 2, POLRound: 1, BlockID: bi, - Timestamp: time.Now(), + Timestamp: cmttime.Now(), Signature: cmtrand.Bytes(20), } pbProposal := proposal.ToProto() @@ -223,6 +224,7 @@ func TestWALMsgProto(t *testing.T) { } pbParts, err := parts.ToProto() require.NoError(t, err) + receiveTime := time.Date(2026, 1, 2, 3, 4, 5, 600000000, time.UTC) testsCases := []struct { testName string @@ -266,6 +268,31 @@ func TestWALMsgProto(t *testing.T) { }, }, }, false}, + {"successful msgInfo with receive time", msgInfo{ + Msg: &BlockPartMessage{ + Height: 100, + Round: 1, + Part: &parts, + }, + PeerID: p2p.ID("string"), + ReceiveTime: receiveTime, + }, &cmtcons.WALMessage{ + Sum: &cmtcons.WALMessage_MsgInfo{ + MsgInfo: &cmtcons.MsgInfo{ + Msg: cmtcons.Message{ + Sum: &cmtcons.Message_BlockPart{ + BlockPart: &cmtcons.BlockPart{ + Height: 100, + Round: 1, + Part: *pbParts, + }, + }, + }, + PeerID: "string", + ReceiveTime: &receiveTime, + }, + }, + }, false}, {"successful timeoutInfo", timeoutInfo{ Duration: time.Duration(100), Height: 1, @@ -437,6 +464,12 @@ func TestConsMsgsVectors(t *testing.T) { }}, "3a1808ffffffffffffffff7f10ffffffff07180120ffffffff07", }, + { + "HasProposalBlockPart", &cmtcons.Message{Sum: &cmtcons.Message_HasProposalBlockPart{ + HasProposalBlockPart: &cmtcons.HasProposalBlockPart{Height: 1, Round: 1, Index: 1}, + }}, + "5206080110011801", + }, { "VoteSetMaj23", &cmtcons.Message{Sum: &cmtcons.Message_VoteSetMaj23{ VoteSetMaj23: &cmtcons.VoteSetMaj23{Height: 1, Round: 1, Type: cmtproto.PrevoteType, BlockID: pbBi}, @@ -460,3 +493,28 @@ func TestConsMsgsVectors(t *testing.T) { }) } } + +// TestHasProposalBlockPartWireCompat pins the decode path for the +// has_proposal_block_part gossip hint (oneof field 10) exactly as the +// bera-v1.x line encodes it. Mixed-version gossip during a rolling upgrade +// depends on these bytes unwrapping instead of failing as an unknown sum. +func TestHasProposalBlockPartWireCompat(t *testing.T) { + oldLineBytes, err := hex.DecodeString("5206080510011802") + require.NoError(t, err) + + var m cmtcons.Message + require.NoError(t, proto.Unmarshal(oldLineBytes, &m)) + + unwrapped, err := m.Unwrap() + require.NoError(t, err) + + msg, err := MsgFromProto(unwrapped) + require.NoError(t, err) + require.NoError(t, msg.ValidateBasic()) + + hpbp, ok := msg.(*HasProposalBlockPartMessage) + require.True(t, ok) + require.Equal(t, int64(5), hpbp.Height) + require.Equal(t, int32(1), hpbp.Round) + require.Equal(t, int32(2), hpbp.Index) +} diff --git a/consensus/pbts_test.go b/consensus/pbts_test.go new file mode 100644 index 00000000000..46747406905 --- /dev/null +++ b/consensus/pbts_test.go @@ -0,0 +1,237 @@ +package consensus + +import ( + "math" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/cometbft/cometbft/abci/example/kvstore" + "github.com/cometbft/cometbft/internal/test" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" + cmttimemocks "github.com/cometbft/cometbft/types/time/mocks" +) + +// TestPBTSProposerWaitTime verifies that proposerWaitTime returns the correct +// amount of time a proposer must wait for its local clock to catch up to the +// previous block time under PBTS. +func TestPBTSProposerWaitTime(t *testing.T) { + genesisTime, err := time.Parse(time.RFC3339, "2019-03-13T23:00:00Z") + require.NoError(t, err) + testCases := []struct { + name string + previousBlockTime time.Time + localTime time.Time + expectedWait time.Duration + }{ + { + name: "block time greater than local time", + previousBlockTime: genesisTime.Add(5 * time.Nanosecond), + localTime: genesisTime.Add(1 * time.Nanosecond), + expectedWait: 4 * time.Nanosecond, + }, + { + name: "local time greater than block time", + previousBlockTime: genesisTime.Add(1 * time.Nanosecond), + localTime: genesisTime.Add(5 * time.Nanosecond), + expectedWait: 0, + }, + { + name: "both times equal", + previousBlockTime: genesisTime.Add(5 * time.Nanosecond), + localTime: genesisTime.Add(5 * time.Nanosecond), + expectedWait: 0, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + mockSource := new(cmttimemocks.Source) + mockSource.On("Now").Return(testCase.localTime) + + ti := proposerWaitTime(mockSource, testCase.previousBlockTime) + assert.Equal(t, testCase.expectedWait, ti) + }) + } +} + +// TestPBTSProposalIsTimely verifies the PBTS timeliness window: a proposal is +// timely when the receiver's local clock falls within +// [proposalTime - Precision, proposalTime + MessageDelay + Precision]. +func TestPBTSProposalIsTimely(t *testing.T) { + genesisTime, err := time.Parse(time.RFC3339, "2019-03-13T23:00:00Z") + require.NoError(t, err) + + sp := types.SynchronyParams{ + Precision: 10 * time.Millisecond, + MessageDelay: 140 * time.Millisecond, + } + + testCases := []struct { + name string + proposalTime time.Time + recvTime time.Time + expectedTimely bool + }{ + { + name: "timely: recv equals proposal time", + proposalTime: genesisTime, + recvTime: genesisTime, + expectedTimely: true, + }, + { + name: "timely: recv at lower bound (proposal - precision)", + proposalTime: genesisTime, + recvTime: genesisTime.Add(-sp.Precision), + expectedTimely: true, + }, + { + name: "timely: recv at upper bound (proposal + delay + precision)", + proposalTime: genesisTime, + recvTime: genesisTime.Add(sp.MessageDelay + sp.Precision), + expectedTimely: true, + }, + { + name: "not timely: too far in the past (recv after upper bound)", + proposalTime: genesisTime, + recvTime: genesisTime.Add(sp.MessageDelay + sp.Precision + time.Millisecond), + expectedTimely: false, + }, + { + name: "not timely: too far in the future (recv before lower bound)", + proposalTime: genesisTime, + recvTime: genesisTime.Add(-sp.Precision - time.Millisecond), + expectedTimely: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + p := &types.Proposal{Timestamp: tc.proposalTime} + assert.Equal(t, tc.expectedTimely, p.IsTimely(tc.recvTime, sp)) + }) + } +} + +// TestPBTSSynchronyParamsInRound verifies that the synchrony message delay +// applies an exponential back-off as the round increases, while precision stays +// constant. This is the adaptive component of PBTS. +func TestPBTSSynchronyParamsInRound(t *testing.T) { + sp := types.SynchronyParams{ + Precision: 10 * time.Millisecond, + MessageDelay: 100 * time.Millisecond, + } + + round0 := sp.InRound(0) + assert.Equal(t, sp.Precision, round0.Precision) + assert.Equal(t, sp.MessageDelay, round0.MessageDelay) + + // Message delay grows with the round; precision is unchanged. + round1 := sp.InRound(1) + assert.Equal(t, sp.Precision, round1.Precision) + assert.True(t, round1.MessageDelay > sp.MessageDelay, + "message delay should grow after round 0: got %v, base %v", round1.MessageDelay, sp.MessageDelay) + + round5 := sp.InRound(5) + assert.True(t, round5.MessageDelay >= round1.MessageDelay, + "message delay should be non-decreasing in the round") + + // Regression for the overflow behind upstream #4815 (and the round>10 + // bypass it motivated). At very high rounds (303 was observed during a + // network stall) the exponential back-off must cap at MaxMessageDelay + // instead of overflowing int64. + for _, round := range []int32{256, 303, math.MaxInt32} { + spHigh := types.DefaultSynchronyParams().InRound(round) + assert.Equal(t, types.MaxMessageDelay, spHigh.MessageDelay, + "message delay should cap at MaxMessageDelay in round %d", round) + } +} + +// TestPBTSFarFutureProposalRejectedAtHighRound is a regression test for the +// removal of `if cs.Proposal.Round > 10 { return true }` in proposalIsTimely. +// +// With that check in place a malicious proposer at round >10 could get an +// arbitrarily far-future block.Time prevoted by honest validators; once +// committed it poisons LastBlockTime and halts the chain. +func TestPBTSFarFutureProposalRejectedAtHighRound(t *testing.T) { + const numValidators = 4 + election := func(h int64, r int32) int { + return (int(h-1) + int(r)) % numValidators + } + + c := test.ConsensusParams() + app := kvstore.NewInMemoryApplication() + genesisTime := cmttime.Now().Add(-10 * time.Second) + cs, vss := randStateWithAppImplGenesisTime(numValidators, app, c, genesisTime) + + myPubKey, err := vss[0].GetPubKey() + require.NoError(t, err) + myAddress := myPubKey.Address() + + proposalCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal) + newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound) + voteCh := subscribe(cs.eventBus, types.EventQueryVote) + + height, round := cs.Height, cs.Round + chainID := cs.state.ChainID + + // One year ahead + const farFutureOffset = 365 * 24 * time.Hour + const lastRound = int32(12) + sawHighRound := false + + startTestRound(cs, height, round) + + for ; round <= lastRound; round++ { + t.Log("Starting round", round) + ensureNewRound(newRoundCh, height, round) + proposer := election(height, round) + + if proposer == 0 { + // The observed validator is the proposer: let it propose its own + // (timely) block; the stubs vote nil to skip the round. + ensureNewProposal(proposalCh, height, round) + ensurePrevote(voteCh, height, round) + } else { + block, blockParts, blockID := createProposalBlockWithTime(t, cs, cmttime.Now().Add(farFutureOffset)) + proposal := types.NewProposal(height, round, -1, blockID, block.Time) + signProposal(t, proposal, chainID, vss[proposer]) + require.NoError(t, cs.SetProposalAndBlock(proposal, block, blockParts, "peer")) + + ensureProposal(proposalCh, height, round, blockID) + ensurePrevote(voteCh, height, round) + + // Ensure far-future proposal have been prevoted nil at any round. + gotVote := cs.Votes.Prevotes(round).GetByAddress(myAddress) + require.NotNil(t, gotVote, "observed validator did not prevote at round %d", round) + require.True(t, gotVote.BlockID.IsNil(), + "round %d: far-future proposal must be prevoted nil, got block %X", + round, gotVote.BlockID.Hash) + if round > 10 { + sawHighRound = true + } + } + + // nil vote for stubs to broadcast to keep advancing in rounds. + var vote types.BlockID + + for _, vs := range vss[2:] { + signAddVotes(cs, cmtproto.PrevoteType, vote.Hash, vote.PartSetHeader, false, vs) + ensurePrevote(voteCh, height, round) + } + ensurePrecommit(voteCh, height, round) + for _, vs := range vss[2:] { + signAddVotes(cs, cmtproto.PrecommitType, vote.Hash, vote.PartSetHeader, true, vs) + ensurePrecommit(voteCh, height, round) + } + + incrementRound(vss[1:]...) + } + + require.True(t, sawHighRound, "test never exercised a round > 10") + require.Equal(t, height, cs.Height, + "a far-future proposal was committed: chain would halt on a poisoned LastBlockTime") +} diff --git a/consensus/reactor.go b/consensus/reactor.go index d0973bb84f9..589be856b79 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -281,11 +281,13 @@ func (conR *Reactor) Receive(e p2p.Envelope) { return } ps.ApplyNewRoundStepMessage(msg) - conR.conS.statsMsgQueue <- msgInfo{msg, e.Src.ID()} + conR.conS.statsMsgQueue <- msgInfo{msg, e.Src.ID(), time.Time{}} case *NewValidBlockMessage: ps.ApplyNewValidBlockMessage(msg) case *HasVoteMessage: ps.ApplyHasVoteMessage(msg) + case *HasProposalBlockPartMessage: + ps.ApplyHasProposalBlockPartMessage(msg) case *VoteSetMaj23Message: rs := conR.getRoundState() height, votes := rs.Height, rs.Votes @@ -342,13 +344,13 @@ func (conR *Reactor) Receive(e p2p.Envelope) { } ps.SetHasProposal(msg.Proposal) - conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID()} + conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID(), cmttime.Now()} case *ProposalPOLMessage: ps.ApplyProposalPOLMessage(msg) case *BlockPartMessage: ps.SetHasProposalBlockPart(msg.Height, msg.Round, int(msg.Part.Index)) conR.Metrics.BlockParts.With("peer_id", string(e.Src.ID())).Add(1) - conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID()} + conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID(), time.Time{}} default: conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg))) } @@ -365,7 +367,10 @@ func (conR *Reactor) Receive(e p2p.Envelope) { height, valSize, lastCommitSize := rs.Height, rs.Validators.Size(), rs.LastCommit.Size() ps.SetHasVoteFromPeer(msg.Vote, height, valSize, lastCommitSize) - conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID()} + conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID(), time.Time{}} + + case *CommitMessage: + conR.conS.peerMsgQueue <- msgInfo{msg, e.Src.ID(), time.Time{}} default: // don't punish (leave room for soft upgrades) @@ -480,6 +485,24 @@ func (conR *Reactor) subscribeToBroadcastEvents() { conR.Logger.Error("Error adding listener for Vote events", "err", err) } + err = conR.conS.evsw.AddListenerForEvent( + subscriber, + types.EventProposalBlockPart, + func(data cmtevents.EventData) { + conR.broadcastHasProposalBlockPartMessage(data.(*BlockPartMessage)) + + // update reactor's view of round state + // NOTE this is safe to do without locking cs because the eventBus is + // synchronous. If it were not, we could pass rs in this event + // instead + rs := conR.conS.getRoundState() + conR.updateRoundState(&rs) + }, + ) + if err != nil { + conR.Logger.Error("Error adding listener for ProposalBlockPart events", "err", err) + } + err = conR.conS.evsw.AddListenerForEvent( subscriber, types.EventNewConsensusParams, @@ -579,6 +602,21 @@ func (conR *Reactor) broadcastHasVoteMessage(vote *types.Vote) { */ } +// Broadcasts HasProposalBlockPartMessage to peers that care. +func (conR *Reactor) broadcastHasProposalBlockPartMessage(partMsg *BlockPartMessage) { + msg := &cmtcons.HasProposalBlockPart{ + Height: partMsg.Height, + Round: partMsg.Round, + Index: int32(partMsg.Part.Index), + } + go func() { + conR.Switch.TryBroadcast(p2p.Envelope{ + ChannelID: StateChannel, + Message: msg, + }) + }() +} + func makeRoundStepMessage(rs *cstypes.RoundState) (nrsMsg *cmtcons.NewRoundStep) { nrsMsg = &cmtcons.NewRoundStep{ Height: rs.Height, @@ -684,6 +722,20 @@ OUTER_LOOP: "height", prs.Height, "vote", vote, ) + } else if !ps.HasCatchupCommit() { + // With BLS aggregation individual votes cannot be extracted from + // a stored commit, so a lagging peer is sent the whole commit. + c := getEntireCommitToSend(logger, conR.conS, &rs, ps, prs) + if commit, ok := (c).(*types.Commit); ok { + if ps.sendCommit(commit) { + continue OUTER_LOOP + } + logger.Error("Failed to send commit to peer", + "height", prs.Height, + "commit", commit) + } else if c != nil { + logger.Error("Commit should have been returned, instead unknown type.", "type", fmt.Sprintf("%T", c)) + } } switch sleeping { @@ -898,9 +950,14 @@ func pickVoteToSend( // Special catchup logic. // If peer is lagging by height 1, send LastCommit. if prs.Height != 0 && rs.Height == prs.Height+1 { - if vote := ps.PickVoteToSend(rs.LastCommit); vote != nil { - logger.Debug("Picked rs.LastCommit to send", "height", prs.Height) - return vote + // Individual votes cannot be extracted from a whole (aggregated) + // *types.Commit; the peer will catch up via block sync or the next + // proposal instead. + if _, isWholeCommit := rs.LastCommit.(*types.Commit); !isWholeCommit { + if vote := ps.PickVoteToSend(rs.LastCommit); vote != nil { + logger.Debug("Picked rs.LastCommit to send", "height", prs.Height) + return vote + } } } @@ -915,7 +972,7 @@ func pickVoteToSend( func() { conS.mtx.RLock() defer conS.mtx.RUnlock() - veEnabled = conS.state.ConsensusParams.ABCI.VoteExtensionsEnabled(prs.Height) + veEnabled = conS.state.ConsensusParams.Feature.VoteExtensionsEnabled(prs.Height) }() if veEnabled { ec = conS.blockStore.LoadBlockExtendedCommit(prs.Height) @@ -924,6 +981,12 @@ func pickVoteToSend( if c == nil { return nil } + if c.HasAggregatedSignature() { + // Individual votes cannot be extracted from an aggregated + // commit; the whole commit is gossiped instead (see + // getEntireCommitToSend). + return nil + } ec = c.WrappedExtendedCommit() } if ec == nil { @@ -937,6 +1000,49 @@ func pickVoteToSend( return nil } +// getEntireCommitToSend returns the whole stored commit for the peer's +// height when the peer is lagging by more than one height. It is used when +// commits are aggregated, since individual votes cannot be extracted from an +// aggregated commit. +func getEntireCommitToSend(_ log.Logger, + conS *State, + rs *cstypes.RoundState, + _ *PeerState, + prs *cstypes.PeerRoundState, +) types.VoteSetReader { + // Catchup logic + // If peer is lagging by more than 1, send Commit. + blockStoreBase := conS.blockStore.Base() + if blockStoreBase > 0 && prs.Height != 0 && rs.Height >= prs.Height+2 && prs.Height >= blockStoreBase { + // Load the block's commit for prs.Height, + // which contains precommit signatures for prs.Height. + var commit types.VoteSetReader + var veEnabled bool + func() { + conS.mtx.RLock() + defer conS.mtx.RUnlock() + veEnabled = conS.state.ConsensusParams.Feature.VoteExtensionsEnabled(prs.Height) + }() + if veEnabled { + ec := conS.blockStore.LoadBlockExtendedCommit(prs.Height) + if ec == nil { + return nil + } + commit = ec + } else { + c := conS.blockStore.LoadBlockCommit(prs.Height) + if c == nil || !c.HasAggregatedSignature() { + // Non-aggregated commits are gossiped vote by vote in + // pickVoteToSend. + return nil + } + commit = c + } + return commit + } + return nil +} + func pickVoteCurrentHeight( logger log.Logger, rs *cstypes.RoundState, @@ -1192,6 +1298,17 @@ func (ps *PeerState) SetHasProposalBlockPart(height int64, round int32, index in ps.mtx.Lock() defer ps.mtx.Unlock() + ps.setHasProposalBlockPart(height, round, index) +} + +func (ps *PeerState) setHasProposalBlockPart(height int64, round int32, index int) { + ps.logger.Debug("setHasProposalBlockPart", + "peerH/R", + log.NewLazySprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), + "H/R", + log.NewLazySprintf("%d/%d", height, round), + "index", index) + if ps.PRS.Height != height || ps.PRS.Round != round { return } @@ -1281,6 +1398,47 @@ func (ps *PeerState) SendProposalSetHasProposal( } } +// sendCommit sends the aggregated commit to the peer. +func (ps *PeerState) sendCommit(commit *types.Commit) bool { + ps.logger.Debug("Sending commit message", "ps", ps, "commit", commit) + if ps.peer.Send(p2p.Envelope{ + ChannelID: VoteChannel, + Message: &cmtcons.Commit{ + Commit: commit.ToProto(), + }, + }) { + ps.SetHasCatchupCommit(commit) + return true + } + return false +} + +// SetHasCatchupCommit sets the given commit as known by the peer. +func (ps *PeerState) SetHasCatchupCommit(commit *types.Commit) { + ps.mtx.Lock() + defer ps.mtx.Unlock() + + ps.setHasCatchupCommit(commit.Height, commit.Round) +} + +// CONTRACT: Caller must hold the mutex. +func (ps *PeerState) setHasCatchupCommit(height int64, round int32) { + ps.logger.Debug("setHasCatchupCommit", + "peerH/R", + log.NewLazySprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), + "H/R", + log.NewLazySprintf("%d/%d", height, round)) + + ps.PRS.HasCatchupCommit = true +} + +func (ps *PeerState) HasCatchupCommit() bool { + ps.mtx.Lock() + defer ps.mtx.Unlock() + + return ps.PRS.HasCatchupCommit +} + // sendVoteSetHasVote sends the vote to the peer. // Returns true and marks the peer as having the vote if the vote was sent. func (ps *PeerState) sendVoteSetHasVote(vote *types.Vote) bool { @@ -1304,6 +1462,13 @@ func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) *types.Vote { ps.mtx.Lock() defer ps.mtx.Unlock() + // Individual votes cannot be extracted from a whole *types.Commit + // (GetByIndex returns nil), so there is never a vote to pick. Whole + // commits are gossiped via sendCommit instead. + if _, isWholeCommit := votes.(*types.Commit); isWholeCommit { + return nil + } + if votes.Size() == 0 { return nil } @@ -1559,6 +1724,8 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) { // We'll update the BitArray capacity later. ps.PRS.CatchupCommitRound = -1 ps.PRS.CatchupCommit = nil + + ps.PRS.HasCatchupCommit = false } } @@ -1608,6 +1775,18 @@ func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) { ps.setHasVote(msg.Height, msg.Round, msg.Type, msg.Index) } +// ApplyHasProposalBlockPartMessage updates the peer state for the new block part. +func (ps *PeerState) ApplyHasProposalBlockPartMessage(msg *HasProposalBlockPartMessage) { + ps.mtx.Lock() + defer ps.mtx.Unlock() + + if ps.PRS.Height != msg.Height { + return + } + + ps.setHasProposalBlockPart(msg.Height, msg.Round, int(msg.Index)) +} + // ApplyVoteSetBitsMessage updates the peer state for the bit-array of votes // it claims to have for the corresponding BlockID. // `ourVotes` is a BitArray of votes we have for msg.BlockID @@ -1664,7 +1843,9 @@ func init() { cmtjson.RegisterType(&ProposalPOLMessage{}, "tendermint/ProposalPOL") cmtjson.RegisterType(&BlockPartMessage{}, "tendermint/BlockPart") cmtjson.RegisterType(&VoteMessage{}, "tendermint/Vote") + cmtjson.RegisterType(&CommitMessage{}, "tendermint/Commit") cmtjson.RegisterType(&HasVoteMessage{}, "tendermint/HasVote") + cmtjson.RegisterType(&HasProposalBlockPartMessage{}, "tendermint/HasProposalBlockPart") cmtjson.RegisterType(&VoteSetMaj23Message{}, "tendermint/VoteSetMaj23") cmtjson.RegisterType(&VoteSetBitsMessage{}, "tendermint/VoteSetBits") } @@ -1887,6 +2068,29 @@ func (m *VoteMessage) String() string { //------------------------------------- +// CommitMessage is sent instead of individual votes once the precommit +// signatures have been aggregated into a single commit. +type CommitMessage struct { + Commit *types.Commit +} + +// ValidateBasic checks whether the commit within the message is well-formed. +func (m *CommitMessage) ValidateBasic() error { + // Bound the signature count here since Commit.ValidateBasic skips it for + // height-zero commits. Mirrors the blocksync guard in #5860. + if n := len(m.Commit.Signatures); n > types.MaxVotesCount { + return fmt.Errorf("too many commit signatures: %d (max %d)", n, types.MaxVotesCount) + } + return m.Commit.ValidateBasic() +} + +// String returns a string representation. +func (m *CommitMessage) String() string { + return fmt.Sprintf("[Commit with aggregated signatures %v]", m.Commit) +} + +//------------------------------------- + // HasVoteMessage is sent to indicate that a particular vote has been received. type HasVoteMessage struct { Height int64 diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 4114c309881..84340d8552f 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -360,7 +360,7 @@ func TestSwitchToConsensusVoteExtensions(t *testing.T) { cs.state.LastBlockHeight = testCase.storedHeight cs.state.LastValidators = cs.state.Validators.Copy() - cs.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.initialRequiredHeight + cs.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = testCase.initialRequiredHeight propBlock, err := cs.createProposalBlock(ctx) require.NoError(t, err) @@ -392,7 +392,7 @@ func TestSwitchToConsensusVoteExtensions(t *testing.T) { require.NoError(t, err) require.True(t, added) - veHeightParam := types.ABCIParams{VoteExtensionsEnableHeight: veHeight} + veHeightParam := types.FeatureParams{VoteExtensionsEnableHeight: veHeight} if testCase.includeExtensions { cs.blockStore.SaveBlockWithExtendedCommit(propBlock, blockParts, voteSet.MakeExtendedCommit(veHeightParam)) } else { @@ -986,6 +986,36 @@ func TestBlockPartMessageValidateBasic(t *testing.T) { assert.Equal(t, true, message.ValidateBasic() != nil, "Validate Basic had an unexpected result") } +func TestCommitMessageValidateBasic(t *testing.T) { + // A height-zero commit bypasses Commit.ValidateBasic's per-signature checks, + // so the signature-count guard in CommitMessage.ValidateBasic is what bounds + // an otherwise arbitrarily large gossiped commit. + commitWithSigs := func(n int) *types.Commit { + return &types.Commit{Height: 0, Round: 0, Signatures: make([]types.CommitSig, n)} + } + + testCases := []struct { + testName string + numSigs int + expectErr bool + }{ + {"at cap", types.MaxVotesCount, false}, + {"above cap", types.MaxVotesCount + 1, true}, + } + + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + message := CommitMessage{Commit: commitWithSigs(tc.numSigs)} + err := message.ValidateBasic() + if tc.expectErr { + require.ErrorContains(t, err, "too many commit signatures") + } else { + require.NoError(t, err) + } + }) + } +} + func TestHasVoteMessageValidateBasic(t *testing.T) { const ( validSignedMsgType cmtproto.SignedMsgType = 0x01 @@ -1136,7 +1166,8 @@ func TestMarshalJSONPeerState(t *testing.T) { "last_commit_round": -1, "last_commit": null, "catchup_commit_round": -1, - "catchup_commit": null + "catchup_commit": null, + "has_catchup_commit": false }, "stats":{ "votes":"0", @@ -1144,6 +1175,27 @@ func TestMarshalJSONPeerState(t *testing.T) { }`, string(data)) } +// TestPickVoteToSendWholeCommit checks that vote gossip never tries to pick +// individual votes out of a whole *types.Commit (its GetByIndex returns nil); +// whole commits are gossiped via sendCommit instead. Regression test for the +// post-restart LastCommit gossip path, where LastCommit holds a whole commit. +func TestPickVoteToSendWholeCommit(t *testing.T) { + ps := NewPeerState(nil).SetLogger(log.TestingLogger()) + + commit := &types.Commit{ + Height: 1078, + Round: 0, + BlockID: types.BlockID{Hash: cmtrand.Bytes(tmhash.Size), PartSetHeader: types.PartSetHeader{Total: 1, Hash: cmtrand.Bytes(tmhash.Size)}}, + Signatures: []types.CommitSig{ + {BlockIDFlag: types.BlockIDFlagCommit, ValidatorAddress: cmtrand.Bytes(20), Signature: cmtrand.Bytes(64)}, + {BlockIDFlag: types.BlockIDFlagAbsent}, + {BlockIDFlag: types.BlockIDFlagCommit, ValidatorAddress: cmtrand.Bytes(20), Signature: cmtrand.Bytes(64)}, + }, + } + + require.Nil(t, ps.PickVoteToSend(commit)) +} + func TestVoteMessageValidateBasic(t *testing.T) { _, vss := randState(2) diff --git a/consensus/replay.go b/consensus/replay.go index 6c400f851d3..af617c11350 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -70,13 +70,16 @@ func (cs *State) readReplayMessage(msg *TimedWALMessage, newStepSub types.Subscr case *ProposalMessage: p := msg.Proposal cs.Logger.Info("Replay: Proposal", "height", p.Height, "round", p.Round, "header", - p.BlockID.PartSetHeader, "pol", p.POLRound, "peer", peerID) + p.BlockID.PartSetHeader, "pol", p.POLRound, "peer", peerID, "receive_time", m.ReceiveTime) case *BlockPartMessage: cs.Logger.Info("Replay: BlockPart", "height", msg.Height, "round", msg.Round, "peer", peerID) case *VoteMessage: v := msg.Vote cs.Logger.Info("Replay: Vote", "height", v.Height, "round", v.Round, "type", v.Type, "blockID", v.BlockID, "peer", peerID, "extensionLen", len(v.Extension), "extSigLen", len(v.ExtensionSignature)) + case *CommitMessage: + c := msg.Commit + cs.Logger.Info("Replay: Commit", "height", c.Height, "round", c.Round, "peer", peerID) } cs.handleMsg(m) @@ -498,7 +501,7 @@ func (h *Handshaker) replayBlocks( assertAppHashEqualsOneFromBlock(appHash, block) } - appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, h.stateStore, h.genDoc.InitialHeight) + appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, h.stateStore, h.genDoc.InitialHeight, storeBlockHeight) if err != nil { return nil, err } @@ -530,7 +533,7 @@ func (h *Handshaker) replayBlock(state sm.State, height int64, proxyApp proxy.Ap blockExec.SetEventBus(h.eventBus) var err error - state, err = blockExec.ApplyBlock(state, meta.BlockID, block) + state, err = blockExec.ApplyBlock(state, meta.BlockID, block, block.Height) if err != nil { return sm.State{}, err } diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 27e7e289bc6..d6416be2e19 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -34,6 +34,7 @@ import ( sm "github.com/cometbft/cometbft/state" smmocks "github.com/cometbft/cometbft/state/mocks" "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" ) func TestMain(m *testing.M) { @@ -462,7 +463,7 @@ func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (* require.NoError(t, err) blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} - proposal := types.NewProposal(vss[1].Height, round, -1, blockID) + proposal := types.NewProposal(vss[1].Height, round, -1, blockID, propBlock.Time) p := proposal.ToProto() if err := vss[1].SignProposal(test.DefaultTestChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -493,7 +494,7 @@ func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (* require.NoError(t, err) blockID = types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} - proposal = types.NewProposal(vss[2].Height, round, -1, blockID) + proposal = types.NewProposal(vss[2].Height, round, -1, blockID, propBlock.Time) p = proposal.ToProto() if err := vss[2].SignProposal(test.DefaultTestChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -551,7 +552,7 @@ func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (* selfIndex := valIndexFn(0) - proposal = types.NewProposal(vss[3].Height, round, -1, blockID) + proposal = types.NewProposal(vss[3].Height, round, -1, blockID, cmttime.Now()) p = proposal.ToProto() if err := vss[3].SignProposal(test.DefaultTestChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -610,7 +611,7 @@ func setupChainWithChangingValidators(t *testing.T, name string, nBlocks int) (* sort.Sort(ValidatorStubsByPower(newVss)) selfIndex = valIndexFn(0) - proposal = types.NewProposal(vss[1].Height, round, -1, blockID) + proposal = types.NewProposal(vss[1].Height, round, -1, blockID, propBlock.Time) p = proposal.ToProto() if err := vss[1].SignProposal(test.DefaultTestChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -852,7 +853,7 @@ func applyBlock(t *testing.T, stateStore sm.Store, mempool mempool.Mempool, evpo bps, err := blk.MakePartSet(testPartSize) require.NoError(t, err) blkID := types.BlockID{Hash: blk.Hash(), PartSetHeader: bps.Header()} - newState, err := blockExec.ApplyBlock(st, blkID, blk) + newState, err := blockExec.ApplyBlock(st, blkID, blk, blk.Height) require.NoError(t, err) return newState } diff --git a/consensus/state.go b/consensus/state.go index 4b569bda640..a1277e89648 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -6,9 +6,11 @@ import ( "errors" "fmt" "io" + "math" "os" "runtime/debug" "sort" + "strconv" "time" "github.com/cosmos/gogoproto/proto" @@ -16,6 +18,7 @@ import ( cfg "github.com/cometbft/cometbft/config" cstypes "github.com/cometbft/cometbft/consensus/types" "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/bls12381" cmtevents "github.com/cometbft/cometbft/libs/events" "github.com/cometbft/cometbft/libs/fail" cmtjson "github.com/cometbft/cometbft/libs/json" @@ -36,8 +39,9 @@ var msgQueueSize = 1000 // msgs from the reactor which may update the state type msgInfo struct { - Msg Message `json:"msg"` - PeerID p2p.ID `json:"peer_key"` + Msg Message `json:"msg"` + PeerID p2p.ID `json:"peer_key"` + ReceiveTime time.Time `json:"receive_time"` } // internally generated messages which may update the state @@ -121,7 +125,7 @@ type State struct { // some functions can be overwritten for testing decideProposal func(height int64, round int32) doPrevote func(height int64, round int32) - setProposal func(proposal *types.Proposal) error + setProposal func(proposal *types.Proposal, recvTime time.Time) error // closed when we finish shutting down done chan struct{} @@ -474,21 +478,80 @@ func (cs *State) OpenWAL(walFile string) (WAL, error) { // AddVote inputs a vote. func (cs *State) AddVote(vote *types.Vote, peerID p2p.ID) (added bool, err error) { if peerID == "" { - cs.internalMsgQueue <- msgInfo{&VoteMessage{vote}, ""} + cs.internalMsgQueue <- msgInfo{&VoteMessage{vote}, "", time.Time{}} } else { - cs.peerMsgQueue <- msgInfo{&VoteMessage{vote}, peerID} + cs.peerMsgQueue <- msgInfo{&VoteMessage{vote}, peerID, time.Time{}} } // TODO: wait for event?! return false, nil } +// AddCommit ingests a whole aggregated commit received from a peer (gossiped +// instead of individual votes once signatures have been aggregated). It is +// used by nodes that fall behind to catch up to the rest of the network. +func (cs *State) AddCommit(commit *types.Commit, peerID p2p.ID) (bool, error) { + cs.Logger.Debug( + "Adding whole commit", + "commit_height", commit.Height, + "commit_round", commit.Round, + "commit_blockId", commit.BlockID, + "cs_height", cs.Height, + "cs_round", cs.Round, + ) + + // Height mismatch is ignored. + // Not necessarily a bad peer, but not favorable behavior. + if commit.Height != cs.Height { + cs.Logger.Debug("Commit ignored and not added", + "commit_height", commit.Height, + "cs_height", cs.Height, + "peer", peerID) + return false, nil + } + + // Check to see if the chain is configured to extend votes. + extEnabled := cs.isVoteExtensionsEnabled(commit.Height) + if extEnabled { + // We don't support receiving commits with vote extensions enabled ATM. + cs.Logger.Error("Received commit with vote extensions enabled", "height", commit.Height, "peer_ID", peerID) + return false, nil + } + + if cs.Votes.GetCommit(commit.Round) != nil { + cs.Logger.Debug("Received commit, but we already have one", "height", commit.Height, "peer_ID", peerID) + return false, nil + } + if !commit.HasAggregatedSignature() { + // Only accept aggregated commits + cs.Logger.Error("Received non aggregated commit", "commit", commit.Height, "peer_ID", peerID, "commit", commit) + return false, nil + } + // No need to check blockID. If the commit is valid, 2/3 of the voting power has signed it. + if err := cs.Validators.VerifyCommit(cs.state.ChainID, commit.BlockID, cs.Height, commit); err != nil { + cs.Logger.Error("Failed to verify commit from peer", "err", err, "height", cs.Height, "round", commit.Round, "peer_ID", peerID) + return false, err + } + height := cs.Height + cs.Votes.SetCommit(commit) + + // Executed as Commit could be from a higher round + cs.enterNewRound(height, commit.Round) + cs.enterPrecommit(height, commit.Round) + + cs.enterCommit(height, commit.Round) + // We skip timeoutCommit as this function is hit only when the node is late + cs.enterNewRound(cs.Height, 0) + + return true, nil +} + // SetProposal inputs a proposal. func (cs *State) SetProposal(proposal *types.Proposal, peerID p2p.ID) error { if peerID == "" { - cs.internalMsgQueue <- msgInfo{&ProposalMessage{proposal}, ""} + cs.internalMsgQueue <- msgInfo{&ProposalMessage{proposal}, "", cmttime.Now()} } else { - cs.peerMsgQueue <- msgInfo{&ProposalMessage{proposal}, peerID} + cs.peerMsgQueue <- msgInfo{&ProposalMessage{proposal}, peerID, cmttime.Now()} } // TODO: wait for event?! @@ -498,9 +561,9 @@ func (cs *State) SetProposal(proposal *types.Proposal, peerID p2p.ID) error { // AddProposalBlockPart inputs a part of the proposal block. func (cs *State) AddProposalBlockPart(height int64, round int32, part *types.Part, peerID p2p.ID) error { if peerID == "" { - cs.internalMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, ""} + cs.internalMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, "", time.Time{}} } else { - cs.peerMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, peerID} + cs.peerMsgQueue <- msgInfo{&BlockPartMessage{height, round, part}, peerID, time.Time{}} } // TODO: wait for event?! @@ -593,7 +656,7 @@ func (cs *State) reconstructSeenCommit(state sm.State) { // the method will panic on an absent ExtendedCommit or an ExtendedCommit without // extension data. func (cs *State) reconstructLastCommit(state sm.State) { - extensionsEnabled := state.ConsensusParams.ABCI.VoteExtensionsEnabled(state.LastBlockHeight) + extensionsEnabled := state.ConsensusParams.Feature.VoteExtensionsEnabled(state.LastBlockHeight) if !extensionsEnabled { cs.reconstructSeenCommit(state) return @@ -621,7 +684,7 @@ func (cs *State) votesFromExtendedCommit(state sm.State) (*types.VoteSet, error) return vs, nil } -func (cs *State) votesFromSeenCommit(state sm.State) (*types.VoteSet, error) { +func (cs *State) votesFromSeenCommit(state sm.State) (types.VoteSetReader, error) { commit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight) if commit == nil { commit = cs.blockStore.LoadBlockCommit(state.LastBlockHeight) @@ -633,11 +696,10 @@ func (cs *State) votesFromSeenCommit(state sm.State) (*types.VoteSet, error) { return nil, fmt.Errorf("heights don't match in votesFromSeenCommit %v!=%v", commit.Height, state.LastBlockHeight) } - vs := commit.ToVoteSet(state.ChainID, state.LastValidators) - if !vs.HasTwoThirdsMajority() { - return nil, ErrCommitQuorumNotMet - } - return vs, nil + // With BLS aggregation the individual precommit votes cannot be + // reconstructed from an aggregated seen commit, so the whole *Commit + // stands in for the vote set. + return commit, nil } // Updates State and increments height to match that of state. @@ -689,14 +751,18 @@ func (cs *State) updateToState(state sm.State) { case state.LastBlockHeight == 0: // Very first commit should be empty. cs.LastCommit = (*types.VoteSet)(nil) case cs.CommitRound > -1 && cs.Votes != nil: // Otherwise, use cs.Votes - if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() { - panic(fmt.Sprintf( - "wanted to form a commit, but precommits (H/R: %d/%d) didn't have 2/3+: %v", - state.LastBlockHeight, cs.CommitRound, cs.Votes.Precommits(cs.CommitRound), - )) - } + if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil { + cs.LastCommit = commit + } else { + if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() { + panic(fmt.Sprintf( + "wanted to form a commit, but precommits (H/R: %d/%d) didn't have 2/3+: %v", + state.LastBlockHeight, cs.CommitRound, cs.Votes.Precommits(cs.CommitRound), + )) + } - cs.LastCommit = cs.Votes.Precommits(cs.CommitRound) + cs.LastCommit = cs.Votes.Precommits(cs.CommitRound) + } case cs.LastCommit == nil: // NOTE: when consensus starts, it has no votes. reconstructLastCommit @@ -717,19 +783,28 @@ func (cs *State) updateToState(state sm.State) { cs.updateHeight(height) cs.updateRoundStep(0, cstypes.RoundStepNewHeight) + // The app can set a delay between the time when this block is committed + // and the next height is started (ADR-115, previously `timeout_commit` + // in config.toml). + timeoutCommit := state.NextBlockDelay + // If the ABCI app didn't set a delay, use the deprecated config value. + if timeoutCommit == 0 { + timeoutCommit = cs.config.TimeoutCommit + } if cs.CommitTime.IsZero() { // "Now" makes it easier to sync up dev nodes. // We add timeoutCommit to allow transactions // to be gathered for the first block. // And alternative solution that relies on clocks: // cs.StartTime = state.LastBlockTime.Add(timeoutCommit) - cs.StartTime = cs.config.Commit(cmttime.Now()) + cs.StartTime = cmttime.Now().Add(timeoutCommit) } else { - cs.StartTime = cs.config.Commit(cs.CommitTime) + cs.StartTime = cs.CommitTime.Add(timeoutCommit) } cs.Validators = validators cs.Proposal = nil + cs.ProposalReceiveTime = time.Time{} cs.ProposalBlock = nil cs.ProposalBlockParts = nil cs.LockedRound = -1 @@ -738,7 +813,7 @@ func (cs *State) updateToState(state sm.State) { cs.ValidRound = -1 cs.ValidBlock = nil cs.ValidBlockParts = nil - if state.ConsensusParams.ABCI.VoteExtensionsEnabled(height) { + if state.ConsensusParams.Feature.VoteExtensionsEnabled(height) { cs.Votes = cstypes.NewExtendedHeightVoteSet(state.ChainID, height, validators) } else { cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators) @@ -922,7 +997,7 @@ func (cs *State) handleMsg(mi msgInfo) { case *ProposalMessage: // will not cause transition. // once proposal is set, we can receive block parts - err = cs.setProposal(msg.Proposal) + err = cs.setProposal(msg.Proposal, mi.ReceiveTime) case *BlockPartMessage: // if the proposal is complete, we'll enterPrevote or tryFinalizeCommit @@ -971,6 +1046,16 @@ func (cs *State) handleMsg(mi msgInfo) { // the peer is sending us CatchupCommit precommits. // We could make note of this and help filter in broadcastHasVoteMessage(). + case *CommitMessage: + _, err := cs.AddCommit(msg.Commit, peerID) + // XXX The function above returns `added` + // If this boolean is true we should implement + // stats for commit messages + // cs.statsMsgQueue <- mi + if err != nil { + cs.Logger.Error("Failed to add commit ", "commit", msg.Commit, "err", err) + } + case *ingestVerifiedBlockRequest: cs.handleIngestVerifiedBlockRequest(msg) default: @@ -1185,6 +1270,16 @@ func (cs *State) enterPropose(height int64, round int32) { return } + // If this validator is the proposer of this round, and the previous block time is later than + // our local clock time, wait to propose until our local clock time has passed the block time. + if cs.isPBTSEnabled(height) && cs.privValidatorPubKey != nil && cs.isProposer(cs.privValidatorPubKey.Address()) { + proposerWaitTime := proposerWaitTime(cmttime.DefaultSource{}, cs.state.LastBlockTime) + if proposerWaitTime > 0 { + cs.scheduleTimeout(proposerWaitTime, height, round, cstypes.RoundStepNewRound) + return + } + } + logger.Debug("entering propose step", "current", log.NewLazySprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) defer func() { @@ -1272,17 +1367,17 @@ func (cs *State) defaultDecideProposal(height int64, round int32) { // Make proposal propBlockID := types.BlockID{Hash: block.Hash(), PartSetHeader: blockParts.Header()} - proposal := types.NewProposal(height, round, cs.ValidRound, propBlockID) + proposal := types.NewProposal(height, round, cs.ValidRound, propBlockID, block.Time) p := proposal.ToProto() if err := cs.privValidator.SignProposal(cs.state.ChainID, p); err == nil { proposal.Signature = p.Signature // send proposal and block parts on internal msg queue - cs.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, ""}) + cs.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, "", cmttime.Now()}) for i := 0; i < int(blockParts.Total()); i++ { part := blockParts.GetPart(i) - cs.sendInternalMessage(msgInfo{&BlockPartMessage{cs.Height, cs.Round, part}, ""}) + cs.sendInternalMessage(msgInfo{&BlockPartMessage{cs.Height, cs.Round, part}, "", time.Time{}}) } cs.Logger.Debug("signed proposal", "height", height, "round", round, "proposal", proposal) @@ -1320,15 +1415,34 @@ func (cs *State) createProposalBlock(ctx context.Context) (*types.Block, error) // TODO(sergio): wouldn't it be easier if CreateProposalBlock accepted cs.LastCommit directly? var lastExtCommit *types.ExtendedCommit + lastCommitAsVs, ok := cs.LastCommit.(*types.VoteSet) switch { case cs.Height == cs.state.InitialHeight: // We're creating a proposal for the first block. // The commit is empty, but not nil. lastExtCommit = &types.ExtendedCommit{} - case cs.LastCommit.HasTwoThirdsMajority(): - // Make the commit from LastCommit - lastExtCommit = cs.LastCommit.MakeExtendedCommit(cs.state.ConsensusParams.ABCI) + case ok && lastCommitAsVs.HasTwoThirdsMajority(): + // Make the commit from LastCommit. + _, blsKey := cs.privValidatorPubKey.(*bls12381.PubKey) + _, blsKey2 := cs.privValidatorPubKey.(bls12381.PubKey) + canBeAggregated := (blsKey || blsKey2) && + cs.state.Validators.AllKeysHaveSameType() + if canBeAggregated { + if !cs.isPBTSEnabled(cs.Height) { + panic("Wanted to aggregate LastCommit, but PBTS is not enabled for height " + strconv.FormatInt(cs.Height, 10)) + } + lastExtCommit = lastCommitAsVs.MakeBLSCommit() + } else { + lastExtCommit = lastCommitAsVs.MakeExtendedCommit(cs.state.ConsensusParams.Feature) + } + case !ok: + lastCommitAsCommit, ok := cs.LastCommit.(*types.Commit) + if !ok { + return nil, errors.New("last commit is neither a VoteSet nor a Commit") + } + // XXX This will need to be extended to support vote extensions. + lastExtCommit = lastCommitAsCommit.WrappedExtendedCommit() default: // This shouldn't happen. return nil, ErrProposalWithoutPreviousCommit @@ -1379,6 +1493,70 @@ func (cs *State) enterPrevote(height int64, round int32) { // (so we have more time to try and collect +2/3 prevotes for a single block) } +func (cs *State) timelyProposalMargins() (time.Duration, time.Duration) { + sp := cs.state.ConsensusParams.Synchrony.InRound(cs.Round) + + // cs.ProposalReceiveTime - cs.Proposal.Timestamp >= -1 * Precision + // cs.ProposalReceiveTime - cs.Proposal.Timestamp <= MessageDelay + Precision + return -sp.Precision, sp.MessageDelay + sp.Precision +} + +func (cs *State) proposalIsTimely() bool { + sp := cs.state.ConsensusParams.Synchrony.InRound(cs.Proposal.Round) + + return cs.Proposal.IsTimely(cs.ProposalReceiveTime, sp) +} + +// isPBTSEnabled returns true if PBTS is enabled at the given height. +func (cs *State) isPBTSEnabled(height int64) bool { + return cs.state.ConsensusParams.Feature.PbtsEnabled(height) +} + +func (cs *State) isVoteExtensionsEnabled(height int64) bool { + return cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(height) +} + +// canAggregateCommits returns true if the validator set uses BLS12-381 keys +// exclusively, in which case precommit signatures are aggregated into a +// single commit signature (mirrors the routing in types.isAggregatedCommit). +func (cs *State) canAggregateCommits() bool { + proposerKey := cs.Validators.GetProposer().PubKey + _, blsKey := proposerKey.(*bls12381.PubKey) + _, blsKey2 := proposerKey.(bls12381.PubKey) + return (blsKey || blsKey2) && cs.Validators.AllKeysHaveSameType() +} + +// proposerWaitTime determines how long the proposer should wait to propose its next block. +// If the result is zero, a block can be proposed immediately. +// +// Block times must be monotonically increasing, so if the block time of the previous +// block is larger than the proposer's current time, then the proposer will sleep +// until its local clock exceeds the previous block time. +func proposerWaitTime(lt cmttime.Source, bt time.Time) time.Duration { + t := lt.Now() + if bt.After(t) { + return bt.Sub(t) + } + return 0 +} + +func (cs *State) calculateProposalTimestampDifferenceMetric() { + if cs.Proposal != nil && cs.Proposal.POLRound == -1 { + sp := cs.state.ConsensusParams.Synchrony.InRound(cs.Proposal.Round) + + isTimely := cs.Proposal.IsTimely(cs.ProposalReceiveTime, sp) + diff := cs.ProposalReceiveTime.Sub(cs.Proposal.Timestamp) + // Clamp overflowed durations to avoid recording garbage metric values. + // time.Sub sub/overflows when the difference exceeds ~±292 years. + if diff == time.Duration(math.MinInt64) || diff == time.Duration(math.MaxInt64) { + return + } + cs.metrics.ProposalTimestampDifference. + With("is_timely", strconv.FormatBool(isTimely)). + Observe(diff.Seconds()) + } +} + func (cs *State) defaultDoPrevote(height int64, round int32) { logger := cs.Logger.With("height", height, "round", round) @@ -1396,6 +1574,46 @@ func (cs *State) defaultDoPrevote(height int64, round int32) { return } + // We did not receive a valid proposal for this round (and thus executing + // this from a timeout). + if cs.Proposal == nil { + logger.Debug("prevote step: did not receive a valid Proposal; prevoting nil") + cs.signAddVote(cmtproto.PrevoteType, nil, types.PartSetHeader{}, nil) + return + } + + // Timestamp validation using Proposer-Based TimeStamp (PBTS) algorithm. + // See: https://github.com/cometbft/cometbft/blob/main/spec/consensus/proposer-based-timestamp/ + if cs.isPBTSEnabled(height) { + if !cs.Proposal.Timestamp.Equal(cs.ProposalBlock.Time) { + logger.Debug("prevote step: proposal timestamp not equal; prevoting nil") + cs.signAddVote(cmtproto.PrevoteType, nil, types.PartSetHeader{}, nil) + return + } + + if cs.Proposal.POLRound == -1 && !cs.proposalIsTimely() { + lowerBound, upperBound := cs.timelyProposalMargins() + // Use .String() to avoid passing a raw time.Duration to the logger, + // which can panic in some logger implementations + // when time.Sub overflows to math.MinInt64 for far-future timestamps. + logger.Info("prevote step: Proposal is not timely; prevoting nil", + "timestamp", cs.Proposal.Timestamp.Format(time.RFC3339Nano), + "receive_time", cs.ProposalReceiveTime.Format(time.RFC3339Nano), + "timestamp_difference", cs.ProposalReceiveTime.Sub(cs.Proposal.Timestamp).String(), + "lower_bound", lowerBound, + "upper_bound", upperBound) + cs.signAddVote(cmtproto.PrevoteType, nil, types.PartSetHeader{}, nil) + return + } + + if cs.Proposal.POLRound == -1 { + logger.Debug("prevote step: Proposal is timely", + "timestamp", cs.Proposal.Timestamp.Format(time.RFC3339Nano), + "receive_time", cs.ProposalReceiveTime.Format(time.RFC3339Nano), + "timestamp_difference", cs.ProposalReceiveTime.Sub(cs.Proposal.Timestamp).String()) + } + } + // Validate proposal block, from consensus' perspective err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock) if err != nil { @@ -1655,9 +1873,15 @@ func (cs *State) enterCommit(height int64, commitRound int32) { cs.tryFinalizeCommit(height) }() - blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority() - if !ok { - panic("RunActionCommit() expects +2/3 precommits") + var blockID types.BlockID + if commit := cs.Votes.GetCommit(commitRound); commit != nil { + blockID = commit.BlockID + } else { + var ok bool + blockID, ok = cs.Votes.Precommits(commitRound).TwoThirdsMajority() + if !ok || blockID.IsNil() { + panic("RunActionCommit() expects +2/3 precommits") + } } // The Locked* fields no longer matter. @@ -1700,10 +1924,16 @@ func (cs *State) tryFinalizeCommit(height int64) { panic(fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height)) } - blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() - if !ok || len(blockID.Hash) == 0 { - logger.Error("failed attempt to finalize commit; there was no +2/3 majority or +2/3 was for nil") - return + var blockID types.BlockID + if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil { + blockID = commit.BlockID + } else { + var ok bool + blockID, ok = cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() + if !ok || len(blockID.Hash) == 0 { + logger.Error("failed attempt to finalize commit; there was no +2/3 majority or +2/3 was for nil") + return + } } if !cs.ProposalBlock.HashesTo(blockID.Hash) { @@ -1732,14 +1962,20 @@ func (cs *State) finalizeCommit(height int64) { return } - cs.calculatePrevoteMessageDelayMetrics() + var blockID types.BlockID + if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil { + blockID = commit.BlockID + } else { + cs.calculatePrevoteMessageDelayMetrics() - blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() + var ok bool + blockID, ok = cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() + if !ok { + panic("cannot finalize commit; commit does not have 2/3 majority") + } + } block, blockParts := cs.ProposalBlock, cs.ProposalBlockParts - if !ok { - panic("cannot finalize commit; commit does not have 2/3 majority") - } if !blockParts.HasHeader(blockID.PartSetHeader) { panic("expected ProposalBlockParts header to be commit header") } @@ -1765,10 +2001,17 @@ func (cs *State) finalizeCommit(height int64) { // Save to blockStore. if cs.blockStore.Height() < block.Height { - // NOTE: the seenCommit is local justification to commit this block, - // but may differ from the LastCommit included in the next block - seenExtendedCommit := cs.Votes.Precommits(cs.CommitRound).MakeExtendedCommit(cs.state.ConsensusParams.ABCI) - if cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(block.Height) { + var seenExtendedCommit *types.ExtendedCommit + if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil { + seenExtendedCommit = commit.WrappedExtendedCommit() + } else if cs.canAggregateCommits() { + // NOTE: the seenCommit is local justification to commit this block, + // but may differ from the LastCommit included in the next block + seenExtendedCommit = cs.Votes.Precommits(cs.CommitRound).MakeBLSCommit() + } else { + seenExtendedCommit = cs.Votes.Precommits(cs.CommitRound).MakeExtendedCommit(cs.state.ConsensusParams.Feature) + } + if cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(block.Height) { cs.blockStore.SaveBlockWithExtendedCommit(block, blockParts, seenExtendedCommit) } else { cs.blockStore.SaveBlock(block, blockParts, seenExtendedCommit.ToCommit()) @@ -1816,6 +2059,7 @@ func (cs *State) finalizeCommit(height int64) { PartSetHeader: blockParts.Header(), }, block, + block.Height, ) if err != nil { panic(fmt.Sprintf("failed to apply block; error %v", err)) @@ -1825,6 +2069,9 @@ func (cs *State) finalizeCommit(height int64) { // must be called before we update state cs.recordMetrics(height, block) + cs.metrics.NextBlockDelay.Observe( + stateCopy.NextBlockDelay.Seconds(), + ) // NewHeightStep! cs.updateToState(stateCopy) @@ -1937,10 +2184,10 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { //----------------------------------------------------------------------------- -func (cs *State) defaultSetProposal(proposal *types.Proposal) error { +func (cs *State) defaultSetProposal(proposal *types.Proposal, recvTime time.Time) error { // Already have one // TODO: possibly catch double proposals - if cs.Proposal != nil { + if cs.Proposal != nil || proposal == nil { return nil } @@ -1975,6 +2222,8 @@ func (cs *State) defaultSetProposal(proposal *types.Proposal) error { proposal.Signature = p.Signature cs.Proposal = proposal + cs.ProposalReceiveTime = recvTime + cs.calculateProposalTimestampDifferenceMetric() // We don't update cs.ProposalBlockParts if it is already set. // This happens if we're already in cstypes.RoundStepCommit or if there is a valid block in the current round. // TODO: We can check if Proposal is for a different block as this is a sign of misbehavior! @@ -2027,6 +2276,8 @@ func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID p2p.ID) (add // NOTE: we are disregarding possible duplicates above where heights dont match or we're not expecting block parts yet // but between the matches_current = true and false, we have all the info. cs.metrics.DuplicateBlockPart.Add(1) + } else { + cs.evsw.FireEvent(types.EventProposalBlockPart, msg) } maxBytes := cs.state.ConsensusParams.Block.MaxBytes @@ -2192,7 +2443,14 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error return added, err } - added, err = cs.LastCommit.AddVote(vote) + lastCommitAsVs, ok := cs.LastCommit.(*types.VoteSet) + if !ok { + // LastCommit is a whole aggregated *types.Commit; individual late + // precommits cannot be added to it. + cs.Logger.Debug("Cannot add precommit vote to a commit; precommit ignored", "vote", vote) + return added, err + } + added, err = lastCommitAsVs.AddVote(vote) if !added { // If the vote wasn't added but there's no error, it's a duplicate vote if err == nil { @@ -2201,7 +2459,7 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error return added, err } - cs.Logger.Debug("added vote to last precommits", "last_commit", cs.LastCommit.StringShort()) + cs.Logger.Debug("added vote to last precommits", "last_commit", lastCommitAsVs.StringShort()) if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil { return added, err } @@ -2209,7 +2467,7 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error cs.evsw.FireEvent(types.EventVote, vote) // if we can skip timeoutCommit and have all the votes now, - if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() { + if cs.config.SkipTimeoutCommit && lastCommitAsVs.HasAll() { // go straight to new round (skip timeout commit) // cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight) cs.enterNewRound(cs.Height, 0) @@ -2226,7 +2484,7 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error } // Check to see if the chain is configured to extend votes. - extEnabled := cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(vote.Height) + extEnabled := cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(vote.Height) if extEnabled { // The chain is configured to extend votes, check that the vote is // not for a nil block and verify the extensions signature against the @@ -2438,12 +2696,15 @@ func (cs *State) signVote( ValidatorIndex: valIdx, Height: cs.Height, Round: cs.Round, - Timestamp: cs.voteTime(), Type: msgType, BlockID: types.BlockID{Hash: hash, PartSetHeader: header}, + // Vote timestamps are pinned to zero: they are excluded from the + // signed bytes so that BLS signatures can be aggregated, and the + // block time comes from the proposer (PBTS) instead of BFT Time. + Timestamp: time.Time{}, } - extEnabled := cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(vote.Height) + extEnabled := cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(vote.Height) if msgType == cmtproto.PrecommitType && !vote.BlockID.IsZero() { // if the signedMessage type is for a non-nil precommit, add // VoteExtension @@ -2487,27 +2748,6 @@ func (cs *State) signVote( return vote, err } -func (cs *State) voteTime() time.Time { - now := cmttime.Now() - minVoteTime := now - // Minimum time increment between blocks - const timeIota = time.Millisecond - // TODO: We should remove next line in case we don't vote for v in case cs.ProposalBlock == nil, - // even if cs.LockedBlock != nil. See https://github.com/cometbft/cometbft/tree/v0.38.x/spec/. - if cs.LockedBlock != nil { - // See the BFT time spec - // https://github.com/cometbft/cometbft/blob/v0.38.x/spec/consensus/bft-time.md - minVoteTime = cs.LockedBlock.Time.Add(timeIota) - } else if cs.ProposalBlock != nil { - minVoteTime = cs.ProposalBlock.Time.Add(timeIota) - } - - if now.After(minVoteTime) { - return now - } - return minVoteTime -} - // sign the vote and publish on internalMsgQueue // block information is only used to extend votes (precommit only); should be nil in all other cases func (cs *State) signAddVote( @@ -2538,12 +2778,12 @@ func (cs *State) signAddVote( return } hasExt := len(vote.ExtensionSignature) > 0 - extEnabled := cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(vote.Height) + extEnabled := cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(vote.Height) if vote.Type == cmtproto.PrecommitType && !vote.BlockID.IsZero() && hasExt != extEnabled { panic(fmt.Errorf("vote extension absence/presence does not match extensions enabled %t!=%t, height %d, type %v", hasExt, extEnabled, vote.Height, vote.Type)) } - cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""}) + cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, "", time.Time{}}) cs.Logger.Debug("signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote) } diff --git a/consensus/state_ingest.go b/consensus/state_ingest.go index 4091ae2acd6..0474ad244d9 100644 --- a/consensus/state_ingest.go +++ b/consensus/state_ingest.go @@ -127,7 +127,7 @@ func (ic *IngestCandidate) Verify(state state.State) error { height = ic.Height() blockID = ic.BlockID() chainID = state.ChainID - extensionsPresent = state.ConsensusParams.ABCI.VoteExtensionsEnabled(height) + extensionsPresent = state.ConsensusParams.Feature.VoteExtensionsEnabled(height) ) // ensure invariant @@ -282,7 +282,7 @@ func (cs *State) ingestBlock(ic IngestCandidate) error { } // the following flow is similar to finalizeCommit(height) - stateCopy, err := cs.blockExec.ApplyVerifiedBlock(stateCopy, ic.BlockID(), block) + stateCopy, err := cs.blockExec.ApplyVerifiedBlock(stateCopy, ic.BlockID(), block, block.Height) if err != nil { // we can't recover from this error panic(errors.Wrapf(err, "failed to apply verified block (height: %d, hash: %x)", block.Height, block.Hash())) diff --git a/consensus/state_ingest_test.go b/consensus/state_ingest_test.go index fc081a77a92..59c22342937 100644 --- a/consensus/state_ingest_test.go +++ b/consensus/state_ingest_test.go @@ -34,7 +34,7 @@ func TestStateIngestVerifiedBlock(t *testing.T) { t.Run("ingestedBlockWithVoteExtensions", func(t *testing.T) { // ARRANGE ts := newIngestTestSuite(t) - ts.cs.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = 1 + ts.cs.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = 1 // Given a verified block with extended commit ic := ts.MakeIngestCandidate() @@ -237,7 +237,7 @@ func TestIngestCandidate(t *testing.T) { name: "extensions invariant mismatch", voteExtensions: true, mutate: func(t *testing.T, ic *IngestCandidate, st *sm.State) { - st.ConsensusParams.ABCI.VoteExtensionsEnableHeight = 0 + st.ConsensusParams.Feature.VoteExtensionsEnableHeight = 0 }, errContains: "invalid ext commit state", }, @@ -337,9 +337,9 @@ func TestIngestCandidate(t *testing.T) { ts := newIngestTestSuite(t) if tt.voteExtensions { - ts.cs.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = 1 + ts.cs.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = 1 } else { - ts.cs.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = 0 + ts.cs.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = 0 } if tt.arrange != nil { @@ -429,7 +429,7 @@ func (ts *ingestTestSuite) MakeIngestCandidateUnverified() IngestCandidate { } var ( - extensionsEnabled = ts.cs.state.ConsensusParams.ABCI.VoteExtensionsEnabled(block.Height) + extensionsEnabled = ts.cs.state.ConsensusParams.Feature.VoteExtensionsEnabled(block.Height) chainID = ts.cs.state.ChainID blockHeight = block.Height blockID = types.BlockID{ @@ -454,7 +454,7 @@ func (ts *ingestTestSuite) MakeIngestCandidateUnverified() IngestCandidate { require.True(ts.t, added) } - extCommit := voteSet.MakeExtendedCommit(ts.cs.state.ConsensusParams.ABCI) + extCommit := voteSet.MakeExtendedCommit(ts.cs.state.ConsensusParams.Feature) commit := extCommit.ToCommit() if !extensionsEnabled { extCommit = nil diff --git a/consensus/state_test.go b/consensus/state_test.go index 344054e17fd..85b8f5a06e0 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -27,6 +27,7 @@ import ( cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" smmocks "github.com/cometbft/cometbft/state/mocks" "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" ) /* @@ -216,7 +217,7 @@ func TestStateBadProposal(t *testing.T) { propBlockParts, err := propBlock.MakePartSet(partSize) require.NoError(t, err) blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} - proposal := types.NewProposal(vs2.Height, round, -1, blockID) + proposal := types.NewProposal(vs2.Height, round, -1, blockID, propBlock.Time) p := proposal.ToProto() if err := vs2.SignProposal(cs1.state.ChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -289,7 +290,7 @@ func TestStateOversizedBlock(t *testing.T) { incrementRound(vss[1:]...) blockID := types.BlockID{Hash: propBlock.Hash(), PartSetHeader: propBlockParts.Header()} - proposal := types.NewProposal(height, round, -1, blockID) + proposal := types.NewProposal(height, round, -1, blockID, propBlock.Time) p := proposal.ToProto() if err := vs2.SignProposal(cs1.state.ChainID, p); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -1170,7 +1171,7 @@ func TestStateLockPOLSafety2(t *testing.T) { round++ // moving to the next round // in round 2 we see the polkad block from round 0 - newProp := types.NewProposal(height, round, 0, propBlockID0) + newProp := types.NewProposal(height, round, 0, propBlockID0, cmttime.Now()) p := newProp.ToProto() if err := vs3.SignProposal(cs1.state.ChainID, p); err != nil { t.Fatal(err) @@ -1579,7 +1580,7 @@ func TestVerifyVoteExtensionNotCalledOnAbsentPrecommit(t *testing.T) { m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe() cs1, vss := randStateWithApp(4, m) height, round := cs1.Height, cs1.Round - cs1.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = cs1.Height + cs1.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = cs1.Height proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) @@ -1886,7 +1887,7 @@ func TestVoteExtensionEnableHeight(t *testing.T) { m.On("FinalizeBlock", mock.Anything, mock.Anything).Return(&abci.ResponseFinalizeBlock{}, nil).Maybe() m.On("Commit", mock.Anything, mock.Anything).Return(&abci.ResponseCommit{}, nil).Maybe() cs1, vss := randStateWithAppWithHeight(numValidators, m, testCase.enableHeight) - cs1.state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.enableHeight + cs1.state.ConsensusParams.Feature.VoteExtensionsEnableHeight = testCase.enableHeight height, round := cs1.Height, cs1.Round timeoutCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) @@ -2020,7 +2021,7 @@ func TestStateDoesntCrashOnInvalidVote(t *testing.T) { voteMessage := &VoteMessage{vote} assert.NotPanics(t, func() { - cs.handleMsg(msgInfo{voteMessage, peer.ID()}) + cs.handleMsg(msgInfo{voteMessage, peer.ID(), time.Time{}}) }) added, err := cs.AddVote(vote, peer.ID()) @@ -2531,26 +2532,26 @@ func TestStateOutputsBlockPartsStats(t *testing.T) { } cs.ProposalBlockParts = types.NewPartSetFromHeader(parts.Header()) - cs.handleMsg(msgInfo{msg, peer.ID()}) + cs.handleMsg(msgInfo{msg, peer.ID(), time.Time{}}) statsMessage := <-cs.statsMsgQueue require.Equal(t, msg, statsMessage.Msg, "") require.Equal(t, peer.ID(), statsMessage.PeerID, "") // sending the same part from different peer - cs.handleMsg(msgInfo{msg, "peer2"}) + cs.handleMsg(msgInfo{msg, "peer2", time.Time{}}) // sending the part with the same height, but different round msg.Round = 1 - cs.handleMsg(msgInfo{msg, peer.ID()}) + cs.handleMsg(msgInfo{msg, peer.ID(), time.Time{}}) // sending the part from the smaller height msg.Height = 0 - cs.handleMsg(msgInfo{msg, peer.ID()}) + cs.handleMsg(msgInfo{msg, peer.ID(), time.Time{}}) // sending the part from the bigger height msg.Height = 3 - cs.handleMsg(msgInfo{msg, peer.ID()}) + cs.handleMsg(msgInfo{msg, peer.ID(), time.Time{}}) select { case <-cs.statsMsgQueue: @@ -2603,20 +2604,20 @@ func TestStateOutputVoteStats(t *testing.T) { vote := signVote(vss[1], cmtproto.PrecommitType, randBytes, types.PartSetHeader{}, true) voteMessage := &VoteMessage{vote} - cs.handleMsg(msgInfo{voteMessage, peer.ID()}) + cs.handleMsg(msgInfo{voteMessage, peer.ID(), time.Time{}}) statsMessage := <-cs.statsMsgQueue require.Equal(t, voteMessage, statsMessage.Msg, "") require.Equal(t, peer.ID(), statsMessage.PeerID, "") // sending the same part from different peer - cs.handleMsg(msgInfo{&VoteMessage{vote}, "peer2"}) + cs.handleMsg(msgInfo{&VoteMessage{vote}, "peer2", time.Time{}}) // sending the vote for the bigger height incrementHeight(vss[1]) vote = signVote(vss[1], cmtproto.PrecommitType, randBytes, types.PartSetHeader{}, true) - cs.handleMsg(msgInfo{&VoteMessage{vote}, peer.ID()}) + cs.handleMsg(msgInfo{&VoteMessage{vote}, peer.ID(), time.Time{}}) select { case <-cs.statsMsgQueue: @@ -2635,7 +2636,7 @@ func TestHandleMsgReleasesLockBeforeStatsMsgQueueSend(t *testing.T) { // Unbuffered channel with no consumer simulates a saturated queue. cs.statsMsgQueue = make(chan msgInfo) - go cs.handleMsg(msgInfo{&VoteMessage{vote}, peer.ID()}) + go cs.handleMsg(msgInfo{&VoteMessage{vote}, peer.ID(), time.Time{}}) time.Sleep(20 * time.Millisecond) rsResult := make(chan *cstypes.RoundState, 1) diff --git a/consensus/types/height_vote_set.go b/consensus/types/height_vote_set.go index a4bb05f9906..90f56115776 100644 --- a/consensus/types/height_vote_set.go +++ b/consensus/types/height_vote_set.go @@ -46,6 +46,7 @@ type HeightVoteSet struct { round int32 // max tracked round roundVoteSets map[int32]RoundVoteSet // keys: [0...round] peerCatchupRounds map[p2p.ID][]int32 // keys: peer.ID; values: at most 2 rounds + commit *types.Commit // a whole aggregated commit received via gossip (catch-up) } func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet { @@ -165,6 +166,25 @@ func (hvs *HeightVoteSet) Precommits(round int32) *types.VoteSet { return hvs.getVoteSet(round, cmtproto.PrecommitType) } +// GetCommit returns the whole aggregated commit for the given round, if one +// was received via gossip (catch-up path). +func (hvs *HeightVoteSet) GetCommit(round int32) *types.Commit { + hvs.mtx.Lock() + defer hvs.mtx.Unlock() + if hvs.commit == nil || hvs.commit.Round != round { + return nil + } + + return hvs.commit +} + +// SetCommit stores a whole aggregated commit received via gossip. +func (hvs *HeightVoteSet) SetCommit(commit *types.Commit) { + hvs.mtx.Lock() + defer hvs.mtx.Unlock() + hvs.commit = commit +} + // Last round and blockID that has +2/3 prevotes for a particular block or nil. // Returns -1 if no such round exists. func (hvs *HeightVoteSet) POLInfo() (polRound int32, polBlockID types.BlockID) { diff --git a/consensus/types/peer_round_state.go b/consensus/types/peer_round_state.go index 4e8b440c015..4b1219f3504 100644 --- a/consensus/types/peer_round_state.go +++ b/consensus/types/peer_round_state.go @@ -39,6 +39,8 @@ type PeerRoundState struct { // All commit precommits peer has for this height & CatchupCommitRound CatchupCommit *bits.BitArray `json:"catchup_commit"` + + HasCatchupCommit bool `json:"has_catchup_commit"` // True if peer has a catchup commit. } // String returns a string representation of the PeerRoundState diff --git a/consensus/types/round_state.go b/consensus/types/round_state.go index b9929647160..a413bd564cd 100644 --- a/consensus/types/round_state.go +++ b/consensus/types/round_state.go @@ -71,14 +71,15 @@ type RoundState struct { StartTime time.Time `json:"start_time"` // Subjective time when +2/3 precommits for Block at Round were found - CommitTime time.Time `json:"commit_time"` - Validators *types.ValidatorSet `json:"validators"` - Proposal *types.Proposal `json:"proposal"` - ProposalBlock *types.Block `json:"proposal_block"` - ProposalBlockParts *types.PartSet `json:"proposal_block_parts"` - LockedRound int32 `json:"locked_round"` - LockedBlock *types.Block `json:"locked_block"` - LockedBlockParts *types.PartSet `json:"locked_block_parts"` + CommitTime time.Time `json:"commit_time"` + Validators *types.ValidatorSet `json:"validators"` + Proposal *types.Proposal `json:"proposal"` + ProposalReceiveTime time.Time `json:"proposal_receive_time"` + ProposalBlock *types.Block `json:"proposal_block"` + ProposalBlockParts *types.PartSet `json:"proposal_block_parts"` + LockedRound int32 `json:"locked_round"` + LockedBlock *types.Block `json:"locked_block"` + LockedBlockParts *types.PartSet `json:"locked_block_parts"` // The variables below starting with "Valid..." derive their name from // the algorithm presented in this paper: @@ -97,7 +98,7 @@ type RoundState struct { ValidBlockParts *types.PartSet `json:"valid_block_parts"` Votes *HeightVoteSet `json:"votes"` CommitRound int32 `json:"commit_round"` // - LastCommit *types.VoteSet `json:"last_commit"` // Last precommits at Height-1 + LastCommit types.VoteSetReader `json:"last_commit"` // Last precommits at Height-1 LastValidators *types.ValidatorSet `json:"last_validators"` TriggeredTimeoutPrecommit bool `json:"triggered_timeout_precommit"` } @@ -186,6 +187,16 @@ func (rs *RoundState) String() string { // StringIndented returns a string func (rs *RoundState) StringIndented(indent string) string { + var lcStr string + switch lc := rs.LastCommit.(type) { + case *types.VoteSet: + lcStr = lc.StringShort() + case *types.Commit: + lcStr = lc.StringIndented(" ") + default: + lcStr = fmt.Sprintf("", lc) + } + return fmt.Sprintf(`RoundState{ %s H:%v R:%v S:%v %s StartTime: %v @@ -212,7 +223,7 @@ func (rs *RoundState) StringIndented(indent string) string { indent, rs.ValidRound, indent, rs.ValidBlockParts.StringShort(), rs.ValidBlock.StringShort(), indent, rs.Votes.StringIndented(indent+" "), - indent, rs.LastCommit.StringShort(), + indent, lcStr, indent, rs.LastValidators.StringIndented(indent+" "), indent) } diff --git a/crypto/bls12381/aggregation.go b/crypto/bls12381/aggregation.go new file mode 100644 index 00000000000..1186dbafe6b --- /dev/null +++ b/crypto/bls12381/aggregation.go @@ -0,0 +1,37 @@ +//go:build bls12381 + +package bls12381 + +import ( + "errors" +) + +// ErrAggregation is returned when aggregation fails. +var ErrAggregation = errors.New("bls12381: failed to aggregate signatures") + +// AggregateSignatures aggregates the given compressed signatures. +// +// Does not group-check the signatures. +func AggregateSignatures(sigsToAgg [][]byte) ([]byte, error) { + var agProj blstAggregateSignature + if !agProj.AggregateCompressed(sigsToAgg, false) { + return nil, ErrAggregation + } + agSig := agProj.ToAffine() + return agSig.Compress(), nil +} + +// VerifyAggregateSignature verifies the given compressed aggregate signature. +// +// Group-checks the signature. +func VerifyAggregateSignature(agSigCompressed []byte, pubks []*PubKey, msg []byte) bool { + agSig := new(blstSignature).Uncompress(agSigCompressed) + if agSig == nil { + return false + } + blsPubKeys := make([]*blstPublicKey, len(pubks)) + for i, pubk := range pubks { + blsPubKeys[i] = pubk.pk + } + return agSig.FastAggregateVerify(true, blsPubKeys, msg, dstMinPk) +} diff --git a/crypto/bls12381/aggregation_none.go b/crypto/bls12381/aggregation_none.go new file mode 100644 index 00000000000..a7a88c3d019 --- /dev/null +++ b/crypto/bls12381/aggregation_none.go @@ -0,0 +1,24 @@ +//go:build !bls12381 + +package bls12381 + +import ( + "errors" +) + +// ErrAggregation is returned when aggregation fails. +var ErrAggregation = errors.New("bls12381: failed to aggregate signatures") + +// AggregateSignatures aggregates the given compressed signatures. +// +// It returns ErrDisabled when built without the `bls12381` build tag. +func AggregateSignatures([][]byte) ([]byte, error) { + return nil, ErrDisabled +} + +// VerifyAggregateSignature verifies the given compressed aggregate signature. +// +// It returns false when built without the `bls12381` build tag. +func VerifyAggregateSignature([]byte, []*PubKey, []byte) bool { + return false +} diff --git a/crypto/bls12381/aggregation_test.go b/crypto/bls12381/aggregation_test.go new file mode 100644 index 00000000000..43541695417 --- /dev/null +++ b/crypto/bls12381/aggregation_test.go @@ -0,0 +1,63 @@ +//go:build bls12381 + +package bls12381_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/cometbft/cometbft/crypto/bls12381" +) + +func TestAggregateAndVerify(t *testing.T) { + // Generate private keys. + genPrivKeyFn := func() *bls12381.PrivKey { + k, err := bls12381.GenPrivKey() + require.NoError(t, err) + return k + } + + privateKeys := []*bls12381.PrivKey{ + genPrivKeyFn(), + genPrivKeyFn(), + genPrivKeyFn(), + } + + msg := []byte("hello world") + + // Generate signatures. + signatures := make([][]byte, len(privateKeys)) + for i, privKey := range privateKeys { + sig, err := privKey.Sign(msg) + require.NoError(t, err) + signatures[i] = sig + + valid := privKey.PubKey().VerifySignature(msg, sig) + require.True(t, valid) + } + + // Aggregate signatures. + aggregatedSignature, err := bls12381.AggregateSignatures(signatures) + require.NoError(t, err) + require.NotNil(t, aggregatedSignature) + + pubKeys := make([]*bls12381.PubKey, len(privateKeys)) + for i, privKey := range privateKeys { + pubKey := privKey.PubKey().(bls12381.PubKey) + pubKeys[i] = &pubKey + } + + // Verify aggregated signature + valid := bls12381.VerifyAggregateSignature(aggregatedSignature, pubKeys, msg) + require.True(t, valid) + + // Test with invalid aggregated signature + invalidSignature := []byte("Invalid") + valid = bls12381.VerifyAggregateSignature(invalidSignature, pubKeys, msg) + require.False(t, valid) + + // Verify aggregated signature with one missing pubKey + valid = bls12381.VerifyAggregateSignature(aggregatedSignature, pubKeys[1:], msg) + require.False(t, valid) +} diff --git a/crypto/bls12381/const.go b/crypto/bls12381/const.go index 439a3d55b3e..18cf579ba90 100644 --- a/crypto/bls12381/const.go +++ b/crypto/bls12381/const.go @@ -5,6 +5,8 @@ const ( PrivKeySize = 32 // PubKeySize defines the length of the PubKey byte array. PubKeySize = 96 + // PubKeyCompressedSize defines the length of the compressed PubKey byte array. + PubKeyCompressedSize = 48 // SignatureLength defines the byte length of a BLS signature. SignatureLength = 96 // KeyType is the string constant for the BLS12-381 algorithm. diff --git a/crypto/bls12381/key.go b/crypto/bls12381/key.go index 20f081b563f..5882a91361d 100644 --- a/crypto/bls12381/key.go +++ b/crypto/bls12381/key.go @@ -31,17 +31,17 @@ var _ crypto.PrivKey = &PrivKey{} type PrivKey []byte // GenPrivKeyFromSecret returns ErrDisabled. -func GenPrivKeyFromSecret([]byte) (PrivKey, error) { +func GenPrivKeyFromSecret([]byte) (*PrivKey, error) { return nil, ErrDisabled } // NewPrivateKeyFromBytes returns ErrDisabled. -func NewPrivateKeyFromBytes([]byte) (PrivKey, error) { +func NewPrivateKeyFromBytes([]byte) (*PrivKey, error) { return nil, ErrDisabled } // GenPrivKey returns ErrDisabled. -func GenPrivKey() (PrivKey, error) { +func GenPrivKey() (*PrivKey, error) { return nil, ErrDisabled } @@ -94,6 +94,11 @@ func NewPublicKeyFromBytes([]byte) (*PubKey, error) { return nil, ErrDisabled } +// NewPublicKeyFromCompressedBytes returns ErrDisabled. +func NewPublicKeyFromCompressedBytes([]byte) (*PubKey, error) { + return nil, ErrDisabled +} + // Address always panics. func (PubKey) Address() crypto.Address { panic("bls12_381 is disabled") @@ -109,6 +114,11 @@ func (PubKey) Bytes() []byte { panic("bls12_381 is disabled") } +// Compress always panics. +func (PubKey) Compress() []byte { + panic("bls12_381 is disabled") +} + // Type returns the key's type. func (PubKey) Type() string { return KeyType diff --git a/crypto/bls12381/key_bls12381.go b/crypto/bls12381/key_bls12381.go index e7b89a22fec..95f5a3a3e76 100644 --- a/crypto/bls12381/key_bls12381.go +++ b/crypto/bls12381/key_bls12381.go @@ -22,6 +22,10 @@ const ( ) var ( + // ErrPubKeyDecompression is returned when the decompression of a compressed + // 48-byte long BLS12-381 public key fails. + ErrPubKeyDecompression = errors.New("bls12381: public key decompression error") + // ErrDeserialization is returned when deserialization fails. ErrDeserialization = errors.New("bls12381: deserialization error") // ErrInfinitePubKey is returned when the public key is infinite. It is part @@ -36,10 +40,11 @@ var ( // Changing this to 'minimal-signature-size' would render CometBFT not Ethereum // compatible. type ( - blstPublicKey = blst.P1Affine - blstSignature = blst.P2Affine - blstAggregateSignature = blst.P1Aggregate - blstAggregatePublicKey = blst.P2Aggregate + blstPublicKey = blst.P1Affine + blstSignature = blst.P2Affine + // Signatures live in G2 under min-pubkey-size, so an aggregate signature + // is a P2 aggregate. + blstAggregateSignature = blst.P2Aggregate ) // -------------------------------------. @@ -174,6 +179,20 @@ func NewPublicKeyFromBytes(bz []byte) (*PubKey, error) { return &PubKey{pk: pk}, nil } +// NewPublicKeyFromCompressedBytes returns a new BLS12-381 public key from the +// given bytes. bz must be a compressed BLS12-381 public key of length 48 bytes. +func NewPublicKeyFromCompressedBytes(bz []byte) (*PubKey, error) { + pk := new(blstPublicKey).Uncompress(bz) + if pk == nil { + return nil, ErrPubKeyDecompression + } + // Subgroup and infinity check + if !pk.KeyValidate() { + return nil, ErrInfinitePubKey + } + return &PubKey{pk: pk}, nil +} + // Address returns the address of the key. // // The function will panic if the public key is invalid. @@ -181,6 +200,13 @@ func (pubKey PubKey) Address() crypto.Address { return crypto.Address(tmhash.SumTruncated(pubKey.pk.Serialize())) } +// Compress returns a compressed 48-byte long BLS12-381 public key. +// It does not modify the original public key. Rather, it returns a new slice +// storing the compressed key. +func (pubKey PubKey) Compress() []byte { + return pubKey.pk.Compress() +} + // VerifySignature verifies the given signature. func (pubKey PubKey) VerifySignature(msg, sig []byte) bool { signature := new(blstSignature).Uncompress(sig) diff --git a/crypto/encoding/codec.go b/crypto/encoding/codec.go index 1eeb12f148f..b661aa90ed0 100644 --- a/crypto/encoding/codec.go +++ b/crypto/encoding/codec.go @@ -45,23 +45,32 @@ func init() { json.RegisterType((*pc.PublicKey_Secp256K1Eth)(nil), "tendermint.crypto.PublicKey_Secp256K1Eth") } -// PubKeyToProto takes crypto.PubKey and transforms it to a protobuf Pubkey +// PubKeyToProto takes crypto.PubKey and transforms it to a protobuf Pubkey. +// +// NOTE: it switches on the key type, not the concrete Go type, because BLS +// public keys appear both as values and as pointers (PubKeyFromProto returns +// a pointer), and both must serialize identically. func PubKeyToProto(k crypto.PubKey) (pc.PublicKey, error) { var kp pc.PublicKey - switch k := k.(type) { - case ed25519.PubKey: + + if k == nil { + return kp, fmt.Errorf("toproto: key type %v is not supported", k) + } + + switch k.Type() { + case ed25519.KeyType: kp = pc.PublicKey{ Sum: &pc.PublicKey_Ed25519{ - Ed25519: k, + Ed25519: k.Bytes(), }, } - case secp256k1.PubKey: + case secp256k1.KeyType: kp = pc.PublicKey{ Sum: &pc.PublicKey_Secp256K1{ - Secp256K1: k, + Secp256K1: k.Bytes(), }, } - case bls12381.PubKey: + case bls12381.KeyType: if !bls12381.Enabled { return kp, ErrUnsupportedKey{Key: k} } @@ -71,13 +80,13 @@ func PubKeyToProto(k crypto.PubKey) (pc.PublicKey, error) { Bls12381: k.Bytes(), }, } - case mldsa65.PubKey: + case mldsa65.KeyType: kp = pc.PublicKey{ Sum: &pc.PublicKey_Mldsa65{ Mldsa65: k.Bytes(), }, } - case secp256k1eth.PubKey: + case secp256k1eth.KeyType: kp = pc.PublicKey{ Sum: &pc.PublicKey_Secp256K1Eth{ Secp256K1Eth: k.Bytes(), @@ -113,14 +122,18 @@ func PubKeyFromProto(k pc.PublicKey) (crypto.PubKey, error) { return nil, ErrUnsupportedKey{Key: k} } - if len(k.Bls12381) != bls12381.PubKeySize { + switch len(k.Bls12381) { + case bls12381.PubKeySize: + return bls12381.NewPublicKeyFromBytes(k.Bls12381) + case bls12381.PubKeyCompressedSize: + return bls12381.NewPublicKeyFromCompressedBytes(k.Bls12381) + default: return nil, ErrInvalidKeyLen{ Key: k, Got: len(k.Bls12381), Want: bls12381.PubKeySize, } } - return bls12381.NewPublicKeyFromBytes(k.Bls12381) case *pc.PublicKey_Mldsa65: if len(k.Mldsa65) != mldsa65.PubKeySize { return nil, ErrInvalidKeyLen{ @@ -179,15 +192,18 @@ func PubKeyFromTypeAndBytes(pkType string, bytes []byte) (crypto.PubKey, error) return nil, ErrUnsupportedKey{Key: pkType} } - if len(bytes) != bls12381.PubKeySize { + switch len(bytes) { + case bls12381.PubKeySize: + return bls12381.NewPublicKeyFromBytes(bytes) + case bls12381.PubKeyCompressedSize: + return bls12381.NewPublicKeyFromCompressedBytes(bytes) + default: return nil, ErrInvalidKeyLen{ Key: pkType, Got: len(bytes), Want: bls12381.PubKeySize, } } - - return bls12381.NewPublicKeyFromBytes(bytes) case mldsa65.KeyType: if len(bytes) != mldsa65.PubKeySize { return nil, ErrInvalidKeyLen{ diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 6f2deec31fb..29632bbf696 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -82,7 +82,7 @@ func TestEvidencePoolBasic(t *testing.T) { next := pool.EvidenceFront() assert.Equal(t, ev, next.Value.(types.Evidence)) - const evidenceBytes int64 = 372 + const evidenceBytes int64 = 382 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 diff --git a/internal/test/commit.go b/internal/test/commit.go index 3decd96b65e..b9a28716602 100644 --- a/internal/test/commit.go +++ b/internal/test/commit.go @@ -36,7 +36,7 @@ func MakeCommitFromVoteSet(blockID types.BlockID, voteSet *types.VoteSet, valida } } - return voteSet.MakeExtendedCommit(types.ABCIParams{VoteExtensionsEnableHeight: 0}).ToCommit(), nil + return voteSet.MakeExtendedCommit(types.FeatureParams{VoteExtensionsEnableHeight: 0}).ToCommit(), nil } func MakeCommit(blockID types.BlockID, height int64, round int32, valSet *types.ValidatorSet, privVals []types.PrivValidator, chainID string, now time.Time) (*types.Commit, error) { diff --git a/internal/test/config.go b/internal/test/config.go index 0a7da5f5c43..3b0f8efaca3 100644 --- a/internal/test/config.go +++ b/internal/test/config.go @@ -62,10 +62,15 @@ var testGenesisFmt = `{ "ed25519" ] }, - "abci": { - "vote_extensions_enable_height": "0" + "synchrony": { + "message_delay": "500000000", + "precision": "10000000" }, - "version": {} + "version": {}, + "feature": { + "vote_extensions_enable_height": "0", + "pbts_enable_height": "1" + } }, "validators": [ { diff --git a/internal/test/params.go b/internal/test/params.go index c4421d53cc4..eabc337798a 100644 --- a/internal/test/params.go +++ b/internal/test/params.go @@ -9,6 +9,6 @@ import ( func ConsensusParams() *types.ConsensusParams { c := types.DefaultConsensusParams() // enable vote extensions - c.ABCI.VoteExtensionsEnableHeight = 1 + c.Feature.VoteExtensionsEnableHeight = 1 return c } diff --git a/node/setup.go b/node/setup.go index b8e131e97ff..820a8f4d8f1 100644 --- a/node/setup.go +++ b/node/setup.go @@ -539,11 +539,11 @@ func createPEXReactorAndAddToSwitch( Seeds: splitAndTrimEmpty(config.P2P.Seeds, ",", " "), SeedMode: config.P2P.SeedMode, // See consensus/reactor.go: blocksToContributeToBecomeGoodPeer 10000 - // blocks assuming 10s blocks ~ 28 hours. + // blocks assuming 2s blocks ~ 6 hours. // TODO (melekes): make it dynamic based on the actual block latencies // from the live network. // https://github.com/tendermint/tendermint/issues/3523 - SeedDisconnectWaitPeriod: 28 * time.Hour, + SeedDisconnectWaitPeriod: 6 * time.Hour, PersistentPeersMaxDialPeriod: config.P2P.PersistentPeersMaxDialPeriod, } diff --git a/privval/file.go b/privval/file.go index a4d024e057d..9040f7d6365 100644 --- a/privval/file.go +++ b/privval/file.go @@ -274,6 +274,11 @@ func (pv *FilePV) SignProposal(chainID string, proposal *cmtproto.Proposal) erro return nil } +// SignBytes signs the given bytes. Implements PrivValidator. +func (pv *FilePV) SignBytes(bytes []byte) ([]byte, error) { + return pv.Key.PrivKey.Sign(bytes) +} + // Save persists the FilePV to disk. func (pv *FilePV) Save() { pv.Key.Save() @@ -335,16 +340,13 @@ func (pv *FilePV) signVote(chainID string, vote *cmtproto.Vote) error { // We might crash before writing to the wal, // causing us to try to re-sign for the same HRS. // If signbytes are the same, use the last signature. - // If they only differ by timestamp, use last timestamp and signature - // Otherwise, return error + // Otherwise, return error. + // NOTE: vote sign bytes do not include a timestamp (it was removed so + // that BLS signatures can be aggregated), so there is no + // differ-only-by-timestamp case for votes. if sameHRS { if bytes.Equal(signBytes, lss.SignBytes) { vote.Signature = lss.Signature - } else if timestamp, ok := checkVotesOnlyDifferByTimestamp(lss.SignBytes, signBytes); ok { - // Compares the canonicalized votes (i.e. without vote extensions - // or vote extension signatures). - vote.Timestamp = timestamp - vote.Signature = lss.Signature } else { err = fmt.Errorf("conflicting data") } @@ -422,28 +424,6 @@ func (pv *FilePV) saveSigned(height int64, round int32, step int8, //----------------------------------------------------------------------------------------- -// Returns the timestamp from the lastSignBytes. -// Returns true if the only difference in the votes is their timestamp. -// Performs these checks on the canonical votes (excluding the vote extension -// and vote extension signatures). -func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) { - var lastVote, newVote cmtproto.CanonicalVote - if err := protoio.UnmarshalDelimited(lastSignBytes, &lastVote); err != nil { - panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into vote: %v", err)) - } - if err := protoio.UnmarshalDelimited(newSignBytes, &newVote); err != nil { - panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err)) - } - - lastTime := lastVote.Timestamp - // set the times to the same value and check equality - now := cmttime.Now() - lastVote.Timestamp = now - newVote.Timestamp = now - - return lastTime, proto.Equal(&newVote, &lastVote) -} - // returns the timestamp from the lastSignBytes. // returns true if the only difference in the proposals is their timestamp func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) { diff --git a/privval/file_test.go b/privval/file_test.go index cba7cc3165b..12904171318 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -291,9 +291,10 @@ func TestDifferByTimestamp(t *testing.T) { signBytes := types.VoteSignBytes(chainID, v) sig := v.Signature extSig := v.ExtensionSignature - timeStamp := vote.Timestamp - // manipulate the timestamp. should get changed back + // The vote timestamp is not part of the signed bytes (it was removed + // to enable BLS signature aggregation), so re-signing the same vote + // reuses the previous signature regardless of the timestamp value. v.Timestamp = v.Timestamp.Add(time.Millisecond) var emptySig []byte v.Signature = emptySig @@ -301,7 +302,6 @@ func TestDifferByTimestamp(t *testing.T) { err = privVal.SignVote("mychainid", v) assert.NoError(t, err, "expected no error on signing same vote") - assert.Equal(t, timeStamp, v.Timestamp) assert.Equal(t, signBytes, types.VoteSignBytes(chainID, v)) assert.Equal(t, sig, v.Signature) assert.Equal(t, extSig, v.ExtensionSignature) @@ -349,17 +349,15 @@ func TestVoteExtensionsAreAlwaysSigned(t *testing.T) { assert.True(t, pubKey.VerifySignature(vesb2, vpb2.ExtensionSignature)) assert.False(t, pubKey.VerifySignature(vesb1, vpb2.ExtensionSignature)) - // We now manipulate the timestamp of the vote with the extension, as per - // TestDifferByTimestamp - expectedTimestamp := vpb2.Timestamp - + // We now manipulate the timestamp of the vote with the extension. The + // timestamp is not part of the signed bytes, so signing succeeds and the + // extension is re-signed. vpb2.Timestamp = vpb2.Timestamp.Add(time.Millisecond) vpb2.Signature = nil vpb2.ExtensionSignature = nil err = privVal.SignVote("mychainid", vpb2) assert.NoError(t, err, "expected no error signing same vote with manipulated timestamp and vote extension") - assert.Equal(t, expectedTimestamp, vpb2.Timestamp) vesb3 := types.VoteExtensionSignBytes("mychainid", vpb2) assert.True(t, pubKey.VerifySignature(vesb3, vpb2.ExtensionSignature)) diff --git a/privval/msgs.go b/privval/msgs.go index 4b440e612df..2bcc33b0bf9 100644 --- a/privval/msgs.go +++ b/privval/msgs.go @@ -28,6 +28,10 @@ func mustWrapMsg(pb proto.Message) privvalproto.Message { msg.Sum = &privvalproto.Message_SignedProposalResponse{SignedProposalResponse: pb} case *privvalproto.SignProposalRequest: msg.Sum = &privvalproto.Message_SignProposalRequest{SignProposalRequest: pb} + case *privvalproto.SignBytesRequest: + msg.Sum = &privvalproto.Message_SignBytesRequest{SignBytesRequest: pb} + case *privvalproto.SignBytesResponse: + msg.Sum = &privvalproto.Message_SignBytesResponse{SignBytesResponse: pb} case *privvalproto.PingRequest: msg.Sum = &privvalproto.Message_PingRequest{PingRequest: pb} case *privvalproto.PingResponse: diff --git a/privval/retry_signer_client.go b/privval/retry_signer_client.go index 606f1b0876b..4bcb10a48f7 100644 --- a/privval/retry_signer_client.go +++ b/privval/retry_signer_client.go @@ -118,3 +118,24 @@ func (sc *RetrySignerClient) SignProposal(chainID string, proposal *cmtproto.Pro } return fmt.Errorf("exhausted all attempts to sign proposal: %w", err) } + +func (sc *RetrySignerClient) SignBytes(bytes []byte) ([]byte, error) { + var ( + sig []byte + err error + ) + for i := 0; i < sc.retries || sc.retries == 0; i++ { + sig, err = sc.next.SignBytes(bytes) + if err == nil { + return sig, nil + } + // If remote signer errors, we don't retry. + if _, ok := err.(*RemoteSignerError); ok { + return nil, err + } + if !sc.sleep() { + return nil, fmt.Errorf("aborted signing bytes: %w", err) + } + } + return nil, fmt.Errorf("exhausted all attempts to sign bytes: %w", err) +} diff --git a/privval/signer_client.go b/privval/signer_client.go index 062e6cdf4d7..3e967ad9474 100644 --- a/privval/signer_client.go +++ b/privval/signer_client.go @@ -133,3 +133,21 @@ func (sc *SignerClient) SignProposal(chainID string, proposal *cmtproto.Proposal return nil } + +// SignBytes requests a remote signer to sign bytes. +func (sc *SignerClient) SignBytes(bytes []byte) ([]byte, error) { + response, err := sc.endpoint.SendRequest(mustWrapMsg(&privvalproto.SignBytesRequest{Value: bytes})) + if err != nil { + return nil, err + } + + resp := response.GetSignBytesResponse() + if resp == nil { + return nil, cmterrors.ErrRequiredField{Field: "response"} + } + if resp.Error != nil { + return nil, &RemoteSignerError{Code: int(resp.Error.Code), Description: resp.Error.Description} + } + + return resp.Signature, nil +} diff --git a/privval/signer_client_test.go b/privval/signer_client_test.go index 6a8d66d98aa..d8be377cfe8 100644 --- a/privval/signer_client_test.go +++ b/privval/signer_client_test.go @@ -344,6 +344,29 @@ func TestSignerVoteKeepAlive(t *testing.T) { } } +func TestSignerSignBytes(t *testing.T) { + for _, tc := range getSignerTestCases(t) { + t.Cleanup(func() { + if err := tc.signerServer.Stop(); err != nil { + t.Error(err) + } + }) + t.Cleanup(func() { + if err := tc.signerClient.Close(); err != nil { + t.Error(err) + } + }) + + bytes := cmtrand.Bytes(32) + signature, err := tc.signerClient.SignBytes(bytes) + require.NoError(t, err) + + pubKey, err := tc.mockPV.GetPubKey() + require.NoError(t, err) + require.True(t, pubKey.VerifySignature(bytes, signature)) + } +} + func TestSignerSignProposalErrors(t *testing.T) { for _, tc := range getSignerTestCases(t) { // Replace service with a mock that always fails diff --git a/privval/signer_requestHandler.go b/privval/signer_requestHandler.go index 23a9ffd190c..cbfb091fe2a 100644 --- a/privval/signer_requestHandler.go +++ b/privval/signer_requestHandler.go @@ -88,6 +88,16 @@ func DefaultValidationRequestHandler( } else { res = mustWrapMsg(&privvalproto.SignedProposalResponse{Proposal: *proposal, Error: nil}) } + case *privvalproto.Message_SignBytesRequest: + var signature []byte + signature, err = privVal.SignBytes(r.SignBytesRequest.Value) + if err != nil { + res = mustWrapMsg(&privvalproto.SignBytesResponse{ + Signature: nil, Error: &privvalproto.RemoteSignerError{Code: 0, Description: err.Error()}, + }) + } else { + res = mustWrapMsg(&privvalproto.SignBytesResponse{Signature: signature, Error: nil}) + } case *privvalproto.Message_PingRequest: err, res = nil, mustWrapMsg(&privvalproto.PingResponse{}) diff --git a/proto/buf.yaml b/proto/buf.yaml index a646c2030a7..06acd6a646a 100644 --- a/proto/buf.yaml +++ b/proto/buf.yaml @@ -5,6 +5,12 @@ deps: breaking: use: - FILE + ignore: + # Intentional wire-format changes from the bera re-fork (PR #51): + # CanonicalVote drops the timestamp field and ConsensusParams replaces + # authority with synchrony to align with bera-v1.x. + - tendermint/types/canonical.proto + - tendermint/types/params.proto lint: use: - BASIC diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index e9194cab6eb..b1cee0ff65e 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package tendermint.abci; import "gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "tendermint/crypto/keys.proto"; // For more information on gogo.proto, see: @@ -171,6 +172,10 @@ message RequestProcessProposal { bytes next_validators_hash = 7; // address of the public key of the original proposer of the block. bytes proposer_address = 8; + // address of the public key of the validator that will propose the block at the + // next height (also the proposer at round 1 of the current height if consensus + // goes into multiple rounds). + bytes next_proposer_address = 9; } // Extends a vote with application-injected data @@ -216,6 +221,8 @@ message RequestFinalizeBlock { bytes next_validators_hash = 7; // proposer_address is the address of the public key of the original proposer of the block. bytes proposer_address = 8; + // If the node is syncing/replaying blocks - target height. If not, syncing_to == height. + int64 syncing_to_height = 9; } //---------------------------------------- @@ -402,6 +409,12 @@ message ResponseFinalizeBlock { tendermint.types.ConsensusParams consensus_param_updates = 4; // app_hash is the hash of the applications' state which is used to confirm that execution of the transactions was deterministic. It is up to the application to decide which algorithm to use. bytes app_hash = 5; + // delay between the time when this block is committed and the next height is started. + // previously `timeout_commit` in config.toml + google.protobuf.Duration next_block_delay = 6 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; } //---------------------------------------- @@ -480,6 +493,14 @@ message Validator { message ValidatorUpdate { tendermint.crypto.PublicKey pub_key = 1 [(gogoproto.nullable) = false]; int64 power = 2; + // Berachain: the bera-v1.x line encodes the public key as raw bytes plus a + // key type (pub_key instead being reserved there). Both forms are accepted + // when decoding, and both are written to the state store, so that + // FinalizeBlock responses persisted by either line can be read by the other + // (e.g. a rolling upgrade or rollback while the last block's response is + // needed for crash recovery, or /block_results for historical heights). + bytes pub_key_bytes = 3; + string pub_key_type = 4; } message VoteInfo { diff --git a/proto/tendermint/consensus/message.go b/proto/tendermint/consensus/message.go index 8acfc137ffd..4df89cc1b84 100644 --- a/proto/tendermint/consensus/message.go +++ b/proto/tendermint/consensus/message.go @@ -17,9 +17,17 @@ var ( _ p2p.Wrapper = &NewValidBlock{} _ p2p.Wrapper = &NewRoundStep{} _ p2p.Wrapper = &HasVote{} + _ p2p.Wrapper = &HasProposalBlockPart{} _ p2p.Wrapper = &BlockPart{} + _ p2p.Wrapper = &Commit{} ) +func (m *Commit) Wrap() proto.Message { + cm := &Message{} + cm.Sum = &Message_Commit{Commit: m} + return cm +} + func (m *VoteSetBits) Wrap() proto.Message { cm := &Message{} cm.Sum = &Message_VoteSetBits{VoteSetBits: m} @@ -38,6 +46,12 @@ func (m *HasVote) Wrap() proto.Message { return cm } +func (m *HasProposalBlockPart) Wrap() proto.Message { + cm := &Message{} + cm.Sum = &Message_HasProposalBlockPart{HasProposalBlockPart: m} + return cm +} + func (m *Vote) Wrap() proto.Message { cm := &Message{} cm.Sum = &Message_Vote{Vote: m} @@ -99,12 +113,18 @@ func (m *Message) Unwrap() (proto.Message, error) { case *Message_HasVote: return m.GetHasVote(), nil + case *Message_HasProposalBlockPart: + return m.GetHasProposalBlockPart(), nil + case *Message_VoteSetMaj23: return m.GetVoteSetMaj23(), nil case *Message_VoteSetBits: return m.GetVoteSetBits(), nil + case *Message_Commit: + return m.GetCommit(), nil + default: return nil, fmt.Errorf("unknown message: %T", msg) } diff --git a/proto/tendermint/consensus/types.pb.go b/proto/tendermint/consensus/types.pb.go index 7fda3f33bd4..b665a9ec373 100644 --- a/proto/tendermint/consensus/types.pb.go +++ b/proto/tendermint/consensus/types.pb.go @@ -394,6 +394,52 @@ func (m *Vote) GetVote() *types.Vote { return nil } +// An entire Commit is sent when signatures are aggregated and we cannot +// extract a single signature. +type Commit struct { + Commit *types.Commit `protobuf:"bytes,1,opt,name=commit,proto3" json:"commit,omitempty"` +} + +func (m *Commit) Reset() { *m = Commit{} } +func (m *Commit) String() string { return proto.CompactTextString(m) } +func (*Commit) ProtoMessage() {} +func (*Commit) Descriptor() ([]byte, []int) { + return fileDescriptor_81a22d2efc008981, []int{6} +} +func (m *Commit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Commit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Commit.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 *Commit) XXX_Merge(src proto.Message) { + xxx_messageInfo_Commit.Merge(m, src) +} +func (m *Commit) XXX_Size() int { + return m.Size() +} +func (m *Commit) XXX_DiscardUnknown() { + xxx_messageInfo_Commit.DiscardUnknown(m) +} + +var xxx_messageInfo_Commit proto.InternalMessageInfo + +func (m *Commit) GetCommit() *types.Commit { + if m != nil { + return m.Commit + } + return nil +} + // HasVote is sent to indicate that a particular vote has been received. type HasVote struct { Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` @@ -406,7 +452,7 @@ func (m *HasVote) Reset() { *m = HasVote{} } func (m *HasVote) String() string { return proto.CompactTextString(m) } func (*HasVote) ProtoMessage() {} func (*HasVote) Descriptor() ([]byte, []int) { - return fileDescriptor_81a22d2efc008981, []int{6} + return fileDescriptor_81a22d2efc008981, []int{7} } func (m *HasVote) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -463,6 +509,67 @@ func (m *HasVote) GetIndex() int32 { return 0 } +// HasProposalBlockPart is sent to indicate that a particular block part has been received. +type HasProposalBlockPart struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Round int32 `protobuf:"varint,2,opt,name=round,proto3" json:"round,omitempty"` + Index int32 `protobuf:"varint,3,opt,name=index,proto3" json:"index,omitempty"` +} + +func (m *HasProposalBlockPart) Reset() { *m = HasProposalBlockPart{} } +func (m *HasProposalBlockPart) String() string { return proto.CompactTextString(m) } +func (*HasProposalBlockPart) ProtoMessage() {} +func (*HasProposalBlockPart) Descriptor() ([]byte, []int) { + return fileDescriptor_81a22d2efc008981, []int{8} +} +func (m *HasProposalBlockPart) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *HasProposalBlockPart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_HasProposalBlockPart.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 *HasProposalBlockPart) XXX_Merge(src proto.Message) { + xxx_messageInfo_HasProposalBlockPart.Merge(m, src) +} +func (m *HasProposalBlockPart) XXX_Size() int { + return m.Size() +} +func (m *HasProposalBlockPart) XXX_DiscardUnknown() { + xxx_messageInfo_HasProposalBlockPart.DiscardUnknown(m) +} + +var xxx_messageInfo_HasProposalBlockPart proto.InternalMessageInfo + +func (m *HasProposalBlockPart) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *HasProposalBlockPart) GetRound() int32 { + if m != nil { + return m.Round + } + return 0 +} + +func (m *HasProposalBlockPart) GetIndex() int32 { + if m != nil { + return m.Index + } + return 0 +} + // VoteSetMaj23 is sent to indicate that a given BlockID has seen +2/3 votes. type VoteSetMaj23 struct { Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` @@ -475,7 +582,7 @@ func (m *VoteSetMaj23) Reset() { *m = VoteSetMaj23{} } func (m *VoteSetMaj23) String() string { return proto.CompactTextString(m) } func (*VoteSetMaj23) ProtoMessage() {} func (*VoteSetMaj23) Descriptor() ([]byte, []int) { - return fileDescriptor_81a22d2efc008981, []int{7} + return fileDescriptor_81a22d2efc008981, []int{9} } func (m *VoteSetMaj23) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -545,7 +652,7 @@ func (m *VoteSetBits) Reset() { *m = VoteSetBits{} } func (m *VoteSetBits) String() string { return proto.CompactTextString(m) } func (*VoteSetBits) ProtoMessage() {} func (*VoteSetBits) Descriptor() ([]byte, []int) { - return fileDescriptor_81a22d2efc008981, []int{8} + return fileDescriptor_81a22d2efc008981, []int{10} } func (m *VoteSetBits) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -620,6 +727,8 @@ type Message struct { // *Message_HasVote // *Message_VoteSetMaj23 // *Message_VoteSetBits + // *Message_HasProposalBlockPart + // *Message_Commit Sum isMessage_Sum `protobuf_oneof:"sum"` } @@ -627,7 +736,7 @@ func (m *Message) Reset() { *m = Message{} } func (m *Message) String() string { return proto.CompactTextString(m) } func (*Message) ProtoMessage() {} func (*Message) Descriptor() ([]byte, []int) { - return fileDescriptor_81a22d2efc008981, []int{9} + return fileDescriptor_81a22d2efc008981, []int{11} } func (m *Message) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -689,16 +798,24 @@ type Message_VoteSetMaj23 struct { type Message_VoteSetBits struct { VoteSetBits *VoteSetBits `protobuf:"bytes,9,opt,name=vote_set_bits,json=voteSetBits,proto3,oneof" json:"vote_set_bits,omitempty"` } +type Message_HasProposalBlockPart struct { + HasProposalBlockPart *HasProposalBlockPart `protobuf:"bytes,10,opt,name=has_proposal_block_part,json=hasProposalBlockPart,proto3,oneof" json:"has_proposal_block_part,omitempty"` +} +type Message_Commit struct { + Commit *Commit `protobuf:"bytes,11,opt,name=commit,proto3,oneof" json:"commit,omitempty"` +} -func (*Message_NewRoundStep) isMessage_Sum() {} -func (*Message_NewValidBlock) isMessage_Sum() {} -func (*Message_Proposal) isMessage_Sum() {} -func (*Message_ProposalPol) isMessage_Sum() {} -func (*Message_BlockPart) isMessage_Sum() {} -func (*Message_Vote) isMessage_Sum() {} -func (*Message_HasVote) isMessage_Sum() {} -func (*Message_VoteSetMaj23) isMessage_Sum() {} -func (*Message_VoteSetBits) isMessage_Sum() {} +func (*Message_NewRoundStep) isMessage_Sum() {} +func (*Message_NewValidBlock) isMessage_Sum() {} +func (*Message_Proposal) isMessage_Sum() {} +func (*Message_ProposalPol) isMessage_Sum() {} +func (*Message_BlockPart) isMessage_Sum() {} +func (*Message_Vote) isMessage_Sum() {} +func (*Message_HasVote) isMessage_Sum() {} +func (*Message_VoteSetMaj23) isMessage_Sum() {} +func (*Message_VoteSetBits) isMessage_Sum() {} +func (*Message_HasProposalBlockPart) isMessage_Sum() {} +func (*Message_Commit) isMessage_Sum() {} func (m *Message) GetSum() isMessage_Sum { if m != nil { @@ -770,6 +887,20 @@ func (m *Message) GetVoteSetBits() *VoteSetBits { return nil } +func (m *Message) GetHasProposalBlockPart() *HasProposalBlockPart { + if x, ok := m.GetSum().(*Message_HasProposalBlockPart); ok { + return x.HasProposalBlockPart + } + return nil +} + +func (m *Message) GetCommit() *Commit { + if x, ok := m.GetSum().(*Message_Commit); ok { + return x.Commit + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*Message) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -782,6 +913,8 @@ func (*Message) XXX_OneofWrappers() []interface{} { (*Message_HasVote)(nil), (*Message_VoteSetMaj23)(nil), (*Message_VoteSetBits)(nil), + (*Message_HasProposalBlockPart)(nil), + (*Message_Commit)(nil), } } @@ -792,7 +925,9 @@ func init() { proto.RegisterType((*ProposalPOL)(nil), "tendermint.consensus.ProposalPOL") proto.RegisterType((*BlockPart)(nil), "tendermint.consensus.BlockPart") proto.RegisterType((*Vote)(nil), "tendermint.consensus.Vote") + proto.RegisterType((*Commit)(nil), "tendermint.consensus.Commit") proto.RegisterType((*HasVote)(nil), "tendermint.consensus.HasVote") + proto.RegisterType((*HasProposalBlockPart)(nil), "tendermint.consensus.HasProposalBlockPart") proto.RegisterType((*VoteSetMaj23)(nil), "tendermint.consensus.VoteSetMaj23") proto.RegisterType((*VoteSetBits)(nil), "tendermint.consensus.VoteSetBits") proto.RegisterType((*Message)(nil), "tendermint.consensus.Message") @@ -801,61 +936,66 @@ func init() { func init() { proto.RegisterFile("tendermint/consensus/types.proto", fileDescriptor_81a22d2efc008981) } var fileDescriptor_81a22d2efc008981 = []byte{ - // 855 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4f, 0x6f, 0xe3, 0x44, - 0x14, 0xb7, 0xa9, 0xd3, 0x24, 0xcf, 0x49, 0x0b, 0xa3, 0xee, 0x2a, 0x04, 0x48, 0x82, 0xb9, 0x54, - 0x08, 0x39, 0x28, 0x3d, 0xac, 0xb4, 0x42, 0x02, 0xcc, 0x9f, 0xf5, 0xae, 0x36, 0xbb, 0xc1, 0x59, - 0xad, 0x10, 0x17, 0xcb, 0x89, 0x87, 0x64, 0xd8, 0xd8, 0x63, 0x79, 0x26, 0x29, 0xbd, 0xf2, 0x09, - 0xf8, 0x00, 0x7c, 0x0d, 0x24, 0x3e, 0x42, 0x8f, 0x3d, 0x72, 0xaa, 0x50, 0xfa, 0x11, 0x10, 0x9c, - 0xd1, 0x8c, 0x27, 0xb1, 0x43, 0xdd, 0x42, 0x2f, 0x48, 0xdc, 0x66, 0xe6, 0xbd, 0xf7, 0x9b, 0x37, - 0xbf, 0xf7, 0xde, 0xcf, 0x86, 0x1e, 0xc7, 0x71, 0x88, 0xd3, 0x88, 0xc4, 0xbc, 0x3f, 0xa5, 0x31, - 0xc3, 0x31, 0x5b, 0xb2, 0x3e, 0x3f, 0x4b, 0x30, 0xb3, 0x93, 0x94, 0x72, 0x8a, 0x8e, 0x72, 0x0f, - 0x7b, 0xeb, 0xd1, 0x3e, 0x9a, 0xd1, 0x19, 0x95, 0x0e, 0x7d, 0xb1, 0xca, 0x7c, 0xdb, 0x45, 0xb4, - 0x05, 0x99, 0xb0, 0xfe, 0x84, 0xf0, 0x1d, 0xb4, 0xf6, 0xdb, 0x05, 0x0f, 0x79, 0x5e, 0xb4, 0x5a, - 0x3f, 0xeb, 0xd0, 0x78, 0x86, 0x4f, 0x3d, 0xba, 0x8c, 0xc3, 0x31, 0xc7, 0x09, 0xba, 0x0f, 0xfb, - 0x73, 0x4c, 0x66, 0x73, 0xde, 0xd2, 0x7b, 0xfa, 0xf1, 0x9e, 0xa7, 0x76, 0xe8, 0x08, 0x2a, 0xa9, - 0x70, 0x6a, 0xbd, 0xd6, 0xd3, 0x8f, 0x2b, 0x5e, 0xb6, 0x41, 0x08, 0x0c, 0xc6, 0x71, 0xd2, 0xda, - 0xeb, 0xe9, 0xc7, 0x4d, 0x4f, 0xae, 0xd1, 0x03, 0x68, 0x31, 0x3c, 0xa5, 0x71, 0xc8, 0x7c, 0x46, - 0xe2, 0x29, 0xf6, 0x19, 0x0f, 0x52, 0xee, 0x73, 0x12, 0xe1, 0x96, 0x21, 0x31, 0xef, 0x29, 0xfb, - 0x58, 0x98, 0xc7, 0xc2, 0xfa, 0x82, 0x44, 0x18, 0xbd, 0x0f, 0x6f, 0x2c, 0x02, 0xc6, 0xfd, 0x29, - 0x8d, 0x22, 0xc2, 0xfd, 0xec, 0xba, 0x8a, 0xbc, 0xee, 0x50, 0x18, 0x3e, 0x93, 0xe7, 0x32, 0x55, - 0xeb, 0x0f, 0x1d, 0x9a, 0xcf, 0xf0, 0xe9, 0xcb, 0x60, 0x41, 0x42, 0x67, 0x41, 0xa7, 0xaf, 0xee, - 0x98, 0xf8, 0xd7, 0x70, 0x6f, 0x22, 0xc2, 0xfc, 0x44, 0xe4, 0xc6, 0x30, 0xf7, 0xe7, 0x38, 0x08, - 0x71, 0x2a, 0x5f, 0x62, 0x0e, 0xba, 0x76, 0xa1, 0x06, 0x19, 0x5f, 0xa3, 0x20, 0xe5, 0x63, 0xcc, - 0x5d, 0xe9, 0xe6, 0x18, 0xe7, 0x97, 0x5d, 0xcd, 0x43, 0x12, 0x63, 0xc7, 0x82, 0x3e, 0x06, 0x33, - 0x47, 0x66, 0xf2, 0xc5, 0xe6, 0xa0, 0x53, 0xc4, 0x13, 0x75, 0xb2, 0x45, 0x9d, 0x6c, 0x87, 0xf0, - 0x4f, 0xd3, 0x34, 0x38, 0xf3, 0x60, 0x0b, 0xc4, 0xd0, 0x5b, 0x50, 0x27, 0x4c, 0x91, 0x20, 0x9f, - 0x5f, 0xf3, 0x6a, 0x84, 0x65, 0x8f, 0xb7, 0x5c, 0xa8, 0x8d, 0x52, 0x9a, 0x50, 0x16, 0x2c, 0xd0, - 0x47, 0x50, 0x4b, 0xd4, 0x5a, 0xbe, 0xd9, 0x1c, 0xb4, 0x4b, 0xd2, 0x56, 0x1e, 0x2a, 0xe3, 0x6d, - 0x84, 0xf5, 0x93, 0x0e, 0xe6, 0xc6, 0x38, 0x7a, 0xfe, 0xf4, 0x46, 0xfe, 0x3e, 0x00, 0xb4, 0x89, - 0xf1, 0x13, 0xba, 0xf0, 0x8b, 0x64, 0xbe, 0xbe, 0xb1, 0x8c, 0xe8, 0x42, 0xd6, 0x05, 0x3d, 0x82, - 0x46, 0xd1, 0x5b, 0xd1, 0xf9, 0x0f, 0xcf, 0x57, 0xb9, 0x99, 0x05, 0x34, 0xeb, 0x15, 0xd4, 0x9d, - 0x0d, 0x27, 0x77, 0xac, 0xed, 0x87, 0x60, 0x08, 0xee, 0xd5, 0xdd, 0xf7, 0xcb, 0x4b, 0xa9, 0xee, - 0x94, 0x9e, 0xd6, 0x00, 0x8c, 0x97, 0x94, 0x8b, 0x0e, 0x34, 0x56, 0x94, 0x63, 0xc5, 0x66, 0x49, - 0xa4, 0xf0, 0xf2, 0xa4, 0x8f, 0xf5, 0x83, 0x0e, 0x55, 0x37, 0x60, 0x32, 0xee, 0x6e, 0xf9, 0x9d, - 0x80, 0x21, 0xd0, 0x64, 0x7e, 0x07, 0x65, 0xad, 0x36, 0x26, 0xb3, 0x18, 0x87, 0x43, 0x36, 0x7b, - 0x71, 0x96, 0x60, 0x4f, 0x3a, 0x0b, 0x28, 0x12, 0x87, 0xf8, 0x7b, 0xd9, 0x50, 0x15, 0x2f, 0xdb, - 0x58, 0xbf, 0xe8, 0xd0, 0x10, 0x19, 0x8c, 0x31, 0x1f, 0x06, 0xdf, 0x0d, 0x4e, 0xfe, 0x8b, 0x4c, - 0xbe, 0x80, 0x5a, 0xd6, 0xe0, 0x24, 0x54, 0xdd, 0xfd, 0xe6, 0xf5, 0x40, 0x59, 0xbb, 0xc7, 0x9f, - 0x3b, 0x87, 0x82, 0xe5, 0xf5, 0x65, 0xb7, 0xaa, 0x0e, 0xbc, 0xaa, 0x8c, 0x7d, 0x1c, 0x5a, 0xbf, - 0xeb, 0x60, 0xaa, 0xd4, 0x1d, 0xc2, 0xd9, 0xff, 0x27, 0x73, 0xf4, 0x10, 0x2a, 0xa2, 0x03, 0x98, - 0x1c, 0xce, 0x7f, 0xdb, 0xdc, 0x59, 0x88, 0xf5, 0xa7, 0x01, 0xd5, 0x21, 0x66, 0x2c, 0x98, 0x61, - 0xf4, 0x04, 0x0e, 0x62, 0x7c, 0x9a, 0x0d, 0x94, 0x2f, 0x65, 0x34, 0xeb, 0x3b, 0xcb, 0x2e, 0xfb, - 0x00, 0xd8, 0x45, 0x99, 0x76, 0x35, 0xaf, 0x11, 0x17, 0x65, 0x7b, 0x08, 0x87, 0x02, 0x6b, 0x25, - 0xf4, 0xd0, 0x97, 0x89, 0x4a, 0xbe, 0xcc, 0xc1, 0x7b, 0x37, 0x82, 0xe5, 0xda, 0xe9, 0x6a, 0x5e, - 0x33, 0xde, 0x11, 0xd3, 0xa2, 0xb4, 0x94, 0x8c, 0x70, 0x8e, 0xb3, 0x51, 0x10, 0xb7, 0x20, 0x2d, - 0xe8, 0xcb, 0xbf, 0x89, 0x40, 0xc6, 0xf5, 0xbb, 0xb7, 0x23, 0x8c, 0x9e, 0x3f, 0x75, 0x77, 0x35, - 0x00, 0x7d, 0x02, 0x90, 0x4b, 0xa9, 0x62, 0xbb, 0x5b, 0x8e, 0xb2, 0xd5, 0x0a, 0x57, 0xf3, 0xea, - 0x5b, 0x31, 0x15, 0x52, 0x20, 0x07, 0x7a, 0xff, 0xba, 0x3c, 0xe6, 0xb1, 0xa2, 0x0b, 0x5d, 0x2d, - 0x1b, 0x6b, 0xf4, 0x10, 0x6a, 0xf3, 0x80, 0xf9, 0x32, 0xaa, 0x2a, 0xa3, 0xde, 0x29, 0x8f, 0x52, - 0xb3, 0xef, 0x6a, 0x5e, 0x75, 0xae, 0x64, 0xe0, 0x09, 0x1c, 0x88, 0x38, 0xf9, 0x39, 0x89, 0xc4, - 0x38, 0xb6, 0x6a, 0xb7, 0x15, 0xb4, 0x38, 0xb8, 0xa2, 0xa0, 0xab, 0xe2, 0x20, 0x3f, 0x82, 0xe6, - 0x16, 0x4b, 0xf4, 0x53, 0xab, 0x7e, 0x1b, 0x89, 0x85, 0x41, 0x12, 0x24, 0xae, 0xf2, 0xad, 0x53, - 0x81, 0x3d, 0xb6, 0x8c, 0x9c, 0xaf, 0xce, 0xd7, 0x1d, 0xfd, 0x62, 0xdd, 0xd1, 0x7f, 0x5b, 0x77, - 0xf4, 0x1f, 0xaf, 0x3a, 0xda, 0xc5, 0x55, 0x47, 0xfb, 0xf5, 0xaa, 0xa3, 0x7d, 0xf3, 0x60, 0x46, - 0xf8, 0x7c, 0x39, 0xb1, 0xa7, 0x34, 0xea, 0x4f, 0x69, 0x84, 0xf9, 0xe4, 0x5b, 0x9e, 0x2f, 0xb2, - 0x3f, 0x8e, 0xb2, 0x7f, 0x96, 0xc9, 0xbe, 0xb4, 0x9d, 0xfc, 0x15, 0x00, 0x00, 0xff, 0xff, 0x94, - 0xdf, 0xca, 0xd0, 0xd2, 0x08, 0x00, 0x00, + // 930 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x56, 0x4f, 0x6f, 0xdc, 0x44, + 0x14, 0xb7, 0xc9, 0xfe, 0xcb, 0x73, 0xfe, 0xc0, 0x68, 0x5b, 0x96, 0x50, 0x36, 0xc1, 0x5c, 0xa2, + 0x0a, 0xed, 0x56, 0x1b, 0x89, 0x4a, 0x11, 0x12, 0xb0, 0xfc, 0xa9, 0x5b, 0x35, 0xed, 0xe2, 0xad, + 0x2a, 0xd4, 0x8b, 0xe5, 0xb5, 0x87, 0xdd, 0xa1, 0x6b, 0x8f, 0xe5, 0x99, 0x24, 0xe4, 0xca, 0x27, + 0x40, 0xe2, 0xca, 0xd7, 0x40, 0xe2, 0x23, 0xf4, 0xd8, 0x23, 0xa7, 0x0a, 0x25, 0x1f, 0x01, 0x71, + 0x47, 0xf3, 0x3c, 0xeb, 0x9d, 0x10, 0x27, 0x10, 0x0e, 0x48, 0xbd, 0xd9, 0x7e, 0xef, 0xfd, 0xe6, + 0xbd, 0xdf, 0x7b, 0xef, 0x37, 0x86, 0x1d, 0x49, 0xd3, 0x98, 0xe6, 0x09, 0x4b, 0x65, 0x3f, 0xe2, + 0xa9, 0xa0, 0xa9, 0x38, 0x14, 0x7d, 0x79, 0x92, 0x51, 0xd1, 0xcb, 0x72, 0x2e, 0x39, 0x69, 0x2f, + 0x3d, 0x7a, 0xa5, 0xc7, 0x56, 0x7b, 0xca, 0xa7, 0x1c, 0x1d, 0xfa, 0xea, 0xa9, 0xf0, 0xdd, 0x32, + 0xd1, 0xe6, 0x6c, 0x22, 0xfa, 0x13, 0x26, 0xcf, 0xa1, 0x6d, 0xdd, 0x32, 0x3c, 0xf0, 0xbb, 0x69, + 0x75, 0x7f, 0xb1, 0x61, 0xed, 0x11, 0x3d, 0xf6, 0xf9, 0x61, 0x1a, 0x8f, 0x25, 0xcd, 0xc8, 0x4d, + 0x68, 0xcc, 0x28, 0x9b, 0xce, 0x64, 0xc7, 0xde, 0xb1, 0x77, 0x57, 0x7c, 0xfd, 0x46, 0xda, 0x50, + 0xcf, 0x95, 0x53, 0xe7, 0x8d, 0x1d, 0x7b, 0xb7, 0xee, 0x17, 0x2f, 0x84, 0x40, 0x4d, 0x48, 0x9a, + 0x75, 0x56, 0x76, 0xec, 0xdd, 0x75, 0x1f, 0x9f, 0xc9, 0x5d, 0xe8, 0x08, 0x1a, 0xf1, 0x34, 0x16, + 0x81, 0x60, 0x69, 0x44, 0x03, 0x21, 0xc3, 0x5c, 0x06, 0x92, 0x25, 0xb4, 0x53, 0x43, 0xcc, 0x1b, + 0xda, 0x3e, 0x56, 0xe6, 0xb1, 0xb2, 0x3e, 0x61, 0x09, 0x25, 0xb7, 0xe1, 0xad, 0x79, 0x28, 0x64, + 0x10, 0xf1, 0x24, 0x61, 0x32, 0x28, 0x8e, 0xab, 0xe3, 0x71, 0x9b, 0xca, 0xf0, 0x39, 0x7e, 0xc7, + 0x54, 0xdd, 0x3f, 0x6d, 0x58, 0x7f, 0x44, 0x8f, 0x9f, 0x86, 0x73, 0x16, 0x0f, 0xe7, 0x3c, 0x7a, + 0x7e, 0xcd, 0xc4, 0xbf, 0x81, 0x1b, 0x13, 0x15, 0x16, 0x64, 0x2a, 0x37, 0x41, 0x65, 0x30, 0xa3, + 0x61, 0x4c, 0x73, 0xac, 0xc4, 0x19, 0x6c, 0xf7, 0x8c, 0x1e, 0x14, 0x7c, 0x8d, 0xc2, 0x5c, 0x8e, + 0xa9, 0xf4, 0xd0, 0x6d, 0x58, 0x7b, 0xf1, 0x6a, 0xdb, 0xf2, 0x09, 0x62, 0x9c, 0xb3, 0x90, 0x4f, + 0xc0, 0x59, 0x22, 0x0b, 0xac, 0xd8, 0x19, 0x74, 0x4d, 0x3c, 0xd5, 0xa7, 0x9e, 0xea, 0x53, 0x6f, + 0xc8, 0xe4, 0x67, 0x79, 0x1e, 0x9e, 0xf8, 0x50, 0x02, 0x09, 0xf2, 0x2e, 0xac, 0x32, 0xa1, 0x49, + 0xc0, 0xf2, 0x5b, 0x7e, 0x8b, 0x89, 0xa2, 0x78, 0xd7, 0x83, 0xd6, 0x28, 0xe7, 0x19, 0x17, 0xe1, + 0x9c, 0x7c, 0x0c, 0xad, 0x4c, 0x3f, 0x63, 0xcd, 0xce, 0x60, 0xab, 0x22, 0x6d, 0xed, 0xa1, 0x33, + 0x2e, 0x23, 0xdc, 0x9f, 0x6d, 0x70, 0x16, 0xc6, 0xd1, 0xe3, 0x87, 0x97, 0xf2, 0xf7, 0x21, 0x90, + 0x45, 0x4c, 0x90, 0xf1, 0x79, 0x60, 0x92, 0xf9, 0xe6, 0xc2, 0x32, 0xe2, 0x73, 0xec, 0x0b, 0xb9, + 0x07, 0x6b, 0xa6, 0xb7, 0xa6, 0xf3, 0x1f, 0xca, 0xd7, 0xb9, 0x39, 0x06, 0x9a, 0xfb, 0x1c, 0x56, + 0x87, 0x0b, 0x4e, 0xae, 0xd9, 0xdb, 0x3b, 0x50, 0x53, 0xdc, 0xeb, 0xb3, 0x6f, 0x56, 0xb7, 0x52, + 0x9f, 0x89, 0x9e, 0xee, 0x00, 0x6a, 0x4f, 0xb9, 0x54, 0x13, 0x58, 0x3b, 0xe2, 0x92, 0x6a, 0x36, + 0x2b, 0x22, 0x95, 0x97, 0x8f, 0x3e, 0xee, 0x3e, 0x34, 0x8a, 0x9e, 0x90, 0x3b, 0xd0, 0xd0, 0xdd, + 0x2a, 0xe2, 0x3a, 0x17, 0xe3, 0xf4, 0xe8, 0x6a, 0x3f, 0xf7, 0x07, 0x1b, 0x9a, 0x5e, 0x28, 0xf0, + 0xcc, 0xeb, 0xd5, 0xb6, 0x07, 0x35, 0x85, 0x88, 0xb5, 0x6d, 0x54, 0x8d, 0xe9, 0x98, 0x4d, 0x53, + 0x1a, 0x1f, 0x88, 0xe9, 0x93, 0x93, 0x8c, 0xfa, 0xe8, 0xac, 0xa0, 0x58, 0x1a, 0xd3, 0xef, 0x71, + 0x18, 0xeb, 0x7e, 0xf1, 0xe2, 0x3e, 0x83, 0xb6, 0x17, 0x8a, 0x72, 0x3e, 0xfe, 0x23, 0xd9, 0x25, + 0xf6, 0x8a, 0x89, 0xfd, 0xab, 0x0d, 0x6b, 0xaa, 0xba, 0x31, 0x95, 0x07, 0xe1, 0x77, 0x83, 0xbd, + 0xff, 0xa3, 0xca, 0x2f, 0xa1, 0x55, 0x2c, 0x1e, 0x8b, 0xf5, 0xd6, 0xbd, 0x73, 0x31, 0x10, 0xcb, + 0xbc, 0xff, 0xc5, 0x70, 0x53, 0x75, 0xff, 0xf4, 0xd5, 0x76, 0x53, 0x7f, 0xf0, 0x9b, 0x18, 0x7b, + 0x3f, 0x76, 0xff, 0xb0, 0xc1, 0xd1, 0xa9, 0x0f, 0x99, 0x14, 0xaf, 0x4f, 0xe6, 0x64, 0x1f, 0xea, + 0x6a, 0x32, 0x05, 0x8a, 0xc6, 0xbf, 0x5d, 0xba, 0x22, 0xc4, 0xfd, 0xa9, 0x01, 0xcd, 0x03, 0x2a, + 0x44, 0x38, 0xa5, 0xe4, 0x01, 0x6c, 0xa4, 0xf4, 0xb8, 0x58, 0xf4, 0x00, 0xe5, 0xbd, 0x98, 0x6b, + 0xb7, 0x57, 0x75, 0x31, 0xf5, 0xcc, 0xeb, 0xc3, 0xb3, 0xfc, 0xb5, 0xd4, 0xbc, 0x4e, 0x0e, 0x60, + 0x53, 0x61, 0x1d, 0x29, 0x9d, 0x0e, 0x30, 0x51, 0xe4, 0xcb, 0x19, 0x7c, 0x70, 0x29, 0xd8, 0x52, + 0xd3, 0x3d, 0xcb, 0x5f, 0x4f, 0xcf, 0x89, 0xbc, 0x29, 0x79, 0x15, 0xd2, 0xb2, 0xc4, 0x59, 0x8c, + 0xb5, 0x67, 0x48, 0x1e, 0xf9, 0xea, 0x6f, 0xe2, 0x54, 0x70, 0xfd, 0xfe, 0xd5, 0x08, 0xa3, 0xc7, + 0x0f, 0xbd, 0xf3, 0xda, 0x44, 0x3e, 0x05, 0x58, 0x4a, 0xbc, 0x66, 0x7b, 0xbb, 0x1a, 0xa5, 0x5c, + 0x2b, 0xcf, 0xf2, 0x57, 0x4b, 0x91, 0x57, 0x12, 0x85, 0x42, 0xd3, 0xb8, 0x28, 0xdb, 0xcb, 0x58, + 0x35, 0x85, 0x9e, 0x55, 0xc8, 0x0d, 0xd9, 0x87, 0xd6, 0x2c, 0x14, 0x01, 0x46, 0x35, 0x31, 0xea, + 0xbd, 0xea, 0x28, 0xad, 0x2b, 0x9e, 0xe5, 0x37, 0x67, 0x5a, 0x62, 0x1e, 0xc0, 0x86, 0x8a, 0xc3, + 0x6b, 0x2e, 0x51, 0xeb, 0xd8, 0x69, 0x5d, 0xd5, 0x50, 0x73, 0x71, 0x55, 0x43, 0x8f, 0xcc, 0x45, + 0xbe, 0x07, 0xeb, 0x25, 0x96, 0x9a, 0xa7, 0xce, 0xea, 0x55, 0x24, 0x1a, 0x8b, 0xa4, 0x48, 0x3c, + 0x32, 0xf6, 0x2a, 0x82, 0xb7, 0x55, 0x41, 0x65, 0x43, 0x0c, 0x46, 0x01, 0x21, 0x6f, 0x5f, 0x5a, + 0xdf, 0x05, 0xcd, 0xf2, 0x2c, 0xbf, 0x3d, 0xab, 0xd2, 0xb2, 0x8f, 0x4a, 0x69, 0x76, 0x10, 0xf3, + 0x56, 0x35, 0x66, 0x21, 0xcf, 0x9e, 0xb5, 0x10, 0xe8, 0x61, 0x1d, 0x56, 0xc4, 0x61, 0x32, 0xfc, + 0xfa, 0xc5, 0x69, 0xd7, 0x7e, 0x79, 0xda, 0xb5, 0x7f, 0x3f, 0xed, 0xda, 0x3f, 0x9e, 0x75, 0xad, + 0x97, 0x67, 0x5d, 0xeb, 0xb7, 0xb3, 0xae, 0xf5, 0xec, 0xee, 0x94, 0xc9, 0xd9, 0xe1, 0xa4, 0x17, + 0xf1, 0xa4, 0x1f, 0xf1, 0x84, 0xca, 0xc9, 0xb7, 0x72, 0xf9, 0x50, 0xfc, 0xa6, 0x55, 0xfd, 0xe8, + 0x4d, 0x1a, 0x68, 0xdb, 0xfb, 0x2b, 0x00, 0x00, 0xff, 0xff, 0xda, 0xbc, 0x35, 0x01, 0x07, 0x0a, + 0x00, 0x00, } func (m *NewRoundStep) Marshal() (dAtA []byte, err error) { @@ -1125,6 +1265,41 @@ func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *Commit) 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 *Commit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Commit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Commit != nil { + { + size, err := m.Commit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *HasVote) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1168,6 +1343,44 @@ func (m *HasVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *HasProposalBlockPart) 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 *HasProposalBlockPart) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *HasProposalBlockPart) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Index != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Index)) + i-- + dAtA[i] = 0x18 + } + if m.Round != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Round)) + i-- + dAtA[i] = 0x10 + } + if m.Height != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + func (m *VoteSetMaj23) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1495,6 +1708,48 @@ func (m *Message_VoteSetBits) MarshalToSizedBuffer(dAtA []byte) (int, error) { } return len(dAtA) - i, nil } +func (m *Message_HasProposalBlockPart) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Message_HasProposalBlockPart) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.HasProposalBlockPart != nil { + { + size, err := m.HasProposalBlockPart.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + return len(dAtA) - i, nil +} +func (m *Message_Commit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Message_Commit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.Commit != nil { + { + size, err := m.Commit.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + return len(dAtA) - i, nil +} func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -1612,6 +1867,19 @@ func (m *Vote) Size() (n int) { return n } +func (m *Commit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Commit != nil { + l = m.Commit.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func (m *HasVote) Size() (n int) { if m == nil { return 0 @@ -1633,6 +1901,24 @@ func (m *HasVote) Size() (n int) { return n } +func (m *HasProposalBlockPart) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovTypes(uint64(m.Height)) + } + if m.Round != 0 { + n += 1 + sovTypes(uint64(m.Round)) + } + if m.Index != 0 { + n += 1 + sovTypes(uint64(m.Index)) + } + return n +} + func (m *VoteSetMaj23) Size() (n int) { if m == nil { return 0 @@ -1795,6 +2081,30 @@ func (m *Message_VoteSetBits) Size() (n int) { } return n } +func (m *Message_HasProposalBlockPart) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.HasProposalBlockPart != nil { + l = m.HasProposalBlockPart.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} +func (m *Message_Commit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Commit != nil { + l = m.Commit.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 @@ -2535,6 +2845,92 @@ func (m *Vote) Unmarshal(dAtA []byte) error { } return nil } +func (m *Commit) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Commit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Commit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commit", 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.Commit == nil { + m.Commit = &types.Commit{} + } + if err := m.Commit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *HasVote) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2661,6 +3057,113 @@ func (m *HasVote) Unmarshal(dAtA []byte) error { } return nil } +func (m *HasProposalBlockPart) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: HasProposalBlockPart: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: HasProposalBlockPart: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Round", wireType) + } + m.Round = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Round |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) + } + m.Index = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Index |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *VoteSetMaj23) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -3318,6 +3821,76 @@ func (m *Message) Unmarshal(dAtA []byte) error { } m.Sum = &Message_VoteSetBits{v} iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasProposalBlockPart", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &HasProposalBlockPart{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &Message_HasProposalBlockPart{v} + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Commit", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Commit{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &Message_Commit{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/proto/tendermint/consensus/types.proto b/proto/tendermint/consensus/types.proto index ad0830eac83..18f4dd0d074 100644 --- a/proto/tendermint/consensus/types.proto +++ b/proto/tendermint/consensus/types.proto @@ -52,6 +52,12 @@ message Vote { tendermint.types.Vote vote = 1; } +// An entire Commit is sent when signatures are aggregated and we cannot +// extract a single signature. +message Commit { + tendermint.types.Commit commit = 1; +} + // HasVote is sent to indicate that a particular vote has been received. message HasVote { int64 height = 1; @@ -60,6 +66,13 @@ message HasVote { int32 index = 4; } +// HasProposalBlockPart is sent to indicate that a particular block part has been received. +message HasProposalBlockPart { + int64 height = 1; + int32 round = 2; + int32 index = 3; +} + // VoteSetMaj23 is sent to indicate that a given BlockID has seen +2/3 votes. message VoteSetMaj23 { int64 height = 1; @@ -94,5 +107,9 @@ message Message { HasVote has_vote = 7; VoteSetMaj23 vote_set_maj23 = 8; VoteSetBits vote_set_bits = 9; + // Fields 10 and 11 are fork-pinned wire numbers (bera-v1.x compatibility, relied on by + // rolling upgrades). If an upstream rebase adds new entries here, renumber upstream's to 12+. + HasProposalBlockPart has_proposal_block_part = 10; + Commit commit = 11; } } diff --git a/proto/tendermint/consensus/wal.pb.go b/proto/tendermint/consensus/wal.pb.go index 801f2fe80fc..39e52c09e6f 100644 --- a/proto/tendermint/consensus/wal.pb.go +++ b/proto/tendermint/consensus/wal.pb.go @@ -5,7 +5,7 @@ package consensus import ( fmt "fmt" - types "github.com/cometbft/cometbft/proto/tendermint/types" + types1 "github.com/cometbft/cometbft/proto/tendermint/types" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" _ "github.com/cosmos/gogoproto/types" @@ -33,6 +33,10 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type MsgInfo struct { Msg Message `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg"` PeerID string `protobuf:"bytes,2,opt,name=peer_id,json=peerId,proto3" json:"peer_id,omitempty"` + // Berachain: local receive time of the message, persisted so that a + // WAL-replayed proposal keeps its PBTS timeliness verdict (same field as the + // bera-v1.x line). Only set for messages that have one. + ReceiveTime *time.Time `protobuf:"bytes,3,opt,name=receive_time,json=receiveTime,proto3,stdtime" json:"receive_time,omitempty"` } func (m *MsgInfo) Reset() { *m = MsgInfo{} } @@ -82,6 +86,13 @@ func (m *MsgInfo) GetPeerID() string { return "" } +func (m *MsgInfo) GetReceiveTime() *time.Time { + if m != nil { + return m.ReceiveTime + } + return nil +} + // TimeoutInfo internally generated messages which may update the state type TimeoutInfo struct { Duration time.Duration `protobuf:"bytes,1,opt,name=duration,proto3,stdduration" json:"duration"` @@ -246,7 +257,7 @@ type isWALMessage_Sum interface { } type WALMessage_EventDataRoundState struct { - EventDataRoundState *types.EventDataRoundState `protobuf:"bytes,1,opt,name=event_data_round_state,json=eventDataRoundState,proto3,oneof" json:"event_data_round_state,omitempty"` + EventDataRoundState *types1.EventDataRoundState `protobuf:"bytes,1,opt,name=event_data_round_state,json=eventDataRoundState,proto3,oneof" json:"event_data_round_state,omitempty"` } type WALMessage_MsgInfo struct { MsgInfo *MsgInfo `protobuf:"bytes,2,opt,name=msg_info,json=msgInfo,proto3,oneof" json:"msg_info,omitempty"` @@ -270,7 +281,7 @@ func (m *WALMessage) GetSum() isWALMessage_Sum { return nil } -func (m *WALMessage) GetEventDataRoundState() *types.EventDataRoundState { +func (m *WALMessage) GetEventDataRoundState() *types1.EventDataRoundState { if x, ok := m.GetSum().(*WALMessage_EventDataRoundState); ok { return x.EventDataRoundState } @@ -372,41 +383,43 @@ func init() { func init() { proto.RegisterFile("tendermint/consensus/wal.proto", fileDescriptor_ed0b60c2d348ab09) } var fileDescriptor_ed0b60c2d348ab09 = []byte{ - // 543 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x53, 0x5f, 0x8b, 0xd3, 0x4e, - 0x14, 0x4d, 0xb6, 0xff, 0x6f, 0x7f, 0x3f, 0x84, 0x58, 0x96, 0x5a, 0xd8, 0x34, 0x76, 0x11, 0xfa, - 0x94, 0xc0, 0x8a, 0x28, 0xbe, 0xa8, 0xa5, 0x2b, 0x2d, 0xb8, 0xa0, 0xe3, 0x82, 0x20, 0x42, 0x48, - 0x9b, 0xdb, 0x34, 0xb0, 0x99, 0x29, 0x99, 0x89, 0xe2, 0x93, 0x5f, 0xa1, 0x8f, 0x7e, 0x13, 0xbf, - 0xc2, 0x3e, 0xee, 0xa3, 0x4f, 0xab, 0xb4, 0x5f, 0x44, 0x32, 0x33, 0x6d, 0x83, 0x1b, 0xdf, 0xe6, - 0xe6, 0x9e, 0x7b, 0x4f, 0xce, 0x39, 0x33, 0x60, 0x0b, 0xa4, 0x21, 0xa6, 0x49, 0x4c, 0x85, 0x37, - 0x67, 0x94, 0x23, 0xe5, 0x19, 0xf7, 0xbe, 0x04, 0x57, 0xee, 0x2a, 0x65, 0x82, 0x59, 0x9d, 0x43, - 0xdf, 0xdd, 0xf7, 0x7b, 0x9d, 0x88, 0x45, 0x4c, 0x02, 0xbc, 0xfc, 0xa4, 0xb0, 0x3d, 0x3b, 0x62, - 0x2c, 0xba, 0x42, 0x4f, 0x56, 0xb3, 0x6c, 0xe1, 0x85, 0x59, 0x1a, 0x88, 0x98, 0x51, 0xdd, 0xef, - 0xff, 0xdd, 0x17, 0x71, 0x82, 0x5c, 0x04, 0xc9, 0x4a, 0x03, 0x9c, 0xd2, 0x9f, 0x11, 0x5f, 0x57, - 0xc8, 0x35, 0xe2, 0xa4, 0x80, 0x90, 0xdf, 0x3d, 0xfc, 0x8c, 0x54, 0xe8, 0xf6, 0x00, 0xa1, 0x71, - 0xc1, 0xa3, 0x29, 0x5d, 0x30, 0xeb, 0x09, 0x54, 0x12, 0x1e, 0x75, 0x4d, 0xc7, 0x1c, 0xb6, 0xcf, - 0x4e, 0xdc, 0x32, 0x19, 0xee, 0x05, 0x72, 0x1e, 0x44, 0x38, 0xaa, 0x5e, 0xdf, 0xf6, 0x0d, 0x92, - 0xe3, 0xad, 0x53, 0x68, 0xac, 0x10, 0x53, 0x3f, 0x0e, 0xbb, 0x47, 0x8e, 0x39, 0x6c, 0x8d, 0x60, - 0x73, 0xdb, 0xaf, 0xbf, 0x45, 0x4c, 0xa7, 0x63, 0x52, 0xcf, 0x5b, 0xd3, 0x70, 0xb0, 0x36, 0xa1, - 0x7d, 0x19, 0x27, 0xc8, 0x32, 0x21, 0xb9, 0x5e, 0x40, 0x73, 0x27, 0x55, 0x13, 0x3e, 0x70, 0x95, - 0x56, 0x77, 0xa7, 0xd5, 0x1d, 0x6b, 0xc0, 0xa8, 0x99, 0x93, 0x7d, 0xff, 0xd5, 0x37, 0xc9, 0x7e, - 0xc8, 0x3a, 0x86, 0xfa, 0x12, 0xe3, 0x68, 0x29, 0x24, 0x69, 0x85, 0xe8, 0xca, 0xea, 0x40, 0x2d, - 0x65, 0x19, 0x0d, 0xbb, 0x15, 0xc7, 0x1c, 0xd6, 0x88, 0x2a, 0x2c, 0x0b, 0xaa, 0x5c, 0xe0, 0xaa, - 0x5b, 0x75, 0xcc, 0xe1, 0xff, 0x44, 0x9e, 0x07, 0xa7, 0xd0, 0x3a, 0xa7, 0xe1, 0x44, 0x8d, 0x1d, - 0xd6, 0x99, 0xc5, 0x75, 0x83, 0x1f, 0x47, 0x00, 0x1f, 0x5e, 0xbd, 0xd1, 0xb2, 0xad, 0x4f, 0x70, - 0x2c, 0xdd, 0xf3, 0xc3, 0x40, 0x04, 0xbe, 0xdc, 0xed, 0x73, 0x11, 0x08, 0xd4, 0x22, 0x1e, 0x15, - 0x5d, 0x53, 0x29, 0x9c, 0xe7, 0xf8, 0x71, 0x20, 0x02, 0x92, 0xa3, 0xdf, 0xe7, 0xe0, 0x89, 0x41, - 0xee, 0xe3, 0xdd, 0xcf, 0xd6, 0x73, 0x68, 0x26, 0x3c, 0xf2, 0x63, 0xba, 0x60, 0x52, 0xd5, 0xbf, - 0x53, 0x50, 0x89, 0x4d, 0x0c, 0xd2, 0x48, 0x74, 0x78, 0xaf, 0xe1, 0x3f, 0xa1, 0xfc, 0x55, 0xf3, - 0x15, 0x39, 0xff, 0xb0, 0x7c, 0xbe, 0x90, 0xc4, 0xc4, 0x20, 0x6d, 0x51, 0x08, 0xe6, 0x25, 0x00, - 0xd2, 0xd0, 0xd7, 0x66, 0x54, 0xe5, 0x96, 0x7e, 0xf9, 0x96, 0xbd, 0x7b, 0x13, 0x83, 0xb4, 0x70, - 0x57, 0x8c, 0x6a, 0x50, 0xe1, 0x59, 0x32, 0xf8, 0x06, 0xf7, 0x72, 0x9a, 0xb0, 0xe0, 0xde, 0x33, - 0xa8, 0xe6, 0x54, 0xda, 0xab, 0xde, 0x9d, 0xc0, 0x2f, 0x77, 0x97, 0x5b, 0x25, 0xbe, 0xce, 0x13, - 0x97, 0x13, 0xd6, 0x99, 0xba, 0x9a, 0xca, 0x14, 0xa7, 0xfc, 0x77, 0x0e, 0x44, 0xf2, 0x5e, 0x8e, - 0xde, 0x5d, 0x6f, 0x6c, 0xf3, 0x66, 0x63, 0x9b, 0xbf, 0x37, 0xb6, 0xb9, 0xde, 0xda, 0xc6, 0xcd, - 0xd6, 0x36, 0x7e, 0x6e, 0x6d, 0xe3, 0xe3, 0xd3, 0x28, 0x16, 0xcb, 0x6c, 0xe6, 0xce, 0x59, 0xe2, - 0xcd, 0x59, 0x82, 0x62, 0xb6, 0x10, 0x87, 0x83, 0x7a, 0xa4, 0x65, 0xef, 0x6a, 0x56, 0x97, 0xbd, - 0xc7, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x8f, 0x0a, 0x05, 0xb5, 0x03, 0x04, 0x00, 0x00, + // 568 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xcf, 0x8a, 0xd3, 0x5e, + 0x14, 0xce, 0x9d, 0xfe, 0x3f, 0x9d, 0x1f, 0x3f, 0x88, 0x65, 0xa8, 0x85, 0x49, 0x6a, 0x07, 0xa1, + 0xab, 0x04, 0x46, 0x44, 0x71, 0xa3, 0x96, 0x8e, 0xb6, 0xe0, 0x80, 0x46, 0x41, 0x10, 0x21, 0xa4, + 0xcd, 0x69, 0x1a, 0x98, 0xe4, 0x96, 0xdc, 0x9b, 0x11, 0x57, 0xbe, 0x42, 0x97, 0x3e, 0x85, 0x5b, + 0x5f, 0xa1, 0xcb, 0x59, 0xba, 0xaa, 0xd2, 0xbe, 0x88, 0xe4, 0xde, 0xdb, 0x36, 0x38, 0x11, 0xdc, + 0x9d, 0x93, 0xef, 0x3b, 0xdf, 0x3d, 0xe7, 0x3b, 0x27, 0x60, 0x70, 0x8c, 0x7d, 0x4c, 0xa2, 0x30, + 0xe6, 0xf6, 0x94, 0xc6, 0x0c, 0x63, 0x96, 0x32, 0xfb, 0x93, 0x77, 0x65, 0x2d, 0x12, 0xca, 0xa9, + 0xde, 0x3a, 0xe0, 0xd6, 0x1e, 0xef, 0xb4, 0x02, 0x1a, 0x50, 0x41, 0xb0, 0xb3, 0x48, 0x72, 0x3b, + 0x46, 0x40, 0x69, 0x70, 0x85, 0xb6, 0xc8, 0x26, 0xe9, 0xcc, 0xf6, 0xd3, 0xc4, 0xe3, 0x21, 0x8d, + 0x15, 0x6e, 0xfe, 0x89, 0xf3, 0x30, 0x42, 0xc6, 0xbd, 0x68, 0xa1, 0x08, 0xdd, 0xc2, 0x66, 0xf8, + 0xe7, 0x05, 0x32, 0xc5, 0x38, 0xcd, 0x31, 0xc4, 0x77, 0x1b, 0xaf, 0x31, 0xe6, 0x0a, 0xee, 0x7d, + 0x23, 0x50, 0xbb, 0x64, 0xc1, 0x38, 0x9e, 0x51, 0xfd, 0x21, 0x94, 0x22, 0x16, 0xb4, 0x49, 0x97, + 0xf4, 0x9b, 0xe7, 0xa7, 0x56, 0xd1, 0x1c, 0xd6, 0x25, 0x32, 0xe6, 0x05, 0x38, 0x28, 0xaf, 0xd6, + 0xa6, 0xe6, 0x64, 0x7c, 0xfd, 0x0c, 0x6a, 0x0b, 0xc4, 0xc4, 0x0d, 0xfd, 0xf6, 0x51, 0x97, 0xf4, + 0x1b, 0x03, 0xd8, 0xac, 0xcd, 0xea, 0x6b, 0xc4, 0x64, 0x3c, 0x74, 0xaa, 0x19, 0x34, 0xf6, 0xf5, + 0x97, 0x70, 0x9c, 0xe0, 0x14, 0xc3, 0x6b, 0x74, 0xb3, 0x19, 0xda, 0x25, 0xf1, 0x48, 0xc7, 0x92, + 0x03, 0x5a, 0xbb, 0x01, 0xad, 0x77, 0xbb, 0x01, 0x07, 0xf5, 0xd5, 0xda, 0x24, 0xcb, 0x9f, 0x26, + 0x71, 0x9a, 0xaa, 0x32, 0xc3, 0x7a, 0x4b, 0x02, 0xcd, 0x2c, 0xa0, 0x29, 0x17, 0x4d, 0x3f, 0x85, + 0xfa, 0xce, 0x34, 0xd5, 0xf9, 0xdd, 0x5b, 0xa2, 0x43, 0x45, 0x10, 0x9a, 0xda, 0xd7, 0x4c, 0x73, + 0x5f, 0xa4, 0x9f, 0x40, 0x75, 0x8e, 0x61, 0x30, 0xe7, 0xa2, 0xfb, 0x92, 0xa3, 0x32, 0xbd, 0x05, + 0x95, 0x84, 0xa6, 0xb1, 0x2f, 0x5a, 0xad, 0x38, 0x32, 0xd1, 0x75, 0x28, 0x33, 0x8e, 0x8b, 0x76, + 0xb9, 0x4b, 0xfa, 0xff, 0x39, 0x22, 0xee, 0x9d, 0x41, 0xe3, 0x22, 0xf6, 0x47, 0xb2, 0xec, 0x20, + 0x47, 0xf2, 0x72, 0xbd, 0xef, 0x47, 0x00, 0xef, 0x9f, 0xbf, 0x52, 0xfe, 0xe9, 0x1f, 0xe1, 0x44, + 0xec, 0xc1, 0xf5, 0x3d, 0xee, 0xb9, 0x42, 0xdb, 0x65, 0xdc, 0xe3, 0xa8, 0x86, 0xb8, 0x9f, 0xb7, + 0x5f, 0xee, 0xf3, 0x22, 0xe3, 0x0f, 0x3d, 0xee, 0x39, 0x19, 0xfb, 0x6d, 0x46, 0x1e, 0x69, 0xce, + 0x1d, 0xbc, 0xfd, 0x59, 0x7f, 0x02, 0xf5, 0x88, 0x05, 0x6e, 0x18, 0xcf, 0xa8, 0x98, 0xea, 0xef, + 0xeb, 0x94, 0xab, 0x1f, 0x69, 0x4e, 0x2d, 0x52, 0x57, 0xf0, 0x02, 0x8e, 0xb9, 0xf4, 0x57, 0xd6, + 0xcb, 0x4d, 0xdd, 0x2b, 0xae, 0xcf, 0x6d, 0x62, 0xa4, 0x39, 0x4d, 0x9e, 0x5b, 0xcc, 0x33, 0x00, + 0x8c, 0x7d, 0x57, 0x99, 0x51, 0x16, 0x2a, 0x66, 0xb1, 0xca, 0xde, 0xbd, 0x91, 0xe6, 0x34, 0x70, + 0x97, 0x0c, 0x2a, 0x50, 0x62, 0x69, 0xd4, 0xfb, 0x02, 0xff, 0x67, 0xcf, 0xf8, 0x39, 0xf7, 0x1e, + 0x43, 0x59, 0x5c, 0x11, 0xf9, 0xa7, 0x2b, 0xd2, 0xc4, 0x15, 0x89, 0x0a, 0xfd, 0x5c, 0xde, 0xb8, + 0x34, 0xa5, 0x5b, 0xdc, 0xce, 0xe1, 0x21, 0x71, 0xe0, 0x83, 0x37, 0xab, 0x8d, 0x41, 0x6e, 0x36, + 0x06, 0xf9, 0xb5, 0x31, 0xc8, 0x72, 0x6b, 0x68, 0x37, 0x5b, 0x43, 0xfb, 0xb1, 0x35, 0xb4, 0x0f, + 0x8f, 0x82, 0x90, 0xcf, 0xd3, 0x89, 0x35, 0xa5, 0x91, 0x3d, 0xa5, 0x11, 0xf2, 0xc9, 0x8c, 0x1f, + 0x02, 0xf9, 0xbb, 0x17, 0xfd, 0xa1, 0x93, 0xaa, 0xc0, 0x1e, 0xfc, 0x0e, 0x00, 0x00, 0xff, 0xff, + 0x9d, 0x7a, 0x69, 0x3d, 0x4d, 0x04, 0x00, 0x00, } func (m *MsgInfo) Marshal() (dAtA []byte, err error) { @@ -429,6 +442,16 @@ func (m *MsgInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.ReceiveTime != nil { + n1, err1 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(*m.ReceiveTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.ReceiveTime):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintWal(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x1a + } if len(m.PeerID) > 0 { i -= len(m.PeerID) copy(dAtA[i:], m.PeerID) @@ -484,12 +507,12 @@ func (m *TimeoutInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x10 } - n2, err2 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.Duration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.Duration):]) - if err2 != nil { - return 0, err2 + n3, err3 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.Duration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.Duration):]) + if err3 != nil { + return 0, err3 } - i -= n2 - i = encodeVarintWal(dAtA, i, uint64(n2)) + i -= n3 + i = encodeVarintWal(dAtA, i, uint64(n3)) i-- dAtA[i] = 0xa return len(dAtA) - i, nil @@ -671,12 +694,12 @@ func (m *TimedWALMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x12 } - n8, err8 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err8 != nil { - return 0, err8 + n9, err9 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) + if err9 != nil { + return 0, err9 } - i -= n8 - i = encodeVarintWal(dAtA, i, uint64(n8)) + i -= n9 + i = encodeVarintWal(dAtA, i, uint64(n9)) i-- dAtA[i] = 0xa return len(dAtA) - i, nil @@ -705,6 +728,10 @@ func (m *MsgInfo) Size() (n int) { if l > 0 { n += 1 + l + sovWal(uint64(l)) } + if m.ReceiveTime != nil { + l = github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.ReceiveTime) + n += 1 + l + sovWal(uint64(l)) + } return n } @@ -915,6 +942,42 @@ func (m *MsgInfo) Unmarshal(dAtA []byte) error { } m.PeerID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReceiveTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowWal + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthWal + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthWal + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ReceiveTime == nil { + m.ReceiveTime = new(time.Time) + } + if err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(m.ReceiveTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipWal(dAtA[iNdEx:]) @@ -1203,7 +1266,7 @@ func (m *WALMessage) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - v := &types.EventDataRoundState{} + v := &types1.EventDataRoundState{} if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } diff --git a/proto/tendermint/consensus/wal.proto b/proto/tendermint/consensus/wal.proto index c4c75307363..5dc353d9bd9 100644 --- a/proto/tendermint/consensus/wal.proto +++ b/proto/tendermint/consensus/wal.proto @@ -13,6 +13,13 @@ option go_package = "github.com/cometbft/cometbft/proto/tendermint/consensus"; message MsgInfo { Message msg = 1 [(gogoproto.nullable) = false]; string peer_id = 2 [(gogoproto.customname) = "PeerID"]; + // Berachain: local receive time of the message, persisted so that a + // WAL-replayed proposal keeps its PBTS timeliness verdict (same field as the + // bera-v1.x line). Only set for messages that have one. + google.protobuf.Timestamp receive_time = 3 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = true + ]; } // TimeoutInfo internally generated messages which may update the state diff --git a/proto/tendermint/privval/types.pb.go b/proto/tendermint/privval/types.pb.go index 7c57002811a..28cbf9f8d7b 100644 --- a/proto/tendermint/privval/types.pb.go +++ b/proto/tendermint/privval/types.pb.go @@ -424,6 +424,104 @@ func (m *SignedProposalResponse) GetError() *RemoteSignerError { return nil } +// SignBytesRequest is a request to sign arbitrary bytes +type SignBytesRequest struct { + Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *SignBytesRequest) Reset() { *m = SignBytesRequest{} } +func (m *SignBytesRequest) String() string { return proto.CompactTextString(m) } +func (*SignBytesRequest) ProtoMessage() {} +func (*SignBytesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_cb4e437a5328cf9c, []int{7} +} +func (m *SignBytesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignBytesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignBytesRequest.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 *SignBytesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignBytesRequest.Merge(m, src) +} +func (m *SignBytesRequest) XXX_Size() int { + return m.Size() +} +func (m *SignBytesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SignBytesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SignBytesRequest proto.InternalMessageInfo + +func (m *SignBytesRequest) GetValue() []byte { + if m != nil { + return m.Value + } + return nil +} + +// SignBytesResponse is a response containing a signature or an error +type SignBytesResponse struct { + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + Error *RemoteSignerError `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` +} + +func (m *SignBytesResponse) Reset() { *m = SignBytesResponse{} } +func (m *SignBytesResponse) String() string { return proto.CompactTextString(m) } +func (*SignBytesResponse) ProtoMessage() {} +func (*SignBytesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_cb4e437a5328cf9c, []int{8} +} +func (m *SignBytesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignBytesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignBytesResponse.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 *SignBytesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignBytesResponse.Merge(m, src) +} +func (m *SignBytesResponse) XXX_Size() int { + return m.Size() +} +func (m *SignBytesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SignBytesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SignBytesResponse proto.InternalMessageInfo + +func (m *SignBytesResponse) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *SignBytesResponse) GetError() *RemoteSignerError { + if m != nil { + return m.Error + } + return nil +} + // PingRequest is a request to confirm that the connection is alive. type PingRequest struct { } @@ -432,7 +530,7 @@ func (m *PingRequest) Reset() { *m = PingRequest{} } func (m *PingRequest) String() string { return proto.CompactTextString(m) } func (*PingRequest) ProtoMessage() {} func (*PingRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_cb4e437a5328cf9c, []int{7} + return fileDescriptor_cb4e437a5328cf9c, []int{9} } func (m *PingRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -469,7 +567,7 @@ func (m *PingResponse) Reset() { *m = PingResponse{} } func (m *PingResponse) String() string { return proto.CompactTextString(m) } func (*PingResponse) ProtoMessage() {} func (*PingResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_cb4e437a5328cf9c, []int{8} + return fileDescriptor_cb4e437a5328cf9c, []int{10} } func (m *PingResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -508,6 +606,8 @@ type Message struct { // *Message_SignedProposalResponse // *Message_PingRequest // *Message_PingResponse + // *Message_SignBytesRequest + // *Message_SignBytesResponse Sum isMessage_Sum `protobuf_oneof:"sum"` } @@ -515,7 +615,7 @@ func (m *Message) Reset() { *m = Message{} } func (m *Message) String() string { return proto.CompactTextString(m) } func (*Message) ProtoMessage() {} func (*Message) Descriptor() ([]byte, []int) { - return fileDescriptor_cb4e437a5328cf9c, []int{9} + return fileDescriptor_cb4e437a5328cf9c, []int{11} } func (m *Message) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -574,6 +674,12 @@ type Message_PingRequest struct { type Message_PingResponse struct { PingResponse *PingResponse `protobuf:"bytes,8,opt,name=ping_response,json=pingResponse,proto3,oneof" json:"ping_response,omitempty"` } +type Message_SignBytesRequest struct { + SignBytesRequest *SignBytesRequest `protobuf:"bytes,9,opt,name=sign_bytes_request,json=signBytesRequest,proto3,oneof" json:"sign_bytes_request,omitempty"` +} +type Message_SignBytesResponse struct { + SignBytesResponse *SignBytesResponse `protobuf:"bytes,10,opt,name=sign_bytes_response,json=signBytesResponse,proto3,oneof" json:"sign_bytes_response,omitempty"` +} func (*Message_PubKeyRequest) isMessage_Sum() {} func (*Message_PubKeyResponse) isMessage_Sum() {} @@ -583,6 +689,8 @@ func (*Message_SignProposalRequest) isMessage_Sum() {} func (*Message_SignedProposalResponse) isMessage_Sum() {} func (*Message_PingRequest) isMessage_Sum() {} func (*Message_PingResponse) isMessage_Sum() {} +func (*Message_SignBytesRequest) isMessage_Sum() {} +func (*Message_SignBytesResponse) isMessage_Sum() {} func (m *Message) GetSum() isMessage_Sum { if m != nil { @@ -647,6 +755,20 @@ func (m *Message) GetPingResponse() *PingResponse { return nil } +func (m *Message) GetSignBytesRequest() *SignBytesRequest { + if x, ok := m.GetSum().(*Message_SignBytesRequest); ok { + return x.SignBytesRequest + } + return nil +} + +func (m *Message) GetSignBytesResponse() *SignBytesResponse { + if x, ok := m.GetSum().(*Message_SignBytesResponse); ok { + return x.SignBytesResponse + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*Message) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -658,6 +780,8 @@ func (*Message) XXX_OneofWrappers() []interface{} { (*Message_SignedProposalResponse)(nil), (*Message_PingRequest)(nil), (*Message_PingResponse)(nil), + (*Message_SignBytesRequest)(nil), + (*Message_SignBytesResponse)(nil), } } @@ -670,6 +794,8 @@ func init() { proto.RegisterType((*SignedVoteResponse)(nil), "tendermint.privval.SignedVoteResponse") proto.RegisterType((*SignProposalRequest)(nil), "tendermint.privval.SignProposalRequest") proto.RegisterType((*SignedProposalResponse)(nil), "tendermint.privval.SignedProposalResponse") + proto.RegisterType((*SignBytesRequest)(nil), "tendermint.privval.SignBytesRequest") + proto.RegisterType((*SignBytesResponse)(nil), "tendermint.privval.SignBytesResponse") proto.RegisterType((*PingRequest)(nil), "tendermint.privval.PingRequest") proto.RegisterType((*PingResponse)(nil), "tendermint.privval.PingResponse") proto.RegisterType((*Message)(nil), "tendermint.privval.Message") @@ -678,55 +804,60 @@ func init() { func init() { proto.RegisterFile("tendermint/privval/types.proto", fileDescriptor_cb4e437a5328cf9c) } var fileDescriptor_cb4e437a5328cf9c = []byte{ - // 757 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x95, 0x4d, 0x4f, 0xe3, 0x46, - 0x18, 0xc7, 0x6d, 0xc8, 0x0b, 0x3c, 0x21, 0x21, 0x0c, 0x94, 0x86, 0x88, 0x9a, 0x34, 0x55, 0x5b, - 0x94, 0x43, 0x52, 0x51, 0xb5, 0x17, 0x7a, 0x29, 0x60, 0x35, 0x51, 0x84, 0x9d, 0x4e, 0x42, 0x41, - 0x48, 0x95, 0x95, 0x97, 0xc1, 0x58, 0x10, 0x8f, 0xd7, 0xe3, 0x20, 0xe5, 0xbc, 0xb7, 0x3d, 0xad, - 0xb4, 0x5f, 0x62, 0xcf, 0xfb, 0x29, 0x38, 0x72, 0xdc, 0xd3, 0x6a, 0x05, 0x5f, 0x64, 0x95, 0xf1, - 0xc4, 0x76, 0xde, 0xd0, 0xae, 0xb8, 0x79, 0x9e, 0xe7, 0xc9, 0xef, 0xf9, 0xff, 0x3d, 0xff, 0xc8, - 0xa0, 0x78, 0xc4, 0xee, 0x11, 0xb7, 0x6f, 0xd9, 0x5e, 0xc5, 0x71, 0xad, 0xbb, 0xbb, 0xf6, 0x6d, - 0xc5, 0x1b, 0x3a, 0x84, 0x95, 0x1d, 0x97, 0x7a, 0x14, 0xa1, 0xb0, 0x5f, 0x16, 0xfd, 0xfc, 0x96, - 0x49, 0x4d, 0xca, 0xdb, 0x95, 0xd1, 0x93, 0x3f, 0x99, 0xdf, 0x8d, 0x90, 0xba, 0xee, 0xd0, 0xf1, - 0x68, 0xe5, 0x86, 0x0c, 0xd9, 0x9c, 0x2e, 0xe7, 0x47, 0xb7, 0x14, 0x6b, 0xb0, 0x81, 0x49, 0x9f, - 0x7a, 0xa4, 0x69, 0x99, 0x36, 0x71, 0x55, 0xd7, 0xa5, 0x2e, 0x42, 0x10, 0xeb, 0xd2, 0x1e, 0xc9, - 0xc9, 0x05, 0x79, 0x3f, 0x8e, 0xf9, 0x33, 0x2a, 0x40, 0xaa, 0x47, 0x58, 0xd7, 0xb5, 0x1c, 0xcf, - 0xa2, 0x76, 0x6e, 0xa9, 0x20, 0xef, 0xaf, 0xe2, 0x68, 0xa9, 0x58, 0x82, 0x74, 0x63, 0xd0, 0xa9, - 0x93, 0x21, 0x26, 0xaf, 0x06, 0x84, 0x79, 0x68, 0x07, 0x56, 0xba, 0xd7, 0x6d, 0xcb, 0x36, 0xac, - 0x1e, 0x47, 0xad, 0xe2, 0x24, 0x3f, 0xd7, 0x7a, 0xc5, 0x37, 0x32, 0x64, 0xc6, 0xc3, 0xcc, 0xa1, - 0x36, 0x23, 0xe8, 0x10, 0x92, 0xce, 0xa0, 0x63, 0xdc, 0x90, 0x21, 0x1f, 0x4e, 0x1d, 0xec, 0x96, - 0x23, 0x6f, 0xc0, 0xf7, 0x55, 0x6e, 0x0c, 0x3a, 0xb7, 0x56, 0xb7, 0x4e, 0x86, 0x47, 0xb1, 0xfb, - 0x4f, 0x7b, 0x12, 0x4e, 0x38, 0x1c, 0x82, 0x0e, 0x21, 0x4e, 0x46, 0xd2, 0xb9, 0xae, 0xd4, 0xc1, - 0xcf, 0xe5, 0xd9, 0x97, 0x57, 0x9e, 0xf1, 0x89, 0xfd, 0xdf, 0x14, 0x2f, 0x60, 0x7d, 0x54, 0xfd, - 0x8f, 0x7a, 0x64, 0x2c, 0xbd, 0x04, 0xb1, 0x3b, 0xea, 0x11, 0xa1, 0x64, 0x3b, 0x8a, 0xf3, 0xdf, - 0x1e, 0x1f, 0xe6, 0x33, 0x13, 0x36, 0x97, 0x26, 0x6d, 0xbe, 0x96, 0x01, 0xf1, 0x85, 0x3d, 0x1f, - 0x2e, 0xac, 0xfe, 0xf6, 0x35, 0x74, 0xe1, 0xd0, 0xdf, 0xf1, 0x22, 0x7f, 0xd7, 0xb0, 0x39, 0xaa, - 0x36, 0x5c, 0xea, 0x50, 0xd6, 0xbe, 0x1d, 0x7b, 0xfc, 0x13, 0x56, 0x1c, 0x51, 0x12, 0x4a, 0xf2, - 0xb3, 0x4a, 0x82, 0x1f, 0x05, 0xb3, 0xcf, 0xf9, 0x7d, 0x27, 0xc3, 0xb6, 0xef, 0x37, 0x5c, 0x26, - 0x3c, 0xff, 0xf5, 0x2d, 0xdb, 0x84, 0xf7, 0x70, 0xe7, 0x8b, 0xfc, 0xa7, 0x21, 0xd5, 0xb0, 0x6c, - 0x53, 0xf8, 0x2e, 0x66, 0x60, 0xcd, 0x3f, 0xfa, 0xca, 0x8a, 0x1f, 0xe2, 0x90, 0x3c, 0x25, 0x8c, - 0xb5, 0x4d, 0x82, 0xea, 0xb0, 0x2e, 0x42, 0x68, 0xb8, 0xfe, 0xb8, 0x10, 0xfb, 0xe3, 0xbc, 0x8d, - 0x13, 0x71, 0xaf, 0x4a, 0x38, 0xed, 0x4c, 0xe4, 0x5f, 0x83, 0x6c, 0x08, 0xf3, 0x97, 0x09, 0xfd, - 0xc5, 0xe7, 0x68, 0xfe, 0x64, 0x55, 0xc2, 0x19, 0x67, 0xf2, 0x1f, 0xf2, 0x2f, 0x6c, 0x30, 0xcb, - 0xb4, 0x8d, 0x51, 0x22, 0x02, 0x79, 0xcb, 0x1c, 0xf8, 0xd3, 0x3c, 0xe0, 0x54, 0xa8, 0xab, 0x12, - 0x5e, 0x67, 0x53, 0x39, 0xbf, 0x84, 0x2d, 0xc6, 0xef, 0x6b, 0x0c, 0x15, 0x32, 0x63, 0x9c, 0xfa, - 0xcb, 0x22, 0xea, 0x64, 0x9e, 0xab, 0x12, 0x46, 0x6c, 0x36, 0xe5, 0xff, 0xc3, 0x77, 0x5c, 0xee, - 0xf8, 0x12, 0x03, 0xc9, 0x71, 0x0e, 0xff, 0x75, 0x11, 0x7c, 0x2a, 0xa7, 0x55, 0x09, 0x6f, 0xb2, - 0x39, 0xf1, 0xbd, 0x82, 0x9c, 0x90, 0x1e, 0x59, 0x20, 0xe4, 0x27, 0xf8, 0x86, 0xd2, 0x62, 0xf9, - 0xd3, 0xf1, 0xac, 0x4a, 0x78, 0x9b, 0xcd, 0x0f, 0xee, 0x09, 0xac, 0x39, 0x96, 0x6d, 0x06, 0xea, - 0x93, 0x9c, 0xbd, 0x37, 0xf7, 0x06, 0xc3, 0x94, 0x55, 0x25, 0x9c, 0x72, 0xc2, 0x23, 0xfa, 0x07, - 0xd2, 0x82, 0x22, 0x24, 0xae, 0x70, 0x4c, 0x61, 0x31, 0x26, 0x10, 0xb6, 0xe6, 0x44, 0xce, 0x47, - 0x71, 0x58, 0x66, 0x83, 0x7e, 0xe9, 0xbd, 0x0c, 0x09, 0x1e, 0x72, 0x86, 0x10, 0x64, 0x54, 0x8c, - 0x75, 0xdc, 0x34, 0xce, 0xb4, 0xba, 0xa6, 0x9f, 0x6b, 0x59, 0x09, 0x29, 0x90, 0x0f, 0x6a, 0xea, - 0x45, 0x43, 0x3d, 0x6e, 0xa9, 0x27, 0x06, 0x56, 0x9b, 0x0d, 0x5d, 0x6b, 0xaa, 0x59, 0x19, 0xe5, - 0x60, 0x4b, 0xf4, 0x35, 0xdd, 0x38, 0xd6, 0x35, 0x4d, 0x3d, 0x6e, 0xd5, 0x74, 0x2d, 0xbb, 0x84, - 0x7e, 0x80, 0x1d, 0xd1, 0x09, 0xcb, 0x46, 0xab, 0x76, 0xaa, 0xea, 0x67, 0xad, 0xec, 0x32, 0xfa, - 0x1e, 0x36, 0x45, 0x1b, 0xab, 0x7f, 0x9f, 0x04, 0x8d, 0x58, 0x84, 0x78, 0x8e, 0x6b, 0x2d, 0x35, - 0xe8, 0xc4, 0x8f, 0xf4, 0xfb, 0x47, 0x45, 0x7e, 0x78, 0x54, 0xe4, 0xcf, 0x8f, 0x8a, 0xfc, 0xf6, - 0x49, 0x91, 0x1e, 0x9e, 0x14, 0xe9, 0xe3, 0x93, 0x22, 0x5d, 0xfe, 0x61, 0x5a, 0xde, 0xf5, 0xa0, - 0x53, 0xee, 0xd2, 0x7e, 0xa5, 0x4b, 0xfb, 0xc4, 0xeb, 0x5c, 0x79, 0xe1, 0x83, 0xff, 0x9d, 0x9b, - 0xfd, 0x4a, 0x76, 0x12, 0xbc, 0xf3, 0xfb, 0x97, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa9, 0x65, 0xab, - 0xc1, 0x42, 0x07, 0x00, 0x00, + // 839 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4b, 0x6f, 0xe3, 0x54, + 0x14, 0xb6, 0xdb, 0x24, 0x6d, 0x4f, 0xfa, 0x48, 0x6f, 0x43, 0xc9, 0x44, 0xc5, 0x13, 0xcc, 0xab, + 0xca, 0x22, 0x41, 0x83, 0x60, 0x33, 0x6c, 0x48, 0x6b, 0xe1, 0x28, 0x1a, 0x3b, 0xdc, 0x64, 0xe8, + 0x68, 0x24, 0x64, 0xe5, 0x71, 0xc7, 0xb5, 0x26, 0xf1, 0x35, 0xbe, 0x76, 0xa4, 0xac, 0xd9, 0xb1, + 0x42, 0xe2, 0x4f, 0xf0, 0x53, 0x66, 0x39, 0x4b, 0x56, 0x08, 0xda, 0x3f, 0x82, 0x7c, 0x7d, 0x63, + 0x3b, 0xaf, 0x0a, 0xd4, 0x9d, 0xef, 0x39, 0xc7, 0xdf, 0xe3, 0xf8, 0x7e, 0x92, 0x41, 0x09, 0x88, + 0x3b, 0x26, 0xfe, 0xd4, 0x71, 0x83, 0xa6, 0xe7, 0x3b, 0xb3, 0xd9, 0x60, 0xd2, 0x0c, 0xe6, 0x1e, + 0x61, 0x0d, 0xcf, 0xa7, 0x01, 0x45, 0x28, 0xed, 0x37, 0x44, 0xbf, 0x5a, 0xb6, 0xa9, 0x4d, 0x79, + 0xbb, 0x19, 0x3d, 0xc5, 0x93, 0xd5, 0x8b, 0x0c, 0xd2, 0xc8, 0x9f, 0x7b, 0x01, 0x6d, 0xbe, 0x25, + 0x73, 0xb6, 0xa1, 0xcb, 0xf1, 0xb3, 0x2c, 0x6a, 0x1b, 0x4e, 0x31, 0x99, 0xd2, 0x80, 0xf4, 0x1c, + 0xdb, 0x25, 0xbe, 0xe6, 0xfb, 0xd4, 0x47, 0x08, 0x72, 0x23, 0x3a, 0x26, 0x15, 0xb9, 0x26, 0x5f, + 0xe6, 0x31, 0x7f, 0x46, 0x35, 0x28, 0x8e, 0x09, 0x1b, 0xf9, 0x8e, 0x17, 0x38, 0xd4, 0xad, 0xec, + 0xd4, 0xe4, 0xcb, 0x03, 0x9c, 0x2d, 0xa9, 0x75, 0x38, 0xea, 0x86, 0xc3, 0x0e, 0x99, 0x63, 0xf2, + 0x73, 0x48, 0x58, 0x80, 0x9e, 0xc0, 0xfe, 0xe8, 0x76, 0xe0, 0xb8, 0x96, 0x33, 0xe6, 0x50, 0x07, + 0x78, 0x8f, 0x9f, 0xdb, 0x63, 0xf5, 0x57, 0x19, 0x8e, 0x17, 0xc3, 0xcc, 0xa3, 0x2e, 0x23, 0xe8, + 0x39, 0xec, 0x79, 0xe1, 0xd0, 0x7a, 0x4b, 0xe6, 0x7c, 0xb8, 0xf8, 0xec, 0xa2, 0x91, 0xd9, 0x40, + 0xec, 0xab, 0xd1, 0x0d, 0x87, 0x13, 0x67, 0xd4, 0x21, 0xf3, 0x56, 0xee, 0xdd, 0x5f, 0x4f, 0x25, + 0x5c, 0xf0, 0x38, 0x08, 0x7a, 0x0e, 0x79, 0x12, 0x49, 0xe7, 0xba, 0x8a, 0xcf, 0x3e, 0x6b, 0xac, + 0x2f, 0xaf, 0xb1, 0xe6, 0x13, 0xc7, 0xef, 0xa8, 0xaf, 0xe0, 0x24, 0xaa, 0xfe, 0x48, 0x03, 0xb2, + 0x90, 0x5e, 0x87, 0xdc, 0x8c, 0x06, 0x44, 0x28, 0x39, 0xcf, 0xc2, 0xc5, 0xdb, 0xe3, 0xc3, 0x7c, + 0x66, 0xc9, 0xe6, 0xce, 0xb2, 0xcd, 0x5f, 0x64, 0x40, 0x9c, 0x70, 0x1c, 0x83, 0x0b, 0xab, 0x5f, + 0xfe, 0x17, 0x74, 0xe1, 0x30, 0xe6, 0x78, 0x94, 0xbf, 0x5b, 0x38, 0x8b, 0xaa, 0x5d, 0x9f, 0x7a, + 0x94, 0x0d, 0x26, 0x0b, 0x8f, 0xdf, 0xc0, 0xbe, 0x27, 0x4a, 0x42, 0x49, 0x75, 0x5d, 0x49, 0xf2, + 0x52, 0x32, 0xfb, 0x90, 0xdf, 0xdf, 0x65, 0x38, 0x8f, 0xfd, 0xa6, 0x64, 0xc2, 0xf3, 0xb7, 0xff, + 0x87, 0x4d, 0x78, 0x4f, 0x39, 0x1f, 0xe5, 0xff, 0x12, 0x4a, 0x51, 0xb5, 0x35, 0x0f, 0x08, 0x5b, + 0x98, 0x2f, 0x43, 0x7e, 0x36, 0x98, 0x84, 0xf1, 0x37, 0x38, 0xc4, 0xf1, 0x41, 0x75, 0xe1, 0x34, + 0x33, 0x29, 0x94, 0x5f, 0xc0, 0x01, 0x73, 0x6c, 0x77, 0x10, 0x84, 0xfe, 0x62, 0x3c, 0x2d, 0x3c, + 0x4e, 0xd9, 0x11, 0x14, 0xbb, 0x8e, 0x6b, 0x0b, 0x51, 0xea, 0x31, 0x1c, 0xc6, 0xc7, 0x98, 0x59, + 0xfd, 0xa7, 0x00, 0x7b, 0x2f, 0x08, 0x63, 0x03, 0x9b, 0xa0, 0x0e, 0x9c, 0x88, 0x78, 0x58, 0x7e, + 0x3c, 0x2e, 0xd6, 0xf8, 0xf1, 0x26, 0xc6, 0xa5, 0x20, 0xea, 0x12, 0x3e, 0xf2, 0x96, 0x92, 0x69, + 0x40, 0x29, 0x05, 0x8b, 0xc9, 0x84, 0x7e, 0xf5, 0x21, 0xb4, 0x78, 0x52, 0x97, 0xf0, 0xb1, 0xb7, + 0x9c, 0xdd, 0x1f, 0xe0, 0x34, 0xda, 0x88, 0x15, 0xdd, 0xd5, 0x44, 0xde, 0x2e, 0x07, 0xfc, 0x64, + 0x13, 0xe0, 0x4a, 0xdc, 0x74, 0x09, 0x9f, 0xb0, 0x95, 0x04, 0xbe, 0x86, 0x32, 0xe3, 0x37, 0x69, + 0x01, 0x2a, 0x64, 0xe6, 0x38, 0xea, 0xe7, 0xdb, 0x50, 0x97, 0x93, 0xa6, 0x4b, 0x18, 0xb1, 0xf5, + 0xfc, 0xfd, 0x04, 0x1f, 0x70, 0xb9, 0x8b, 0xeb, 0x95, 0x48, 0xce, 0x73, 0xf0, 0x2f, 0xb6, 0x81, + 0xaf, 0x24, 0x48, 0x97, 0xf0, 0x19, 0xdb, 0x10, 0xac, 0x37, 0x50, 0x11, 0xd2, 0x33, 0x04, 0x42, + 0x7e, 0x81, 0x33, 0xd4, 0xb7, 0xcb, 0x5f, 0x0d, 0x8e, 0x2e, 0xe1, 0x73, 0xb6, 0x39, 0x52, 0xd7, + 0x70, 0xe8, 0x39, 0xae, 0x9d, 0xa8, 0xdf, 0xe3, 0xd8, 0x4f, 0x37, 0x7e, 0xc1, 0xf4, 0x96, 0xe9, + 0x12, 0x2e, 0x7a, 0xe9, 0x11, 0x7d, 0x0f, 0x47, 0x02, 0x45, 0x48, 0xdc, 0xe7, 0x30, 0xb5, 0xed, + 0x30, 0x89, 0xb0, 0x43, 0x2f, 0x73, 0x46, 0x7d, 0xe0, 0xbb, 0xb6, 0x86, 0x51, 0x7a, 0x12, 0x51, + 0x07, 0x1c, 0xed, 0xd3, 0x6d, 0x86, 0xb3, 0xa1, 0xd4, 0x25, 0x5c, 0x62, 0xab, 0x41, 0xbd, 0x81, + 0xb3, 0x25, 0x54, 0x21, 0x12, 0xb6, 0xa7, 0x6d, 0x2d, 0xc1, 0xba, 0x84, 0x4f, 0xd9, 0x6a, 0xb1, + 0x95, 0x87, 0x5d, 0x16, 0x4e, 0xeb, 0x7f, 0xc8, 0x50, 0xe0, 0x99, 0x64, 0x08, 0xc1, 0xb1, 0x86, + 0xb1, 0x89, 0x7b, 0xd6, 0x4b, 0xa3, 0x63, 0x98, 0x37, 0x46, 0x49, 0x42, 0x0a, 0x54, 0x93, 0x9a, + 0xf6, 0xaa, 0xab, 0x5d, 0xf5, 0xb5, 0x6b, 0x0b, 0x6b, 0xbd, 0xae, 0x69, 0xf4, 0xb4, 0x92, 0x8c, + 0x2a, 0x50, 0x16, 0x7d, 0xc3, 0xb4, 0xae, 0x4c, 0xc3, 0xd0, 0xae, 0xfa, 0x6d, 0xd3, 0x28, 0xed, + 0xa0, 0x8f, 0xe0, 0x89, 0xe8, 0xa4, 0x65, 0xab, 0xdf, 0x7e, 0xa1, 0x99, 0x2f, 0xfb, 0xa5, 0x5d, + 0xf4, 0x21, 0x9c, 0x89, 0x36, 0xd6, 0xbe, 0xbb, 0x4e, 0x1a, 0xb9, 0x0c, 0xe2, 0x0d, 0x6e, 0xf7, + 0xb5, 0xa4, 0x93, 0x6f, 0x99, 0xef, 0xee, 0x14, 0xf9, 0xfd, 0x9d, 0x22, 0xff, 0x7d, 0xa7, 0xc8, + 0xbf, 0xdd, 0x2b, 0xd2, 0xfb, 0x7b, 0x45, 0xfa, 0xf3, 0x5e, 0x91, 0x5e, 0x7f, 0x6d, 0x3b, 0xc1, + 0x6d, 0x38, 0x6c, 0x8c, 0xe8, 0xb4, 0x39, 0xa2, 0x53, 0x12, 0x0c, 0xdf, 0x04, 0xe9, 0x43, 0xfc, + 0xc3, 0xb0, 0xfe, 0xbb, 0x31, 0x2c, 0xf0, 0xce, 0x57, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xf7, + 0xec, 0xc6, 0xc0, 0x8b, 0x08, 0x00, 0x00, } func (m *RemoteSignerError) Marshal() (dAtA []byte, err error) { @@ -1013,6 +1144,78 @@ func (m *SignedProposalResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *SignBytesRequest) 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 *SignBytesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignBytesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignBytesResponse) 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 *SignBytesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignBytesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Error != nil { + { + size, err := m.Error.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintTypes(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *PingRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1259,6 +1462,48 @@ func (m *Message_PingResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { } return len(dAtA) - i, nil } +func (m *Message_SignBytesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Message_SignBytesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.SignBytesRequest != nil { + { + size, err := m.SignBytesRequest.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + return len(dAtA) - i, nil +} +func (m *Message_SignBytesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Message_SignBytesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.SignBytesResponse != nil { + { + size, err := m.SignBytesResponse.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + return len(dAtA) - i, nil +} func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -1378,6 +1623,36 @@ func (m *SignedProposalResponse) Size() (n int) { return n } +func (m *SignBytesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Value) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + +func (m *SignBytesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if m.Error != nil { + l = m.Error.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func (m *PingRequest) Size() (n int) { if m == nil { return 0 @@ -1504,6 +1779,30 @@ func (m *Message_PingResponse) Size() (n int) { } return n } +func (m *Message_SignBytesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SignBytesRequest != nil { + l = m.SignBytesRequest.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} +func (m *Message_SignBytesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SignBytesResponse != nil { + l = m.SignBytesResponse.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 @@ -2287,6 +2586,210 @@ func (m *SignedProposalResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *SignBytesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignBytesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignBytesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) + if m.Value == nil { + m.Value = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignBytesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignBytesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignBytesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Error", 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.Error == nil { + m.Error = &RemoteSignerError{} + } + if err := m.Error.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *PingRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2696,6 +3199,76 @@ func (m *Message) Unmarshal(dAtA []byte) error { } m.Sum = &Message_PingResponse{v} iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignBytesRequest", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SignBytesRequest{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &Message_SignBytesRequest{v} + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SignBytesResponse", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SignBytesResponse{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Sum = &Message_SignBytesResponse{v} + 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 4d9a25238fe..7411ec323b0 100644 --- a/proto/tendermint/privval/types.proto +++ b/proto/tendermint/privval/types.proto @@ -56,6 +56,17 @@ message SignedProposalResponse { RemoteSignerError error = 2; } +// SignBytesRequest is a request to sign arbitrary bytes +message SignBytesRequest { + bytes value = 1; +} + +// SignBytesResponse is a response containing a signature or an error +message SignBytesResponse { + bytes signature = 1; + RemoteSignerError error = 2; +} + // PingRequest is a request to confirm that the connection is alive. message PingRequest {} @@ -72,5 +83,7 @@ message Message { SignedProposalResponse signed_proposal_response = 6; PingRequest ping_request = 7; PingResponse ping_response = 8; + SignBytesRequest sign_bytes_request = 9; + SignBytesResponse sign_bytes_response = 10; } } diff --git a/proto/tendermint/state/types.pb.go b/proto/tendermint/state/types.pb.go index 56273c8af0b..a2e0bbdfb2a 100644 --- a/proto/tendermint/state/types.pb.go +++ b/proto/tendermint/state/types.pb.go @@ -12,6 +12,7 @@ import ( proto "github.com/cosmos/gogoproto/proto" _ "github.com/cosmos/gogoproto/types" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + _ "github.com/golang/protobuf/ptypes/duration" io "io" math "math" math_bits "math/bits" @@ -445,6 +446,9 @@ type State struct { LastResultsHash []byte `protobuf:"bytes,12,opt,name=last_results_hash,json=lastResultsHash,proto3" json:"last_results_hash,omitempty"` // the latest AppHash we've received from calling abci.Commit() AppHash []byte `protobuf:"bytes,13,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` + // delay between the time when this block is committed and the next height is started. + // previously `timeout_commit` in config.toml + NextBlockDelay time.Duration `protobuf:"bytes,15,opt,name=next_block_delay,json=nextBlockDelay,proto3,stdduration" json:"next_block_delay"` } func (m *State) Reset() { *m = State{} } @@ -578,6 +582,13 @@ func (m *State) GetAppHash() []byte { return nil } +func (m *State) GetNextBlockDelay() time.Duration { + if m != nil { + return m.NextBlockDelay + } + return 0 +} + func init() { proto.RegisterType((*LegacyABCIResponses)(nil), "tendermint.state.LegacyABCIResponses") proto.RegisterType((*ResponseBeginBlock)(nil), "tendermint.state.ResponseBeginBlock") @@ -592,68 +603,71 @@ func init() { func init() { proto.RegisterFile("tendermint/state/types.proto", fileDescriptor_ccfacf933f22bf93) } var fileDescriptor_ccfacf933f22bf93 = []byte{ - // 964 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xcf, 0x6f, 0x1b, 0x45, - 0x14, 0xce, 0xd6, 0xad, 0x7f, 0x3c, 0xc7, 0x89, 0x33, 0x26, 0xad, 0xeb, 0xb6, 0xb6, 0xb1, 0xda, - 0x2a, 0x42, 0x68, 0x2d, 0xb5, 0x27, 0x2e, 0x45, 0xb1, 0x13, 0x88, 0xa5, 0x80, 0xd0, 0x26, 0x54, - 0x2a, 0x87, 0xae, 0xc6, 0xbb, 0x63, 0x7b, 0x84, 0xbd, 0xbb, 0xda, 0x19, 0x1b, 0x87, 0x3b, 0x37, - 0x0e, 0xbd, 0xf2, 0x1f, 0xf5, 0xd8, 0x23, 0x17, 0x02, 0x38, 0x12, 0x07, 0xfe, 0x0a, 0x34, 0x3f, - 0xf6, 0x87, 0xbd, 0x41, 0x04, 0xe5, 0xe6, 0x9d, 0xf7, 0xbd, 0xef, 0x7d, 0xef, 0x9b, 0x79, 0x33, - 0x86, 0xc7, 0x9c, 0x78, 0x2e, 0x09, 0x67, 0xd4, 0xe3, 0x5d, 0xc6, 0x31, 0x27, 0x5d, 0x7e, 0x11, - 0x10, 0x66, 0x06, 0xa1, 0xcf, 0x7d, 0x54, 0x4d, 0xa2, 0xa6, 0x8c, 0x36, 0x3e, 0x1a, 0xfb, 0x63, - 0x5f, 0x06, 0xbb, 0xe2, 0x97, 0xc2, 0x35, 0x5a, 0x63, 0xdf, 0x1f, 0x4f, 0x49, 0x57, 0x7e, 0x0d, - 0xe7, 0xa3, 0x2e, 0xa7, 0x33, 0xc2, 0x38, 0x9e, 0x05, 0x1a, 0xf0, 0x28, 0x55, 0x06, 0x0f, 0x1d, - 0x9a, 0xae, 0xd2, 0x78, 0x92, 0x0a, 0xca, 0xf5, 0x6e, 0x80, 0x43, 0x3c, 0x8b, 0xc2, 0x8f, 0x33, - 0xe1, 0x74, 0x72, 0x3b, 0x13, 0x5d, 0xe0, 0x29, 0x75, 0x31, 0xf7, 0x43, 0x8d, 0x68, 0xa6, 0x10, - 0x0b, 0x12, 0x32, 0xea, 0x7b, 0x69, 0x86, 0xce, 0x6f, 0x06, 0xd4, 0x4e, 0xc9, 0x18, 0x3b, 0x17, - 0x87, 0xbd, 0xfe, 0xc0, 0x22, 0x2c, 0xf0, 0x3d, 0x46, 0x18, 0x7a, 0x05, 0x65, 0x97, 0x4c, 0xe9, - 0x82, 0x84, 0x36, 0x5f, 0xb2, 0xba, 0xd1, 0xce, 0x1d, 0x94, 0x5f, 0x3c, 0x31, 0x53, 0x96, 0x88, - 0x4e, 0xcc, 0xe3, 0x25, 0x71, 0xce, 0x97, 0x16, 0x61, 0xf3, 0x29, 0xb7, 0x40, 0x67, 0x9c, 0x2f, - 0x19, 0xfa, 0x1c, 0x4a, 0xc4, 0x73, 0xed, 0xe1, 0xd4, 0x77, 0xbe, 0xaf, 0xdf, 0x69, 0x1b, 0x07, - 0xe5, 0x17, 0x1d, 0x73, 0xd3, 0x50, 0x33, 0xaa, 0x77, 0xec, 0xb9, 0x3d, 0x81, 0xb4, 0x8a, 0x44, - 0xff, 0x42, 0xc7, 0x50, 0x1e, 0x92, 0x31, 0xf5, 0x34, 0x45, 0x4e, 0x52, 0x3c, 0xfd, 0x77, 0x8a, - 0x9e, 0x00, 0x2b, 0x12, 0x18, 0xc6, 0xbf, 0x3b, 0x6f, 0x01, 0x65, 0x11, 0xe8, 0x04, 0xf2, 0x64, - 0x41, 0x3c, 0x1e, 0x35, 0x76, 0x3f, 0xdb, 0x98, 0x08, 0xf7, 0xea, 0xef, 0x2f, 0x5b, 0x5b, 0x7f, - 0x5f, 0xb6, 0xaa, 0x0a, 0xfd, 0xa9, 0x3f, 0xa3, 0x9c, 0xcc, 0x02, 0x7e, 0x61, 0xe9, 0xfc, 0xce, - 0xcf, 0x77, 0xa0, 0xba, 0xd9, 0x05, 0x3a, 0x83, 0xbd, 0x78, 0x1f, 0xec, 0x79, 0xe0, 0x62, 0x4e, - 0xa2, 0x4a, 0xed, 0x4c, 0xa5, 0xd7, 0x11, 0xf2, 0x5b, 0x09, 0xec, 0xdd, 0x15, 0x35, 0xad, 0xea, - 0x62, 0x7d, 0x99, 0xa1, 0x37, 0xf0, 0xc0, 0x11, 0x55, 0x3c, 0x36, 0x67, 0xb6, 0x3c, 0x23, 0x31, - 0xb5, 0xf2, 0xf7, 0xe3, 0x34, 0xb5, 0xda, 0xe3, 0x7e, 0x94, 0xf0, 0x8d, 0x3c, 0x53, 0xd6, 0xbe, - 0xb3, 0xb6, 0x10, 0x51, 0x27, 0x76, 0xe4, 0x6e, 0x69, 0xc7, 0x4f, 0x06, 0xec, 0xc4, 0x0d, 0xb1, - 0x81, 0x37, 0xf2, 0x51, 0x1f, 0x2a, 0x89, 0x19, 0x8c, 0xf0, 0xba, 0x21, 0xd5, 0x36, 0xb3, 0x6a, - 0xe3, 0xc4, 0x33, 0xc2, 0xad, 0xed, 0x45, 0xea, 0x0b, 0x99, 0x50, 0x9b, 0x62, 0xc6, 0xed, 0x09, - 0xa1, 0xe3, 0x09, 0xb7, 0x9d, 0x09, 0xf6, 0xc6, 0xc4, 0x95, 0x8d, 0xe7, 0xac, 0x3d, 0x11, 0x3a, - 0x91, 0x91, 0xbe, 0x0a, 0x74, 0x7e, 0x31, 0xa0, 0xb6, 0xd1, 0xbc, 0x14, 0x63, 0x41, 0x75, 0xc3, - 0x44, 0xa6, 0xf5, 0xfc, 0xb7, 0x7b, 0x7a, 0x67, 0x76, 0xd7, 0x3d, 0x64, 0xff, 0x5b, 0xdb, 0x5f, - 0x06, 0xec, 0xad, 0x0d, 0x9b, 0x54, 0xf6, 0x06, 0xf6, 0xa7, 0x72, 0x0e, 0x6d, 0x61, 0xb8, 0x1d, - 0x46, 0x41, 0x2d, 0xef, 0x59, 0xf6, 0xe4, 0x5f, 0x33, 0xb6, 0x56, 0x4d, 0x71, 0x1c, 0x0e, 0x1d, - 0x9a, 0xcc, 0xf2, 0x7d, 0xc8, 0x2b, 0x6d, 0x5a, 0x93, 0xfe, 0x42, 0x6f, 0xe1, 0x41, 0x54, 0xc6, - 0x1e, 0x51, 0x0f, 0x4f, 0xe9, 0x8f, 0x64, 0x6d, 0xdc, 0x9e, 0x67, 0xce, 0x41, 0x44, 0xfa, 0x85, - 0x86, 0xab, 0x81, 0xdb, 0x0f, 0xaf, 0x5b, 0xee, 0x4c, 0xa0, 0xf0, 0x5a, 0x5d, 0x39, 0xe8, 0x10, - 0x4a, 0xb1, 0x6d, 0xba, 0xa3, 0xb5, 0xcb, 0x44, 0x5f, 0x4d, 0x89, 0xe5, 0xda, 0xec, 0x24, 0x0b, - 0x35, 0xa0, 0xc8, 0xfc, 0x11, 0xff, 0x01, 0x87, 0x44, 0xf6, 0x51, 0xb2, 0xe2, 0xef, 0xce, 0x9f, - 0x79, 0xb8, 0x77, 0x26, 0x4c, 0x41, 0x9f, 0x41, 0x41, 0x73, 0xe9, 0x32, 0x0f, 0xb3, 0xc6, 0x69, - 0x51, 0xba, 0x44, 0x84, 0x47, 0xcf, 0xa1, 0xe8, 0x4c, 0x30, 0xf5, 0x6c, 0xaa, 0x36, 0xaf, 0xd4, - 0x2b, 0xaf, 0x2e, 0x5b, 0x85, 0xbe, 0x58, 0x1b, 0x1c, 0x59, 0x05, 0x19, 0x1c, 0xb8, 0xe8, 0x19, - 0xec, 0x50, 0x8f, 0x72, 0x8a, 0xa7, 0x7a, 0xcb, 0xeb, 0x3b, 0xd2, 0xd6, 0x8a, 0x5e, 0x55, 0xbb, - 0x8d, 0x3e, 0x01, 0xb9, 0xf7, 0xca, 0xd0, 0x08, 0x99, 0x93, 0xc8, 0x5d, 0x11, 0x90, 0x1e, 0x69, - 0xac, 0x05, 0x95, 0x14, 0x96, 0xba, 0xf5, 0xbb, 0x59, 0xed, 0xea, 0x4c, 0xca, 0xac, 0xc1, 0x51, - 0xaf, 0x26, 0xb4, 0xaf, 0x2e, 0x5b, 0xe5, 0xd3, 0x88, 0x6a, 0x70, 0x64, 0x95, 0x63, 0xde, 0x81, - 0x8b, 0x4e, 0x61, 0x37, 0xc5, 0x29, 0xde, 0xa4, 0xfa, 0x3d, 0xc9, 0xda, 0x30, 0xd5, 0x83, 0x65, - 0x46, 0x0f, 0x96, 0x79, 0x1e, 0x3d, 0x58, 0xbd, 0xa2, 0xa0, 0x7d, 0xf7, 0x7b, 0xcb, 0xb0, 0x2a, - 0x31, 0x97, 0x88, 0xa2, 0x2f, 0x61, 0xd7, 0x23, 0x4b, 0x6e, 0xc7, 0x53, 0xc9, 0xea, 0xf9, 0x1b, - 0xcd, 0xf1, 0x8e, 0x48, 0x4b, 0xae, 0x04, 0xf4, 0x0a, 0x20, 0xc5, 0x51, 0xb8, 0x11, 0x47, 0x2a, - 0x43, 0x08, 0x91, 0x6d, 0xa5, 0x48, 0x8a, 0x37, 0x13, 0x22, 0xd2, 0x52, 0x42, 0xfa, 0xd0, 0x4c, - 0x8f, 0x6d, 0xc2, 0x17, 0x4f, 0x70, 0x49, 0x6e, 0xd6, 0xa3, 0x64, 0x82, 0x93, 0x6c, 0x3d, 0xcb, - 0xd7, 0xde, 0x27, 0x70, 0xcb, 0xfb, 0xe4, 0x6b, 0x78, 0xba, 0x76, 0x9f, 0x6c, 0xf0, 0xc7, 0xf2, - 0xca, 0x52, 0x5e, 0x3b, 0x75, 0xc1, 0xac, 0x13, 0x45, 0x1a, 0xa3, 0x83, 0x18, 0xca, 0x57, 0x9a, - 0xd9, 0x13, 0xcc, 0x26, 0xf5, 0xed, 0xb6, 0x71, 0xb0, 0xad, 0x0e, 0xa2, 0x7a, 0xbd, 0xd9, 0x09, - 0x66, 0x13, 0xf4, 0x10, 0x8a, 0x38, 0x08, 0x14, 0xa4, 0x22, 0x21, 0x05, 0x1c, 0x04, 0x22, 0xd4, - 0xfb, 0xea, 0xfd, 0xaa, 0x69, 0x7c, 0x58, 0x35, 0x8d, 0x3f, 0x56, 0x4d, 0xe3, 0xdd, 0x55, 0x73, - 0xeb, 0xc3, 0x55, 0x73, 0xeb, 0xd7, 0xab, 0xe6, 0xd6, 0x77, 0x2f, 0xc7, 0x94, 0x4f, 0xe6, 0x43, - 0xd3, 0xf1, 0x67, 0x5d, 0xc7, 0x9f, 0x11, 0x3e, 0x1c, 0xf1, 0xe4, 0x87, 0xfa, 0xbf, 0xb4, 0xf9, - 0x4f, 0x6b, 0x98, 0x97, 0xeb, 0x2f, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x11, 0x85, 0xd5, 0x91, - 0x84, 0x09, 0x00, 0x00, + // 1009 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4d, 0x6f, 0xdb, 0x46, + 0x10, 0x35, 0xe3, 0xc4, 0x92, 0x47, 0xfe, 0x90, 0xd7, 0x75, 0xc2, 0x28, 0x89, 0xa4, 0x0a, 0x49, + 0x60, 0x14, 0x05, 0x05, 0x24, 0xa7, 0x5e, 0x52, 0x58, 0x92, 0x5b, 0x0b, 0x70, 0x8a, 0x82, 0x76, + 0x03, 0xa4, 0x87, 0x10, 0x2b, 0x72, 0x25, 0x2d, 0x4a, 0x91, 0x04, 0x77, 0xa5, 0xca, 0xbd, 0xf7, + 0xd6, 0x43, 0x8e, 0xed, 0xbf, 0xe8, 0xcf, 0xc8, 0x31, 0xc7, 0x5e, 0xea, 0x16, 0x32, 0xd0, 0x43, + 0x7f, 0x45, 0xb1, 0x1f, 0xfc, 0x90, 0xe8, 0xa2, 0x2e, 0x72, 0x23, 0xf7, 0xcd, 0xbc, 0x7d, 0xf3, + 0x76, 0x66, 0x49, 0x78, 0xc8, 0x49, 0xe0, 0x91, 0x78, 0x42, 0x03, 0xde, 0x66, 0x1c, 0x73, 0xd2, + 0xe6, 0x17, 0x11, 0x61, 0x56, 0x14, 0x87, 0x3c, 0x44, 0xd5, 0x0c, 0xb5, 0x24, 0x5a, 0xfb, 0x68, + 0x14, 0x8e, 0x42, 0x09, 0xb6, 0xc5, 0x93, 0x8a, 0xab, 0xd5, 0x47, 0x61, 0x38, 0xf2, 0x49, 0x5b, + 0xbe, 0x0d, 0xa6, 0xc3, 0xb6, 0x37, 0x8d, 0x31, 0xa7, 0x61, 0xa0, 0xf1, 0xc6, 0x2a, 0xce, 0xe9, + 0x84, 0x30, 0x8e, 0x27, 0x91, 0x0e, 0x78, 0x90, 0x93, 0x81, 0x07, 0x2e, 0xcd, 0xab, 0xa8, 0x3d, + 0xca, 0x81, 0x72, 0xbd, 0x1d, 0xe1, 0x18, 0x4f, 0x12, 0xf8, 0x61, 0x01, 0xce, 0x27, 0x37, 0x0b, + 0xe8, 0x0c, 0xfb, 0xd4, 0xc3, 0x3c, 0x8c, 0x13, 0xf1, 0xb9, 0x88, 0x19, 0x89, 0x19, 0x0d, 0x83, + 0x3c, 0x43, 0xeb, 0x77, 0x03, 0xf6, 0x4f, 0xc9, 0x08, 0xbb, 0x17, 0x47, 0x9d, 0x6e, 0xdf, 0x26, + 0x2c, 0x0a, 0x03, 0x46, 0x18, 0x7a, 0x01, 0x15, 0x8f, 0xf8, 0x74, 0x46, 0x62, 0x87, 0xcf, 0x99, + 0x69, 0x34, 0xd7, 0x0f, 0x2b, 0xcf, 0x1e, 0x59, 0x39, 0xcb, 0x44, 0x25, 0xd6, 0xf1, 0x9c, 0xb8, + 0xe7, 0x73, 0x9b, 0xb0, 0xa9, 0xcf, 0x6d, 0xd0, 0x19, 0xe7, 0x73, 0x86, 0x3e, 0x87, 0x4d, 0x12, + 0x78, 0xce, 0xc0, 0x0f, 0xdd, 0xef, 0xcc, 0x5b, 0x4d, 0xe3, 0xb0, 0xf2, 0xac, 0x65, 0xad, 0x1a, + 0x6e, 0x25, 0xfb, 0x1d, 0x07, 0x5e, 0x47, 0x44, 0xda, 0x65, 0xa2, 0x9f, 0xd0, 0x31, 0x54, 0x06, + 0x64, 0x44, 0x03, 0x4d, 0xb1, 0x2e, 0x29, 0x1e, 0xff, 0x3b, 0x45, 0x47, 0x04, 0x2b, 0x12, 0x18, + 0xa4, 0xcf, 0xad, 0x37, 0x80, 0x8a, 0x11, 0xe8, 0x04, 0x36, 0xc8, 0x8c, 0x04, 0x3c, 0x29, 0xec, + 0x6e, 0xb1, 0x30, 0x01, 0x77, 0xcc, 0x77, 0x97, 0x8d, 0xb5, 0xbf, 0x2f, 0x1b, 0x55, 0x15, 0xfd, + 0x69, 0x38, 0xa1, 0x9c, 0x4c, 0x22, 0x7e, 0x61, 0xeb, 0xfc, 0xd6, 0x4f, 0xb7, 0xa0, 0xba, 0x5a, + 0x05, 0x3a, 0x83, 0xbd, 0xf4, 0x1c, 0x9c, 0x69, 0xe4, 0x61, 0x4e, 0x92, 0x9d, 0x9a, 0x85, 0x9d, + 0x5e, 0x25, 0x91, 0xdf, 0xc8, 0xc0, 0xce, 0x6d, 0xb1, 0xa7, 0x5d, 0x9d, 0x2d, 0x2f, 0x33, 0xf4, + 0x1a, 0xee, 0xb9, 0x62, 0x97, 0x80, 0x4d, 0x99, 0x23, 0x7b, 0x24, 0xa5, 0x56, 0xfe, 0x7e, 0x9c, + 0xa7, 0x56, 0x67, 0xdc, 0x4d, 0x12, 0xbe, 0x96, 0x3d, 0x65, 0x1f, 0xb8, 0x4b, 0x0b, 0x09, 0x75, + 0x66, 0xc7, 0xfa, 0x07, 0xda, 0xf1, 0xa3, 0x01, 0x3b, 0x69, 0x41, 0xac, 0x1f, 0x0c, 0x43, 0xd4, + 0x85, 0xed, 0xcc, 0x0c, 0x46, 0xb8, 0x69, 0x48, 0xb5, 0xf5, 0xa2, 0xda, 0x34, 0xf1, 0x8c, 0x70, + 0x7b, 0x6b, 0x96, 0x7b, 0x43, 0x16, 0xec, 0xfb, 0x98, 0x71, 0x67, 0x4c, 0xe8, 0x68, 0xcc, 0x1d, + 0x77, 0x8c, 0x83, 0x11, 0xf1, 0x64, 0xe1, 0xeb, 0xf6, 0x9e, 0x80, 0x4e, 0x24, 0xd2, 0x55, 0x40, + 0xeb, 0x17, 0x03, 0xf6, 0x57, 0x8a, 0x97, 0x62, 0x6c, 0xa8, 0xae, 0x98, 0xc8, 0xb4, 0x9e, 0xff, + 0x76, 0x4f, 0x9f, 0xcc, 0xee, 0xb2, 0x87, 0xec, 0x7f, 0x6b, 0xfb, 0xcb, 0x80, 0xbd, 0xa5, 0x61, + 0x93, 0xca, 0x5e, 0xc3, 0x81, 0x2f, 0xe7, 0xd0, 0x11, 0x86, 0x3b, 0x71, 0x02, 0x6a, 0x79, 0x4f, + 0x8a, 0x9d, 0x7f, 0xcd, 0xd8, 0xda, 0xfb, 0x8a, 0xe3, 0x68, 0xe0, 0xd2, 0x6c, 0x96, 0xef, 0xc2, + 0x86, 0xd2, 0xa6, 0x35, 0xe9, 0x37, 0xf4, 0x06, 0xee, 0x25, 0xdb, 0x38, 0x43, 0x1a, 0x60, 0x9f, + 0xfe, 0x40, 0x96, 0xc6, 0xed, 0x69, 0xa1, 0x0f, 0x12, 0xd2, 0x2f, 0x74, 0xb8, 0x1a, 0xb8, 0x83, + 0xf8, 0xba, 0xe5, 0xd6, 0x18, 0x4a, 0xaf, 0xd4, 0x95, 0x83, 0x8e, 0x60, 0x33, 0xb5, 0x4d, 0x57, + 0xb4, 0x74, 0x99, 0xe8, 0xab, 0x29, 0xb3, 0x5c, 0x9b, 0x9d, 0x65, 0xa1, 0x1a, 0x94, 0x59, 0x38, + 0xe4, 0xdf, 0xe3, 0x98, 0xc8, 0x3a, 0x36, 0xed, 0xf4, 0xbd, 0xf5, 0x6b, 0x09, 0xee, 0x9c, 0x09, + 0x53, 0xd0, 0x67, 0x50, 0xd2, 0x5c, 0x7a, 0x9b, 0xfb, 0x45, 0xe3, 0xb4, 0x28, 0xbd, 0x45, 0x12, + 0x8f, 0x9e, 0x42, 0xd9, 0x1d, 0x63, 0x1a, 0x38, 0x54, 0x1d, 0xde, 0x66, 0xa7, 0xb2, 0xb8, 0x6c, + 0x94, 0xba, 0x62, 0xad, 0xdf, 0xb3, 0x4b, 0x12, 0xec, 0x7b, 0xe8, 0x09, 0xec, 0xd0, 0x80, 0x72, + 0x8a, 0x7d, 0x7d, 0xe4, 0xe6, 0x8e, 0xb4, 0x75, 0x5b, 0xaf, 0xaa, 0xd3, 0x46, 0x9f, 0x80, 0x3c, + 0x7b, 0x65, 0x68, 0x12, 0xb9, 0x2e, 0x23, 0x77, 0x05, 0x20, 0x3d, 0xd2, 0xb1, 0x36, 0x6c, 0xe7, + 0x62, 0xa9, 0x67, 0xde, 0x2e, 0x6a, 0x57, 0x3d, 0x29, 0xb3, 0xfa, 0xbd, 0xce, 0xbe, 0xd0, 0xbe, + 0xb8, 0x6c, 0x54, 0x4e, 0x13, 0xaa, 0x7e, 0xcf, 0xae, 0xa4, 0xbc, 0x7d, 0x0f, 0x9d, 0xc2, 0x6e, + 0x8e, 0x53, 0x7c, 0x93, 0xcc, 0x3b, 0x92, 0xb5, 0x66, 0xa9, 0x0f, 0x96, 0x95, 0x7c, 0xb0, 0xac, + 0xf3, 0xe4, 0x83, 0xd5, 0x29, 0x0b, 0xda, 0xb7, 0x7f, 0x34, 0x0c, 0x7b, 0x3b, 0xe5, 0x12, 0x28, + 0xfa, 0x12, 0x76, 0x03, 0x32, 0xe7, 0x4e, 0x3a, 0x95, 0xcc, 0xdc, 0xb8, 0xd1, 0x1c, 0xef, 0x88, + 0xb4, 0xec, 0x4a, 0x40, 0x2f, 0x00, 0x72, 0x1c, 0xa5, 0x1b, 0x71, 0xe4, 0x32, 0x84, 0x10, 0x59, + 0x56, 0x8e, 0xa4, 0x7c, 0x33, 0x21, 0x22, 0x2d, 0x27, 0xa4, 0x0b, 0xf5, 0xfc, 0xd8, 0x66, 0x7c, + 0xe9, 0x04, 0x6f, 0xca, 0xc3, 0x7a, 0x90, 0x4d, 0x70, 0x96, 0xad, 0x67, 0xf9, 0xda, 0xfb, 0x04, + 0x3e, 0xf0, 0x3e, 0xf9, 0x0a, 0x1e, 0x2f, 0xdd, 0x27, 0x2b, 0xfc, 0xa9, 0xbc, 0x8a, 0x94, 0xd7, + 0xcc, 0x5d, 0x30, 0xcb, 0x44, 0x89, 0xc6, 0xa4, 0x11, 0x63, 0xf9, 0x95, 0x66, 0xce, 0x18, 0xb3, + 0xb1, 0xb9, 0xd5, 0x34, 0x0e, 0xb7, 0x54, 0x23, 0xaa, 0xaf, 0x37, 0x3b, 0xc1, 0x6c, 0x8c, 0xee, + 0x43, 0x19, 0x47, 0x91, 0x0a, 0xd9, 0x96, 0x21, 0x25, 0x1c, 0x45, 0x12, 0x7a, 0x09, 0x55, 0xd9, + 0x01, 0xaa, 0x9f, 0x3c, 0xe2, 0xe3, 0x0b, 0x73, 0x57, 0xb7, 0xe9, 0x6a, 0x43, 0xf5, 0xf4, 0x1f, + 0x92, 0xea, 0xa7, 0x9f, 0x45, 0x3f, 0xc9, 0x3e, 0x90, 0xfd, 0xd4, 0x13, 0xa9, 0x9d, 0x97, 0xef, + 0x16, 0x75, 0xe3, 0xfd, 0xa2, 0x6e, 0xfc, 0xb9, 0xa8, 0x1b, 0x6f, 0xaf, 0xea, 0x6b, 0xef, 0xaf, + 0xea, 0x6b, 0xbf, 0x5d, 0xd5, 0xd7, 0xbe, 0x7d, 0x3e, 0xa2, 0x7c, 0x3c, 0x1d, 0x58, 0x6e, 0x38, + 0x69, 0xbb, 0xe1, 0x84, 0xf0, 0xc1, 0x90, 0x67, 0x0f, 0xea, 0xf7, 0x6c, 0xf5, 0xc7, 0x6e, 0xb0, + 0x21, 0xd7, 0x9f, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0x6b, 0x28, 0xc8, 0x81, 0xf3, 0x09, 0x00, + 0x00, } func (m *LegacyABCIResponses) Marshal() (dAtA []byte, err error) { @@ -1007,6 +1021,14 @@ func (m *State) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + n9, err9 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.NextBlockDelay, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.NextBlockDelay):]) + if err9 != nil { + return 0, err9 + } + i -= n9 + i = encodeVarintTypes(dAtA, i, uint64(n9)) + i-- + dAtA[i] = 0x7a if m.InitialHeight != 0 { i = encodeVarintTypes(dAtA, i, uint64(m.InitialHeight)) i-- @@ -1082,12 +1104,12 @@ func (m *State) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n13, err13 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.LastBlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.LastBlockTime):]) - if err13 != nil { - return 0, err13 + n14, err14 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.LastBlockTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.LastBlockTime):]) + if err14 != nil { + return 0, err14 } - i -= n13 - i = encodeVarintTypes(dAtA, i, uint64(n13)) + i -= n14 + i = encodeVarintTypes(dAtA, i, uint64(n14)) i-- dAtA[i] = 0x2a { @@ -1314,6 +1336,8 @@ func (m *State) Size() (n int) { if m.InitialHeight != 0 { n += 1 + sovTypes(uint64(m.InitialHeight)) } + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.NextBlockDelay) + n += 1 + l + sovTypes(uint64(l)) return n } @@ -2625,6 +2649,39 @@ func (m *State) Unmarshal(dAtA []byte) error { break } } + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NextBlockDelay", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.NextBlockDelay, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/proto/tendermint/state/types.proto b/proto/tendermint/state/types.proto index f9ed2753218..c2711e32b0c 100644 --- a/proto/tendermint/state/types.proto +++ b/proto/tendermint/state/types.proto @@ -2,6 +2,7 @@ syntax = "proto3"; package tendermint.state; import "gogoproto/gogo.proto"; +import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "tendermint/abci/types.proto"; import "tendermint/types/params.proto"; @@ -101,4 +102,11 @@ message State { // the latest AppHash we've received from calling abci.Commit() bytes app_hash = 13; + + // delay between the time when this block is committed and the next height is started. + // previously `timeout_commit` in config.toml + google.protobuf.Duration next_block_delay = 15 [ + (gogoproto.nullable) = false, + (gogoproto.stdduration) = true + ]; } diff --git a/proto/tendermint/types/canonical.pb.go b/proto/tendermint/types/canonical.pb.go index 9df89c73a2b..b26d13bcb14 100644 --- a/proto/tendermint/types/canonical.pb.go +++ b/proto/tendermint/types/canonical.pb.go @@ -225,12 +225,11 @@ func (m *CanonicalProposal) GetChainID() string { } type CanonicalVote struct { - Type SignedMsgType `protobuf:"varint,1,opt,name=type,proto3,enum=tendermint.types.SignedMsgType" json:"type,omitempty"` - Height int64 `protobuf:"fixed64,2,opt,name=height,proto3" json:"height,omitempty"` - Round int64 `protobuf:"fixed64,3,opt,name=round,proto3" json:"round,omitempty"` - BlockID *CanonicalBlockID `protobuf:"bytes,4,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` - Timestamp time.Time `protobuf:"bytes,5,opt,name=timestamp,proto3,stdtime" json:"timestamp"` - ChainID string `protobuf:"bytes,6,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + Type SignedMsgType `protobuf:"varint,1,opt,name=type,proto3,enum=tendermint.types.SignedMsgType" json:"type,omitempty"` + Height int64 `protobuf:"fixed64,2,opt,name=height,proto3" json:"height,omitempty"` + Round int64 `protobuf:"fixed64,3,opt,name=round,proto3" json:"round,omitempty"` + BlockID *CanonicalBlockID `protobuf:"bytes,4,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + ChainID string `protobuf:"bytes,6,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` } func (m *CanonicalVote) Reset() { *m = CanonicalVote{} } @@ -294,13 +293,6 @@ func (m *CanonicalVote) GetBlockID() *CanonicalBlockID { return nil } -func (m *CanonicalVote) GetTimestamp() time.Time { - if m != nil { - return m.Timestamp - } - return time.Time{} -} - func (m *CanonicalVote) GetChainID() string { if m != nil { return m.ChainID @@ -389,40 +381,41 @@ func init() { func init() { proto.RegisterFile("tendermint/types/canonical.proto", fileDescriptor_8d1a1a84ff7267ed) } var fileDescriptor_8d1a1a84ff7267ed = []byte{ - // 525 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0xc1, 0x6e, 0x9b, 0x40, - 0x10, 0x35, 0x0e, 0xb6, 0x61, 0x13, 0xb7, 0xee, 0x2a, 0x8a, 0xa8, 0x15, 0x01, 0xe2, 0x50, 0xd1, - 0x0b, 0x48, 0xf1, 0x1f, 0x90, 0x56, 0xaa, 0xab, 0x46, 0x8d, 0x48, 0x94, 0x43, 0x2f, 0xd6, 0x02, - 0x1b, 0x40, 0x05, 0x16, 0xc1, 0x5a, 0x6a, 0x2e, 0xed, 0x2f, 0xe4, 0x3b, 0xfa, 0x25, 0x39, 0xe6, - 0xd8, 0x5e, 0xdc, 0x0a, 0xff, 0x48, 0xb5, 0x0b, 0x06, 0x2b, 0xa9, 0x2c, 0x55, 0xad, 0x7a, 0x41, - 0x33, 0x6f, 0xde, 0xce, 0x3c, 0xbd, 0x1d, 0x16, 0xe8, 0x14, 0x67, 0x01, 0x2e, 0xd2, 0x38, 0xa3, - 0x36, 0xbd, 0xc9, 0x71, 0x69, 0xfb, 0x28, 0x23, 0x59, 0xec, 0xa3, 0xc4, 0xca, 0x0b, 0x42, 0x09, - 0x9c, 0x74, 0x0c, 0x8b, 0x33, 0xa6, 0x87, 0x21, 0x09, 0x09, 0x2f, 0xda, 0x2c, 0xaa, 0x79, 0x53, - 0x2d, 0x24, 0x24, 0x4c, 0xb0, 0xcd, 0x33, 0x6f, 0x79, 0x6d, 0xd3, 0x38, 0xc5, 0x25, 0x45, 0x69, - 0xde, 0x10, 0x8e, 0x1f, 0x8d, 0xe2, 0xdf, 0xba, 0x6a, 0x7c, 0x06, 0x93, 0xd3, 0xcd, 0x64, 0x27, - 0x21, 0xfe, 0xc7, 0xf9, 0x2b, 0x08, 0x81, 0x18, 0xa1, 0x32, 0x52, 0x04, 0x5d, 0x30, 0x0f, 0x5c, - 0x1e, 0xc3, 0x2b, 0xf0, 0x34, 0x47, 0x05, 0x5d, 0x94, 0x98, 0x2e, 0x22, 0x8c, 0x02, 0x5c, 0x28, - 0x7d, 0x5d, 0x30, 0xf7, 0x4f, 0x4c, 0xeb, 0xa1, 0x50, 0xab, 0x6d, 0x78, 0x8e, 0x0a, 0x7a, 0x81, - 0xe9, 0x1b, 0xce, 0x77, 0xc4, 0xbb, 0x95, 0xd6, 0x73, 0xc7, 0xf9, 0x36, 0x68, 0x38, 0xe0, 0xe8, - 0xf7, 0x74, 0x78, 0x08, 0x06, 0x94, 0x50, 0x94, 0x70, 0x19, 0x63, 0xb7, 0x4e, 0x5a, 0x6d, 0xfd, - 0x4e, 0x9b, 0xf1, 0xbd, 0x0f, 0x9e, 0x75, 0x4d, 0x0a, 0x92, 0x93, 0x12, 0x25, 0x70, 0x06, 0x44, - 0x26, 0x87, 0x1f, 0x7f, 0x72, 0xa2, 0x3d, 0x96, 0x79, 0x11, 0x87, 0x19, 0x0e, 0xce, 0xca, 0xf0, - 0xf2, 0x26, 0xc7, 0x2e, 0x27, 0xc3, 0x23, 0x30, 0x8c, 0x70, 0x1c, 0x46, 0x94, 0x0f, 0x98, 0xb8, - 0x4d, 0xc6, 0xc4, 0x14, 0x64, 0x99, 0x05, 0xca, 0x1e, 0x87, 0xeb, 0x04, 0xbe, 0x04, 0x72, 0x4e, - 0x92, 0x45, 0x5d, 0x11, 0x75, 0xc1, 0xdc, 0x73, 0x0e, 0xaa, 0x95, 0x26, 0x9d, 0xbf, 0x7f, 0xe7, - 0x32, 0xcc, 0x95, 0x72, 0x92, 0xf0, 0x08, 0xbe, 0x05, 0x92, 0xc7, 0xec, 0x5d, 0xc4, 0x81, 0x32, - 0xe0, 0xc6, 0x19, 0x3b, 0x8c, 0x6b, 0x6e, 0xc2, 0xd9, 0xaf, 0x56, 0xda, 0xa8, 0x49, 0xdc, 0x11, - 0x6f, 0x30, 0x0f, 0xa0, 0x03, 0xe4, 0xf6, 0x92, 0x95, 0x21, 0x6f, 0x36, 0xb5, 0xea, 0x35, 0xb0, - 0x36, 0x6b, 0x60, 0x5d, 0x6e, 0x18, 0x8e, 0xc4, 0x7c, 0xbf, 0xfd, 0xa1, 0x09, 0x6e, 0x77, 0x0c, - 0xbe, 0x00, 0x92, 0x1f, 0xa1, 0x38, 0x63, 0x7a, 0x46, 0xba, 0x60, 0xca, 0xf5, 0xac, 0x53, 0x86, - 0xb1, 0x59, 0xbc, 0x38, 0x0f, 0x8c, 0xaf, 0x7d, 0x30, 0x6e, 0x65, 0x5d, 0x11, 0x8a, 0xff, 0x87, - 0xaf, 0xdb, 0x66, 0x89, 0xff, 0xd2, 0xac, 0xc1, 0xdf, 0x9b, 0x35, 0xdc, 0x61, 0xd6, 0x97, 0xad, - 0x65, 0x66, 0x5e, 0xbd, 0xfe, 0x44, 0x71, 0x56, 0xc6, 0x24, 0x83, 0xc7, 0x40, 0xc6, 0x9b, 0xa4, - 0xf9, 0xaf, 0x3a, 0xe0, 0x0f, 0xdd, 0x79, 0xbe, 0xa5, 0x86, 0xb9, 0x23, 0xb7, 0x02, 0x9c, 0xb3, - 0xbb, 0x4a, 0x15, 0xee, 0x2b, 0x55, 0xf8, 0x59, 0xa9, 0xc2, 0xed, 0x5a, 0xed, 0xdd, 0xaf, 0xd5, - 0xde, 0xb7, 0xb5, 0xda, 0xfb, 0x30, 0x0b, 0x63, 0x1a, 0x2d, 0x3d, 0xcb, 0x27, 0xa9, 0xed, 0x93, - 0x14, 0x53, 0xef, 0x9a, 0x76, 0x41, 0xfd, 0xaa, 0x3c, 0x7c, 0x28, 0xbc, 0x21, 0xc7, 0x67, 0xbf, - 0x02, 0x00, 0x00, 0xff, 0xff, 0x35, 0x24, 0xc5, 0xf2, 0xae, 0x04, 0x00, 0x00, + // 530 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0x4f, 0x6b, 0x9c, 0x40, + 0x14, 0xdf, 0x49, 0xdc, 0x5d, 0x77, 0x92, 0x6d, 0xb7, 0x43, 0x08, 0xdb, 0x25, 0xa8, 0x78, 0x28, + 0xf6, 0xa2, 0x90, 0xfd, 0x06, 0xa6, 0x85, 0x6e, 0x68, 0x68, 0x30, 0x21, 0x87, 0x5e, 0x96, 0x51, + 0x27, 0x2a, 0x55, 0x47, 0x74, 0x16, 0x9a, 0x4b, 0xfb, 0x15, 0xf2, 0xb1, 0x72, 0xcc, 0xb1, 0xbd, + 0x6c, 0x8b, 0x7b, 0xed, 0x87, 0x28, 0x33, 0xba, 0x2a, 0x49, 0x5b, 0x28, 0x85, 0x5e, 0xe4, 0xfd, + 0xf9, 0xcd, 0x7b, 0xbf, 0xf7, 0x9b, 0x37, 0x42, 0x8d, 0x91, 0xd4, 0x27, 0x79, 0x12, 0xa5, 0xcc, + 0x62, 0x37, 0x19, 0x29, 0x2c, 0x0f, 0xa7, 0x34, 0x8d, 0x3c, 0x1c, 0x9b, 0x59, 0x4e, 0x19, 0x45, + 0x93, 0x16, 0x61, 0x0a, 0xc4, 0xec, 0x20, 0xa0, 0x01, 0x15, 0x49, 0x8b, 0x5b, 0x15, 0x6e, 0xa6, + 0x06, 0x94, 0x06, 0x31, 0xb1, 0x84, 0xe7, 0xae, 0xae, 0x2d, 0x16, 0x25, 0xa4, 0x60, 0x38, 0xc9, + 0x6a, 0xc0, 0xd1, 0xa3, 0x56, 0xe2, 0x5b, 0x65, 0xf5, 0x4f, 0x70, 0x72, 0xb2, 0xed, 0x6c, 0xc7, + 0xd4, 0xfb, 0xb0, 0x78, 0x85, 0x10, 0x94, 0x42, 0x5c, 0x84, 0x53, 0xa0, 0x01, 0x63, 0xdf, 0x11, + 0x36, 0xba, 0x82, 0x4f, 0x33, 0x9c, 0xb3, 0x65, 0x41, 0xd8, 0x32, 0x24, 0xd8, 0x27, 0xf9, 0x74, + 0x47, 0x03, 0xc6, 0xde, 0xb1, 0x61, 0x3e, 0x24, 0x6a, 0x36, 0x05, 0xcf, 0x71, 0xce, 0x2e, 0x08, + 0x7b, 0x23, 0xf0, 0xb6, 0x74, 0xb7, 0x56, 0x7b, 0xce, 0x38, 0xeb, 0x06, 0x75, 0x1b, 0x1e, 0xfe, + 0x1a, 0x8e, 0x0e, 0x60, 0x9f, 0x51, 0x86, 0x63, 0x41, 0x63, 0xec, 0x54, 0x4e, 0xc3, 0x6d, 0xa7, + 0xe5, 0xa6, 0x7f, 0xdd, 0x81, 0xcf, 0xda, 0x22, 0x39, 0xcd, 0x68, 0x81, 0x63, 0x34, 0x87, 0x12, + 0xa7, 0x23, 0x8e, 0x3f, 0x39, 0x56, 0x1f, 0xd3, 0xbc, 0x88, 0x82, 0x94, 0xf8, 0x67, 0x45, 0x70, + 0x79, 0x93, 0x11, 0x47, 0x80, 0xd1, 0x21, 0x1c, 0x84, 0x24, 0x0a, 0x42, 0x26, 0x1a, 0x4c, 0x9c, + 0xda, 0xe3, 0x64, 0x72, 0xba, 0x4a, 0xfd, 0xe9, 0xae, 0x08, 0x57, 0x0e, 0x7a, 0x09, 0x47, 0x19, + 0x8d, 0x97, 0x55, 0x46, 0xd2, 0x80, 0xb1, 0x6b, 0xef, 0x97, 0x6b, 0x55, 0x3e, 0x7f, 0xf7, 0xd6, + 0xe1, 0x31, 0x47, 0xce, 0x68, 0x2c, 0x2c, 0x74, 0x0a, 0x65, 0x97, 0xcb, 0xbb, 0x8c, 0xfc, 0x69, + 0x5f, 0x08, 0xa7, 0xff, 0x41, 0xb8, 0xfa, 0x26, 0xec, 0xbd, 0x72, 0xad, 0x0e, 0x6b, 0xc7, 0x19, + 0x8a, 0x02, 0x0b, 0x1f, 0xd9, 0x70, 0xd4, 0x5c, 0xf2, 0x74, 0x20, 0x8a, 0xcd, 0xcc, 0x6a, 0x0d, + 0xcc, 0xed, 0x1a, 0x98, 0x97, 0x5b, 0x84, 0x2d, 0x73, 0xdd, 0x6f, 0xbf, 0xa9, 0xc0, 0x69, 0x8f, + 0xa1, 0x17, 0x50, 0xf6, 0x42, 0x1c, 0xa5, 0x9c, 0xcf, 0x50, 0x03, 0xc6, 0xa8, 0xea, 0x75, 0xc2, + 0x63, 0xbc, 0x97, 0x48, 0x2e, 0x7c, 0xfd, 0x07, 0x80, 0xe3, 0x86, 0xd6, 0x15, 0x65, 0xe4, 0x7f, + 0xe8, 0xda, 0x15, 0x4b, 0xfa, 0x47, 0xb1, 0xba, 0x83, 0x0e, 0x7e, 0x3f, 0xe8, 0xa9, 0x24, 0xf7, + 0x27, 0x03, 0xfd, 0x73, 0x67, 0x1d, 0xf9, 0xb4, 0xaf, 0x3f, 0x32, 0x92, 0x16, 0x11, 0x4d, 0xd1, + 0x11, 0x1c, 0x91, 0xad, 0x53, 0xbf, 0x8c, 0x36, 0xf0, 0x97, 0xf3, 0x3d, 0xef, 0x70, 0xe2, 0xf3, + 0x8d, 0x1a, 0x1a, 0xf6, 0xd9, 0x5d, 0xa9, 0x80, 0xfb, 0x52, 0x01, 0xdf, 0x4b, 0x05, 0xdc, 0x6e, + 0x94, 0xde, 0xfd, 0x46, 0xe9, 0x7d, 0xd9, 0x28, 0xbd, 0xf7, 0xf3, 0x20, 0x62, 0xe1, 0xca, 0x35, + 0x3d, 0x9a, 0x58, 0x1e, 0x4d, 0x08, 0x73, 0xaf, 0x59, 0x6b, 0x54, 0xff, 0x85, 0x87, 0x4f, 0xdd, + 0x1d, 0x88, 0xf8, 0xfc, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf3, 0x3c, 0x27, 0x43, 0x70, 0x04, + 0x00, 0x00, } func (m *CanonicalBlockID) Marshal() (dAtA []byte, err error) { @@ -599,14 +592,6 @@ func (m *CanonicalVote) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x32 } - n4, err4 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp):]) - if err4 != nil { - return 0, err4 - } - i -= n4 - i = encodeVarintCanonical(dAtA, i, uint64(n4)) - i-- - dAtA[i] = 0x2a if m.BlockID != nil { { size, err := m.BlockID.MarshalToSizedBuffer(dAtA[:i]) @@ -780,8 +765,6 @@ func (m *CanonicalVote) Size() (n int) { l = m.BlockID.Size() n += 1 + l + sovCanonical(uint64(l)) } - l = github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Timestamp) - n += 1 + l + sovCanonical(uint64(l)) l = len(m.ChainID) if l > 0 { n += 1 + l + sovCanonical(uint64(l)) @@ -1351,39 +1334,6 @@ func (m *CanonicalVote) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 5: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 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 err := github_com_cosmos_gogoproto_types.StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ChainID", wireType) diff --git a/proto/tendermint/types/canonical.proto b/proto/tendermint/types/canonical.proto index adcad2d8846..2bb700b6657 100644 --- a/proto/tendermint/types/canonical.proto +++ b/proto/tendermint/types/canonical.proto @@ -35,11 +35,11 @@ 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"]; - google.protobuf.Timestamp timestamp = 5 [ - (gogoproto.nullable) = false, - (gogoproto.stdtime) = true - ]; string chain_id = 6 [(gogoproto.customname) = "ChainID"]; + + // The per-vote timestamp was removed so that all validators sign identical + // bytes for a given block, enabling BLS signature aggregation. + reserved 5; } // CanonicalVoteExtension provides us a way to serialize a vote extension from diff --git a/proto/tendermint/types/params.pb.go b/proto/tendermint/types/params.pb.go index 156b73ad7f1..66b916134cd 100644 --- a/proto/tendermint/types/params.pb.go +++ b/proto/tendermint/types/params.pb.go @@ -8,6 +8,7 @@ import ( _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" + types "github.com/cosmos/gogoproto/types" _ "github.com/golang/protobuf/ptypes/duration" io "io" math "math" @@ -35,7 +36,12 @@ type ConsensusParams struct { Validator *ValidatorParams `protobuf:"bytes,3,opt,name=validator,proto3" json:"validator,omitempty"` Version *VersionParams `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` Abci *ABCIParams `protobuf:"bytes,5,opt,name=abci,proto3" json:"abci,omitempty"` - Authority *AuthorityParams `protobuf:"bytes,6,opt,name=authority,proto3" json:"authority,omitempty"` + // NOTE(berachain): synchrony=6 and feature=7 carry the field numbers used + // by bera-v1.x so that state persisted by existing networks decodes + // unchanged. Upstream v0.39 assigns authority=6; it is moved to 8 here. + Synchrony *SynchronyParams `protobuf:"bytes,6,opt,name=synchrony,proto3" json:"synchrony,omitempty"` + Feature *FeatureParams `protobuf:"bytes,7,opt,name=feature,proto3" json:"feature,omitempty"` + Authority *AuthorityParams `protobuf:"bytes,8,opt,name=authority,proto3" json:"authority,omitempty"` } func (m *ConsensusParams) Reset() { *m = ConsensusParams{} } @@ -106,6 +112,20 @@ func (m *ConsensusParams) GetAbci() *ABCIParams { return nil } +func (m *ConsensusParams) GetSynchrony() *SynchronyParams { + if m != nil { + return m.Synchrony + } + return nil +} + +func (m *ConsensusParams) GetFeature() *FeatureParams { + if m != nil { + return m.Feature + } + return nil +} + func (m *ConsensusParams) GetAuthority() *AuthorityParams { if m != nil { return m.Authority @@ -389,6 +409,142 @@ func (m *HashedParams) GetBlockMaxGas() int64 { return 0 } +// SynchronyParams determine the validity of block timestamps. +// +// These parameters are part of the Proposer-Based Timestamps (PBTS) algorithm. +// For more information on the relationship of the synchrony parameters to +// block timestamps validity, refer to the PBTS specification: +// https://github.com/tendermint/spec/blob/master/spec/consensus/proposer-based-timestamp/README.md +type SynchronyParams struct { + // Bound for how skewed a proposer's clock may be from any validator on the + // network while still producing valid proposals. + Precision *time.Duration `protobuf:"bytes,1,opt,name=precision,proto3,stdduration" json:"precision,omitempty"` + // Bound for how long a proposal message may take to reach all validators on + // a network and still be considered valid. + MessageDelay *time.Duration `protobuf:"bytes,2,opt,name=message_delay,json=messageDelay,proto3,stdduration" json:"message_delay,omitempty"` +} + +func (m *SynchronyParams) Reset() { *m = SynchronyParams{} } +func (m *SynchronyParams) String() string { return proto.CompactTextString(m) } +func (*SynchronyParams) ProtoMessage() {} +func (*SynchronyParams) Descriptor() ([]byte, []int) { + return fileDescriptor_e12598271a686f57, []int{6} +} +func (m *SynchronyParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SynchronyParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SynchronyParams.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 *SynchronyParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_SynchronyParams.Merge(m, src) +} +func (m *SynchronyParams) XXX_Size() int { + return m.Size() +} +func (m *SynchronyParams) XXX_DiscardUnknown() { + xxx_messageInfo_SynchronyParams.DiscardUnknown(m) +} + +var xxx_messageInfo_SynchronyParams proto.InternalMessageInfo + +func (m *SynchronyParams) GetPrecision() *time.Duration { + if m != nil { + return m.Precision + } + return nil +} + +func (m *SynchronyParams) GetMessageDelay() *time.Duration { + if m != nil { + return m.MessageDelay + } + return nil +} + +// FeatureParams configure the height from which features of CometBFT are enabled. +type FeatureParams struct { + // Height during which vote extensions will be enabled. + // + // A value of 0 means vote extensions are disabled. A value > 0 denotes + // the height at which vote extensions will be (or have been) enabled. + // + // Cannot be set to heights lower or equal to the current blockchain height. + VoteExtensionsEnableHeight *types.Int64Value `protobuf:"bytes,1,opt,name=vote_extensions_enable_height,json=voteExtensionsEnableHeight,proto3" json:"vote_extensions_enable_height,omitempty"` + // Height at which Proposer-Based Timestamps (PBTS) will be enabled. + // + // A value of 0 means PBTS is disabled. A value > 0 denotes the height at + // which PBTS will be (or has been) enabled. + // + // Cannot be set to heights lower or equal to the current blockchain height. + PbtsEnableHeight *types.Int64Value `protobuf:"bytes,2,opt,name=pbts_enable_height,json=pbtsEnableHeight,proto3" json:"pbts_enable_height,omitempty"` + // Height at which Stable Block Time (SBT) will be enabled. + SbtEnableHeight *types.Int64Value `protobuf:"bytes,3,opt,name=sbt_enable_height,json=sbtEnableHeight,proto3" json:"sbt_enable_height,omitempty"` +} + +func (m *FeatureParams) Reset() { *m = FeatureParams{} } +func (m *FeatureParams) String() string { return proto.CompactTextString(m) } +func (*FeatureParams) ProtoMessage() {} +func (*FeatureParams) Descriptor() ([]byte, []int) { + return fileDescriptor_e12598271a686f57, []int{7} +} +func (m *FeatureParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *FeatureParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_FeatureParams.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 *FeatureParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_FeatureParams.Merge(m, src) +} +func (m *FeatureParams) XXX_Size() int { + return m.Size() +} +func (m *FeatureParams) XXX_DiscardUnknown() { + xxx_messageInfo_FeatureParams.DiscardUnknown(m) +} + +var xxx_messageInfo_FeatureParams proto.InternalMessageInfo + +func (m *FeatureParams) GetVoteExtensionsEnableHeight() *types.Int64Value { + if m != nil { + return m.VoteExtensionsEnableHeight + } + return nil +} + +func (m *FeatureParams) GetPbtsEnableHeight() *types.Int64Value { + if m != nil { + return m.PbtsEnableHeight + } + return nil +} + +func (m *FeatureParams) GetSbtEnableHeight() *types.Int64Value { + if m != nil { + return m.SbtEnableHeight + } + return nil +} + // ABCIParams configure functionality specific to the Application Blockchain Interface. type ABCIParams struct { // vote_extensions_enable_height configures the first height during which @@ -407,7 +563,7 @@ func (m *ABCIParams) Reset() { *m = ABCIParams{} } func (m *ABCIParams) String() string { return proto.CompactTextString(m) } func (*ABCIParams) ProtoMessage() {} func (*ABCIParams) Descriptor() ([]byte, []int) { - return fileDescriptor_e12598271a686f57, []int{6} + return fileDescriptor_e12598271a686f57, []int{8} } func (m *ABCIParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -454,7 +610,7 @@ func (m *AuthorityParams) Reset() { *m = AuthorityParams{} } func (m *AuthorityParams) String() string { return proto.CompactTextString(m) } func (*AuthorityParams) ProtoMessage() {} func (*AuthorityParams) Descriptor() ([]byte, []int) { - return fileDescriptor_e12598271a686f57, []int{7} + return fileDescriptor_e12598271a686f57, []int{9} } func (m *AuthorityParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -497,6 +653,8 @@ func init() { proto.RegisterType((*ValidatorParams)(nil), "tendermint.types.ValidatorParams") proto.RegisterType((*VersionParams)(nil), "tendermint.types.VersionParams") proto.RegisterType((*HashedParams)(nil), "tendermint.types.HashedParams") + proto.RegisterType((*SynchronyParams)(nil), "tendermint.types.SynchronyParams") + proto.RegisterType((*FeatureParams)(nil), "tendermint.types.FeatureParams") proto.RegisterType((*ABCIParams)(nil), "tendermint.types.ABCIParams") proto.RegisterType((*AuthorityParams)(nil), "tendermint.types.AuthorityParams") } @@ -504,46 +662,56 @@ func init() { func init() { proto.RegisterFile("tendermint/types/params.proto", fileDescriptor_e12598271a686f57) } var fileDescriptor_e12598271a686f57 = []byte{ - // 614 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x94, 0xcb, 0x6e, 0xd3, 0x4c, - 0x14, 0xc7, 0xe3, 0x3a, 0x6d, 0x93, 0x93, 0x2f, 0x4d, 0x34, 0xfa, 0x24, 0x4c, 0x69, 0x9c, 0xe0, - 0x05, 0xaa, 0x54, 0xc9, 0x46, 0x64, 0x05, 0x42, 0xaa, 0x92, 0x12, 0xb5, 0x05, 0x95, 0x8b, 0x85, - 0x58, 0x74, 0x63, 0x8d, 0x93, 0xa9, 0x63, 0x35, 0xf6, 0x58, 0x9e, 0x71, 0x94, 0xbc, 0x05, 0x4b, - 0x96, 0x5d, 0xc2, 0x1b, 0xf0, 0x08, 0x5d, 0x76, 0xc9, 0x0a, 0x50, 0xb2, 0x61, 0xcf, 0x0b, 0x20, - 0x8f, 0xed, 0x38, 0x17, 0xd8, 0x8d, 0xe7, 0xfc, 0x7e, 0x73, 0xf9, 0xcf, 0x91, 0xa1, 0xc1, 0x89, - 0x3f, 0x20, 0xa1, 0xe7, 0xfa, 0xdc, 0xe0, 0xd3, 0x80, 0x30, 0x23, 0xc0, 0x21, 0xf6, 0x98, 0x1e, - 0x84, 0x94, 0x53, 0x54, 0xcf, 0xcb, 0xba, 0x28, 0xef, 0xff, 0xef, 0x50, 0x87, 0x8a, 0xa2, 0x11, - 0x8f, 0x12, 0x6e, 0x5f, 0x75, 0x28, 0x75, 0x46, 0xc4, 0x10, 0x5f, 0x76, 0x74, 0x65, 0x0c, 0xa2, - 0x10, 0x73, 0x97, 0xfa, 0x49, 0x5d, 0xfb, 0xbd, 0x05, 0xb5, 0x13, 0xea, 0x33, 0xe2, 0xb3, 0x88, - 0xbd, 0x15, 0x3b, 0xa0, 0x36, 0x6c, 0xdb, 0x23, 0xda, 0xbf, 0x56, 0xa4, 0x96, 0x74, 0x58, 0x79, - 0xd2, 0xd0, 0xd7, 0xf7, 0xd2, 0xbb, 0x71, 0x39, 0xa1, 0xcd, 0x84, 0x45, 0xcf, 0xa1, 0x44, 0xc6, - 0xee, 0x80, 0xf8, 0x7d, 0xa2, 0x6c, 0x09, 0xaf, 0xb5, 0xe9, 0xf5, 0x52, 0x22, 0x55, 0x17, 0x06, - 0x3a, 0x86, 0xf2, 0x18, 0x8f, 0xdc, 0x01, 0xe6, 0x34, 0x54, 0x64, 0xa1, 0x3f, 0xdc, 0xd4, 0x3f, - 0x64, 0x48, 0xea, 0xe7, 0x0e, 0x7a, 0x0a, 0xbb, 0x63, 0x12, 0x32, 0x97, 0xfa, 0x4a, 0x51, 0xe8, - 0xcd, 0xbf, 0xe8, 0x09, 0x90, 0xca, 0x19, 0x8f, 0x1e, 0x43, 0x11, 0xdb, 0x7d, 0x57, 0xd9, 0x16, - 0xde, 0xc1, 0xa6, 0xd7, 0xe9, 0x9e, 0x9c, 0xa7, 0x92, 0x20, 0xe3, 0xd3, 0xe2, 0x88, 0x0f, 0x69, - 0xe8, 0xf2, 0xa9, 0xb2, 0xf3, 0xaf, 0xd3, 0x76, 0x32, 0x24, 0x3b, 0xed, 0xc2, 0xd1, 0xce, 0xa1, - 0xb2, 0x14, 0x21, 0x7a, 0x00, 0x65, 0x0f, 0x4f, 0x2c, 0x7b, 0xca, 0x09, 0x13, 0xa1, 0xcb, 0x66, - 0xc9, 0xc3, 0x93, 0x6e, 0xfc, 0x8d, 0xee, 0xc1, 0x6e, 0x5c, 0x74, 0x30, 0x13, 0xb9, 0xca, 0xe6, - 0x8e, 0x87, 0x27, 0xa7, 0x98, 0xbd, 0x2c, 0x96, 0xe4, 0x7a, 0x51, 0xfb, 0x22, 0xc1, 0xde, 0x6a, - 0xac, 0xe8, 0x08, 0x50, 0x6c, 0x60, 0x87, 0x58, 0x7e, 0xe4, 0x59, 0xe2, 0x7d, 0xb2, 0x75, 0x6b, - 0x1e, 0x9e, 0x74, 0x1c, 0xf2, 0x3a, 0xf2, 0xc4, 0x01, 0x18, 0xba, 0x80, 0x7a, 0x06, 0x67, 0xad, - 0x91, 0xbe, 0xdf, 0x7d, 0x3d, 0xe9, 0x1d, 0x3d, 0xeb, 0x1d, 0xfd, 0x45, 0x0a, 0x74, 0x4b, 0xb7, - 0xdf, 0x9b, 0x85, 0x4f, 0x3f, 0x9a, 0x92, 0xb9, 0x97, 0xac, 0x97, 0x55, 0x56, 0xaf, 0x22, 0xaf, - 0x5e, 0x45, 0x3b, 0x86, 0xda, 0xda, 0x13, 0x22, 0x0d, 0xaa, 0x41, 0x64, 0x5b, 0xd7, 0x64, 0x6a, - 0x89, 0xd4, 0x14, 0xa9, 0x25, 0x1f, 0x96, 0xcd, 0x4a, 0x10, 0xd9, 0xaf, 0xc8, 0xf4, 0x7d, 0x3c, - 0xf5, 0xac, 0xf4, 0xf5, 0xa6, 0x29, 0xfd, 0xba, 0x69, 0x4a, 0xda, 0x11, 0x54, 0x57, 0x1e, 0x11, - 0xd5, 0x41, 0xc6, 0x41, 0x20, 0xee, 0x56, 0x34, 0xe3, 0xe1, 0x12, 0x7c, 0x09, 0xff, 0x9d, 0x61, - 0x36, 0x24, 0x83, 0x94, 0x7d, 0x04, 0x35, 0x11, 0x85, 0xb5, 0x9e, 0x75, 0x55, 0x4c, 0x5f, 0x64, - 0x81, 0x6b, 0x50, 0xcd, 0xb9, 0x3c, 0xf6, 0x4a, 0x46, 0x9d, 0x62, 0xa6, 0xbd, 0x01, 0xc8, 0xbb, - 0x02, 0x75, 0xa0, 0x31, 0xa6, 0x9c, 0x58, 0x64, 0xc2, 0x89, 0x1f, 0x9f, 0x8e, 0x59, 0xc4, 0xc7, - 0xf6, 0x88, 0x58, 0x43, 0xe2, 0x3a, 0x43, 0x9e, 0xee, 0xb3, 0x1f, 0x43, 0xbd, 0x05, 0xd3, 0x13, - 0xc8, 0x99, 0x20, 0x34, 0x03, 0x6a, 0x6b, 0xfd, 0x82, 0x0e, 0x96, 0xbb, 0x2c, 0x5e, 0xa1, 0xbc, - 0xd4, 0x42, 0xdd, 0x77, 0x9f, 0x67, 0xaa, 0x74, 0x3b, 0x53, 0xa5, 0xbb, 0x99, 0x2a, 0xfd, 0x9c, - 0xa9, 0xd2, 0xc7, 0xb9, 0x5a, 0xb8, 0x9b, 0xab, 0x85, 0x6f, 0x73, 0xb5, 0x70, 0xd9, 0x76, 0x5c, - 0x3e, 0x8c, 0x6c, 0xbd, 0x4f, 0x3d, 0xa3, 0x4f, 0x3d, 0xc2, 0xed, 0x2b, 0x9e, 0x0f, 0x92, 0xbf, - 0xc4, 0xfa, 0x0f, 0xc6, 0xde, 0x11, 0xf3, 0xed, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xd7, 0xf7, - 0xad, 0x2a, 0x7b, 0x04, 0x00, 0x00, + // 775 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x95, 0xcd, 0x6e, 0xd3, 0x4a, + 0x14, 0xc7, 0xe3, 0x3a, 0x6d, 0x93, 0x49, 0xd3, 0xe4, 0x8e, 0xae, 0x74, 0x7d, 0xfb, 0xe1, 0x14, + 0x2f, 0x50, 0xa5, 0x4a, 0x0e, 0xa2, 0x08, 0x09, 0x04, 0xaa, 0x92, 0xb6, 0xb4, 0x05, 0x95, 0x42, + 0x40, 0x5d, 0x74, 0x63, 0x8d, 0x93, 0xa9, 0x63, 0x35, 0xf6, 0x58, 0x9e, 0x71, 0x88, 0xdf, 0x82, + 0x0d, 0x88, 0x65, 0x97, 0xf0, 0x06, 0x3c, 0x42, 0x97, 0x5d, 0xb2, 0x2a, 0x28, 0xdd, 0xb0, 0xe3, + 0x15, 0xd0, 0x8c, 0xed, 0x7c, 0x38, 0x84, 0x76, 0x67, 0xfb, 0xfc, 0x7f, 0xff, 0x73, 0xe6, 0x9c, + 0x63, 0x1b, 0xac, 0x32, 0xec, 0xb6, 0xb0, 0xef, 0xd8, 0x2e, 0xab, 0xb2, 0xd0, 0xc3, 0xb4, 0xea, + 0x21, 0x1f, 0x39, 0x54, 0xf7, 0x7c, 0xc2, 0x08, 0x2c, 0x0f, 0xc3, 0xba, 0x08, 0x2f, 0xfd, 0x6b, + 0x11, 0x8b, 0x88, 0x60, 0x95, 0x5f, 0x45, 0xba, 0x25, 0xd5, 0x22, 0xc4, 0xea, 0xe0, 0xaa, 0xb8, + 0x33, 0x83, 0xd3, 0x6a, 0x2b, 0xf0, 0x11, 0xb3, 0x89, 0x3b, 0x2d, 0xfe, 0xce, 0x47, 0x9e, 0x87, + 0xfd, 0x38, 0x8f, 0xf6, 0x4b, 0x06, 0xa5, 0x6d, 0xe2, 0x52, 0xec, 0xd2, 0x80, 0xbe, 0x12, 0x15, + 0xc0, 0x4d, 0x30, 0x6b, 0x76, 0x48, 0xf3, 0x4c, 0x91, 0xd6, 0xa4, 0xf5, 0xc2, 0xfd, 0x55, 0x3d, + 0x5d, 0x8b, 0x5e, 0xe7, 0xe1, 0x48, 0xdd, 0x88, 0xb4, 0xf0, 0x09, 0xc8, 0xe1, 0xae, 0xdd, 0xc2, + 0x6e, 0x13, 0x2b, 0x33, 0x82, 0x5b, 0x9b, 0xe4, 0x76, 0x63, 0x45, 0x8c, 0x0e, 0x08, 0xb8, 0x05, + 0xf2, 0x5d, 0xd4, 0xb1, 0x5b, 0x88, 0x11, 0x5f, 0x91, 0x05, 0x7e, 0x67, 0x12, 0x3f, 0x4e, 0x24, + 0x31, 0x3f, 0x64, 0xe0, 0x23, 0x30, 0xdf, 0xc5, 0x3e, 0xb5, 0x89, 0xab, 0x64, 0x05, 0x5e, 0xf9, + 0x03, 0x1e, 0x09, 0x62, 0x38, 0xd1, 0xc3, 0x7b, 0x20, 0x8b, 0xcc, 0xa6, 0xad, 0xcc, 0x0a, 0x6e, + 0x65, 0x92, 0xab, 0xd5, 0xb7, 0x0f, 0x62, 0x48, 0x28, 0x79, 0xb5, 0x34, 0x74, 0x9b, 0x6d, 0x9f, + 0xb8, 0xa1, 0x32, 0x37, 0xad, 0xda, 0x37, 0x89, 0x24, 0xa9, 0x76, 0xc0, 0xf0, 0x6a, 0x4f, 0x31, + 0x62, 0x81, 0x8f, 0x95, 0xf9, 0x69, 0xd5, 0x3e, 0x8b, 0x04, 0x49, 0xb5, 0xb1, 0x9e, 0xe7, 0x46, + 0x01, 0x6b, 0x13, 0xdf, 0x66, 0xa1, 0x92, 0x9b, 0x96, 0xbb, 0x96, 0x48, 0x92, 0xdc, 0x03, 0x46, + 0x3b, 0x00, 0x85, 0x91, 0xf1, 0xc1, 0x65, 0x90, 0x77, 0x50, 0xcf, 0x30, 0x43, 0x86, 0xa9, 0x18, + 0xb8, 0xdc, 0xc8, 0x39, 0xa8, 0x57, 0xe7, 0xf7, 0xf0, 0x3f, 0x30, 0xcf, 0x83, 0x16, 0xa2, 0x62, + 0xa6, 0x72, 0x63, 0xce, 0x41, 0xbd, 0x3d, 0x44, 0x9f, 0x67, 0x73, 0x72, 0x39, 0xab, 0x7d, 0x91, + 0xc0, 0xe2, 0xf8, 0x48, 0xe1, 0x06, 0x80, 0x9c, 0x40, 0x16, 0x36, 0xdc, 0xc0, 0x31, 0xc4, 0x6e, + 0x24, 0xbe, 0x25, 0x07, 0xf5, 0x6a, 0x16, 0x7e, 0x19, 0x38, 0xa2, 0x00, 0x0a, 0x0f, 0x41, 0x39, + 0x11, 0x27, 0x6b, 0x1b, 0xef, 0xce, 0xff, 0x7a, 0xb4, 0xb7, 0x7a, 0xb2, 0xb7, 0xfa, 0x4e, 0x2c, + 0xa8, 0xe7, 0x2e, 0xae, 0x2a, 0x99, 0x4f, 0xdf, 0x2b, 0x52, 0x63, 0x31, 0xf2, 0x4b, 0x22, 0xe3, + 0x47, 0x91, 0xc7, 0x8f, 0xa2, 0x6d, 0x81, 0x52, 0x6a, 0x7d, 0xa0, 0x06, 0x8a, 0x5e, 0x60, 0x1a, + 0x67, 0x38, 0x34, 0x44, 0xd7, 0x14, 0x69, 0x4d, 0x5e, 0xcf, 0x37, 0x0a, 0x5e, 0x60, 0xbe, 0xc0, + 0xe1, 0x5b, 0xfe, 0xe8, 0x71, 0xee, 0xeb, 0x79, 0x45, 0xfa, 0x79, 0x5e, 0x91, 0xb4, 0x0d, 0x50, + 0x1c, 0x5b, 0x20, 0x58, 0x06, 0x32, 0xf2, 0x3c, 0x71, 0xb6, 0x6c, 0x83, 0x5f, 0x8e, 0x88, 0x4f, + 0xc0, 0xc2, 0x3e, 0xa2, 0x6d, 0xdc, 0x8a, 0xb5, 0x77, 0x41, 0x49, 0xb4, 0xc2, 0x48, 0xf7, 0xba, + 0x28, 0x1e, 0x1f, 0x26, 0x0d, 0xd7, 0x40, 0x71, 0xa8, 0x1b, 0xb6, 0xbd, 0x90, 0xa8, 0xf6, 0x10, + 0xd5, 0x3e, 0x4a, 0xa0, 0x94, 0xda, 0x2d, 0xf8, 0x14, 0xe4, 0x3d, 0x1f, 0x37, 0x6d, 0xf1, 0x02, + 0x48, 0x37, 0xb5, 0x30, 0x2b, 0xda, 0x37, 0x24, 0xe0, 0x0e, 0x28, 0x3a, 0x98, 0x52, 0x31, 0x08, + 0xdc, 0x41, 0xe1, 0xcd, 0x53, 0x88, 0x2c, 0x16, 0x62, 0x6a, 0x87, 0x43, 0xda, 0x87, 0x19, 0x50, + 0x1c, 0xdb, 0x5a, 0xd8, 0x02, 0xab, 0x5d, 0xc2, 0xb0, 0x81, 0x7b, 0x0c, 0xbb, 0x3c, 0x13, 0x35, + 0xb0, 0x8b, 0xcc, 0x0e, 0x36, 0xda, 0xd8, 0xb6, 0xda, 0x2c, 0x2e, 0x75, 0x79, 0x22, 0xcf, 0x81, + 0xcb, 0x1e, 0x3e, 0x38, 0x46, 0x9d, 0x00, 0xd7, 0xb3, 0x17, 0x57, 0x15, 0xa9, 0xb1, 0xc4, 0x7d, + 0x76, 0x07, 0x36, 0xbb, 0xc2, 0x65, 0x5f, 0x98, 0xc0, 0x23, 0x00, 0x3d, 0x93, 0xa5, 0xad, 0x67, + 0x6e, 0x6b, 0x5d, 0xe6, 0xf0, 0x98, 0xe1, 0x21, 0xf8, 0x87, 0x9a, 0x2c, 0xe5, 0x27, 0xdf, 0xd6, + 0xaf, 0x44, 0x4d, 0x36, 0x6a, 0xa7, 0x1d, 0x01, 0x30, 0xfc, 0x84, 0xc0, 0xda, 0x6d, 0x7a, 0x22, + 0xff, 0xed, 0xc0, 0x5a, 0x15, 0x94, 0x52, 0x2f, 0x38, 0x5c, 0x19, 0xfd, 0x2c, 0x70, 0x87, 0xfc, + 0xc8, 0x3b, 0x5f, 0x7f, 0xfd, 0xb9, 0xaf, 0x4a, 0x17, 0x7d, 0x55, 0xba, 0xec, 0xab, 0xd2, 0x8f, + 0xbe, 0x2a, 0xbd, 0xbf, 0x56, 0x33, 0x97, 0xd7, 0x6a, 0xe6, 0xdb, 0xb5, 0x9a, 0x39, 0xd9, 0xb4, + 0x6c, 0xd6, 0x0e, 0x4c, 0xbd, 0x49, 0x9c, 0x6a, 0x93, 0x38, 0x98, 0x99, 0xa7, 0x6c, 0x78, 0x11, + 0xfd, 0x72, 0xd2, 0x7f, 0x2b, 0x73, 0x4e, 0x3c, 0xdf, 0xfc, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xaa, + 0x98, 0xd6, 0xfd, 0xc8, 0x06, 0x00, 0x00, } func (this *ConsensusParams) Equal(that interface{}) bool { @@ -580,6 +748,12 @@ func (this *ConsensusParams) Equal(that interface{}) bool { if !this.Abci.Equal(that1.Abci) { return false } + if !this.Synchrony.Equal(that1.Synchrony) { + return false + } + if !this.Feature.Equal(that1.Feature) { + return false + } if !this.Authority.Equal(that1.Authority) { return false } @@ -722,6 +896,75 @@ func (this *HashedParams) Equal(that interface{}) bool { } return true } +func (this *SynchronyParams) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*SynchronyParams) + if !ok { + that2, ok := that.(SynchronyParams) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Precision != nil && that1.Precision != nil { + if *this.Precision != *that1.Precision { + return false + } + } else if this.Precision != nil { + return false + } else if that1.Precision != nil { + return false + } + if this.MessageDelay != nil && that1.MessageDelay != nil { + if *this.MessageDelay != *that1.MessageDelay { + return false + } + } else if this.MessageDelay != nil { + return false + } else if that1.MessageDelay != nil { + return false + } + return true +} +func (this *FeatureParams) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*FeatureParams) + if !ok { + that2, ok := that.(FeatureParams) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.VoteExtensionsEnableHeight.Equal(that1.VoteExtensionsEnableHeight) { + return false + } + if !this.PbtsEnableHeight.Equal(that1.PbtsEnableHeight) { + return false + } + if !this.SbtEnableHeight.Equal(that1.SbtEnableHeight) { + return false + } + return true +} func (this *ABCIParams) Equal(that interface{}) bool { if that == nil { return this == nil @@ -800,6 +1043,30 @@ func (m *ConsensusParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintParams(dAtA, i, uint64(size)) } i-- + dAtA[i] = 0x42 + } + if m.Feature != nil { + { + size, err := m.Feature.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + if m.Synchrony != nil { + { + size, err := m.Synchrony.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- dAtA[i] = 0x32 } if m.Abci != nil { @@ -923,12 +1190,12 @@ func (m *EvidenceParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x18 } - n7, err7 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.MaxAgeDuration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.MaxAgeDuration):]) - if err7 != nil { - return 0, err7 + n9, err9 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.MaxAgeDuration, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.MaxAgeDuration):]) + if err9 != nil { + return 0, err9 } - i -= n7 - i = encodeVarintParams(dAtA, i, uint64(n7)) + i -= n9 + i = encodeVarintParams(dAtA, i, uint64(n9)) i-- dAtA[i] = 0x12 if m.MaxAgeNumBlocks != 0 { @@ -1032,6 +1299,108 @@ func (m *HashedParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *SynchronyParams) 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 *SynchronyParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SynchronyParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.MessageDelay != nil { + n10, err10 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(*m.MessageDelay, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(*m.MessageDelay):]) + if err10 != nil { + return 0, err10 + } + i -= n10 + i = encodeVarintParams(dAtA, i, uint64(n10)) + i-- + dAtA[i] = 0x12 + } + if m.Precision != nil { + n11, err11 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(*m.Precision, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(*m.Precision):]) + if err11 != nil { + return 0, err11 + } + i -= n11 + i = encodeVarintParams(dAtA, i, uint64(n11)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *FeatureParams) 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 *FeatureParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *FeatureParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.SbtEnableHeight != nil { + { + size, err := m.SbtEnableHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.PbtsEnableHeight != nil { + { + size, err := m.PbtsEnableHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.VoteExtensionsEnableHeight != nil { + { + size, err := m.VoteExtensionsEnableHeight.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintParams(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *ABCIParams) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -1219,6 +1588,14 @@ func (m *ConsensusParams) Size() (n int) { l = m.Abci.Size() n += 1 + l + sovParams(uint64(l)) } + if m.Synchrony != nil { + l = m.Synchrony.Size() + n += 1 + l + sovParams(uint64(l)) + } + if m.Feature != nil { + l = m.Feature.Size() + n += 1 + l + sovParams(uint64(l)) + } if m.Authority != nil { l = m.Authority.Size() n += 1 + l + sovParams(uint64(l)) @@ -1300,33 +1677,71 @@ func (m *HashedParams) Size() (n int) { return n } -func (m *ABCIParams) Size() (n int) { +func (m *SynchronyParams) Size() (n int) { if m == nil { return 0 } var l int _ = l - if m.VoteExtensionsEnableHeight != 0 { - n += 1 + sovParams(uint64(m.VoteExtensionsEnableHeight)) + if m.Precision != nil { + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(*m.Precision) + n += 1 + l + sovParams(uint64(l)) + } + if m.MessageDelay != nil { + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(*m.MessageDelay) + n += 1 + l + sovParams(uint64(l)) } return n } -func (m *AuthorityParams) Size() (n int) { +func (m *FeatureParams) Size() (n int) { if m == nil { return 0 } var l int _ = l - l = len(m.Authority) - if l > 0 { + if m.VoteExtensionsEnableHeight != nil { + l = m.VoteExtensionsEnableHeight.Size() + n += 1 + l + sovParams(uint64(l)) + } + if m.PbtsEnableHeight != nil { + l = m.PbtsEnableHeight.Size() + n += 1 + l + sovParams(uint64(l)) + } + if m.SbtEnableHeight != nil { + l = m.SbtEnableHeight.Size() n += 1 + l + sovParams(uint64(l)) } return n } -func sovParams(x uint64) (n int) { - return (math_bits.Len64(x|1) + 6) / 7 +func (m *ABCIParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.VoteExtensionsEnableHeight != 0 { + n += 1 + sovParams(uint64(m.VoteExtensionsEnableHeight)) + } + return n +} + +func (m *AuthorityParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovParams(uint64(l)) + } + return n +} + +func sovParams(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 } func sozParams(x uint64) (n int) { return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63)))) @@ -1541,6 +1956,78 @@ func (m *ConsensusParams) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Synchrony", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Synchrony == nil { + m.Synchrony = &SynchronyParams{} + } + if err := m.Synchrony.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Feature", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Feature == nil { + m.Feature = &FeatureParams{} + } + if err := m.Feature.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) } @@ -2045,6 +2532,286 @@ func (m *HashedParams) Unmarshal(dAtA []byte) error { } return nil } +func (m *SynchronyParams) 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 ErrIntOverflowParams + } + 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: SynchronyParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SynchronyParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Precision", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Precision == nil { + m.Precision = new(time.Duration) + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(m.Precision, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MessageDelay", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.MessageDelay == nil { + m.MessageDelay = new(time.Duration) + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(m.MessageDelay, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *FeatureParams) 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 ErrIntOverflowParams + } + 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: FeatureParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: FeatureParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VoteExtensionsEnableHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.VoteExtensionsEnableHeight == nil { + m.VoteExtensionsEnableHeight = &types.Int64Value{} + } + if err := m.VoteExtensionsEnableHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PbtsEnableHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.PbtsEnableHeight == nil { + m.PbtsEnableHeight = &types.Int64Value{} + } + if err := m.PbtsEnableHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SbtEnableHeight", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowParams + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthParams + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthParams + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SbtEnableHeight == nil { + m.SbtEnableHeight = &types.Int64Value{} + } + if err := m.SbtEnableHeight.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipParams(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthParams + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ABCIParams) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/proto/tendermint/types/params.proto b/proto/tendermint/types/params.proto index 10e9c71122c..2e8fa97c8f4 100644 --- a/proto/tendermint/types/params.proto +++ b/proto/tendermint/types/params.proto @@ -3,6 +3,7 @@ package tendermint.types; import "gogoproto/gogo.proto"; import "google/protobuf/duration.proto"; +import "google/protobuf/wrappers.proto"; option go_package = "github.com/cometbft/cometbft/proto/tendermint/types"; option (gogoproto.equal_all) = true; @@ -15,7 +16,12 @@ message ConsensusParams { ValidatorParams validator = 3; VersionParams version = 4; ABCIParams abci = 5; - AuthorityParams authority = 6; + // NOTE(berachain): synchrony=6 and feature=7 carry the field numbers used + // by bera-v1.x so that state persisted by existing networks decodes + // unchanged. Upstream v0.39 assigns authority=6; it is moved to 8 here. + SynchronyParams synchrony = 6; + FeatureParams feature = 7; + AuthorityParams authority = 8; } // BlockParams contains limits on the block size. @@ -79,6 +85,43 @@ message HashedParams { int64 block_max_gas = 2; } +// SynchronyParams determine the validity of block timestamps. +// +// These parameters are part of the Proposer-Based Timestamps (PBTS) algorithm. +// For more information on the relationship of the synchrony parameters to +// block timestamps validity, refer to the PBTS specification: +// https://github.com/tendermint/spec/blob/master/spec/consensus/proposer-based-timestamp/README.md +message SynchronyParams { + // Bound for how skewed a proposer's clock may be from any validator on the + // network while still producing valid proposals. + google.protobuf.Duration precision = 1 [(gogoproto.stdduration) = true]; + // Bound for how long a proposal message may take to reach all validators on + // a network and still be considered valid. + google.protobuf.Duration message_delay = 2 [(gogoproto.stdduration) = true]; +} + +// FeatureParams configure the height from which features of CometBFT are enabled. +message FeatureParams { + // Height during which vote extensions will be enabled. + // + // A value of 0 means vote extensions are disabled. A value > 0 denotes + // the height at which vote extensions will be (or have been) enabled. + // + // Cannot be set to heights lower or equal to the current blockchain height. + google.protobuf.Int64Value vote_extensions_enable_height = 1 [(gogoproto.nullable) = true]; + + // Height at which Proposer-Based Timestamps (PBTS) will be enabled. + // + // A value of 0 means PBTS is disabled. A value > 0 denotes the height at + // which PBTS will be (or has been) enabled. + // + // Cannot be set to heights lower or equal to the current blockchain height. + google.protobuf.Int64Value pbts_enable_height = 2 [(gogoproto.nullable) = true]; + + // Height at which Stable Block Time (SBT) will be enabled. + google.protobuf.Int64Value sbt_enable_height = 3 [(gogoproto.nullable) = true]; +} + // ABCIParams configure functionality specific to the Application Blockchain Interface. message ABCIParams { // vote_extensions_enable_height configures the first height during which diff --git a/proto/tendermint/types/validator.pb.go b/proto/tendermint/types/validator.pb.go index 45faec7e6ea..4c79d5b58da 100644 --- a/proto/tendermint/types/validator.pb.go +++ b/proto/tendermint/types/validator.pb.go @@ -32,6 +32,15 @@ const ( BlockIDFlagAbsent BlockIDFlag = 1 BlockIDFlagCommit BlockIDFlag = 2 BlockIDFlagNil BlockIDFlag = 3 + // Aggregated commit + // Voted for the block and contains aggregated signature. + BlockIDFlagAggCommit BlockIDFlag = 4 + // Voted for the block. + BlockIDFlagAggCommitAbsent BlockIDFlag = 5 + // Voted for nil and contains aggregated signature. + BlockIDFlagAggNil BlockIDFlag = 6 + // Voted for nil. + BlockIDFlagAggNilAbsent BlockIDFlag = 7 ) var BlockIDFlag_name = map[int32]string{ @@ -39,13 +48,21 @@ var BlockIDFlag_name = map[int32]string{ 1: "BLOCK_ID_FLAG_ABSENT", 2: "BLOCK_ID_FLAG_COMMIT", 3: "BLOCK_ID_FLAG_NIL", + 4: "BLOCK_ID_FLAG_AGG_COMMIT", + 5: "BLOCK_ID_FLAG_AGG_COMMIT_ABSENT", + 6: "BLOCK_ID_FLAG_AGG_NIL", + 7: "BLOCK_ID_FLAG_AGG_NIL_ABSENT", } var BlockIDFlag_value = map[string]int32{ - "BLOCK_ID_FLAG_UNKNOWN": 0, - "BLOCK_ID_FLAG_ABSENT": 1, - "BLOCK_ID_FLAG_COMMIT": 2, - "BLOCK_ID_FLAG_NIL": 3, + "BLOCK_ID_FLAG_UNKNOWN": 0, + "BLOCK_ID_FLAG_ABSENT": 1, + "BLOCK_ID_FLAG_COMMIT": 2, + "BLOCK_ID_FLAG_NIL": 3, + "BLOCK_ID_FLAG_AGG_COMMIT": 4, + "BLOCK_ID_FLAG_AGG_COMMIT_ABSENT": 5, + "BLOCK_ID_FLAG_AGG_NIL": 6, + "BLOCK_ID_FLAG_AGG_NIL_ABSENT": 7, } func (x BlockIDFlag) String() string { @@ -117,10 +134,14 @@ func (m *ValidatorSet) GetTotalVotingPower() int64 { } type Validator struct { - Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - PubKey crypto.PublicKey `protobuf:"bytes,2,opt,name=pub_key,json=pubKey,proto3" json:"pub_key"` - 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"` + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + // NOTE(berachain): bera-v1.x serializes keys via pub_key_bytes/pub_key_type + // (the v1.x layout); pub_key is kept as a decode fallback only. + PubKey *crypto.PublicKey `protobuf:"bytes,2,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` // Deprecated: Do not use. + 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"` + PubKeyBytes []byte `protobuf:"bytes,5,opt,name=pub_key_bytes,json=pubKeyBytes,proto3" json:"pub_key_bytes,omitempty"` + PubKeyType string `protobuf:"bytes,6,opt,name=pub_key_type,json=pubKeyType,proto3" json:"pub_key_type,omitempty"` } func (m *Validator) Reset() { *m = Validator{} } @@ -163,11 +184,12 @@ func (m *Validator) GetAddress() []byte { return nil } -func (m *Validator) GetPubKey() crypto.PublicKey { +// Deprecated: Do not use. +func (m *Validator) GetPubKey() *crypto.PublicKey { if m != nil { return m.PubKey } - return crypto.PublicKey{} + return nil } func (m *Validator) GetVotingPower() int64 { @@ -184,6 +206,20 @@ func (m *Validator) GetProposerPriority() int64 { return 0 } +func (m *Validator) GetPubKeyBytes() []byte { + if m != nil { + return m.PubKeyBytes + } + return nil +} + +func (m *Validator) GetPubKeyType() string { + if m != nil { + return m.PubKeyType + } + 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"` @@ -246,39 +282,45 @@ func init() { func init() { proto.RegisterFile("tendermint/types/validator.proto", fileDescriptor_4e92274df03d3088) } var fileDescriptor_4e92274df03d3088 = []byte{ - // 502 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x93, 0xcd, 0x6e, 0xd3, 0x40, - 0x14, 0x85, 0x3d, 0x4d, 0xd5, 0x96, 0x49, 0x04, 0xce, 0xa8, 0x45, 0x91, 0xa9, 0x8c, 0xe9, 0x2a, - 0xfc, 0xc8, 0x16, 0x54, 0x88, 0x45, 0x57, 0x49, 0x4a, 0x51, 0x94, 0xc4, 0x89, 0x92, 0xb6, 0x48, - 0x6c, 0xac, 0x38, 0x19, 0xcc, 0xc8, 0x3f, 0x33, 0x1a, 0x4f, 0x52, 0xf9, 0x0d, 0x50, 0x56, 0xbc, - 0x40, 0x56, 0xb0, 0x60, 0xcd, 0x53, 0x74, 0xd9, 0x1d, 0xac, 0x10, 0x4a, 0x5e, 0x04, 0xc5, 0x6e, - 0x62, 0xb7, 0x01, 0x75, 0x77, 0x7d, 0xcf, 0x39, 0xf7, 0x7e, 0x1e, 0xe9, 0x42, 0x4d, 0xe0, 0x60, - 0x88, 0xb9, 0x4f, 0x02, 0x61, 0x88, 0x88, 0xe1, 0xd0, 0x18, 0xf7, 0x3d, 0x32, 0xec, 0x0b, 0xca, - 0x75, 0xc6, 0xa9, 0xa0, 0x48, 0x4e, 0x1d, 0x7a, 0xec, 0x50, 0x76, 0x1d, 0xea, 0xd0, 0x58, 0x34, - 0x16, 0x55, 0xe2, 0x53, 0xf6, 0x33, 0x93, 0x06, 0x3c, 0x62, 0x82, 0x1a, 0x2e, 0x8e, 0xc2, 0x44, - 0x3d, 0xf8, 0x01, 0x60, 0xe1, 0x7c, 0x39, 0xb9, 0x87, 0x05, 0x3a, 0x82, 0x70, 0xb5, 0x29, 0x2c, - 0x01, 0x2d, 0x57, 0xce, 0xbf, 0x7a, 0xa4, 0xdf, 0xde, 0xa5, 0xaf, 0x32, 0xdd, 0x8c, 0x1d, 0xbd, - 0x81, 0x3b, 0x8c, 0x53, 0x46, 0x43, 0xcc, 0x4b, 0x1b, 0x1a, 0xb8, 0x2b, 0xba, 0x32, 0xa3, 0x17, - 0x10, 0x09, 0x2a, 0xfa, 0x9e, 0x35, 0xa6, 0x82, 0x04, 0x8e, 0xc5, 0xe8, 0x05, 0xe6, 0xa5, 0x9c, - 0x06, 0xca, 0xb9, 0xae, 0x1c, 0x2b, 0xe7, 0xb1, 0xd0, 0x59, 0xf4, 0x17, 0xd0, 0xf7, 0x56, 0x53, - 0x50, 0x09, 0x6e, 0xf7, 0x87, 0x43, 0x8e, 0xc3, 0x05, 0x2e, 0x28, 0x17, 0xba, 0xcb, 0x4f, 0x74, - 0x04, 0xb7, 0xd9, 0xc8, 0xb6, 0x5c, 0x1c, 0x5d, 0xd3, 0xec, 0x67, 0x69, 0x92, 0xc7, 0xd0, 0x3b, - 0x23, 0xdb, 0x23, 0x83, 0x06, 0x8e, 0xaa, 0x9b, 0x97, 0xbf, 0x1f, 0x4b, 0xdd, 0x2d, 0x36, 0xb2, - 0x1b, 0x38, 0x42, 0x4f, 0x60, 0xe1, 0x1f, 0x30, 0xf9, 0x71, 0xca, 0x81, 0x9e, 0xc3, 0xe2, 0xf2, - 0x0f, 0x2c, 0xc6, 0x09, 0xe5, 0x44, 0x44, 0xa5, 0xcd, 0x04, 0x7a, 0x29, 0x74, 0xae, 0xfb, 0x07, - 0x2e, 0x7c, 0xd0, 0x23, 0x3e, 0xf3, 0x70, 0x4a, 0xfe, 0x3a, 0xe5, 0x03, 0x77, 0xf3, 0xfd, 0x97, - 0x6c, 0x63, 0x8d, 0xec, 0xd9, 0x4f, 0x00, 0xf3, 0x55, 0x8f, 0x0e, 0xdc, 0xfa, 0xf1, 0x89, 0xd7, - 0x77, 0xd0, 0x4b, 0xb8, 0x57, 0x6d, 0xb6, 0x6b, 0x0d, 0xab, 0x7e, 0x6c, 0x9d, 0x34, 0x2b, 0xef, - 0xac, 0x33, 0xb3, 0x61, 0xb6, 0xdf, 0x9b, 0xb2, 0xa4, 0x3c, 0x9c, 0x4c, 0x35, 0x94, 0xf1, 0x9e, - 0x05, 0x6e, 0x40, 0x2f, 0x02, 0x64, 0xc0, 0xdd, 0x9b, 0x91, 0x4a, 0xb5, 0xf7, 0xd6, 0x3c, 0x95, - 0x81, 0xb2, 0x37, 0x99, 0x6a, 0xc5, 0x4c, 0xa2, 0x62, 0x87, 0x38, 0x10, 0xeb, 0x81, 0x5a, 0xbb, - 0xd5, 0xaa, 0x9f, 0xca, 0x1b, 0x6b, 0x81, 0x1a, 0xf5, 0x7d, 0x22, 0xd0, 0x53, 0x58, 0xbc, 0x19, - 0x30, 0xeb, 0x4d, 0x39, 0xa7, 0xa0, 0xc9, 0x54, 0xbb, 0x9f, 0x71, 0x9b, 0xc4, 0x53, 0x76, 0x3e, - 0x7f, 0x55, 0xa5, 0xef, 0xdf, 0x54, 0x50, 0x6d, 0x5d, 0xce, 0x54, 0x70, 0x35, 0x53, 0xc1, 0x9f, - 0x99, 0x0a, 0xbe, 0xcc, 0x55, 0xe9, 0x6a, 0xae, 0x4a, 0xbf, 0xe6, 0xaa, 0xf4, 0xe1, 0xd0, 0x21, - 0xe2, 0xd3, 0xc8, 0xd6, 0x07, 0xd4, 0x37, 0x06, 0xd4, 0xc7, 0xc2, 0xfe, 0x28, 0xd2, 0x22, 0xb9, - 0x8b, 0xdb, 0x57, 0x65, 0x6f, 0xc5, 0xfd, 0xc3, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbb, 0x8c, - 0xe3, 0x20, 0x70, 0x03, 0x00, 0x00, + // 608 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xcd, 0x6e, 0xd3, 0x4e, + 0x14, 0xc5, 0x33, 0x49, 0x9b, 0xb6, 0x93, 0xfc, 0xff, 0xb8, 0xa3, 0x16, 0x2c, 0xb7, 0x32, 0x43, + 0x56, 0xe1, 0x43, 0x36, 0x50, 0x01, 0x42, 0x88, 0x45, 0x9d, 0xd2, 0x2a, 0x4a, 0xeb, 0x56, 0xe9, + 0x07, 0x12, 0x1b, 0x2b, 0x4e, 0x06, 0x63, 0xc5, 0xf6, 0x58, 0xf6, 0xa4, 0x95, 0xdf, 0x00, 0x79, + 0xc5, 0x0b, 0x78, 0x05, 0x0b, 0xd6, 0xbc, 0x03, 0x12, 0xcb, 0x2e, 0x59, 0xa2, 0xe6, 0x1d, 0x58, + 0x23, 0xdb, 0x71, 0xe2, 0x26, 0xa9, 0xba, 0xb3, 0xef, 0x3d, 0xe7, 0xde, 0xdf, 0xb9, 0x8b, 0x81, + 0x98, 0x11, 0xa7, 0x47, 0x3c, 0xdb, 0x74, 0x98, 0xcc, 0x02, 0x97, 0xf8, 0xf2, 0x79, 0xc7, 0x32, + 0x7b, 0x1d, 0x46, 0x3d, 0xc9, 0xf5, 0x28, 0xa3, 0x88, 0x9b, 0x28, 0xa4, 0x44, 0x21, 0xac, 0x19, + 0xd4, 0xa0, 0x49, 0x53, 0x8e, 0xbf, 0x52, 0x9d, 0xb0, 0x99, 0x9b, 0xd4, 0xf5, 0x02, 0x97, 0x51, + 0xb9, 0x4f, 0x02, 0x3f, 0xed, 0xd6, 0x7e, 0x00, 0x58, 0x3d, 0xcb, 0x26, 0x1f, 0x13, 0x86, 0xde, + 0x40, 0x38, 0xde, 0xe4, 0xf3, 0x00, 0x97, 0xea, 0x95, 0xe7, 0x1b, 0xd2, 0xf4, 0x2e, 0x69, 0xec, + 0x69, 0xe7, 0xe4, 0xe8, 0x15, 0x5c, 0x76, 0x3d, 0xea, 0x52, 0x9f, 0x78, 0x7c, 0x11, 0x83, 0xdb, + 0xac, 0x63, 0x31, 0x7a, 0x02, 0x11, 0xa3, 0xac, 0x63, 0x69, 0xe7, 0x94, 0x99, 0x8e, 0xa1, 0xb9, + 0xf4, 0x82, 0x78, 0x7c, 0x09, 0x83, 0x7a, 0xa9, 0xcd, 0x25, 0x9d, 0xb3, 0xa4, 0x71, 0x14, 0xd7, + 0x6b, 0x7f, 0x01, 0x5c, 0x19, 0x4f, 0x41, 0x3c, 0x5c, 0xea, 0xf4, 0x7a, 0x1e, 0xf1, 0x63, 0x5c, + 0x50, 0xaf, 0xb6, 0xb3, 0x5f, 0xf4, 0x1a, 0x2e, 0xb9, 0x03, 0x5d, 0xeb, 0x93, 0x60, 0x44, 0xb3, + 0x99, 0xa7, 0x49, 0x8f, 0x21, 0x1d, 0x0d, 0x74, 0xcb, 0xec, 0xb6, 0x48, 0xa0, 0x14, 0x79, 0xd0, + 0x2e, 0xbb, 0x03, 0xbd, 0x45, 0x02, 0xf4, 0x00, 0x56, 0xe7, 0xa0, 0x54, 0xce, 0x27, 0x14, 0xe8, + 0x31, 0x5c, 0xcd, 0xf8, 0x35, 0xd7, 0x33, 0xa9, 0x67, 0xb2, 0x80, 0x5f, 0x48, 0x91, 0xb3, 0xc6, + 0xd1, 0xa8, 0x8e, 0x6a, 0xf0, 0xbf, 0x11, 0x8a, 0xa6, 0x07, 0x8c, 0xf8, 0xfc, 0x62, 0x82, 0x5a, + 0x49, 0xd7, 0x29, 0x71, 0x09, 0x61, 0x58, 0xcd, 0x34, 0xf1, 0xa5, 0xf8, 0x32, 0x06, 0xf5, 0x95, + 0x36, 0x4c, 0x25, 0x27, 0x81, 0x4b, 0x6a, 0x7d, 0x78, 0xe7, 0xd8, 0xb4, 0x5d, 0x8b, 0x4c, 0xd2, + 0xbf, 0x98, 0x64, 0x04, 0xb7, 0x67, 0xbc, 0x31, 0x5f, 0x71, 0x26, 0xdf, 0xa3, 0x9f, 0x25, 0x58, + 0x51, 0x2c, 0xda, 0xed, 0x37, 0x77, 0x76, 0xad, 0x8e, 0x81, 0x9e, 0xc1, 0x75, 0x65, 0xff, 0xb0, + 0xd1, 0xd2, 0x9a, 0x3b, 0xda, 0xee, 0xfe, 0xf6, 0x9e, 0x76, 0xaa, 0xb6, 0xd4, 0xc3, 0xf7, 0x2a, + 0x57, 0x10, 0xee, 0x86, 0x11, 0x46, 0x39, 0xed, 0xa9, 0xd3, 0x77, 0xe8, 0x85, 0x83, 0x64, 0xb8, + 0x76, 0xdd, 0xb2, 0xad, 0x1c, 0xbf, 0x53, 0x4f, 0x38, 0x20, 0xac, 0x87, 0x11, 0x5e, 0xcd, 0x39, + 0xb6, 0x75, 0x9f, 0x38, 0x6c, 0xd6, 0xd0, 0x38, 0x3c, 0x38, 0x68, 0x9e, 0x70, 0xc5, 0x19, 0x43, + 0x83, 0xda, 0xb6, 0xc9, 0xd0, 0x43, 0xb8, 0x7a, 0xdd, 0xa0, 0x36, 0xf7, 0xb9, 0x92, 0x80, 0xc2, + 0x08, 0xff, 0x9f, 0x53, 0xab, 0xa6, 0x85, 0x5e, 0x42, 0x7e, 0x0a, 0x66, 0x6f, 0x3c, 0x7f, 0x41, + 0xe0, 0xc3, 0x08, 0xaf, 0xe5, 0x81, 0x8c, 0x6c, 0x45, 0x03, 0xde, 0xbf, 0xc9, 0x97, 0xe5, 0x59, + 0x14, 0xc4, 0x30, 0xc2, 0xc2, 0x3c, 0xfb, 0x28, 0xd8, 0xd3, 0xe9, 0xe3, 0xc5, 0x43, 0x62, 0xd6, + 0xf2, 0xec, 0x29, 0x8c, 0x04, 0xf7, 0x2d, 0xdc, 0x9c, 0xeb, 0xc8, 0x76, 0x2e, 0x09, 0x1b, 0x61, + 0x84, 0xef, 0xcd, 0x18, 0xd3, 0x85, 0xc2, 0xf2, 0xe7, 0xaf, 0x62, 0xe1, 0xfb, 0x37, 0x11, 0x28, + 0x07, 0xbf, 0xae, 0x44, 0x70, 0x79, 0x25, 0x82, 0x3f, 0x57, 0x22, 0xf8, 0x32, 0x14, 0x0b, 0x97, + 0x43, 0xb1, 0xf0, 0x7b, 0x28, 0x16, 0x3e, 0x6c, 0x19, 0x26, 0xfb, 0x34, 0xd0, 0xa5, 0x2e, 0xb5, + 0xe5, 0x2e, 0xb5, 0x09, 0xd3, 0x3f, 0xb2, 0xc9, 0x47, 0xfa, 0x92, 0x4c, 0xbf, 0x43, 0x7a, 0x39, + 0xa9, 0x6f, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xbd, 0x84, 0x4d, 0xf8, 0xa2, 0x04, 0x00, 0x00, } func (m *ValidatorSet) Marshal() (dAtA []byte, err error) { @@ -355,6 +397,20 @@ func (m *Validator) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.PubKeyType) > 0 { + i -= len(m.PubKeyType) + copy(dAtA[i:], m.PubKeyType) + i = encodeVarintValidator(dAtA, i, uint64(len(m.PubKeyType))) + i-- + dAtA[i] = 0x32 + } + if len(m.PubKeyBytes) > 0 { + i -= len(m.PubKeyBytes) + copy(dAtA[i:], m.PubKeyBytes) + i = encodeVarintValidator(dAtA, i, uint64(len(m.PubKeyBytes))) + i-- + dAtA[i] = 0x2a + } if m.ProposerPriority != 0 { i = encodeVarintValidator(dAtA, i, uint64(m.ProposerPriority)) i-- @@ -365,16 +421,18 @@ func (m *Validator) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x18 } - { - size, err := m.PubKey.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err + if m.PubKey != nil { + { + size, err := m.PubKey.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintValidator(dAtA, i, uint64(size)) } - i -= size - i = encodeVarintValidator(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0x12 } - i-- - dAtA[i] = 0x12 if len(m.Address) > 0 { i -= len(m.Address) copy(dAtA[i:], m.Address) @@ -468,14 +526,24 @@ func (m *Validator) Size() (n int) { if l > 0 { n += 1 + l + sovValidator(uint64(l)) } - l = m.PubKey.Size() - n += 1 + l + sovValidator(uint64(l)) + if m.PubKey != nil { + l = m.PubKey.Size() + n += 1 + l + sovValidator(uint64(l)) + } if m.VotingPower != 0 { n += 1 + sovValidator(uint64(m.VotingPower)) } if m.ProposerPriority != 0 { n += 1 + sovValidator(uint64(m.ProposerPriority)) } + l = len(m.PubKeyBytes) + if l > 0 { + n += 1 + l + sovValidator(uint64(l)) + } + l = len(m.PubKeyType) + if l > 0 { + n += 1 + l + sovValidator(uint64(l)) + } return n } @@ -732,6 +800,9 @@ func (m *Validator) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } + if m.PubKey == nil { + m.PubKey = &crypto.PublicKey{} + } if err := m.PubKey.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } @@ -774,6 +845,72 @@ func (m *Validator) Unmarshal(dAtA []byte) error { break } } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKeyBytes", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowValidator + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthValidator + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthValidator + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PubKeyBytes = append(m.PubKeyBytes[:0], dAtA[iNdEx:postIndex]...) + if m.PubKeyBytes == nil { + m.PubKeyBytes = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PubKeyType", 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.PubKeyType = 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 18639779eef..4659b68f76d 100644 --- a/proto/tendermint/types/validator.proto +++ b/proto/tendermint/types/validator.proto @@ -15,6 +15,16 @@ enum BlockIDFlag { BLOCK_ID_FLAG_ABSENT = 1 [(gogoproto.enumvalue_customname) = "BlockIDFlagAbsent"]; // the vote was not received BLOCK_ID_FLAG_COMMIT = 2 [(gogoproto.enumvalue_customname) = "BlockIDFlagCommit"]; // voted for the block that received the majority BLOCK_ID_FLAG_NIL = 3 [(gogoproto.enumvalue_customname) = "BlockIDFlagNil"]; // voted for nil + + // Aggregated commit + // Voted for the block and contains aggregated signature. + BLOCK_ID_FLAG_AGG_COMMIT = 4 [(gogoproto.enumvalue_customname) = "BlockIDFlagAggCommit"]; + // Voted for the block. + BLOCK_ID_FLAG_AGG_COMMIT_ABSENT = 5 [(gogoproto.enumvalue_customname) = "BlockIDFlagAggCommitAbsent"]; + // Voted for nil and contains aggregated signature. + BLOCK_ID_FLAG_AGG_NIL = 6 [(gogoproto.enumvalue_customname) = "BlockIDFlagAggNil"]; + // Voted for nil. + BLOCK_ID_FLAG_AGG_NIL_ABSENT = 7 [(gogoproto.enumvalue_customname) = "BlockIDFlagAggNilAbsent"]; } message ValidatorSet { @@ -25,9 +35,13 @@ message ValidatorSet { message Validator { bytes address = 1; - tendermint.crypto.PublicKey pub_key = 2 [(gogoproto.nullable) = false]; + // NOTE(berachain): bera-v1.x serializes keys via pub_key_bytes/pub_key_type + // (the v1.x layout); pub_key is kept as a decode fallback only. + tendermint.crypto.PublicKey pub_key = 2 [deprecated = true]; int64 voting_power = 3; int64 proposer_priority = 4; + bytes pub_key_bytes = 5; + string pub_key_type = 6; } message SimpleValidator { diff --git a/rpc/client/evidence_test.go b/rpc/client/evidence_test.go index b25f02d5234..23af080179f 100644 --- a/rpc/client/evidence_test.go +++ b/rpc/client/evidence_test.go @@ -22,15 +22,9 @@ import ( "github.com/cometbft/cometbft/types" ) -// For some reason the empty node used in tests has a time of -// 2018-10-10 08:20:13.695936996 +0000 UTC -// this is because the test genesis time is set here -// so in order to validate evidence we need evidence to be the same time -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, + chainID string, evTime time.Time, ) *types.DuplicateVoteEvidence { var err error @@ -46,7 +40,7 @@ func newEvidence(t *testing.T, val *privval.FilePV, validator := types.NewValidator(val.Key.PubKey, 10) valSet := types.NewValidatorSet([]*types.Validator{validator}) - ev, err := types.NewDuplicateVoteEvidence(vote, vote2, defaultTestTime, valSet) + ev, err := types.NewDuplicateVoteEvidence(vote, vote2, evTime, valSet) require.NoError(t, err) return ev } @@ -55,6 +49,7 @@ func makeEvidences( t *testing.T, val *privval.FilePV, chainID string, + evTime time.Time, ) (correct *types.DuplicateVoteEvidence, fakes []*types.DuplicateVoteEvidence) { vote := types.Vote{ ValidatorAddress: val.Key.Address, @@ -62,7 +57,6 @@ func makeEvidences( Height: 1, Round: 0, Type: cmtproto.PrevoteType, - Timestamp: defaultTestTime, BlockID: types.BlockID{ Hash: tmhash.Sum(cmtrand.Bytes(tmhash.Size)), PartSetHeader: types.PartSetHeader{ @@ -74,7 +68,7 @@ func makeEvidences( vote2 := vote vote2.BlockID.Hash = tmhash.Sum([]byte("blockhash2")) - correct = newEvidence(t, val, &vote, &vote2, chainID) + correct = newEvidence(t, val, &vote, &vote2, chainID, evTime) fakes = make([]*types.DuplicateVoteEvidence, 0) @@ -82,34 +76,34 @@ func makeEvidences( { v := vote2 v.ValidatorAddress = []byte("some_address") - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, evTime)) } // different height { v := vote2 v.Height = vote.Height + 1 - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, evTime)) } // different round { v := vote2 v.Round = vote.Round + 1 - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, evTime)) } // different type { v := vote2 v.Type = cmtproto.PrecommitType - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, evTime)) } // exactly same vote { v := vote - fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID)) + fakes = append(fakes, newEvidence(t, val, &vote, &v, chainID, evTime)) } return correct, fakes @@ -123,9 +117,18 @@ func TestBroadcastEvidence_DuplicateVoteEvidence(t *testing.T) { ) for i, c := range GetClients() { - correct, fakes := makeEvidences(t, pv, chainID) t.Logf("client %d", i) + // The evidence is for height 1; make sure the node has committed the + // block at that height before broadcasting. Under PBTS the block time + // is the proposer's wall clock, so the evidence must be stamped with + // the actual block time. + require.NoError(t, client.WaitForHeight(c, 1, nil)) + h := int64(1) + blk, err := c.Block(context.Background(), &h) + require.NoError(t, err) + correct, fakes := makeEvidences(t, pv, chainID, blk.Block.Time) + result, err := c.BroadcastEvidence(context.Background(), correct) require.NoError(t, err, "BroadcastEvidence(%s) failed", correct) assert.Equal(t, correct.Hash(), result.Hash, "expected result hash to match evidence hash") diff --git a/spec/abci/abci++_methods.md b/spec/abci/abci++_methods.md index 92a7314abc0..cb5b947bf28 100644 --- a/spec/abci/abci++_methods.md +++ b/spec/abci/abci++_methods.md @@ -325,6 +325,7 @@ title: Methods | time | [google.protobuf.Timestamp][protobuf-timestamp] | Timestamp of the block that will be proposed. | 6 | | next_validators_hash | bytes | Merkle root of the next validator set. | 7 | | proposer_address | bytes | [Address](../core/data_structures.md#address) of the validator that is creating the proposal. | 8 | + | syncing_to_height | int64 | If the node is syncing/replaying blocks - target height. If not, syncing_to == height. | 9 | * **Response**: diff --git a/state/execution.go b/state/execution.go index b21acec2f29..0867a95db04 100644 --- a/state/execution.go +++ b/state/execution.go @@ -3,12 +3,12 @@ package state import ( "bytes" "context" + "errors" "fmt" "sync/atomic" "time" abci "github.com/cometbft/cometbft/abci/types" - cryptoenc "github.com/cometbft/cometbft/crypto/encoding" "github.com/cometbft/cometbft/libs/fail" "github.com/cometbft/cometbft/libs/log" "github.com/cometbft/cometbft/mempool" @@ -155,7 +155,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock( &abci.RequestPrepareProposal{ MaxTxBytes: maxDataBytes, Txs: block.Txs.ToSliceOfBytes(), - LocalLastCommit: blockExec.buildExtendedCommitInfo(lastExtCommit, state.InitialHeight, state.ConsensusParams.ABCI), + LocalLastCommit: blockExec.buildExtendedCommitInfo(lastExtCommit, state.InitialHeight, state.ConsensusParams.Feature), Misbehavior: block.Evidence.Evidence.ToABCI(), Height: block.Height, Time: block.Time, @@ -188,14 +188,15 @@ func (blockExec *BlockExecutor) ProcessProposal( state State, ) (bool, error) { resp, err := blockExec.proxyApp.ProcessProposal(context.TODO(), &abci.RequestProcessProposal{ - Hash: block.Header.Hash(), - Height: block.Height, - Time: block.Time, - Txs: block.Txs.ToSliceOfBytes(), - ProposedLastCommit: blockExec.buildLastCommitInfo(block, state.InitialHeight), - Misbehavior: block.Evidence.Evidence.ToABCI(), - ProposerAddress: block.ProposerAddress, - NextValidatorsHash: block.NextValidatorsHash, + Hash: block.Header.Hash(), + Height: block.Height, + Time: block.Time, + Txs: block.Txs.ToSliceOfBytes(), + ProposedLastCommit: blockExec.buildLastCommitInfo(block, state.InitialHeight), + Misbehavior: block.Evidence.Evidence.ToABCI(), + ProposerAddress: block.ProposerAddress, + NextValidatorsHash: block.NextValidatorsHash, + NextProposerAddress: state.NextValidators.GetProposer().Address, }) if err != nil { return false, err @@ -253,9 +254,9 @@ func withSkipLastCommit(opts *blockValidationOptions) { // ApplyVerifiedBlock does the same as `ApplyBlock`, but skips verification. func (blockExec *BlockExecutor) ApplyVerifiedBlock( - state State, blockID types.BlockID, block *types.Block, + state State, blockID types.BlockID, block *types.Block, syncingToHeight int64, ) (State, error) { - return blockExec.applyBlock(state, blockID, block) + return blockExec.applyBlock(state, blockID, block, syncingToHeight) } // ApplyBlock validates the block against the state, executes it against the app, @@ -265,7 +266,7 @@ func (blockExec *BlockExecutor) ApplyVerifiedBlock( // from outside this package to process and commit an entire block. // It takes a blockID to avoid recomputing the parts hash. func (blockExec *BlockExecutor) ApplyBlock( - state State, blockID types.BlockID, block *types.Block, + state State, blockID types.BlockID, block *types.Block, syncingToHeight int64, ) (State, error) { lastValidated := blockExec.GetLastValidatedBlock() @@ -282,10 +283,10 @@ func (blockExec *BlockExecutor) ApplyBlock( blockExec.setLastValidatedBlock(lastValidated, block) } - return blockExec.applyBlock(state, blockID, block) + return blockExec.applyBlock(state, blockID, block, syncingToHeight) } -func (blockExec *BlockExecutor) applyBlock(state State, blockID types.BlockID, block *types.Block) (State, error) { +func (blockExec *BlockExecutor) applyBlock(state State, blockID types.BlockID, block *types.Block, syncingToHeight int64) (State, error) { startTime := time.Now().UnixNano() abciResponse, err := blockExec.proxyApp.FinalizeBlock(context.TODO(), &abci.RequestFinalizeBlock{ Hash: block.Hash(), @@ -296,6 +297,7 @@ func (blockExec *BlockExecutor) applyBlock(state State, blockID types.BlockID, b DecidedLastCommit: blockExec.buildLastCommitInfo(block, state.InitialHeight), Misbehavior: block.Evidence.Evidence.ToABCI(), Txs: block.Txs.ToSliceOfBytes(), + SyncingToHeight: syncingToHeight, }) endTime := time.Now().UnixNano() blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000) @@ -310,6 +312,7 @@ func (blockExec *BlockExecutor) applyBlock(state State, blockID types.BlockID, b "num_txs_res", len(abciResponse.TxResults), "num_val_updates", len(abciResponse.ValidatorUpdates), "block_app_hash", fmt.Sprintf("%X", abciResponse.AppHash), + "syncing_to_height", syncingToHeight, ) // Assert that the application correctly returned tx results for each of the transactions provided in the block @@ -317,8 +320,6 @@ func (blockExec *BlockExecutor) applyBlock(state State, blockID types.BlockID, b return state, fmt.Errorf("expected tx results length to match size of transactions in block. Expected %d, got %d", len(block.Txs), len(abciResponse.TxResults)) } - blockExec.logger.Info("executed block", "height", block.Height, "app_hash", fmt.Sprintf("%X", abciResponse.AppHash)) - fail.Fail() // XXX // Save the results before we commit. @@ -346,6 +347,10 @@ func (blockExec *BlockExecutor) applyBlock(state State, blockID types.BlockID, b blockExec.metrics.ConsensusParamUpdates.Add(1) } + if err := validateNextBlockDelay(abciResponse.NextBlockDelay); err != nil { + return state, fmt.Errorf("error in next block delay: %w", err) + } + // Update the state with the block and responses. state, err = updateState(state, blockID, &block.Header, abciResponse, validatorUpdates) if err != nil { @@ -599,7 +604,7 @@ func (blockExec *BlockExecutor) buildLastCommitInfo(block *types.Block, initialH return BuildLastCommitInfo(block, lastValSet, initialHeight) } -func (blockExec *BlockExecutor) buildExtendedCommitInfo(ec *types.ExtendedCommit, initialHeight int64, ap types.ABCIParams) abci.ExtendedCommitInfo { +func (blockExec *BlockExecutor) buildExtendedCommitInfo(ec *types.ExtendedCommit, initialHeight int64, fp types.FeatureParams) abci.ExtendedCommitInfo { if ec.Height < initialHeight { return abci.ExtendedCommitInfo{} } @@ -607,7 +612,7 @@ func (blockExec *BlockExecutor) buildExtendedCommitInfo(ec *types.ExtendedCommit if err != nil { panic(fmt.Errorf("failed to load validator set at height %d, initial height %d: %w", ec.Height, initialHeight, err)) } - return BuildExtendedCommitInfo(ec, valSet, initialHeight, ap) + return BuildExtendedCommitInfo(ec, valSet, initialHeight, fp) } // BuildLastCommitInfo builds a CommitInfo from the given block and validator set. @@ -652,7 +657,7 @@ func BuildLastCommitInfo(block *types.Block, lastValSet *types.ValidatorSet, ini // BuildExtendedCommitInfo builds an ExtendedCommitInfo from the given block and validator set. // If you want to load the validator set from the store instead of providing it, // use blockExec.buildExtendedCommitInfo. -func BuildExtendedCommitInfo(ec *types.ExtendedCommit, valSet *types.ValidatorSet, initialHeight int64, ap types.ABCIParams) abci.ExtendedCommitInfo { +func BuildExtendedCommitInfo(ec *types.ExtendedCommit, valSet *types.ValidatorSet, initialHeight int64, fp types.FeatureParams) abci.ExtendedCommitInfo { if ec.Height < initialHeight { // There are no extended commits for heights below the initial height. return abci.ExtendedCommitInfo{} @@ -689,7 +694,7 @@ func BuildExtendedCommitInfo(ec *types.ExtendedCommit, valSet *types.ValidatorSe // during that height, we ensure they are present and deliver the data to // the proposer. If they were not enabled during this previous height, we // will not deliver extension data. - if err := ecs.EnsureExtension(ap.VoteExtensionsEnabled(ec.Height)); err != nil { + if err := ecs.EnsureExtension(fp.VoteExtensionsEnabled(ec.Height)); err != nil { panic(fmt.Errorf("commit at height %d has problems with vote extension data; err %w", ec.Height, err)) } @@ -719,8 +724,10 @@ func validateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, continue } - // Check if validator's pubkey matches an ABCI type in the consensus params - pk, err := cryptoenc.PubKeyFromProto(valUpdate.PubKey) + // Check if validator's pubkey matches an ABCI type in the consensus params. + // Accept both the proto pub_key and the bera-v1.x pub_key_bytes + + // pub_key_type encoding, same as PB2TM.ValidatorUpdates below. + pk, err := types.PubKeyFromValidatorUpdate(valUpdate) if err != nil { return err } @@ -800,9 +807,17 @@ func updateState( LastHeightConsensusParamsChanged: lastHeightParamsChanged, LastResultsHash: TxResultsHash(abciResponse.TxResults), AppHash: nil, + NextBlockDelay: abciResponse.NextBlockDelay, }, nil } +func validateNextBlockDelay(nextBlockDelay time.Duration) error { + if nextBlockDelay < 0 { + return errors.New("negative duration") + } + return nil +} + // Fire NewBlock, NewBlockHeader. // Fire TxEvent for every tx. // NOTE: if CometBFT crashes before commit, some or all of these events may be published again. @@ -878,7 +893,7 @@ func ExecCommitBlock( block *types.Block, logger log.Logger, store Store, - initialHeight int64, + initialHeight, finalHeight int64, ) ([]byte, error) { commitInfo := buildLastCommitInfoFromStore(block, store, initialHeight) @@ -891,6 +906,7 @@ func ExecCommitBlock( DecidedLastCommit: commitInfo, Misbehavior: block.Evidence.Evidence.ToABCI(), Txs: block.Txs.ToSliceOfBytes(), + SyncingToHeight: finalHeight, }) if err != nil { logger.Error("error in proxyAppConn.FinalizeBlock", "err", err) diff --git a/state/execution_test.go b/state/execution_test.go index 3249deae503..af3012d17e9 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -73,7 +73,7 @@ func TestApplyBlock(t *testing.T) { require.NoError(t, err) blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} - state, err = blockExec.ApplyBlock(state, blockID, block) + state, err = blockExec.ApplyBlock(state, blockID, block, block.Height) require.Nil(t, err) // TODO check state and mempool @@ -147,7 +147,7 @@ func TestFinalizeBlockDecidedLastCommit(t *testing.T) { bps, err := block.MakePartSet(testPartSize) require.NoError(t, err) blockID := types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} - _, err = blockExec.ApplyBlock(state, blockID, block) + _, err = blockExec.ApplyBlock(state, blockID, block, block.Height) require.NoError(t, err) require.True(t, app.LastTime.After(baseTime)) @@ -184,7 +184,6 @@ func TestFinalizeBlockValidators(t *testing.T) { CommitSig: types.CommitSig{ BlockIDFlag: types.BlockIDFlagCommit, ValidatorAddress: state.Validators.Validators[0].Address, - Timestamp: now, Signature: []byte("Signature1"), }, Extension: []byte("extension1"), @@ -195,7 +194,6 @@ func TestFinalizeBlockValidators(t *testing.T) { CommitSig: types.CommitSig{ BlockIDFlag: types.BlockIDFlagCommit, ValidatorAddress: state.Validators.Validators[1].Address, - Timestamp: now, Signature: []byte("Signature2"), }, Extension: []byte("extension2"), @@ -226,7 +224,7 @@ func TestFinalizeBlockValidators(t *testing.T) { block, err := makeBlock(state, 2, lastCommit.ToCommit()) require.NoError(t, err) - _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1) + _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1, block.Height) require.NoError(t, err, tc.desc) require.True(t, !tc.shouldHaveTime || @@ -297,7 +295,6 @@ func TestFinalizeBlockMisbehavior(t *testing.T) { Signatures: []types.CommitSig{{ BlockIDFlag: types.BlockIDFlagNil, ValidatorAddress: crypto.AddressHash([]byte("validator_address")), - Timestamp: defaultEvidenceTime, Signature: crypto.CRandBytes(types.MaxSignatureSize), }}, }, @@ -359,7 +356,7 @@ func TestFinalizeBlockMisbehavior(t *testing.T) { blockID = types.BlockID{Hash: block.Hash(), PartSetHeader: bps.Header()} - _, err = blockExec.ApplyBlock(state, blockID, block) + _, err = blockExec.ApplyBlock(state, blockID, block, block.Height) require.NoError(t, err) // TODO check state and mempool @@ -440,8 +437,9 @@ func TestProcessProposal(t *testing.T) { Round: 0, Votes: voteInfos, }, - NextValidatorsHash: block1.NextValidatorsHash, - ProposerAddress: block1.ProposerAddress, + NextValidatorsHash: block1.NextValidatorsHash, + ProposerAddress: block1.ProposerAddress, + NextProposerAddress: state.NextValidators.GetProposer().Address, } acceptBlock, err := blockExec.ProcessProposal(block1, state) @@ -493,6 +491,12 @@ func TestValidateValidatorUpdates(t *testing.T) { defaultValidatorParams, true, }, + { + "adding a validator in bera-v1.x form (pub_key_bytes + pub_key_type) is OK", + []abci.ValidatorUpdate{{PubKeyType: pubkey2.Type(), PubKeyBytes: pubkey2.Bytes(), Power: 20}}, + defaultValidatorParams, + false, + }, } for _, tc := range testCases { @@ -644,7 +648,7 @@ func TestFinalizeBlockValidatorUpdates(t *testing.T) { {PubKey: pk, Power: 10}, } - state, err = blockExec.ApplyBlock(state, blockID, block) + state, err = blockExec.ApplyBlock(state, blockID, block, block.Height) require.NoError(t, err) // test new validator was added to NextValidators if assert.Equal(t, state.Validators.Size()+1, state.NextValidators.Size()) { @@ -707,7 +711,7 @@ func TestFinalizeBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { {PubKey: vp, Power: 0}, } - assert.NotPanics(t, func() { state, err = blockExec.ApplyBlock(state, blockID, block) }) + assert.NotPanics(t, func() { state, err = blockExec.ApplyBlock(state, blockID, block, block.Height) }) assert.Error(t, err) assert.NotEmpty(t, state.NextValidators.Validators) } @@ -1073,7 +1077,7 @@ func TestCreateProposalAbsentVoteExtensions(t *testing.T) { stateStore := sm.NewStore(stateDB, sm.StoreOptions{ DiscardABCIResponses: false, }) - state.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testCase.extensionEnableHeight + state.ConsensusParams.Feature.VoteExtensionsEnableHeight = testCase.extensionEnableHeight mp := &mpmocks.Mempool{} mp.On("Lock").Return() mp.On("Unlock").Return() diff --git a/state/helpers_test.go b/state/helpers_test.go index 89bdf932c3d..aad91758af0 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -72,7 +72,7 @@ func makeAndApplyGoodBlock(state sm.State, height int64, lastCommit *types.Commi Hash: block.Hash(), PartSetHeader: partSet.Header(), } - state, err = blockExec.ApplyBlock(state, blockID, block) + state, err = blockExec.ApplyBlock(state, blockID, block, block.Height) if err != nil { return state, types.BlockID{}, err } diff --git a/state/state.go b/state/state.go index 7b2c70effc1..265446caec1 100644 --- a/state/state.go +++ b/state/state.go @@ -77,6 +77,10 @@ type State struct { // the latest AppHash we've received from calling abci.Commit() AppHash []byte + + // delay between the time when this block is committed and the next height is started. + // previously `timeout_commit` in config.toml + NextBlockDelay time.Duration } // Copy makes a copy of the State for mutating. @@ -101,6 +105,8 @@ func (state State) Copy() State { AppHash: state.AppHash, LastResultsHash: state.LastResultsHash, + + NextBlockDelay: state.NextBlockDelay, } } @@ -169,6 +175,7 @@ func (state *State) ToProto() (*cmtstate.State, error) { sm.LastHeightConsensusParamsChanged = state.LastHeightConsensusParamsChanged sm.LastResultsHash = state.LastResultsHash sm.AppHash = state.AppHash + sm.NextBlockDelay = state.NextBlockDelay return sm, nil } @@ -220,6 +227,7 @@ func FromProto(pb *cmtstate.State) (*State, error) { //nolint:golint state.LastHeightConsensusParamsChanged = pb.LastHeightConsensusParamsChanged state.LastResultsHash = pb.LastResultsHash state.AppHash = pb.AppHash + state.NextBlockDelay = pb.NextBlockDelay return state, nil } @@ -243,9 +251,16 @@ func (state State) MakeBlock( // Set time. var timestamp time.Time - if height == state.InitialHeight { + switch { + case state.ConsensusParams.Feature.PbtsEnabled(height): + // Under Proposer-Based Timestamps the proposer stamps the block with + // its local time. With BLS signature aggregation the per-vote + // timestamps that BFT Time medians over are not signed, so PBTS is + // the only viable block-time source for aggregating networks. + timestamp = cmttime.Now() + case height == state.InitialHeight: timestamp = state.LastBlockTime // genesis time - } else { + default: ts, err := MedianTime(lastCommit, state.LastValidators) if err != nil { return nil, fmt.Errorf("error making block while calculating median time: %w", err) diff --git a/state/store.go b/state/store.go index e2052382b57..cf681623c37 100644 --- a/state/store.go +++ b/state/store.go @@ -517,6 +517,10 @@ func (store dbStore) SaveFinalizeBlockResponse(height int64, resp *abci.Response } resp.TxResults = dtxs + // Carry both public key encodings in persisted validator updates, so the + // response can be read by both this fork and the bera-v1.x line. + resp.ValidatorUpdates = types.NormalizeValidatorUpdates(resp.ValidatorUpdates) + // If the flag is false then we save the ABCIResponse. This can be used for the /BlockResults // query or to reindex an event using the command line. if !store.DiscardABCIResponses { diff --git a/state/store_test.go b/state/store_test.go index fb536559352..f5725f2217b 100644 --- a/state/store_test.go +++ b/state/store_test.go @@ -423,6 +423,52 @@ func TestLastFinalizeBlockResponses(t *testing.T) { }) } +// Persisted validator updates carry both public key encodings (pub_key and +// pub_key_bytes + pub_key_type) so that bera-v1.x and this fork can read each +// other's state store, and updates written by bera-v1.x (pub_key_bytes only) +// decode into validators. +func TestSaveFinalizeBlockResponseValidatorUpdatesCarryBothPubKeyEncodings(t *testing.T) { + pubkey := ed25519.GenPrivKey().PubKey() + stateStore := sm.NewStore(dbm.NewMemDB(), sm.StoreOptions{DiscardABCIResponses: false}) + + // As returned by an application built against this fork (pub_key only). + resp := &abci.ResponseFinalizeBlock{ + ValidatorUpdates: []abci.ValidatorUpdate{types.TM2PB.NewValidatorUpdate(pubkey, 7)}, + AppHash: []byte{1}, + } + require.NoError(t, stateStore.SaveFinalizeBlockResponse(5, resp)) + + for _, loaded := range []func() (*abci.ResponseFinalizeBlock, error){ + func() (*abci.ResponseFinalizeBlock, error) { return stateStore.LoadFinalizeBlockResponse(5) }, + func() (*abci.ResponseFinalizeBlock, error) { return stateStore.LoadLastFinalizeBlockResponse(5) }, + } { + got, err := loaded() + require.NoError(t, err) + require.Len(t, got.ValidatorUpdates, 1) + vu := got.ValidatorUpdates[0] + assert.NotNil(t, vu.PubKey.Sum) + assert.Equal(t, pubkey.Bytes(), vu.PubKeyBytes) + assert.Equal(t, pubkey.Type(), vu.PubKeyType) + assert.EqualValues(t, 7, vu.Power) + vals, err := types.PB2TM.ValidatorUpdates(got.ValidatorUpdates) + require.NoError(t, err) + assert.True(t, pubkey.Equals(vals[0].PubKey)) + } + + // As written by the bera-v1.x line (pub_key_bytes + pub_key_type only). + v1xResp := &abci.ResponseFinalizeBlock{ + ValidatorUpdates: []abci.ValidatorUpdate{{PubKeyBytes: pubkey.Bytes(), PubKeyType: pubkey.Type(), Power: 9}}, + } + bz, err := v1xResp.Marshal() + require.NoError(t, err) + got := new(abci.ResponseFinalizeBlock) + require.NoError(t, got.Unmarshal(bz)) + vals, err := types.PB2TM.ValidatorUpdates(got.ValidatorUpdates) + require.NoError(t, err) + assert.True(t, pubkey.Equals(vals[0].PubKey)) + assert.EqualValues(t, 9, vals[0].VotingPower) +} + func TestFinalizeBlockRecoveryUsingLegacyABCIResponses(t *testing.T) { var ( height int64 = 10 diff --git a/state/validation.go b/state/validation.go index 73e8bc1903d..f7678982ce9 100644 --- a/state/validation.go +++ b/state/validation.go @@ -8,6 +8,7 @@ import ( "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/types" + cmttime "github.com/cometbft/cometbft/types/time" ) //----------------------------------------------------- @@ -127,6 +128,14 @@ func validateBlock(state State, block *types.Block, opts ...func(*blockValidatio block.Time, time.Now(), tol, ) } + pbtsEnabled := state.ConsensusParams.Feature.PbtsEnabled(block.Height) + if pbtsEnabled { + // Times must be canonical + if cantime := cmttime.Canonical(block.Time); block.Time != cantime { + return fmt.Errorf("block time %v is not canonical", block.Time) + } + } + switch { case block.Height > state.InitialHeight: if !block.Time.After(state.LastBlockTime) { @@ -136,20 +145,33 @@ func validateBlock(state State, block *types.Block, opts ...func(*blockValidatio ) } - medianTime, err := MedianTime(block.LastCommit, state.LastValidators) - if err != nil { - return fmt.Errorf("error validating block while calculating median time: %w", err) - } - if !block.Time.Equal(medianTime) { - return fmt.Errorf("invalid block time. Expected %v, got %v", - medianTime, - block.Time, - ) + // Under PBTS the block time is the proposer's local time; its + // timeliness is enforced by the consensus prevote step, so only + // monotonicity is validated here. Without PBTS the block time must + // equal the BFT Time median of the last commit's vote timestamps. + if !pbtsEnabled { + medianTime, err := MedianTime(block.LastCommit, state.LastValidators) + if err != nil { + return fmt.Errorf("error validating block while calculating median time: %w", err) + } + if !block.Time.Equal(medianTime) { + return fmt.Errorf("invalid block time. Expected %v, got %v", + medianTime, + block.Time, + ) + } } case block.Height == state.InitialHeight: genesisTime := state.LastBlockTime - if !block.Time.Equal(genesisTime) { + if pbtsEnabled { + if block.Time.Before(genesisTime) { + return fmt.Errorf("block time %v is before genesis time %v", + block.Time, + genesisTime, + ) + } + } else if !block.Time.Equal(genesisTime) { return fmt.Errorf("block time %v is not equal to genesis time %v", block.Time, genesisTime, diff --git a/state/validation_test.go b/state/validation_test.go index bfa99f93845..e6e180c8370 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -197,9 +197,15 @@ func TestValidateBlockCommit(t *testing.T) { /* #2589: test len(block.LastCommit.Signatures) == state.LastValidators.Size() */ - _, err = makeBlock(state, height, wrongSigsCommit) - require.Error(t, err) - require.ErrorContains(t, err, "error making block") + block, err = makeBlock(state, height, wrongSigsCommit) + require.NoError(t, err) + err = blockExec.ValidateBlock(state, block) + _, isErrInvalidCommitSignatures := err.(errors.ErrInvalidCommitSignatures) + require.True(t, isErrInvalidCommitSignatures, + "expected ErrInvalidCommitSignatures at height %d, but got: %v", + height, + err, + ) } /* @@ -392,14 +398,18 @@ func TestValidateBlockTime(t *testing.T) { blockStore := store.NewBlockStore(dbm.NewMemDB()) - blockExec := sm.NewBlockExecutor( - stateStore, - log.TestingLogger(), - proxyApp.Consensus(), - mp, - sm.EmptyEvidencePool{}, - blockStore, - ) + newBlockExec := func(opts ...sm.BlockExecutorOption) *sm.BlockExecutor { + return sm.NewBlockExecutor( + stateStore, + log.TestingLogger(), + proxyApp.Consensus(), + mp, + sm.EmptyEvidencePool{}, + blockStore, + opts..., + ) + } + blockExec := newBlockExec() lastCommit := &types.Commit{} var lastExtCommit *types.ExtendedCommit @@ -412,69 +422,65 @@ func TestValidateBlockTime(t *testing.T) { lastCommit = lastExtCommit.ToCommit() } + // PBTS is always on in this fork, so the block time is the proposer's + // local time and only its monotonicity is validated here. The upstream + // BFT Time subtests (block time must equal the median of the last + // commit's vote timestamps) do not apply. + t.Run("block time before last block time", func(t *testing.T) { height := int64(3) block, err := makeBlock(state, height, lastCommit) require.NoError(t, err) - // Set time to before last block time - block.Time = block.Time.Add(-time.Millisecond * 10) + block.Time = state.LastBlockTime.Add(-time.Millisecond * 10) err = blockExec.ValidateBlock(state, block) require.ErrorContains(t, err, "not greater than last block time") }) - t.Run("block time after last block time, different than median time", func(t *testing.T) { + t.Run("block time equal to last block time", func(t *testing.T) { height := int64(3) block, err := makeBlock(state, height, lastCommit) require.NoError(t, err) - // Set time to after the median time - block.Time = block.Time.Add(time.Second) + + block.Time = state.LastBlockTime err = blockExec.ValidateBlock(state, block) - require.Error(t, err) - require.Contains(t, err.Error(), "invalid block time") + + require.ErrorContains(t, err, "not greater than last block time") }) - t.Run("block time after last block time, same as median time", func(t *testing.T) { + t.Run("block time after last block time", func(t *testing.T) { height := int64(3) block, err := makeBlock(state, height, lastCommit) require.NoError(t, err) + + block.Time = state.LastBlockTime.Add(time.Second) err = blockExec.ValidateBlock(state, block) + require.NoError(t, err) }) t.Run("block time exceeds wall clock tolerance", func(t *testing.T) { - blockExecWithTol := sm.NewBlockExecutor( - stateStore, - log.TestingLogger(), - proxyApp.Consensus(), - mp, - sm.EmptyEvidencePool{}, - blockStore, - sm.BlockExecutorWithBlockTimeTolerance(30*time.Second), - ) + blockExecWithTol := newBlockExec(sm.BlockExecutorWithBlockTimeTolerance(30 * time.Second)) height := int64(3) block, err := makeBlock(state, height, lastCommit) require.NoError(t, err) + block.Time = time.Now().Add(1000 * time.Hour) err = blockExecWithTol.ValidateBlock(state, block) + require.Error(t, err) assert.Contains(t, err.Error(), "too far in the future") }) t.Run("tolerance not set still allows valid blocks", func(t *testing.T) { - blockExecNoTol := sm.NewBlockExecutor( - stateStore, - log.TestingLogger(), - proxyApp.Consensus(), - mp, - sm.EmptyEvidencePool{}, - blockStore, - ) + blockExecNoTol := newBlockExec() height := int64(3) block, err := makeBlock(state, height, lastCommit) require.NoError(t, err) + err = blockExecNoTol.ValidateBlock(state, block) + require.NoError(t, err) }) } @@ -543,8 +549,9 @@ func TestValidateBlockInvalidCommit(t *testing.T) { }, } - _, err := makeBlock(state, height, invalidCommit) + block, err := makeBlock(state, height, invalidCommit) + require.NoError(t, err) + err = blockExec.ValidateBlock(state, block) require.Error(t, err) - require.Contains(t, err.Error(), "commit validator not found in validator set") }) } diff --git a/store/store_test.go b/store/store_test.go index 0008b25d91f..63e2d7f4d31 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -43,7 +43,6 @@ func makeTestExtCommitWithNumSigs(height int64, timestamp time.Time, numSigs int CommitSig: types.CommitSig{ BlockIDFlag: types.BlockIDFlagCommit, ValidatorAddress: cmtrand.Bytes(crypto.AddressSize), - Timestamp: timestamp, Signature: cmtrand.Bytes(64), }, ExtensionSignature: []byte("ExtensionSignature"), @@ -584,6 +583,10 @@ func TestPruneBlocks(t *testing.T) { }) state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile()) require.NoError(t, err) + // Use BFT Time for this test: with PBTS the proposer stamps blocks with + // the current time, which would keep every block inside the evidence + // max-age duration and defeat the height-based retain assertions below. + state.ConsensusParams.Feature.PbtsEnableHeight = 0 db := dbm.NewMemDB() bs := NewBlockStore(db) assert.EqualValues(t, 0, bs.Base()) diff --git a/test/compat/README.md b/test/compat/README.md new file mode 100644 index 00000000000..d72db79853a --- /dev/null +++ b/test/compat/README.md @@ -0,0 +1,121 @@ +# bera-v1.x ↔ bera-v0.40.x compatibility tests + +This directory proves that the `bera-v0.40.x` line (Berachain's PBTS + +BLS-aggregation fork re-based onto upstream CometBFT v0.40.x) stays directly +compatible with the currently deployed `bera-v1.x` network: same sign bytes, +signatures, hashes, persisted state, p2p and WAL wire format, key and genesis +files, and the ability to reuse a `bera-v1.x` data directory and to run a mixed +validator set during a rolling upgrade. + +There are three layers. + +## 1. Golden vectors (unit test, runs in CI) + +`vectors_test.go` replays a fixed set of inputs (4 BLS validators from fixed +seeds, a fixed block ID, time, chain ID, consensus params, ...) through this +fork and asserts byte-equality with `vectors/bera_v1x_vectors.json`, which was +produced by the **bera-v1.x** code for the same inputs: + +- vote / proposal sign bytes and BLS signatures (incl. a vote with a non-zero + timestamp, which must not change the sign bytes); +- aggregated commits (`MakeBLSCommit`, flags 4/5/6), individual ed25519 + commits, their proto encoding and `Commit.Hash()`; bera-v1.x commits verify + with `VerifyCommit`/`VerifyCommitLight`; +- `Header.Hash()`, `Block.Hash()`, block proto, part set, block meta; +- consensus params (`Hash()`, proto with the `synchrony`/`feature` field + numbers, JSON, param updates from `FinalizeBlock`); +- validators / validator sets (`pub_key_bytes` + `pub_key_type` proto, + `ValidatorSet.Hash()`), evidence (`DuplicateVoteEvidence` hash and proto); +- persisted state (`state.State` proto with `next_block_delay`, + `ValidatorsInfo`, `ConsensusParamsInfo`, `ABCIResponsesInfo`, + `BlockStoreState`), and a bera-v1.x `FinalizeBlock` response (validator + updates in bera-v1.x's `pub_key_bytes` form are decoded into validators); +- every consensus p2p message (`HasProposalBlockPart` at 10, whole `Commit` + at 11) and WAL entry (incl. `MsgInfo.receive_time`); +- a `genesis.json`, `priv_validator_key.json` and `priv_validator_state.json` + written by bera-v1.x (loadable here, signatures and the after-signing state + file identical), and the key files written here (identical to bera-v1.x's); +- BLS aggregate signatures. + +Run it with the BLS build tag (CI does): + +```bash +go test -tags bls12381 ./test/compat/ +``` + +Known, intentional encoding deltas (asserted in the test, harmless to +bera-v1.x): this fork's `ConsensusParams` JSON carries an extra, empty +`"authority"` object (upstream v0.39+ param; bera-v1.x ignores unknown keys), +and `ValidatorUpdate.pub_key` is a non-nullable message here so an unset key +is still encoded as an empty field 1 (bera-v1.x reserves that field and skips +it). + +### Regenerating the bera-v1.x vectors + +`gen-v1x/` is a separate Go module that builds against the bera-v1.x line +(pinned in its `go.mod` by a `replace` to a `github.com/berachain/cometbft` +pseudo-version). It needs no checkout: + +```bash +cd test/compat/gen-v1x +go run . > ../vectors/bera_v1x_vectors.json +``` + +The inputs are hard-coded identically in `gen-v1x/main.go` and +`vectors_test.go`; change both together. + +### Reverse direction (bera-v1.x decoding what this fork produces) + +```bash +BERA_COMPAT_WRITE_OUTPUTS=$PWD/test/compat/vectors/bera_v040_outputs.json \ + go test -tags bls12381 -run TestWriteV040Outputs ./test/compat/ +cd test/compat/gen-v1x && go run . -verify ../vectors/bera_v040_outputs.json +``` + +`-verify` checks with bera-v1.x code that every shared value is +byte-identical, and that the fork-specific artifacts (a `FinalizeBlock` +response as persisted here, a genesis file and key files written here, this +fork's commits/blocks/votes/state/p2p/WAL bytes) decode, validate and verify +on bera-v1.x. + +## 2. Data-dir reuse and rolling upgrade (live processes) + +`rolling_upgrade.sh` builds both binaries (bera-v0.40.x from this checkout +with `-tags bls12381,pebbledb`; bera-v1.x straight from the module pinned in +`gen-v1x/go.mod`, via `go build github.com/cometbft/cometbft/cmd/cometbft`) +and runs, with the built-in `kvstore` app and BLS validator keys generated by +the bera-v1.x binary: + +- **Phase A, data-dir reuse**: one validator runs on bera-v1.x, is stopped and + restarted on bera-v0.40.x with the same home directory (no migration), then + rolled back to bera-v1.x and forward again. Each run must replay the other + version's store/WAL, keep producing aggregated commits, serve every + historical height (`/block`, `/block_results`) and keep the app state (txs + sent through either version are queryable on the other). +- **Phase B, rolling upgrade**: a 4-validator all-BLS network starts on + bera-v1.x and validators are moved one at a time to bera-v0.40.x (1+3, 2+2, + 3+1, 4+0), with a mid-soak restart of an already-upgraded node, a tx sent + through the upgraded network, then one validator rolled back to bera-v1.x + and forward again. After every stage it checks that all four validators + signed every block of the soak, that both versions proposed blocks the other + accepted, that commits stayed aggregated with round 0, that block hashes + agree across all nodes, that the moved node still serves early blocks, and + that no consensus/p2p codec errors were logged. + +```bash +test/compat/rolling_upgrade.sh # ~3 minutes, writes a report.md +SOAK_BLOCKS=100 test/compat/rolling_upgrade.sh /tmp/bera-compat # longer soak, keep files +``` + +Note: the bera-v1.x default `db_backend` is `pebbledb` (as is beacon-kit's), +so a standalone bera-v0.40.x binary must be built with the `pebbledb` tag to +open such a data directory (beacon-kit already builds with it). + +## 3. Unit tests ported from the v0.39 re-fork (PR #51) + +The consensus-level behaviour is covered by the tests ported with the fork: +`consensus/aggregation_*_test.go` (aggregated-commit reactor, catch-up via +`AddCommit`, WAL replay restart, PBTS rejection), `consensus/pbts_test.go`, +`TestCommitMessageValidateBasic`, `crypto/bls12381/aggregation_test.go`, plus +the `ValidatorUpdate` dual-encoding tests in `types/protobuf_test.go` and +`state/store_test.go`. diff --git a/test/compat/gen-v1x/go.mod b/test/compat/gen-v1x/go.mod new file mode 100644 index 00000000000..f05bfef9493 --- /dev/null +++ b/test/compat/gen-v1x/go.mod @@ -0,0 +1,100 @@ +module github.com/berachain/cometbft/test/compat/gen-v1x + +go 1.23.6 + +// Build against the bera-v1.x line. The module path of that line is still +// github.com/cometbft/cometbft (with the api/ submodule), so both are replaced. +require ( + github.com/cometbft/cometbft v1.0.1 + github.com/cometbft/cometbft/api v1.0.0 + github.com/cosmos/gogoproto v1.7.0 +) + +require ( + github.com/DataDog/zstd v1.5.6 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect + github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect + github.com/cockroachdb/pebble v1.1.4 // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cometbft/cometbft-db v1.0.4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/dgraph-io/badger/v4 v4.5.1 // indirect + github.com/dgraph-io/ristretto/v2 v2.1.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/getsentry/sentry-go v0.31.1 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/flatbuffers v25.1.24+incompatible // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/orderedcode v0.0.1 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmhodges/levigo v1.0.0 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/linxGnu/grocksdb v1.9.8 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/minio/highwayhash v1.0.3 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasisprotocol/curve25519-voi v0.0.0-20220708102147-0a8a51822cae // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/rs/cors v1.11.1 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sasha-s/go-deadlock v0.3.5 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/stretchr/testify v1.10.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/supranational/blst v0.3.14 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect + go.etcd.io/bbolt v1.4.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.35.0 // indirect + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.4 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace ( + github.com/cometbft/cometbft => github.com/berachain/cometbft v1.0.1-0.20260812140506-d295be45b73a + github.com/cometbft/cometbft/api => github.com/berachain/cometbft/api v1.0.1-0.20260812140506-d295be45b73a +) diff --git a/test/compat/gen-v1x/go.sum b/test/compat/gen-v1x/go.sum new file mode 100644 index 00000000000..e637d8ceb18 --- /dev/null +++ b/test/compat/gen-v1x/go.sum @@ -0,0 +1,385 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY= +github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +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/adlio/schema v1.3.6 h1:k1/zc2jNfeiZBA5aFTRy37jlBIuCkXCm0XmvpzCKI9I= +github.com/adlio/schema v1.3.6/go.mod h1:qkxwLgPBd1FgLRHYVCmQT/rrBr3JH38J9LjmVzWNudg= +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/berachain/cometbft v1.0.1-0.20260812140506-d295be45b73a h1:woh93ajZfpykkAe/sXhjpr12btr1QrqyDec6TDyF4mM= +github.com/berachain/cometbft v1.0.1-0.20260812140506-d295be45b73a/go.mod h1:AB3j5W0TQmQSuwRdzWvIBQPAKquFo7VnckwETlAsuwk= +github.com/berachain/cometbft/api v1.0.1-0.20260812140506-d295be45b73a h1:yrd9Wo4fBwkwRd4jxcQObVmgWXKamk+Q/ZoXUw0J1pY= +github.com/berachain/cometbft/api v1.0.1-0.20260812140506-d295be45b73a/go.mod h1:QaK8NCB4rHDs0MdS+L+QOQsL4UM3YJ9OMCNrH+CGdA0= +github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c= +github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE= +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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 h1:pU88SPhIFid6/k0egdR5V6eALQYq2qbSmukrkgIh/0A= +github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k= +github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo= +github.com/cockroachdb/pebble v1.1.4 h1:5II1uEP4MyHLDnsrbv/EZ36arcb9Mxg3n+owhZ3GrG8= +github.com/cockroachdb/pebble v1.1.4/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/cometbft/cometbft-db v1.0.4 h1:cezb8yx/ZWcF124wqUtAFjAuDksS1y1yXedvtprUFxs= +github.com/cometbft/cometbft-db v1.0.4/go.mod h1:M+BtHAGU2XLrpUxo3Nn1nOCcnVCiLM9yx5OuT0u5SCA= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= +github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/dgraph-io/badger/v4 v4.5.1 h1:7DCIXrQjo1LKmM96YD+hLVJ2EEsyyoWxJfpdd56HLps= +github.com/dgraph-io/badger/v4 v4.5.1/go.mod h1:qn3Be0j3TfV4kPbVoK0arXCD1/nr1ftth6sbL5jxdoA= +github.com/dgraph-io/ristretto/v2 v2.1.0 h1:59LjpOJLNDULHh8MC4UaegN52lC4JnO2dITsie/Pa8I= +github.com/dgraph-io/ristretto/v2 v2.1.0/go.mod h1:uejeqfYXpUomfse0+lO+13ATz4TypQYLJZzBSAemuB4= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +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.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/getsentry/sentry-go v0.31.1 h1:ELVc0h7gwyhnXHDouXkhqTFSO5oslsRDk0++eyE0KJ4= +github.com/getsentry/sentry-go v0.31.1/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG72XcGN/p71BAY= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +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= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/flatbuffers v25.1.24+incompatible h1:4wPqL3K7GzBd1CwyhSd3usxLKOaJN/AC6puCca6Jm7o= +github.com/google/flatbuffers v25.1.24+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +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/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +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/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/linxGnu/grocksdb v1.9.8 h1:vOIKv9/+HKiqJAElJIEYv3ZLcihRxyP7Suu/Mu8Dxjs= +github.com/linxGnu/grocksdb v1.9.8/go.mod h1:C3CNe9UYc9hlEM2pC82AqiGS3LRW537u9LFV4wIZuHk= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= +github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oasisprotocol/curve25519-voi v0.0.0-20220708102147-0a8a51822cae h1:FatpGJD2jmJfhZiFDElaC0QhZUDQnxUeAwTGkfAHN3I= +github.com/oasisprotocol/curve25519-voi v0.0.0-20220708102147-0a8a51822cae/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s= +github.com/onsi/ginkgo v1.6.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/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/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.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= +github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= +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/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +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/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= +github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk= +go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= +golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +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= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/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-20190620200207-3b0461eec859/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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +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-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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +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= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +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.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +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= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/test/compat/gen-v1x/main.go b/test/compat/gen-v1x/main.go new file mode 100644 index 00000000000..19e2a22b087 --- /dev/null +++ b/test/compat/gen-v1x/main.go @@ -0,0 +1,885 @@ +// Command gen-v1x produces the bera-v1.x golden vectors consumed by +// test/compat/vectors_test.go. +// +// It must be built against the bera-v1.x line (see go.mod in this directory), +// and it writes a JSON document to stdout. Every value is derived from fixed +// inputs (the same inputs are hard-coded in vectors_test.go), so the output is +// fully deterministic and can be regenerated at any time: +// +// cd test/compat/gen-v1x && go run . > ../vectors/bera_v1x_vectors.json +// +// NOTE: this program deliberately uses only public bera-v1.x packages (types, +// privval, state, crypto, api protos). Consensus and WAL wire messages are +// built directly from their proto types. +package main + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "flag" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + gogo "github.com/cosmos/gogoproto/types" + + abci "github.com/cometbft/cometbft/abci/types" + cmtcons "github.com/cometbft/cometbft/api/cometbft/consensus/v1" + cmtbits "github.com/cometbft/cometbft/api/cometbft/libs/bits/v1" + cmtstate "github.com/cometbft/cometbft/api/cometbft/state/v1" + cmtstore "github.com/cometbft/cometbft/api/cometbft/store/v1" + cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1" + cmtversion "github.com/cometbft/cometbft/api/cometbft/version/v1" + "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/bls12381" + "github.com/cometbft/cometbft/crypto/ed25519" + cmtjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/privval" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/types" +) + +// Fixed inputs. Keep in sync with vectors_test.go. +const ( + chainID = "bera-compat-1" + height = int64(1234) + round = int32(2) + numVals = 4 + blsSeed = "bera-compat-bls-val-" + ed25Seed = "bera-compat-ed25519-val" +) + +var ( + fixedTime = time.Date(2026, 1, 2, 3, 4, 5, 600000000, time.UTC) + fixedTimeNext = fixedTime.Add(2 * time.Second) + powers = []int64{10, 20, 30, 40} +) + +func h32(label string) []byte { s := sha256.Sum256([]byte(label)); return s[:] } + +func must(err error) { + if err != nil { + panic(err) + } +} + +type out map[string]any + +func hx(b []byte) string { return hex.EncodeToString(b) } + +func main() { + verifyPath := flag.String("verify", "", "path to bera-v0.40.x outputs (written by TestWriteV040Outputs); when set, compare and decode them with bera-v1.x code instead of printing vectors") + flag.Parse() + + v := generate() + if *verifyPath != "" { + os.Exit(verify(v, *verifyPath)) + } + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + must(enc.Encode(v)) +} + +func generate() out { + v := out{} + + // ---- keys & validators ------------------------------------------------- + blsPrivs := make([]*bls12381.PrivKey, numVals) + blsVals := make([]*types.Validator, numVals) + for i := 0; i < numVals; i++ { + pk, err := bls12381.GenPrivKeyFromSecret([]byte(fmt.Sprintf("%s%d", blsSeed, i))) + must(err) + blsPrivs[i] = pk + blsVals[i] = types.NewValidator(pk.PubKey(), powers[i]) + v[fmt.Sprintf("bls_pubkey_%d", i)] = hx(pk.PubKey().Bytes()) + v[fmt.Sprintf("bls_pubkey_compressed_%d", i)] = hx(compress(pk.PubKey())) + v[fmt.Sprintf("bls_address_%d", i)] = hx(pk.PubKey().Address()) + } + blsValSet := types.NewValidatorSet(blsVals) + // NewValidatorSet sorts validators (by power, then address); keep the + // private keys aligned with the set's order so that validator index i + // always refers to blsValSet.Validators[i]. + blsVals, blsPrivs = sortKeys(blsValSet, blsVals, blsPrivs) + v["bls_valset_hash"] = hx(blsValSet.Hash()) + vsp, err := blsValSet.ToProto() + must(err) + bz, err := vsp.Marshal() + must(err) + v["bls_valset_proto"] = hx(bz) + vp, err := blsVals[0].ToProto() + must(err) + bz, err = vp.Marshal() + must(err) + v["bls_validator0_proto"] = hx(bz) + v["bls_validator0_bytes"] = hx(blsVals[0].Bytes()) // SimpleValidator encoding used by ValidatorSet.Hash + + edPriv := ed25519.GenPrivKeyFromSecret([]byte(ed25Seed)) + edVal := types.NewValidator(edPriv.PubKey(), 5) + v["ed25519_pubkey"] = hx(edPriv.PubKey().Bytes()) + v["ed25519_address"] = hx(edPriv.PubKey().Address()) + vp, err = edVal.ToProto() + must(err) + bz, err = vp.Marshal() + must(err) + v["ed25519_validator_proto"] = hx(bz) + edVals := make([]*types.Validator, numVals) + edPrivs := make([]ed25519.PrivKey, numVals) + for i := 0; i < numVals; i++ { + edPrivs[i] = ed25519.GenPrivKeyFromSecret([]byte(fmt.Sprintf("%s-%d", ed25Seed, i))) + edVals[i] = types.NewValidator(edPrivs[i].PubKey(), powers[i]) + } + edValSet := types.NewValidatorSet(edVals) + edVals, edPrivs = sortKeys(edValSet, edVals, edPrivs) + v["ed25519_valset_hash"] = hx(edValSet.Hash()) + + // ---- block id, votes, proposal ------------------------------------------- + blockID := types.BlockID{ + Hash: h32("block"), + PartSetHeader: types.PartSetHeader{Total: 3, Hash: h32("parts")}, + } + bidp := blockID.ToProto() + bz, err = bidp.Marshal() + must(err) + v["blockid_proto"] = hx(bz) + + mkVote := func(t types.SignedMsgType, bid types.BlockID, valIdx int32, addr []byte) *types.Vote { + return &types.Vote{ + Type: t, + Height: height, + Round: round, + BlockID: bid, + ValidatorAddress: addr, + ValidatorIndex: valIdx, + Timestamp: time.Time{}, // per-vote timestamps are not used on this fork + } + } + precommit := mkVote(types.PrecommitType, blockID, 0, blsVals[0].Address) + prevote := mkVote(types.PrevoteType, blockID, 0, blsVals[0].Address) + nilPrecommit := mkVote(types.PrecommitType, types.BlockID{}, 3, blsVals[3].Address) + v["vote_sign_bytes_precommit"] = hx(types.VoteSignBytes(chainID, precommit.ToProto())) + v["vote_sign_bytes_prevote"] = hx(types.VoteSignBytes(chainID, prevote.ToProto())) + v["vote_sign_bytes_nil_precommit"] = hx(types.VoteSignBytes(chainID, nilPrecommit.ToProto())) + sig, err := blsPrivs[0].Sign(types.VoteSignBytes(chainID, precommit.ToProto())) + must(err) + v["vote_bls_signature_val0_precommit"] = hx(sig) + precommit.Signature = sig + bz, err = precommit.ToProto().Marshal() + must(err) + v["vote_proto_signed_precommit"] = hx(bz) + // a vote carrying a non-zero timestamp must still sign identically (timestamp is not signed) + tsVote := mkVote(types.PrecommitType, blockID, 0, blsVals[0].Address) + tsVote.Timestamp = fixedTime + v["vote_sign_bytes_precommit_with_timestamp"] = hx(types.VoteSignBytes(chainID, tsVote.ToProto())) + + proposal := types.NewProposal(height, round, 1, blockID, fixedTime) + v["proposal_sign_bytes"] = hx(types.ProposalSignBytes(chainID, proposal.ToProto())) + psig, err := blsPrivs[1].Sign(types.ProposalSignBytes(chainID, proposal.ToProto())) + must(err) + v["proposal_bls_signature_val1"] = hx(psig) + proposal.Signature = psig + bz, err = proposal.ToProto().Marshal() + must(err) + v["proposal_proto_signed"] = hx(bz) + + // ---- aggregated commit (all-BLS set; vals 0..2 commit, val 3 nil) --------- + voteSet := types.NewVoteSet(chainID, height, round, types.PrecommitType, blsValSet) + for i := 0; i < numVals; i++ { + bid := blockID + if i == 3 { + bid = types.BlockID{} + } + vote := mkVote(types.PrecommitType, bid, int32(i), blsVals[i].Address) + vote.Signature, err = blsPrivs[i].Sign(types.VoteSignBytes(chainID, vote.ToProto())) + must(err) + added, err := voteSet.AddVote(vote) + must(err) + if !added { + panic("vote not added") + } + } + aggCommit := voteSet.MakeBLSCommit().ToCommit() + bz, err = aggCommit.ToProto().Marshal() + must(err) + v["agg_commit_proto"] = hx(bz) + v["agg_commit_hash"] = hx(aggCommit.Hash()) + for i, cs := range aggCommit.Signatures { + v[fmt.Sprintf("agg_commit_sig%d_flag", i)] = int(cs.BlockIDFlag) + } + must(blsValSet.VerifyCommit(chainID, blockID, height, aggCommit)) + + // all-commit variant (no nil votes) + voteSet2 := types.NewVoteSet(chainID, height, round, types.PrecommitType, blsValSet) + for i := 0; i < numVals; i++ { + vote := mkVote(types.PrecommitType, blockID, int32(i), blsVals[i].Address) + vote.Signature, err = blsPrivs[i].Sign(types.VoteSignBytes(chainID, vote.ToProto())) + must(err) + _, err = voteSet2.AddVote(vote) + must(err) + } + aggCommitAll := voteSet2.MakeBLSCommit().ToCommit() + bz, err = aggCommitAll.ToProto().Marshal() + must(err) + v["agg_commit_all_proto"] = hx(bz) + v["agg_commit_all_hash"] = hx(aggCommitAll.Hash()) + + // ---- individual-signature commit (ed25519 set) ------------------------------ + edVoteSet := types.NewVoteSet(chainID, height, round, types.PrecommitType, edValSet) + for i := 0; i < numVals; i++ { + bid := blockID + if i == 1 { + bid = types.BlockID{} + } + vote := mkVote(types.PrecommitType, bid, int32(i), edVals[i].Address) + vote.Signature, err = edPrivs[i].Sign(types.VoteSignBytes(chainID, vote.ToProto())) + must(err) + _, err = edVoteSet.AddVote(vote) + must(err) + } + edCommit := edVoteSet.MakeExtendedCommit(types.FeatureParams{}).ToCommit() + bz, err = edCommit.ToProto().Marshal() + must(err) + v["ed25519_commit_proto"] = hx(bz) + v["ed25519_commit_hash"] = hx(edCommit.Hash()) + + // ---- consensus params ------------------------------------------------------ + params := consensusParams() + v["params_hash"] = hx(params.Hash()) + pp := params.ToProto() + bz, err = pp.Marshal() + must(err) + v["params_proto"] = hx(bz) + bz, err = cmtjson.Marshal(params) + must(err) + v["params_json"] = string(bz) + // a consensus-param update as returned by the app (FinalizeBlock) + upd := &cmtproto.ConsensusParams{ + Block: &cmtproto.BlockParams{MaxBytes: 1, MaxGas: 2}, + Feature: &cmtproto.FeatureParams{ + SbtEnableHeight: &gogo.Int64Value{Value: 9}, + }, + Synchrony: &cmtproto.SynchronyParams{Precision: durPtr(1 * time.Second), MessageDelay: durPtr(3 * time.Second)}, + } + bz, err = upd.Marshal() + must(err) + v["params_update_proto"] = hx(bz) + updated := params.Update(upd) + v["params_updated_hash"] = hx(updated.Hash()) + pp = updated.ToProto() + bz, err = pp.Marshal() + must(err) + v["params_updated_proto"] = hx(bz) + + // ---- header / block -------------------------------------------------------- + lastBlockID := types.BlockID{Hash: h32("last-block"), PartSetHeader: types.PartSetHeader{Total: 1, Hash: h32("last-parts")}} + txs := []types.Tx{types.Tx("tx-one"), types.Tx("tx-two")} + block := types.MakeBlock(height+1, txs, aggCommit, nil) + block.Header.Version = cmtversion.Consensus{Block: 11, App: 1} + block.Header.ChainID = chainID + block.Header.Time = fixedTimeNext + block.Header.LastBlockID = lastBlockID + block.Header.ValidatorsHash = blsValSet.Hash() + block.Header.NextValidatorsHash = blsValSet.Hash() + block.Header.ConsensusHash = params.Hash() + block.Header.AppHash = h32("app") + block.Header.LastResultsHash = h32("results") + block.Header.ProposerAddress = blsVals[1].Address + // evidence hash / data hash / last commit hash are derived + block.Header.EvidenceHash = block.Evidence.Hash() + block.Header.DataHash = block.Data.Hash() + block.Header.LastCommitHash = aggCommit.Hash() + v["header_hash"] = hx(block.Header.Hash()) + hp := block.Header.ToProto() + bz, err = hp.Marshal() + must(err) + v["header_proto"] = hx(bz) + v["block_hash"] = hx(block.Hash()) + bp, err := block.ToProto() + must(err) + bz, err = bp.Marshal() + must(err) + v["block_proto"] = hx(bz) + ps, err := block.MakePartSet(types.BlockPartSizeBytes) + must(err) + v["block_partset_total"] = int(ps.Total()) + v["block_partset_hash"] = hx(ps.Hash()) + part := ps.GetPart(0) + partp, err := part.ToProto() + must(err) + bz, err = partp.Marshal() + must(err) + v["block_part0_proto"] = hx(bz) + v["data_hash"] = hx(block.Data.Hash()) + v["evidence_hash_empty"] = hx(block.Evidence.Hash()) + meta := types.NewBlockMeta(block, ps) + bz, err = meta.ToProto().Marshal() + must(err) + v["block_meta_proto"] = hx(bz) + + // ---- evidence -------------------------------------------------------------- + voteA := mkVote(types.PrecommitType, blockID, 2, blsVals[2].Address) + voteA.Signature, err = blsPrivs[2].Sign(types.VoteSignBytes(chainID, voteA.ToProto())) + must(err) + otherBlockID := types.BlockID{Hash: h32("other-block"), PartSetHeader: types.PartSetHeader{Total: 3, Hash: h32("other-parts")}} + voteB := mkVote(types.PrecommitType, otherBlockID, 2, blsVals[2].Address) + voteB.Signature, err = blsPrivs[2].Sign(types.VoteSignBytes(chainID, voteB.ToProto())) + must(err) + dve, err := types.NewDuplicateVoteEvidence(voteA, voteB, fixedTime, blsValSet) + must(err) + must(dve.ValidateBasic()) + v["dve_hash"] = hx(dve.Hash()) + evp, err := types.EvidenceToProto(dve) + must(err) + bz, err = evp.Marshal() + must(err) + v["dve_proto"] = hx(bz) + v["dve_bytes"] = hx(dve.Bytes()) + evList := types.EvidenceList{dve} + v["evidence_list_hash"] = hx(evList.Hash()) + + // ---- persisted state ------------------------------------------------------- + state := sm.State{ + Version: cmtstate.Version{ + Consensus: cmtversion.Consensus{Block: 11, App: 1}, + Software: "1.0.1", + }, + ChainID: chainID, + InitialHeight: 1, + LastBlockHeight: height, + LastBlockID: blockID, + LastBlockTime: fixedTime, + NextValidators: blsValSet.Copy(), + Validators: blsValSet.Copy(), + LastValidators: blsValSet.Copy(), + LastHeightValidatorsChanged: 100, + ConsensusParams: params, + LastHeightConsensusParamsChanged: 50, + LastResultsHash: h32("results"), + AppHash: h32("app"), + NextBlockDelay: 1500 * time.Millisecond, + } + sp, err := state.ToProto() + must(err) + bz, err = sp.Marshal() + must(err) + v["state_proto"] = hx(bz) + valInfo := &cmtstate.ValidatorsInfo{ValidatorSet: vsp, LastHeightChanged: 100} + bz, err = valInfo.Marshal() + must(err) + v["validators_info_proto"] = hx(bz) + ppp := params.ToProto() + cpInfo := &cmtstate.ConsensusParamsInfo{ConsensusParams: ppp, LastHeightChanged: 50} + bz, err = cpInfo.Marshal() + must(err) + v["consensus_params_info_proto"] = hx(bz) + + // ---- ABCI responses as persisted by bera-v1.x ------------------------------ + fbr := &abci.FinalizeBlockResponse{ + Events: []abci.Event{{Type: "t", Attributes: []abci.EventAttribute{{Key: "k", Value: "v", Index: true}}}}, + TxResults: []*abci.ExecTxResult{{Code: 0, Data: []byte("d"), GasWanted: 1, GasUsed: 1}}, + ValidatorUpdates: []abci.ValidatorUpdate{{ + PubKeyBytes: compress(blsPrivs[0].PubKey()), + PubKeyType: bls12381.KeyType, + Power: 77, + }}, + ConsensusParamUpdates: upd, + AppHash: h32("app-next"), + NextBlockDelay: 1500 * time.Millisecond, + } + bz, err = fbr.Marshal() + must(err) + v["finalize_block_response_proto"] = hx(bz) + respInfo := &cmtstate.ABCIResponsesInfo{FinalizeBlock: fbr, Height: height} + bz, err = respInfo.Marshal() + must(err) + v["abci_responses_info_proto"] = hx(bz) + v["abci_responses_info_legacy_field_check"] = "finalize_block=3" + ppr := &abci.ProcessProposalRequest{ + Hash: h32("block"), + Height: height, + Time: fixedTime, + ProposerAddress: blsVals[1].Address, + NextValidatorsHash: blsValSet.Hash(), + NextProposerAddress: blsVals[2].Address, + } + bz, err = ppr.Marshal() + must(err) + v["process_proposal_request_proto"] = hx(bz) + + // ---- block store ----------------------------------------------------------- + bss := &cmtstore.BlockStoreState{Base: 1, Height: height} + bz, err = bss.Marshal() + must(err) + v["block_store_state_proto"] = hx(bz) + + // ---- consensus p2p messages (wire) ----------------------------------------- + msgs := map[string]*cmtcons.Message{ + "p2p_new_round_step": {Sum: &cmtcons.Message_NewRoundStep{NewRoundStep: &cmtcons.NewRoundStep{ + Height: height, Round: round, Step: 3, SecondsSinceStartTime: 7, LastCommitRound: 1, + }}}, + "p2p_new_valid_block": {Sum: &cmtcons.Message_NewValidBlock{NewValidBlock: &cmtcons.NewValidBlock{ + Height: height, Round: round, BlockPartSetHeader: blockID.PartSetHeader.ToProto(), + BlockParts: bitArrayProto(3, 1), IsCommit: true, + }}}, + "p2p_proposal": {Sum: &cmtcons.Message_Proposal{Proposal: &cmtcons.Proposal{Proposal: *proposal.ToProto()}}}, + "p2p_proposal_pol": {Sum: &cmtcons.Message_ProposalPol{ProposalPol: &cmtcons.ProposalPOL{Height: height, ProposalPolRound: 1, ProposalPol: *bitArrayProto(4, 2)}}}, + "p2p_block_part": {Sum: &cmtcons.Message_BlockPart{BlockPart: &cmtcons.BlockPart{Height: height + 1, Round: round, Part: *partp}}}, + "p2p_vote": {Sum: &cmtcons.Message_Vote{Vote: &cmtcons.Vote{Vote: precommit.ToProto()}}}, + "p2p_has_vote": {Sum: &cmtcons.Message_HasVote{HasVote: &cmtcons.HasVote{Height: height, Round: round, Type: cmtproto.PrecommitType, Index: 2}}}, + "p2p_vote_set_maj23": {Sum: &cmtcons.Message_VoteSetMaj23{VoteSetMaj23: &cmtcons.VoteSetMaj23{ + Height: height, Round: round, Type: cmtproto.PrecommitType, BlockID: blockID.ToProto(), + }}}, + "p2p_vote_set_bits": {Sum: &cmtcons.Message_VoteSetBits{VoteSetBits: &cmtcons.VoteSetBits{ + Height: height, Round: round, Type: cmtproto.PrecommitType, BlockID: blockID.ToProto(), Votes: *bitArrayProto(4, 3), + }}}, + "p2p_has_proposal_block_part": {Sum: &cmtcons.Message_HasProposalBlockPart{HasProposalBlockPart: &cmtcons.HasProposalBlockPart{ + Height: height + 1, Round: round, Index: 0, + }}}, + "p2p_commit": {Sum: &cmtcons.Message_Commit{Commit: &cmtcons.Commit{Commit: aggCommit.ToProto()}}}, + } + for name, m := range msgs { + bz, err := m.Marshal() + must(err) + v[name] = hx(bz) + } + + // ---- WAL ------------------------------------------------------------------- + walVote := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_MsgInfo{MsgInfo: &cmtcons.MsgInfo{ + Msg: *msgs["p2p_vote"], + PeerID: "", + }}}, + } + bz, err = walVote.Marshal() + must(err) + v["wal_msginfo_vote_proto"] = hx(bz) + walProposalRT := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_MsgInfo{MsgInfo: &cmtcons.MsgInfo{ + Msg: *msgs["p2p_proposal"], + PeerID: "peer1", + ReceiveTime: &fixedTimeNext, + }}}, + } + bz, err = walProposalRT.Marshal() + must(err) + v["wal_msginfo_proposal_with_receive_time_proto"] = hx(bz) + walTimeout := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_TimeoutInfo{TimeoutInfo: &cmtcons.TimeoutInfo{ + Duration: 3 * time.Second, Height: height, Round: round, Step: 4, + }}}, + } + bz, err = walTimeout.Marshal() + must(err) + v["wal_timeout_proto"] = hx(bz) + walEnd := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_EndHeight{EndHeight: &cmtcons.EndHeight{Height: height}}}, + } + bz, err = walEnd.Marshal() + must(err) + v["wal_end_height_proto"] = hx(bz) + walRS := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_EventDataRoundState{EventDataRoundState: &cmtproto.EventDataRoundState{ + Height: height, Round: round, Step: "RoundStepPropose", + }}}, + } + bz, err = walRS.Marshal() + must(err) + v["wal_event_round_state_proto"] = hx(bz) + + // ---- genesis & privval files -------------------------------------------------- + genDoc := types.GenesisDoc{ + GenesisTime: fixedTime, + ChainID: chainID, + InitialHeight: 1, + ConsensusParams: ¶ms, + AppHash: h32("genesis-app"), + AppState: json.RawMessage(`{"k":"v"}`), + } + for i := 0; i < numVals; i++ { + genDoc.Validators = append(genDoc.Validators, types.GenesisValidator{ + Address: blsVals[i].Address, PubKey: blsVals[i].PubKey, Power: blsVals[i].VotingPower, Name: fmt.Sprintf("val%d", i), + }) + } + must(genDoc.ValidateAndComplete()) + tmp, err := os.MkdirTemp("", "bera-compat-gen") + must(err) + defer os.RemoveAll(tmp) + genFile := filepath.Join(tmp, "genesis.json") + must(genDoc.SaveAs(genFile)) + bz, err = os.ReadFile(genFile) + must(err) + v["genesis_json"] = string(bz) + v["genesis_validator_hash"] = hx(genDoc.ValidatorHash()) + + keyFile := filepath.Join(tmp, "priv_validator_key.json") + stateFile := filepath.Join(tmp, "priv_validator_state.json") + pv := privval.NewFilePV(blsPrivs[0], keyFile, stateFile) + pv.Save() + bz, err = os.ReadFile(keyFile) + must(err) + v["priv_validator_key_json"] = string(bz) + bz, err = os.ReadFile(stateFile) + must(err) + v["priv_validator_state_json"] = string(bz) + pvProp := types.NewProposal(height, round, -1, blockID, fixedTime).ToProto() + must(pv.SignProposal(chainID, pvProp)) + v["privval_signed_proposal_signature"] = hx(pvProp.Signature) + pvVote := mkVote(types.PrecommitType, blockID, 0, blsVals[0].Address).ToProto() + must(pv.SignVote(chainID, pvVote, false)) + v["privval_signed_vote_signature"] = hx(pvVote.Signature) + v["privval_signed_vote_proto"] = hx(mustMarshal(pvVote)) + bz, err = os.ReadFile(stateFile) + must(err) + v["priv_validator_state_json_after_signing"] = string(bz) + pvKey, err := pv.GetPubKey() + must(err) + v["privval_pubkey"] = hx(pvKey.Bytes()) + // priv key JSON via cmtjson (as used by the key file) and raw bytes + bz, err = cmtjson.Marshal(blsPrivs[0]) + must(err) + v["bls_privkey0_cmtjson"] = string(bz) + v["bls_privkey0_bytes"] = hx(blsPrivs[0].Bytes()) + pvEd := privval.NewFilePV(edPriv, filepath.Join(tmp, "ed.json"), filepath.Join(tmp, "ed_state.json")) + pvEd.Save() + bz, err = os.ReadFile(filepath.Join(tmp, "ed.json")) + must(err) + v["priv_validator_key_ed25519_json"] = string(bz) + + // ---- aggregation primitives --------------------------------------------------- + msg := []byte("bera-compat-aggregate-message") + sigs := make([][]byte, numVals) + pubs := make([]*bls12381.PubKey, numVals) + for i := 0; i < numVals; i++ { + sigs[i], err = blsPrivs[i].Sign(msg) + must(err) + pubs[i] = blsPubPtr(blsPrivs[i].PubKey()) + } + agg, err := bls12381.AggregateSignatures(sigs) + must(err) + if !bls12381.VerifyAggregateSignature(agg, pubs, msg) { + panic("aggregate does not verify") + } + v["bls_aggregate_signature"] = hx(agg) + v["bls_aggregate_message"] = string(msg) + + v["_source"] = "generated by test/compat/gen-v1x against bera-v1.x" + return v +} + +// verify loads the values produced by bera-v0.40.x for the same inputs and +// checks, with bera-v1.x code, that (a) every shared value is byte-identical +// and (b) the fork-specific artifacts decode and validate here. It prints a +// report and returns a process exit code. +func verify(v out, path string) int { + bz, err := os.ReadFile(path) + must(err) + port := map[string]any{} + must(json.Unmarshal(bz, &port)) + fails := 0 + fail := func(format string, args ...any) { + fails++ + fmt.Printf("FAIL: "+format+"\n", args...) + } + ok := func(format string, args ...any) { fmt.Printf("ok: "+format+"\n", args...) } + + // (a) shared values must be identical + shared := 0 + for k, pv := range port { + if strings.HasPrefix(k, "_") || strings.HasPrefix(k, "v040_") { + continue + } + mine, exists := v[k] + if !exists { + fail("%s: produced by bera-v0.40.x but unknown to bera-v1.x generator", k) + continue + } + shared++ + if k == "params_json" { + // bera-v0.40.x carries upstream's extra "authority" consensus-param + // object; everything bera-v1.x knows must match and the extra must be + // empty (bera-v1.x ignores unknown keys, as the genesis check below shows). + var got map[string]json.RawMessage + if err := json.Unmarshal([]byte(fmt.Sprint(pv)), &got); err != nil { + fail("params_json: %v", err) + continue + } + if string(got["authority"]) != `{"authority":""}` { + fail("params_json: unexpected authority value %s", got["authority"]) + } + delete(got, "authority") + stripped, _ := json.Marshal(got) + var mineMap map[string]json.RawMessage + must(json.Unmarshal([]byte(fmt.Sprint(mine)), &mineMap)) + mineBz, _ := json.Marshal(mineMap) + if string(stripped) != string(mineBz) { + fail("params_json: differs beyond the authority object\n bera-v1.x: %s\n bera-v0.40.x: %s", mineBz, stripped) + } + continue + } + if fmt.Sprint(mine) != fmt.Sprint(pv) { + fail("%s: differs\n bera-v1.x: %v\n bera-v0.40.x: %v", k, mine, pv) + } + } + ok("%d shared values byte-identical (unless listed above)", shared) + + hexOf := func(k string) []byte { + s, _ := port[k].(string) + b, err := hex.DecodeString(s) + if err != nil { + fail("%s: not hex: %v", k, err) + } + return b + } + strOf := func(k string) string { s, _ := port[k].(string); return s } + f := fixtureFor(v) + + // (b) fork-specific artifacts decode with bera-v1.x code + // FinalizeBlock response as persisted by bera-v0.40.x: validator updates + // must decode with the public key (pub_key_bytes/pub_key_type), and the + // extra pub_key field (reserved here) must be skipped. + { + info := new(cmtstate.ABCIResponsesInfo) + if err := info.Unmarshal(hexOf("v040_abci_responses_info_proto")); err != nil { + fail("v040_abci_responses_info_proto: decode: %v", err) + } else if info.FinalizeBlock == nil || len(info.FinalizeBlock.ValidatorUpdates) != 1 { + fail("v040_abci_responses_info_proto: missing finalize block / validator updates") + } else { + fb := info.FinalizeBlock + vals, err := types.PB2TM.ValidatorUpdates(fb.ValidatorUpdates) + switch { + case err != nil: + fail("v040 validator updates: %v", err) + case !bytes.Equal(vals[0].PubKey.Bytes(), f.blsVals[0].PubKey.Bytes()) || vals[0].VotingPower != 77: + fail("v040 validator update decoded to wrong validator %v", vals[0]) + case fb.NextBlockDelay != 1500*time.Millisecond: + fail("v040 next_block_delay = %v", fb.NextBlockDelay) + case info.Height != height: + fail("v040 abci responses height = %d", info.Height) + default: + upd := f.params.Update(fb.ConsensusParamUpdates) + if hx(upd.Hash()) != v["params_updated_hash"] { + fail("v040 consensus param update applies differently") + } else { + ok("bera-v0.40.x FinalizeBlock response decodes (validator update pubkey, next_block_delay, param update)") + } + } + } + fbr := new(abci.FinalizeBlockResponse) + if err := fbr.Unmarshal(hexOf("v040_finalize_block_response_proto")); err != nil { + fail("v040_finalize_block_response_proto: decode: %v", err) + } + } + // genesis.json written by bera-v0.40.x (carries an extra "authority" + // consensus-param object, which must be ignored here) + { + gd, err := types.GenesisDocFromJSON([]byte(strOf("v040_genesis_json"))) + switch { + case err != nil: + fail("v040_genesis_json: parse: %v", err) + case gd.ValidateAndComplete() != nil: + fail("v040_genesis_json: validate: %v", gd.ValidateAndComplete()) + case hx(gd.ValidatorHash()) != v["genesis_validator_hash"]: + fail("v040_genesis_json: validator hash differs") + case hx(gd.ConsensusParams.Hash()) != v["params_hash"]: + fail("v040_genesis_json: consensus params hash differs") + case gd.ChainID != chainID || !gd.GenesisTime.Equal(fixedTime): + fail("v040_genesis_json: chain id / time differ") + default: + ok("bera-v0.40.x genesis.json parses (validator hash, params hash, chain id, time)") + } + } + // privval key/state files written by bera-v0.40.x + { + tmp, err := os.MkdirTemp("", "bera-compat-verify") + must(err) + defer os.RemoveAll(tmp) + kf, sf := filepath.Join(tmp, "k.json"), filepath.Join(tmp, "s.json") + must(os.WriteFile(kf, []byte(strOf("v040_priv_validator_key_json")), 0o600)) + must(os.WriteFile(sf, []byte(strOf("v040_priv_validator_state_json")), 0o600)) + pv := privval.LoadFilePV(kf, sf) + pub, err := pv.GetPubKey() + if err != nil || !bytes.Equal(pub.Bytes(), f.blsVals[0].PubKey.Bytes()) { + fail("v040 priv_validator_key.json: wrong key (%v)", err) + } else { + vote := &types.Vote{Type: types.PrecommitType, Height: height, Round: round, BlockID: f.blockID, + ValidatorAddress: f.blsVals[0].Address, ValidatorIndex: 0} + pb := vote.ToProto() + if err := pv.SignVote(chainID, pb, false); err != nil { + fail("v040 priv_validator_key.json: sign: %v", err) + } else if hx(pb.Signature) != v["privval_signed_vote_signature"] { + fail("v040 priv_validator_key.json: signature differs") + } else { + ok("bera-v0.40.x priv_validator_key.json loads and signs identically") + } + } + } + // consensus-critical bera-v0.40.x bytes decode and verify here + { + pc := new(cmtproto.Commit) + if err := pc.Unmarshal(hexOf("agg_commit_proto")); err != nil { + fail("agg_commit_proto: decode: %v", err) + } else if c, err := types.CommitFromProto(pc); err != nil { + fail("agg_commit_proto: from proto: %v", err) + } else if err := f.blsValSet.VerifyCommit(chainID, f.blockID, height, c); err != nil { + fail("agg_commit_proto: verify: %v", err) + } else { + ok("bera-v0.40.x aggregated commit verifies against the validator set") + } + pb := new(cmtproto.Block) + if err := pb.Unmarshal(hexOf("block_proto")); err != nil { + fail("block_proto: decode: %v", err) + } else if b, err := types.BlockFromProto(pb); err != nil { + fail("block_proto: from proto: %v", err) + } else if err := b.ValidateBasic(); err != nil { + fail("block_proto: validate: %v", err) + } else if hx(b.Hash()) != v["block_hash"] { + fail("block_proto: hash differs") + } else { + ok("bera-v0.40.x block decodes, validates and hashes identically") + } + pvt := new(cmtproto.Vote) + if err := pvt.Unmarshal(hexOf("vote_proto_signed_precommit")); err != nil { + fail("vote_proto_signed_precommit: decode: %v", err) + } else if vt, err := types.VoteFromProto(pvt); err != nil { + fail("vote_proto_signed_precommit: from proto: %v", err) + } else if err := vt.Verify(chainID, f.blsVals[0].PubKey); err != nil { + fail("vote_proto_signed_precommit: verify: %v", err) + } else { + ok("bera-v0.40.x signed precommit verifies") + } + ps := new(cmtstate.State) + if err := ps.Unmarshal(hexOf("state_proto")); err != nil { + fail("state_proto: decode: %v", err) + } else if st, err := sm.FromProto(ps); err != nil { + fail("state_proto: from proto: %v", err) + } else if st.NextBlockDelay != 1500*time.Millisecond || st.LastBlockHeight != height || hx(st.ConsensusParams.Hash()) != v["params_hash"] { + fail("state_proto: decoded state differs") + } else { + ok("bera-v0.40.x persisted state loads (next_block_delay, height, params)") + } + for _, k := range []string{"p2p_vote", "p2p_commit", "p2p_proposal", "p2p_block_part", "p2p_has_proposal_block_part", "p2p_new_round_step", "p2p_vote_set_bits"} { + m := new(cmtcons.Message) + if err := m.Unmarshal(hexOf(k)); err != nil { + fail("%s: decode: %v", k, err) + } else if re, err := m.Marshal(); err != nil || hx(re) != v[k] { + fail("%s: does not round-trip", k) + } + } + ok("bera-v0.40.x consensus p2p messages decode and round-trip") + w := new(cmtcons.TimedWALMessage) + if err := w.Unmarshal(hexOf("wal_msginfo_vote_proto")); err != nil { + fail("wal_msginfo_vote_proto: decode: %v", err) + } else { + ok("bera-v0.40.x WAL entry decodes") + } + } + + if fails > 0 { + fmt.Printf("\n%d failure(s)\n", fails) + return 1 + } + fmt.Println("\nall checks passed") + return 0 +} + +// fixtureFor rebuilds the validator set and related inputs (same as generate). +type fixture struct { + blsVals []*types.Validator + blsValSet *types.ValidatorSet + blockID types.BlockID + params types.ConsensusParams +} + +func fixtureFor(_ out) *fixture { + blsPrivs := make([]*bls12381.PrivKey, numVals) + blsVals := make([]*types.Validator, numVals) + for i := 0; i < numVals; i++ { + pk, err := bls12381.GenPrivKeyFromSecret([]byte(fmt.Sprintf("%s%d", blsSeed, i))) + must(err) + blsPrivs[i] = pk + blsVals[i] = types.NewValidator(pk.PubKey(), powers[i]) + } + valSet := types.NewValidatorSet(blsVals) + blsVals, _ = sortKeys(valSet, blsVals, blsPrivs) + return &fixture{ + blsVals: blsVals, + blsValSet: valSet, + blockID: types.BlockID{ + Hash: h32("block"), + PartSetHeader: types.PartSetHeader{Total: 3, Hash: h32("parts")}, + }, + params: consensusParams(), + } +} + +func durPtr(d time.Duration) *time.Duration { return &d } + +func consensusParams() types.ConsensusParams { + return types.ConsensusParams{ + Block: types.BlockParams{MaxBytes: 104857600, MaxGas: -1}, + Evidence: types.EvidenceParams{MaxAgeNumBlocks: 100000, MaxAgeDuration: 172800 * time.Second, MaxBytes: 1048576}, + Validator: types.ValidatorParams{PubKeyTypes: []string{types.ABCIPubKeyTypeBls12381}}, + Version: types.VersionParams{App: 1}, + Synchrony: types.SynchronyParams{Precision: 505 * time.Millisecond, MessageDelay: 15 * time.Second}, + Feature: types.FeatureParams{VoteExtensionsEnableHeight: 0, PbtsEnableHeight: 1, SBTEnableHeight: 7}, + } +} + +// sortKeys reorders vals/privs to follow the order of the validators inside +// valSet (which sorts by voting power, then address). +func sortKeys[K any](valSet *types.ValidatorSet, vals []*types.Validator, privs []K) ([]*types.Validator, []K) { + outVals := make([]*types.Validator, len(vals)) + outPrivs := make([]K, len(privs)) + for i, sv := range valSet.Validators { + found := false + for j, v := range vals { + if bytes.Equal(v.PubKey.Bytes(), sv.PubKey.Bytes()) { + outVals[i] = sv + outPrivs[i] = privs[j] + found = true + break + } + } + if !found { + panic("validator not found") + } + } + return outVals, outPrivs +} + +// blsPubPtr returns the BLS public key as a pointer regardless of whether the +// crypto.PubKey holds a value or a pointer. +func blsPubPtr(pk crypto.PubKey) *bls12381.PubKey { + switch k := pk.(type) { + case *bls12381.PubKey: + return k + case bls12381.PubKey: + return &k + default: + panic(fmt.Sprintf("not a BLS public key: %T", pk)) + } +} + +func compress(pk crypto.PubKey) []byte { return blsPubPtr(pk).Compress() } + +func mustMarshal(m interface{ Marshal() ([]byte, error) }) []byte { + bz, err := m.Marshal() + must(err) + return bz +} + +// bitArrayProto builds the proto form of a bits.BitArray of n bits with a +// single bit set at index set (libs/bits is internal on bera-v1.x). +func bitArrayProto(n int, set int) *cmtbits.BitArray { + elems := make([]uint64, (n+63)/64) + elems[set/64] |= 1 << (uint(set) % 64) + return &cmtbits.BitArray{Bits: int64(n), Elems: elems} +} diff --git a/test/compat/gen-v1x/tools.go b/test/compat/gen-v1x/tools.go new file mode 100644 index 00000000000..ec8b1a709da --- /dev/null +++ b/test/compat/gen-v1x/tools.go @@ -0,0 +1,13 @@ +//go:build tools + +// This file only exists so that `go mod tidy` records the dependencies of the +// bera-v1.x node binary in go.sum. That lets the compatibility scripts build a +// bera-v1.x `cometbft` binary straight from the module cache, without a +// bera-v1.x checkout: +// +// cd test/compat/gen-v1x && go build -o cometbft-v1x github.com/cometbft/cometbft/cmd/cometbft +package main + +import ( + _ "github.com/cometbft/cometbft/cmd/cometbft/commands" +) diff --git a/test/compat/rolling_upgrade.sh b/test/compat/rolling_upgrade.sh new file mode 100755 index 00000000000..be6f1ad26bb --- /dev/null +++ b/test/compat/rolling_upgrade.sh @@ -0,0 +1,352 @@ +#!/usr/bin/env bash +# +# bera-v1.x <-> bera-v0.40.x live compatibility test. +# +# Phase A (data-dir reuse): a single-validator chain is started on the +# bera-v1.x binary, stopped, restarted on the bera-v0.40.x binary with the +# SAME home directory (no migration), and then rolled back to bera-v1.x and +# forward again. Every restart must replay the other version's data dir and +# keep producing blocks; historical blocks written by the other version must +# stay queryable. +# +# Phase B (mixed-validator rolling upgrade): a 4-validator all-BLS network +# (aggregated commits, PBTS) is started on bera-v1.x, then validators are +# moved one by one to bera-v0.40.x (1+3, 2+2, 3+1, 4+0), soaking at each +# stage. In between, an already-upgraded node is restarted on its own data +# dir mid-soak, a node of each version is stopped for a soak and must +# block-sync the missed blocks from the mixed-version peers, and a tx is +# sent through the fully upgraded network. Finally one validator is rolled +# back to bera-v1.x and forward again. +# At every stage all validators must keep signing, both versions must +# propose blocks the other accepts, rounds must stay at 0, block hashes +# must agree across nodes, and the moved node must still serve early blocks. +# +# Requirements: go, python3, curl. The two binaries are built on the fly: +# bera-v0.40.x from this checkout (tags: bls12381,pebbledb), bera-v1.x from +# the module pinned in gen-v1x/go.mod (the bera-v1.x default db_backend is +# pebbledb, which is why the bera-v0.40.x binary needs the pebbledb tag). +# +# Usage: test/compat/rolling_upgrade.sh [workdir] +# env: V1X_BIN, V040_BIN (skip building), SOAK_BLOCKS (default 25), +# KEEP_WORKDIR=1 (do not delete the workdir on success) +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$HERE/../.." && pwd)" +WORK="${1:-$(mktemp -d -t bera-compat-XXXXXX)}" +SOAK_BLOCKS="${SOAK_BLOCKS:-25}" +NUM_VALS=4 +BASE_PORT=27100 # node i: p2p BASE+10i, rpc BASE+10i+1 +CHAIN_ID="bera-compat-upgrade" +mkdir -p "$WORK" +REPORT="$WORK/report.md" +: > "$REPORT" + +log() { printf '%s %s\n' "$(date +%H:%M:%S)" "$*" | tee -a "$REPORT"; } +note() { printf '%s\n' "$*" >> "$REPORT"; } +die() { log "FATAL: $*"; dump_logs; exit 1; } + +PIDS=() +cleanup() { + for p in "${PIDS[@]:-}"; do kill "$p" 2>/dev/null || true; done + for d in "$WORK"/node*; do [ -f "$d/pid" ] && kill "$(cat "$d/pid")" 2>/dev/null || true; done +} +trap cleanup EXIT +dump_logs() { + for d in "$WORK"/node*; do + for l in "$d"/*.log; do [ -f "$l" ] || continue; echo "== $l (tail)"; tail -n 30 "$l" | cut -c1-220; done + done +} + +# ---- binaries --------------------------------------------------------------- +if [ -z "${V040_BIN:-}" ]; then + V040_BIN="$WORK/cometbft-v040" + log "building bera-v0.40.x binary from $ROOT" + (cd "$ROOT" && go build -tags bls12381,pebbledb -o "$V040_BIN" ./cmd/cometbft) +fi +if [ -z "${V1X_BIN:-}" ]; then + V1X_BIN="$WORK/cometbft-v1x" + log "building bera-v1.x binary from the module pinned in gen-v1x/go.mod" + (cd "$HERE/gen-v1x" && go build -o "$V1X_BIN" github.com/cometbft/cometbft/cmd/cometbft) +fi +log "bera-v1.x: $("$V1X_BIN" version) ($V1X_BIN)" +log "bera-v0.40.x: $("$V040_BIN" version) ($V040_BIN)" + +# ---- helpers ---------------------------------------------------------------- +p2p_port() { echo $((BASE_PORT + 10 * $1)); } +rpc_port() { echo $((BASE_PORT + 10 * $1 + 1)); } +rpc() { curl -s --max-time 5 "127.0.0.1:$(rpc_port "$1")$2"; } +jget() { python3 -c "import sys,json; d=json.load(sys.stdin); print(eval('d'+sys.argv[1]))" "$1"; } + +height_of() { rpc "$1" /status | jget "['result']['sync_info']['latest_block_height']" 2>/dev/null || echo 0; } +version_of() { rpc "$1" /status | jget "['result']['node_info']['version']" 2>/dev/null || echo "?"; } + +start_node() { # idx bin label + local i=$1 bin=$2 label=$3 home="$WORK/node$1" + local peers="" j + for j in $(seq 0 $((NUM_VALS - 1))); do + [ "$j" = "$i" ] && continue + [ -f "$WORK/node$j/id" ] || continue + peers+="$(cat "$WORK/node$j/id")@127.0.0.1:$(p2p_port "$j")," + done + peers="${peers%,}" + "$bin" start --home "$home" --proxy_app kvstore \ + --rpc.laddr "tcp://127.0.0.1:$(rpc_port "$i")" \ + --p2p.laddr "tcp://127.0.0.1:$(p2p_port "$i")" \ + ${peers:+--p2p.persistent_peers "$peers"} \ + >> "$home/$label.log" 2>&1 & + echo $! > "$home/pid" + echo "$label" > "$home/running" +} +stop_node() { # idx + local home="$WORK/node$1" _ + if [ -f "$home/pid" ]; then + kill "$(cat "$home/pid")" 2>/dev/null || true + for _ in $(seq 1 50); do kill -0 "$(cat "$home/pid")" 2>/dev/null || break; sleep 0.2; done + kill -9 "$(cat "$home/pid")" 2>/dev/null || true + rm -f "$home/pid" "$home/running" + fi +} +wait_height() { # idx target timeout_s + local i=$1 target=$2 t=${3:-120} h=0 _ + for _ in $(seq 1 $((t * 2))); do + h=$(height_of "$i") + [ "${h:-0}" -ge "$target" ] 2>/dev/null && return 0 + sleep 0.5 + done + die "node$i did not reach height $target (at $h) within ${t}s" +} +wait_all_height() { local k; for k in $(seq 0 $((NUM_VALS - 1))); do wait_height "$k" "$1" "${2:-120}"; done; } +max_height() { local m=0 k h; for k in "$@"; do h=$(height_of "$k"); [ "$h" -gt "$m" ] && m=$h; done; echo "$m"; } + +check_no_errors() { # idx label + local f="$WORK/node$1/$2.log" + # panics and consensus/p2p codec errors that would indicate an interop problem + if grep -E "panic|CONSENSUS FAILURE|wrong Block.Header|invalid commit|failed to verify|unknown message|failed to decode|ErrMsgFromProto|conflicting votes|Vote extension|signature is too big" "$f" | grep -v "use of closed network" | head -3 | grep -q .; then + log "WARN: suspicious log lines in $f:"; grep -E "panic|CONSENSUS FAILURE|wrong Block.Header|invalid commit|failed to verify|unknown message|failed to decode|ErrMsgFromProto|conflicting votes|Vote extension|signature is too big" "$f" | head -5 | cut -c1-200 | tee -a "$REPORT" + return 1 + fi + return 0 +} + +# analyze heights [from,to] as seen by node $1: per-validator signing/proposing, +# max commit round, and block hash agreement across all running nodes. +analyze() { # via_idx from to stage_name + local via=$1 from=$2 to=$3 stage=$4 + python3 - "$via" "$from" "$to" "$stage" "$WORK" "$BASE_PORT" "$NUM_VALS" "$REPORT" <<'PY' +import sys, json, urllib.request, os +via, frm, to, stage, work, base, n, report = int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3]), sys.argv[4], sys.argv[5], int(sys.argv[6]), int(sys.argv[7]), sys.argv[8] +def rpc(i, path): + with urllib.request.urlopen(f"http://127.0.0.1:{base+10*i+1}{path}", timeout=5) as r: + return json.load(r)["result"] +# validator addresses and which binary each node runs +addr, running = {}, {} +for i in range(n): + try: + st = rpc(i, "/status") + addr[i] = st["validator_info"]["address"] + running[i] = open(os.path.join(work, f"node{i}", "running")).read().strip() + except Exception as e: + running[i] = "down" +vals = rpc(via, f"/validators?height={frm}&per_page=100")["validators"] +order = [v["address"] for v in vals] +signed = {a: 0 for a in order}; proposed = {a: 0 for a in order}; missed = {a: [] for a in order}; maxround = 0; agg = 0; blocks = 0 +for h in range(frm, to + 1): + b = rpc(via, f"/block?height={h}")["block"] + lc = b["last_commit"] + blocks += 1 + maxround = max(maxround, int(lc["round"])) + proposed[b["header"]["proposer_address"]] += 1 + flags = [int(s["block_id_flag"]) for s in lc["signatures"]] + if any(f in (4, 5, 6, 7) for f in flags): agg += 1 + for a, f in zip(order, flags): + if f in (2, 4, 5): signed[a] += 1 + else: missed[a].append(f"{h-1}:{f}") +# hash agreement at `to` across all running nodes +hashes = {} +for i in range(n): + if running.get(i, "down") == "down": continue + try: hashes[i] = rpc(i, f"/block?height={to}")["block_id"]["hash"] + except Exception as e: hashes[i] = f"ERR {e}" +ok = True +lines = [f"### {stage}: heights {frm}..{to} ({blocks} blocks) via node{via}"] +for i in range(n): + a = addr.get(i) + if a is None: + lines.append(f"- node{i}: down"); continue + lines.append(f"- node{i} [{running[i]}] {a[:8]}: signed {signed.get(a,0)}/{blocks}, proposed {proposed.get(a,0)}" + + (f", missed (height:flag) {' '.join(missed[a])}" if missed.get(a) else "")) + # a validator that sees +2/3 precommits before its own polka precommits nil and is + # recorded as a nil voter (flag 6/7); normal on a loaded runner, so require most, not all + if signed.get(a, 0) < blocks * 7 // 10: + ok = False; lines.append(f" FAIL: node{i} missed {blocks - signed.get(a,0)} commits") +# each version group that is up must have proposed at least once +for label in set(running.values()) - {"down"}: + p = sum(proposed.get(addr[i], 0) for i in range(n) if running.get(i) == label) + lines.append(f"- proposals by {label} nodes: {p}") + if p == 0 and blocks >= 8: + ok = False; lines.append(f" FAIL: no block proposed by a {label} node") +lines.append(f"- max commit round: {maxround}; blocks with aggregated commit: {agg}/{blocks}") +if maxround > 0: lines.append(" WARN: rounds > 0 seen") +if agg != blocks: ok = False; lines.append(" FAIL: non-aggregated commit seen in an all-BLS network") +if len(set(hashes.values())) != 1: + ok = False; lines.append(f" FAIL: block hash at {to} disagrees: {hashes}") +else: + lines.append(f"- block hash at {to} identical on {len(hashes)} nodes: {list(hashes.values())[0][:16]}...") +lines.append("- RESULT: " + ("PASS" if ok else "FAIL")) +out = "\n".join(lines) +print(out) +with open(report, "a") as f: f.write(out + "\n\n") +sys.exit(0 if ok else 1) +PY +} + +# ============================================================================= +# Phase A: single-node data-dir reuse, both directions +# ============================================================================= +log "=== Phase A: data-dir reuse (single validator) ===" +A="$WORK/single" +rm -rf "$A"; mkdir -p "$A" +"$V1X_BIN" init --home "$A" --key-type bls12_381 >/dev/null 2>&1 +sed -i.bak 's/^timeout_commit = .*/timeout_commit = "200ms"/' "$A/config/config.toml" +ARPC=$((BASE_PORT + 90)); AP2P=$((BASE_PORT + 91)) +a_start() { "$1" start --home "$A" --proxy_app kvstore --rpc.laddr "tcp://127.0.0.1:$ARPC" --p2p.laddr "tcp://127.0.0.1:$AP2P" >> "$A/$2.log" 2>&1 & echo $! > "$A/pid"; } +a_stop() { kill "$(cat "$A/pid")" 2>/dev/null || true; for _ in $(seq 1 50); do kill -0 "$(cat "$A/pid")" 2>/dev/null || break; sleep 0.2; done; } +a_height() { curl -s --max-time 3 "127.0.0.1:$ARPC/status" | jget "['result']['sync_info']['latest_block_height']" 2>/dev/null || echo 0; } +a_wait() { for _ in $(seq 1 240); do h=$(a_height); [ "${h:-0}" -ge "$1" ] && return 0; sleep 0.5; done; die "single node stuck at $(a_height), wanted $1"; } +a_tx() { curl -s --max-time 10 "127.0.0.1:$ARPC/broadcast_tx_commit?tx=\"$1\"" | jget "['result']['height']"; } +a_query() { curl -s --max-time 5 "127.0.0.1:$ARPC/abci_query?path=\"/key\"&data=\"$1\"" | jget "['result']['response']['log']"; } + +H=0 +for step in "v1x:$V1X_BIN:10" "v040:$V040_BIN:10" "v1x:$V1X_BIN:10" "v040:$V040_BIN:10"; do + IFS=: read -r label bin blocks <<< "$step" + a_start "$bin" "$label" + a_wait $((H + 1)) + ver=$(curl -s "127.0.0.1:$ARPC/status" | jget "['result']['node_info']['version']") + txh=$(a_tx "k-$label-$H=v") + a_wait $((txh + blocks)) + # every height written so far (by either version) must be queryable, + # historical commits must be aggregated, and old txs must be in the app state + for h in $(seq 1 5 $((txh + blocks))); do + flags=$(curl -s "127.0.0.1:$ARPC/block?height=$h" | python3 -c "import sys,json; print(sorted(set(int(s['block_id_flag']) for s in json.load(sys.stdin)['result']['block']['last_commit']['signatures'])))") + [ "$h" -gt 1 ] && [ "$flags" != "[4]" ] && die "height $h last_commit flags $flags (expected aggregated [4]) on $label" + curl -s "127.0.0.1:$ARPC/block_results?height=$h" | jget "['result']['height']" >/dev/null || die "block_results $h failed on $label" + done + [ "$(a_query "k-$label-$H")" = "exists" ] || die "tx k-$label-$H not found on $label" + H=$(a_height) + a_stop + log "Phase A: $label ($ver) ran from the shared data dir to height $H (tx at $txh), clean stop" + note "" +done +grep -q "ABCI Replay Blocks\|Replay: Done" "$A/v040.log" || die "bera-v0.40.x did not replay the bera-v1.x data dir?" +log "Phase A PASS: v1.x -> v0.40.x -> v1.x -> v0.40.x on one data dir, final height $H" +note "" + +# ============================================================================= +# Phase B: 4-validator rolling upgrade +# ============================================================================= +log "=== Phase B: mixed-validator rolling upgrade (4 BLS validators) ===" +for i in $(seq 0 $((NUM_VALS - 1))); do + home="$WORK/node$i"; rm -rf "$home"; mkdir -p "$home" + "$V1X_BIN" init --home "$home" --key-type bls12_381 >/dev/null 2>&1 + "$V1X_BIN" show-node-id --home "$home" > "$home/id" + sed -i.bak \ + -e 's/^timeout_commit = .*/timeout_commit = "300ms"/' \ + -e 's/^addr_book_strict = .*/addr_book_strict = false/' \ + -e 's/^allow_duplicate_ip = .*/allow_duplicate_ip = true/' \ + -e "s/^moniker = .*/moniker = \"node$i\"/" \ + "$home/config/config.toml" +done +# one genesis with all four validators, produced from the bera-v1.x files +python3 - "$WORK" "$NUM_VALS" "$CHAIN_ID" <<'PY' +import json, sys, os +work, n, chain = sys.argv[1], int(sys.argv[2]), sys.argv[3] +base = json.load(open(os.path.join(work, "node0", "config", "genesis.json"))) +base["chain_id"] = chain +vals = [] +for i in range(n): + k = json.load(open(os.path.join(work, f"node{i}", "config", "priv_validator_key.json"))) + vals.append({"address": k["address"], "pub_key": k["pub_key"], "power": "10", "name": f"node{i}"}) +base["validators"] = vals +for i in range(n): + json.dump(base, open(os.path.join(work, f"node{i}", "config", "genesis.json"), "w"), indent=2) +print("genesis validators:", [v["address"][:8] for v in vals]) +PY + +for i in $(seq 0 $((NUM_VALS - 1))); do start_node "$i" "$V1X_BIN" v1x; done +wait_all_height 5 120 +log "Phase B: baseline network on bera-v1.x is producing blocks" +FROM=$(max_height 0 1 2 3); wait_all_height $((FROM + SOAK_BLOCKS)); TO=$(max_height 0 1 2 3) +analyze 0 "$FROM" "$TO" "B0 baseline: 4x v1.x" || die "baseline analysis failed" + +stage() { # name idx bin label + local name=$1 i=$2 bin=$3 label=$4 + log "Phase B: $name: moving node$i to $label" + stop_node "$i" + start_node "$i" "$bin" "$label" + wait_height "$i" $(( $(max_height $(seq 0 $((NUM_VALS - 1)) | grep -v "^$i$") ) + 2 )) 120 + local from; from=$(max_height 0 1 2 3) + wait_all_height $((from + SOAK_BLOCKS)) 180 + local to; to=$(max_height 0 1 2 3) + local via=$(( (i + 1) % NUM_VALS )) + analyze "$via" "$from" "$to" "$name" || die "$name analysis failed" + analyze "$i" "$from" "$to" "$name (as seen by the moved node$i)" || die "$name analysis (node$i view) failed" + local j + for j in $(seq 0 $((NUM_VALS - 1))); do check_no_errors "$j" "$(cat "$WORK/node$j/running")" || die "errors in node$j log"; done + # the moved node must be able to serve blocks written before the move + rpc "$i" "/block?height=2" | jget "['result']['block']['header']['height']" >/dev/null || die "node$i cannot serve early blocks" + rpc "$i" "/block_results?height=2" | jget "['result']['height']" >/dev/null || die "node$i cannot serve early block_results" +} +# catch-up: keep a node down for a while, then restart it with the given +# binary; it must block-sync the missed blocks from the (mixed-version) peers +# and rejoin consensus. +catchup() { # name idx bin label + local name=$1 i=$2 bin=$3 label=$4 others from target + others=$(seq 0 $((NUM_VALS - 1)) | grep -v "^$i$" | tr '\n' ' ') + log "Phase B: $name: stopping node$i for $SOAK_BLOCKS blocks, then restarting it on $label" + stop_node "$i" + from=$(max_height $others) + # shellcheck disable=SC2086 + for j in $others; do wait_height "$j" $((from + SOAK_BLOCKS)) 180; done + local stopped_at; stopped_at=$(rpc "$(echo $others | cut -d' ' -f1)" "/block?height=$from" | jget "['result']['block']['header']['height']") + start_node "$i" "$bin" "$label" + target=$(( $(max_height $others) + 3 )) + wait_height "$i" "$target" 180 + # the node must have fetched the missed blocks through blocksync (from the + # mixed-version peers) before switching to consensus + local switch_h + switch_h=$(grep "Time to switch to consensus mode" "$WORK/node$i/$label.log" | tail -1 | sed -n 's/.*height=\([0-9]*\).*/\1/p') + if [ -n "$switch_h" ] && [ "$switch_h" -ge $((stopped_at + SOAK_BLOCKS - 5)) ]; then + log "Phase B: $name: node$i block-synced $stopped_at -> $switch_h, switched to consensus, now at $target" + else + die "$name: node$i did not block-sync the missed blocks (stopped at $stopped_at, switch height '$switch_h')" + fi + local via=$(( (i + 1) % NUM_VALS )) + wait_all_height $((target + 10)) 120 + analyze "$via" "$((target + 1))" "$((target + 10))" "$name (after catch-up)" || die "$name analysis failed" + rpc "$i" "/block?height=$((from + 5))" | jget "['result']['block']['header']['height']" >/dev/null || die "node$i cannot serve a block it block-synced" +} + +stage "B1: 1x v0.40.x + 3x v1.x" 0 "$V040_BIN" v040 +stage "B2: 2x v0.40.x + 2x v1.x (votes must cross the version boundary in every block)" 1 "$V040_BIN" v040 +# restart an upgraded node mid-soak (replay over mixed-version data) +log "Phase B: restarting node0 (v0.40.x) on its own data dir mid-soak" +stop_node 0; start_node 0 "$V040_BIN" v040; wait_height 0 $(( $(max_height 1 2 3) + 2 )) 120 +catchup "B2b: v0.40.x node block-syncs from mixed peers" 1 "$V040_BIN" v040 +catchup "B2c: v1.x node block-syncs from mixed peers" 3 "$V1X_BIN" v1x +stage "B3: 3x v0.40.x + 1x v1.x" 2 "$V040_BIN" v040 +stage "B4: 4x v0.40.x" 3 "$V040_BIN" v040 +# transactions through a v0.40.x node land and are visible everywhere +txh=$(rpc 0 '/broadcast_tx_commit?tx="rolling=ok"' | jget "['result']['height']") +wait_all_height "$txh" 60 +for i in $(seq 0 $((NUM_VALS - 1))); do [ "$(rpc "$i" '/abci_query?path="/key"&data="rolling"' | jget "['result']['response']['log']")" = "exists" ] || die "tx not visible on node$i"; done +log "Phase B: tx committed at height $txh on the fully upgraded network, visible on all nodes" +stage "B5: rollback: node0 back to v1.x (3x v0.40.x + 1x v1.x)" 0 "$V1X_BIN" v1x +stage "B6: forward again: node0 to v0.40.x (4x v0.40.x)" 0 "$V040_BIN" v040 + +log "=== PASS: bera-v1.x <-> bera-v0.40.x data-dir reuse and rolling upgrade ===" +log "report: $REPORT" +if [ -z "${KEEP_WORKDIR:-}" ] && [ -z "${1:-}" ]; then + cleanup; trap - EXIT + cp "$REPORT" "$(dirname "$WORK")/bera-compat-report-$(date +%Y%m%d-%H%M%S).md" 2>/dev/null || true +fi diff --git a/test/compat/vectors/bera_v040_outputs.json b/test/compat/vectors/bera_v040_outputs.json new file mode 100644 index 00000000000..9bca709bb38 --- /dev/null +++ b/test/compat/vectors/bera_v040_outputs.json @@ -0,0 +1,41 @@ +{ + "_source": "generated by test/compat TestWriteV040Outputs against bera-v0.40.x", + "agg_commit_hash": "84a763b0d4704948493e446ef421570c73394c652781dadd11b230d93945bbb8", + "agg_commit_proto": "08d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2287010804121495c39b91af280535b0132107625c6262e2f83dd61a0b088092b8c398feffffff0122608de8b5ff33540f1678428e38911de47fc3ab8d6ccd79f07c8b3ce469f8d99ecccaa7e6138d9d5016b41479ff0eb8f8fd19136435ed2bc1053555145c7eb0feee5ac0ab8c68ebb287628b4c994d999ffe47bb437f78f3fb6e0a909daa4adbff0c222508051214e213983e829c4b946f0ac5ac3c41c4bb25b1361f1a0b088092b8c398feffffff01222508051214e6c734c2265f59ae5135de87c0602b811ac3b29c1a0b088092b8c398feffffff01228701080612148ff7be3b769206c05bbdd81ddad369edb6d8ccd71a0b088092b8c398feffffff012260abb1bb002e2fee585cdeabcedd3398dc554558b3637f7e6a1ac4b2a9d67ea581ae5805d0c54a820f155c477ae58ac3ba03033db6f06a787dcdad46a4e60589149a6e8fbfedca5c648570badd0acf0feebc011adbe5781ade6ef71a02e210cf25", + "block_hash": "53eb6c76d082062996efb85c11dde07969d54f69d088c5320fe88e000139a2e7", + "block_meta_proto": "0a480a2053eb6c76d082062996efb85c11dde07969d54f69d088c5320fe88e000139a2e71224080112209f1327037b27d10cef34002c8e978c9167c9d62eb67813c2a4ce66525568f3d410e1061a96030a04080b1001120d626572612d636f6d7061742d3118d309220c08a7ebdcca0610808c8d9e022a480a20811c5967a1b7320bd1b26156be59801e65f77ab2af703976bf42bb9e443721621224080112209f24376f2cb0ea72f6f3d99634bfb80323f53e367ed0cd0cee9c7170c17b37ff322084a763b0d4704948493e446ef421570c73394c652781dadd11b230d93945bbb83a20303e3d08c4b8e61e7640853a93c47255a8b592e193af9588081f6d800e856b11422096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063144a2096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063145220ab619380cd4e115b18d6334918f00ba86403d95ebcb6eb2e4a2d457a86ff8ca35a20a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f3336220c099142bc3186ded72786ba27e9ea6d2da240fb9fd3fe79b479ecf8e734b28506a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214e213983e829c4b946f0ac5ac3c41c4bb25b1361f2002", + "block_partset_hash": "9f1327037b27d10cef34002c8e978c9167c9d62eb67813c2a4ce66525568f3d4", + "block_proto": "0a96030a04080b1001120d626572612d636f6d7061742d3118d309220c08a7ebdcca0610808c8d9e022a480a20811c5967a1b7320bd1b26156be59801e65f77ab2af703976bf42bb9e443721621224080112209f24376f2cb0ea72f6f3d99634bfb80323f53e367ed0cd0cee9c7170c17b37ff322084a763b0d4704948493e446ef421570c73394c652781dadd11b230d93945bbb83a20303e3d08c4b8e61e7640853a93c47255a8b592e193af9588081f6d800e856b11422096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063144a2096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063145220ab619380cd4e115b18d6334918f00ba86403d95ebcb6eb2e4a2d457a86ff8ca35a20a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f3336220c099142bc3186ded72786ba27e9ea6d2da240fb9fd3fe79b479ecf8e734b28506a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214e213983e829c4b946f0ac5ac3c41c4bb25b1361f12100a0674782d6f6e650a0674782d74776f1a0022b10308d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2287010804121495c39b91af280535b0132107625c6262e2f83dd61a0b088092b8c398feffffff0122608de8b5ff33540f1678428e38911de47fc3ab8d6ccd79f07c8b3ce469f8d99ecccaa7e6138d9d5016b41479ff0eb8f8fd19136435ed2bc1053555145c7eb0feee5ac0ab8c68ebb287628b4c994d999ffe47bb437f78f3fb6e0a909daa4adbff0c222508051214e213983e829c4b946f0ac5ac3c41c4bb25b1361f1a0b088092b8c398feffffff01222508051214e6c734c2265f59ae5135de87c0602b811ac3b29c1a0b088092b8c398feffffff01228701080612148ff7be3b769206c05bbdd81ddad369edb6d8ccd71a0b088092b8c398feffffff012260abb1bb002e2fee585cdeabcedd3398dc554558b3637f7e6a1ac4b2a9d67ea581ae5805d0c54a820f155c477ae58ac3ba03033db6f06a787dcdad46a4e60589149a6e8fbfedca5c648570badd0acf0feebc011adbe5781ade6ef71a02e210cf25", + "bls_valset_hash": "96d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b10006314", + "bls_valset_proto": "0a90010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338310a87010a14e213983e829c4b946f0ac5ac3c41c4bb25b1361f181e201e2a6013d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a168328a12d18f7863277ae7fe349f81c3e3398e85e1b669b8342dd287c500fda77d8e34a3a1051afc6295df02cb2220d3209626c7331325f3338310a87010a14e6c734c2265f59ae5135de87c0602b811ac3b29c181420142a6003d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd04e9ad3f43268964312aeb740a80d590e21aa02bb8b838238555110230cd7cb15965e4ac058da1889ee90efcdf1019d03209626c7331325f3338310a87010a148ff7be3b769206c05bbdd81ddad369edb6d8ccd7180a200a2a6004c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de04fe0514283c1450f6ba79e70ec974751381a21e028958fa4e7a16b2aa813640ba2f48a9db1b4495559d112423ee4ac43209626c7331325f3338311290010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f333831", + "dve_hash": "f663c241f4cc765f3d56e38c259f231d50eb14ef6661df825f3d784468e73061", + "dve_proto": "0ac8030ad801080210d209180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0b088092b8c398feffffff013214e6c734c2265f59ae5135de87c0602b811ac3b29c3802426083ae741b651e23e9697b351d5314b9914ca47b980531df1d6bdd9fbb3b8a291d287de32a11799c5030871fd09aef30540e7c3c7e9c069de0077af7a0ff20173c81db70b2d52fce4b16e06c8f989fd867546cf5dbd306dc40b8fe941ce3b44a3f12d801080210d209180222480a2090e48be2976c4756547456e5352caea7062dc88122d2ba59b21e354276f9db681224080312202fa5d2139445f42698d5a690f0a5f898c15e32f6bd0bc2b23d76528719f84f8d2a0b088092b8c398feffffff013214e6c734c2265f59ae5135de87c0602b811ac3b29c38024260ab4f1416d9466e2e7e178d8be83f92897ed2a9e375e5c0e73e111c6283a226f655e65a3ed08c3145bc9507eb37db94e208c96391bde822af995c56fd15a1f788316c94de3b595c6f79b801971e7e365ba839697112b8a815ee4b2c6d293d9ea5186420142a0c08a5ebdcca0610808c8d9e02", + "genesis_validator_hash": "96d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b10006314", + "header_hash": "53eb6c76d082062996efb85c11dde07969d54f69d088c5320fe88e000139a2e7", + "p2p_block_part": "2a920708d30910021a8a0712e1060a96030a04080b1001120d626572612d636f6d7061742d3118d309220c08a7ebdcca0610808c8d9e022a480a20811c5967a1b7320bd1b26156be59801e65f77ab2af703976bf42bb9e443721621224080112209f24376f2cb0ea72f6f3d99634bfb80323f53e367ed0cd0cee9c7170c17b37ff322084a763b0d4704948493e446ef421570c73394c652781dadd11b230d93945bbb83a20303e3d08c4b8e61e7640853a93c47255a8b592e193af9588081f6d800e856b11422096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063144a2096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063145220ab619380cd4e115b18d6334918f00ba86403d95ebcb6eb2e4a2d457a86ff8ca35a20a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f3336220c099142bc3186ded72786ba27e9ea6d2da240fb9fd3fe79b479ecf8e734b28506a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214e213983e829c4b946f0ac5ac3c41c4bb25b1361f12100a0674782d6f6e650a0674782d74776f1a0022b10308d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2287010804121495c39b91af280535b0132107625c6262e2f83dd61a0b088092b8c398feffffff0122608de8b5ff33540f1678428e38911de47fc3ab8d6ccd79f07c8b3ce469f8d99ecccaa7e6138d9d5016b41479ff0eb8f8fd19136435ed2bc1053555145c7eb0feee5ac0ab8c68ebb287628b4c994d999ffe47bb437f78f3fb6e0a909daa4adbff0c222508051214e213983e829c4b946f0ac5ac3c41c4bb25b1361f1a0b088092b8c398feffffff01222508051214e6c734c2265f59ae5135de87c0602b811ac3b29c1a0b088092b8c398feffffff01228701080612148ff7be3b769206c05bbdd81ddad369edb6d8ccd71a0b088092b8c398feffffff012260abb1bb002e2fee585cdeabcedd3398dc554558b3637f7e6a1ac4b2a9d67ea581ae5805d0c54a820f155c477ae58ac3ba03033db6f06a787dcdad46a4e60589149a6e8fbfedca5c648570badd0acf0feebc011adbe5781ade6ef71a02e210cf251a2408011a209f1327037b27d10cef34002c8e978c9167c9d62eb67813c2a4ce66525568f3d4", + "p2p_commit": "5ab4030ab10308d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2287010804121495c39b91af280535b0132107625c6262e2f83dd61a0b088092b8c398feffffff0122608de8b5ff33540f1678428e38911de47fc3ab8d6ccd79f07c8b3ce469f8d99ecccaa7e6138d9d5016b41479ff0eb8f8fd19136435ed2bc1053555145c7eb0feee5ac0ab8c68ebb287628b4c994d999ffe47bb437f78f3fb6e0a909daa4adbff0c222508051214e213983e829c4b946f0ac5ac3c41c4bb25b1361f1a0b088092b8c398feffffff01222508051214e6c734c2265f59ae5135de87c0602b811ac3b29c1a0b088092b8c398feffffff01228701080612148ff7be3b769206c05bbdd81ddad369edb6d8ccd71a0b088092b8c398feffffff012260abb1bb002e2fee585cdeabcedd3398dc554558b3637f7e6a1ac4b2a9d67ea581ae5805d0c54a820f155c477ae58ac3ba03033db6f06a787dcdad46a4e60589149a6e8fbfedca5c648570badd0acf0feebc011adbe5781ade6ef71a02e210cf25", + "p2p_has_proposal_block_part": "520508d3091002", + "p2p_has_vote": "3a0908d209100218022002", + "p2p_new_round_step": "0a0b08d2091002180320072801", + "p2p_new_valid_block": "123408d20910021a2408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea220508031201022801", + "p2p_proposal": "1ac6010ac301082010d209180220012a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320c08a5ebdcca0610808c8d9e023a6089c32ec1b8eef9447160a3b89b833880067b13e70e1435de8f278028100ac080d11f2717f250e43184b98359c686db9811714d99a8e47bc5172035dda5f02764922143a607ea90468513a8818df204f3bfe90580bea26c318eb926b6dad58a2f", + "p2p_proposal_pol": "220c08d20910011a050804120104", + "p2p_vote": "32d9010ad601080210d209180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0b088092b8c398feffffff01321495c39b91af280535b0132107625c6262e2f83dd64260b24e3ba8f8217e43bb62900617ae91f5c5aeb324677c26cf128b669e5df45b0d74c16e40e190747b167ca50a7298afc813d8fc26b439b6136e34137ed2ae6039d5d606802231f6af7935b746d6d513443274efff8648ed6eff470267647ff6e7", + "p2p_vote_set_bits": "4a5808d2091002180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a050804120108", + "p2p_vote_set_maj23": "425108d2091002180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea", + "params_hash": "ab619380cd4e115b18d6334918f00ba86403d95ebcb6eb2e4a2d457a86ff8ca3", + "params_json": "{\"block\":{\"max_bytes\":\"104857600\",\"max_gas\":\"-1\"},\"evidence\":{\"max_age_num_blocks\":\"100000\",\"max_age_duration\":\"172800000000000\",\"max_bytes\":\"1048576\"},\"validator\":{\"pub_key_types\":[\"bls12_381\"]},\"version\":{\"app\":\"1\"},\"synchrony\":{\"precision\":\"505000000\",\"message_delay\":\"15000000000\"},\"feature\":{\"vote_extensions_enable_height\":\"0\",\"pbts_enable_height\":\"1\",\"sbt_enable_height\":\"7\"},\"authority\":{\"authority\":\"\"}}", + "params_proto": "0a10088080803210ffffffffffffffffff01120e08a08d0612040880c60a188080401a0b0a09626c7331325f33383122020801320c0a0610c0e0e6f0011202080f3a0a0a00120208011a020807", + "proposal_proto_signed": "082010d209180220012a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320c08a5ebdcca0610808c8d9e023a6089c32ec1b8eef9447160a3b89b833880067b13e70e1435de8f278028100ac080d11f2717f250e43184b98359c686db9811714d99a8e47bc5172035dda5f02764922143a607ea90468513a8818df204f3bfe90580bea26c318eb926b6dad58a2f", + "proposal_sign_bytes": "7d082011d20400000000000019020000000000000020012a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320c08a5ebdcca0610808c8d9e023a0d626572612d636f6d7061742d31", + "state_proto": "0a0d0a04080b10011205312e302e31120d626572612d636f6d7061742d3118d20922480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0c08a5ebdcca0610808c8d9e0232c4050a90010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338310a87010a14e213983e829c4b946f0ac5ac3c41c4bb25b1361f181e201e2a6013d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a168328a12d18f7863277ae7fe349f81c3e3398e85e1b669b8342dd287c500fda77d8e34a3a1051afc6295df02cb2220d3209626c7331325f3338310a87010a14e6c734c2265f59ae5135de87c0602b811ac3b29c181420142a6003d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd04e9ad3f43268964312aeb740a80d590e21aa02bb8b838238555110230cd7cb15965e4ac058da1889ee90efcdf1019d03209626c7331325f3338310a87010a148ff7be3b769206c05bbdd81ddad369edb6d8ccd7180a200a2a6004c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de04fe0514283c1450f6ba79e70ec974751381a21e028958fa4e7a16b2aa813640ba2f48a9db1b4495559d112423ee4ac43209626c7331325f3338311290010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338313ac4050a90010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338310a87010a14e213983e829c4b946f0ac5ac3c41c4bb25b1361f181e201e2a6013d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a168328a12d18f7863277ae7fe349f81c3e3398e85e1b669b8342dd287c500fda77d8e34a3a1051afc6295df02cb2220d3209626c7331325f3338310a87010a14e6c734c2265f59ae5135de87c0602b811ac3b29c181420142a6003d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd04e9ad3f43268964312aeb740a80d590e21aa02bb8b838238555110230cd7cb15965e4ac058da1889ee90efcdf1019d03209626c7331325f3338310a87010a148ff7be3b769206c05bbdd81ddad369edb6d8ccd7180a200a2a6004c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de04fe0514283c1450f6ba79e70ec974751381a21e028958fa4e7a16b2aa813640ba2f48a9db1b4495559d112423ee4ac43209626c7331325f3338311290010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f33383142c4050a90010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338310a87010a14e213983e829c4b946f0ac5ac3c41c4bb25b1361f181e201e2a6013d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a168328a12d18f7863277ae7fe349f81c3e3398e85e1b669b8342dd287c500fda77d8e34a3a1051afc6295df02cb2220d3209626c7331325f3338310a87010a14e6c734c2265f59ae5135de87c0602b811ac3b29c181420142a6003d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd04e9ad3f43268964312aeb740a80d590e21aa02bb8b838238555110230cd7cb15965e4ac058da1889ee90efcdf1019d03209626c7331325f3338310a87010a148ff7be3b769206c05bbdd81ddad369edb6d8ccd7180a200a2a6004c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de04fe0514283c1450f6ba79e70ec974751381a21e028958fa4e7a16b2aa813640ba2f48a9db1b4495559d112423ee4ac43209626c7331325f3338311290010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338314864524d0a10088080803210ffffffffffffffffff01120e08a08d0612040880c60a188080401a0b0a09626c7331325f33383122020801320c0a0610c0e0e6f0011202080f3a0a0a00120208011a02080758326220c099142bc3186ded72786ba27e9ea6d2da240fb9fd3fe79b479ecf8e734b28506a20a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f33370017a0808011080cab5ee01", + "v040_abci_responses_info_proto": "10d2091a9a021ad3010a621a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e104d1a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e2209626c7331325f33383122160a040801100232080a020801120208033a041a0208092a20983f90f0fe1081a1611e12eeab5944daa6a66b308f161459f96e58fd76edfdfc320808011080cab5ee01", + "v040_finalize_block_response_proto": "1ad3010a621a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e104d1a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e2209626c7331325f33383122160a040801100232080a020801120208033a041a0208092a20983f90f0fe1081a1611e12eeab5944daa6a66b308f161459f96e58fd76edfdfc320808011080cab5ee01", + "v040_genesis_json": "{\n \"genesis_time\": \"2026-01-02T03:04:05.6Z\",\n \"chain_id\": \"bera-compat-1\",\n \"initial_height\": \"1\",\n \"consensus_params\": {\n \"block\": {\n \"max_bytes\": \"104857600\",\n \"max_gas\": \"-1\"\n },\n \"evidence\": {\n \"max_age_num_blocks\": \"100000\",\n \"max_age_duration\": \"172800000000000\",\n \"max_bytes\": \"1048576\"\n },\n \"validator\": {\n \"pub_key_types\": [\n \"bls12_381\"\n ]\n },\n \"version\": {\n \"app\": \"1\"\n },\n \"synchrony\": {\n \"precision\": \"505000000\",\n \"message_delay\": \"15000000000\"\n },\n \"feature\": {\n \"vote_extensions_enable_height\": \"0\",\n \"pbts_enable_height\": \"1\",\n \"sbt_enable_height\": \"7\"\n },\n \"authority\": {\n \"authority\": \"\"\n }\n },\n \"validators\": [\n {\n \"address\": \"95C39B91AF280535B0132107625C6262E2F83DD6\",\n \"pub_key\": {\n \"type\": \"cometbft/PubKeyBls12_381\",\n \"value\": \"F71KXfW8l48qR6tvEJkriKqNofaFKzzRMU7A8+KUSKMb7VpNWQK2oI3UEcWlfUVsDDGBh9xUinv3DbJv1Ch3NjrvMBkJeDNb4R0SefxvOxfj8OoFuut9mV/y0tk4v95O\"\n },\n \"power\": \"40\",\n \"name\": \"val0\"\n },\n {\n \"address\": \"E213983E829C4B946F0AC5AC3C41C4BB25B1361F\",\n \"pub_key\": {\n \"type\": \"cometbft/PubKeyBls12_381\",\n \"value\": \"E9ZuCq13HmymK7SHvCQ1YTKPSarntSAqRk1Ojp5i2mXzDW5B0X/V9K05ArvdVixaFoMooS0Y94Yyd65/40n4HD4zmOheG2abg0LdKHxQD9p32ONKOhBRr8YpXfAssiIN\"\n },\n \"power\": \"30\",\n \"name\": \"val1\"\n },\n {\n \"address\": \"E6C734C2265F59AE5135DE87C0602B811AC3B29C\",\n \"pub_key\": {\n \"type\": \"cometbft/PubKeyBls12_381\",\n \"value\": \"A9B2AFM45syg9U1+IfJDglEZsUJ39DxCyne9IEnQaVq9XqpDRwu9EBb8CGPPUeT9BOmtP0MmiWQxKut0CoDVkOIaoCu4uDgjhVURAjDNfLFZZeSsBY2hiJ7pDvzfEBnQ\"\n },\n \"power\": \"20\",\n \"name\": \"val2\"\n },\n {\n \"address\": \"8FF7BE3B769206C05BBDD81DDAD369EDB6D8CCD7\",\n \"pub_key\": {\n \"type\": \"cometbft/PubKeyBls12_381\",\n \"value\": \"BMSz0DUXqNnIJ1WnZ2b4grNENLf7eEt/EjWOQHV0qDFeUzsXIJf4sOH9TLwKK5jeBP4FFCg8FFD2unnnDsl0dROBoh4CiVj6TnoWsqqBNkC6L0ip2xtElVWdESQj7krE\"\n },\n \"power\": \"10\",\n \"name\": \"val3\"\n }\n ],\n \"app_hash\": \"69D4821FB36FCE2E5E2F5B5CFBC4BA2C0BF9612CBE5BD8AB4431D447EE698592\",\n \"app_state\": {\n \"k\": \"v\"\n }\n}", + "v040_priv_validator_key_json": "{\n \"address\": \"95C39B91AF280535B0132107625C6262E2F83DD6\",\n \"pub_key\": {\n \"type\": \"cometbft/PubKeyBls12_381\",\n \"value\": \"F71KXfW8l48qR6tvEJkriKqNofaFKzzRMU7A8+KUSKMb7VpNWQK2oI3UEcWlfUVsDDGBh9xUinv3DbJv1Ch3NjrvMBkJeDNb4R0SefxvOxfj8OoFuut9mV/y0tk4v95O\"\n },\n \"priv_key\": {\n \"type\": \"cometbft/PrivKeyBls12_381\",\n \"value\": \"LMRBLE+B3QNZb/0PQ8bXc5/WUMLRIloLrSn//kBc5T8=\"\n }\n}", + "v040_priv_validator_state_json": "{\n \"height\": \"0\",\n \"round\": 0,\n \"step\": 0\n}", + "vote_proto_signed_precommit": "080210d209180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0b088092b8c398feffffff01321495c39b91af280535b0132107625c6262e2f83dd64260b24e3ba8f8217e43bb62900617ae91f5c5aeb324677c26cf128b669e5df45b0d74c16e40e190747b167ca50a7298afc813d8fc26b439b6136e34137ed2ae6039d5d606802231f6af7935b746d6d513443274efff8648ed6eff470267647ff6e7", + "vote_sign_bytes_precommit": "6d080211d20400000000000019020000000000000022480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320d626572612d636f6d7061742d31", + "wal_msginfo_proposal_with_receive_time_proto": "0a0c08a5ebdcca0610808c8d9e0212e40112e1010ac9011ac6010ac301082010d209180220012a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320c08a5ebdcca0610808c8d9e023a6089c32ec1b8eef9447160a3b89b833880067b13e70e1435de8f278028100ac080d11f2717f250e43184b98359c686db9811714d99a8e47bc5172035dda5f02764922143a607ea90468513a8818df204f3bfe90580bea26c318eb926b6dad58a2f120570656572311a0c08a7ebdcca0610808c8d9e02", + "wal_msginfo_vote_proto": "0a0c08a5ebdcca0610808c8d9e0212e20112df010adc0132d9010ad601080210d209180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0b088092b8c398feffffff01321495c39b91af280535b0132107625c6262e2f83dd64260b24e3ba8f8217e43bb62900617ae91f5c5aeb324677c26cf128b669e5df45b0d74c16e40e190747b167ca50a7298afc813d8fc26b439b6136e34137ed2ae6039d5d606802231f6af7935b746d6d513443274efff8648ed6eff470267647ff6e7" +} diff --git a/test/compat/vectors/bera_v1x_vectors.json b/test/compat/vectors/bera_v1x_vectors.json new file mode 100644 index 00000000000..0f1427467fc --- /dev/null +++ b/test/compat/vectors/bera_v1x_vectors.json @@ -0,0 +1,101 @@ +{ + "_source": "generated by test/compat/gen-v1x against bera-v1.x", + "abci_responses_info_legacy_field_check": "finalize_block=3", + "abci_responses_info_proto": "10d2091a9d010a0d0a017412080a016b12017618011207120164280130011a3f104d1a3097bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c2209626c7331325f33383122160a040801100232080a020801120208033a041a0208092a20983f90f0fe1081a1611e12eeab5944daa6a66b308f161459f96e58fd76edfdfc320808011080cab5ee01", + "agg_commit_all_hash": "a74e39daca53afee7b3bfe9540f87d82bbc9eaafd025df9fd9be6fae7981d561", + "agg_commit_all_proto": "08d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2287010804121495c39b91af280535b0132107625c6262e2f83dd61a0b088092b8c398feffffff012260988708da995a24f285902dd64c8c12d4309d03c9cf6dd80be83460742acd51f3a1b14f5d8d40ed3c256a92f824c24d5211e8cbcdc7456c1e36b881ccee6564294203660121940533b5ee7cee338017b6b6860ab542bbfbcdf216bae27b5b30da222508051214e213983e829c4b946f0ac5ac3c41c4bb25b1361f1a0b088092b8c398feffffff01222508051214e6c734c2265f59ae5135de87c0602b811ac3b29c1a0b088092b8c398feffffff012225080512148ff7be3b769206c05bbdd81ddad369edb6d8ccd71a0b088092b8c398feffffff01", + "agg_commit_hash": "84a763b0d4704948493e446ef421570c73394c652781dadd11b230d93945bbb8", + "agg_commit_proto": "08d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2287010804121495c39b91af280535b0132107625c6262e2f83dd61a0b088092b8c398feffffff0122608de8b5ff33540f1678428e38911de47fc3ab8d6ccd79f07c8b3ce469f8d99ecccaa7e6138d9d5016b41479ff0eb8f8fd19136435ed2bc1053555145c7eb0feee5ac0ab8c68ebb287628b4c994d999ffe47bb437f78f3fb6e0a909daa4adbff0c222508051214e213983e829c4b946f0ac5ac3c41c4bb25b1361f1a0b088092b8c398feffffff01222508051214e6c734c2265f59ae5135de87c0602b811ac3b29c1a0b088092b8c398feffffff01228701080612148ff7be3b769206c05bbdd81ddad369edb6d8ccd71a0b088092b8c398feffffff012260abb1bb002e2fee585cdeabcedd3398dc554558b3637f7e6a1ac4b2a9d67ea581ae5805d0c54a820f155c477ae58ac3ba03033db6f06a787dcdad46a4e60589149a6e8fbfedca5c648570badd0acf0feebc011adbe5781ade6ef71a02e210cf25", + "agg_commit_sig0_flag": 4, + "agg_commit_sig1_flag": 5, + "agg_commit_sig2_flag": 5, + "agg_commit_sig3_flag": 6, + "block_hash": "53eb6c76d082062996efb85c11dde07969d54f69d088c5320fe88e000139a2e7", + "block_meta_proto": "0a480a2053eb6c76d082062996efb85c11dde07969d54f69d088c5320fe88e000139a2e71224080112209f1327037b27d10cef34002c8e978c9167c9d62eb67813c2a4ce66525568f3d410e1061a96030a04080b1001120d626572612d636f6d7061742d3118d309220c08a7ebdcca0610808c8d9e022a480a20811c5967a1b7320bd1b26156be59801e65f77ab2af703976bf42bb9e443721621224080112209f24376f2cb0ea72f6f3d99634bfb80323f53e367ed0cd0cee9c7170c17b37ff322084a763b0d4704948493e446ef421570c73394c652781dadd11b230d93945bbb83a20303e3d08c4b8e61e7640853a93c47255a8b592e193af9588081f6d800e856b11422096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063144a2096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063145220ab619380cd4e115b18d6334918f00ba86403d95ebcb6eb2e4a2d457a86ff8ca35a20a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f3336220c099142bc3186ded72786ba27e9ea6d2da240fb9fd3fe79b479ecf8e734b28506a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214e213983e829c4b946f0ac5ac3c41c4bb25b1361f2002", + "block_part0_proto": "12e1060a96030a04080b1001120d626572612d636f6d7061742d3118d309220c08a7ebdcca0610808c8d9e022a480a20811c5967a1b7320bd1b26156be59801e65f77ab2af703976bf42bb9e443721621224080112209f24376f2cb0ea72f6f3d99634bfb80323f53e367ed0cd0cee9c7170c17b37ff322084a763b0d4704948493e446ef421570c73394c652781dadd11b230d93945bbb83a20303e3d08c4b8e61e7640853a93c47255a8b592e193af9588081f6d800e856b11422096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063144a2096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063145220ab619380cd4e115b18d6334918f00ba86403d95ebcb6eb2e4a2d457a86ff8ca35a20a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f3336220c099142bc3186ded72786ba27e9ea6d2da240fb9fd3fe79b479ecf8e734b28506a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214e213983e829c4b946f0ac5ac3c41c4bb25b1361f12100a0674782d6f6e650a0674782d74776f1a0022b10308d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2287010804121495c39b91af280535b0132107625c6262e2f83dd61a0b088092b8c398feffffff0122608de8b5ff33540f1678428e38911de47fc3ab8d6ccd79f07c8b3ce469f8d99ecccaa7e6138d9d5016b41479ff0eb8f8fd19136435ed2bc1053555145c7eb0feee5ac0ab8c68ebb287628b4c994d999ffe47bb437f78f3fb6e0a909daa4adbff0c222508051214e213983e829c4b946f0ac5ac3c41c4bb25b1361f1a0b088092b8c398feffffff01222508051214e6c734c2265f59ae5135de87c0602b811ac3b29c1a0b088092b8c398feffffff01228701080612148ff7be3b769206c05bbdd81ddad369edb6d8ccd71a0b088092b8c398feffffff012260abb1bb002e2fee585cdeabcedd3398dc554558b3637f7e6a1ac4b2a9d67ea581ae5805d0c54a820f155c477ae58ac3ba03033db6f06a787dcdad46a4e60589149a6e8fbfedca5c648570badd0acf0feebc011adbe5781ade6ef71a02e210cf251a2408011a209f1327037b27d10cef34002c8e978c9167c9d62eb67813c2a4ce66525568f3d4", + "block_partset_hash": "9f1327037b27d10cef34002c8e978c9167c9d62eb67813c2a4ce66525568f3d4", + "block_partset_total": 1, + "block_proto": "0a96030a04080b1001120d626572612d636f6d7061742d3118d309220c08a7ebdcca0610808c8d9e022a480a20811c5967a1b7320bd1b26156be59801e65f77ab2af703976bf42bb9e443721621224080112209f24376f2cb0ea72f6f3d99634bfb80323f53e367ed0cd0cee9c7170c17b37ff322084a763b0d4704948493e446ef421570c73394c652781dadd11b230d93945bbb83a20303e3d08c4b8e61e7640853a93c47255a8b592e193af9588081f6d800e856b11422096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063144a2096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063145220ab619380cd4e115b18d6334918f00ba86403d95ebcb6eb2e4a2d457a86ff8ca35a20a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f3336220c099142bc3186ded72786ba27e9ea6d2da240fb9fd3fe79b479ecf8e734b28506a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214e213983e829c4b946f0ac5ac3c41c4bb25b1361f12100a0674782d6f6e650a0674782d74776f1a0022b10308d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2287010804121495c39b91af280535b0132107625c6262e2f83dd61a0b088092b8c398feffffff0122608de8b5ff33540f1678428e38911de47fc3ab8d6ccd79f07c8b3ce469f8d99ecccaa7e6138d9d5016b41479ff0eb8f8fd19136435ed2bc1053555145c7eb0feee5ac0ab8c68ebb287628b4c994d999ffe47bb437f78f3fb6e0a909daa4adbff0c222508051214e213983e829c4b946f0ac5ac3c41c4bb25b1361f1a0b088092b8c398feffffff01222508051214e6c734c2265f59ae5135de87c0602b811ac3b29c1a0b088092b8c398feffffff01228701080612148ff7be3b769206c05bbdd81ddad369edb6d8ccd71a0b088092b8c398feffffff012260abb1bb002e2fee585cdeabcedd3398dc554558b3637f7e6a1ac4b2a9d67ea581ae5805d0c54a820f155c477ae58ac3ba03033db6f06a787dcdad46a4e60589149a6e8fbfedca5c648570badd0acf0feebc011adbe5781ade6ef71a02e210cf25", + "block_store_state_proto": "080110d209", + "blockid_proto": "0a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea", + "bls_address_0": "8ff7be3b769206c05bbdd81ddad369edb6d8ccd7", + "bls_address_1": "e6c734c2265f59ae5135de87c0602b811ac3b29c", + "bls_address_2": "e213983e829c4b946f0ac5ac3c41c4bb25b1361f", + "bls_address_3": "95c39b91af280535b0132107625c6262e2f83dd6", + "bls_aggregate_message": "bera-compat-aggregate-message", + "bls_aggregate_signature": "87d12f70c3d9f41f94ef9d3af3e0e9d0f7411cb7e28066b07898af1e0a1a70fbb08910c5cb9d4573bb057663233beada15d5ab6d6744f908924e9a6cbc1bb0527df503fed7a3a6cf1308101f3fa06b420142b682dabf6bdbbb37b99fce387985", + "bls_privkey0_bytes": "2cc4412c4f81dd03596ffd0f43c6d7739fd650c2d1225a0bad29fffe405ce53f", + "bls_privkey0_cmtjson": "{\"type\":\"cometbft/PrivKeyBls12_381\",\"value\":\"LMRBLE+B3QNZb/0PQ8bXc5/WUMLRIloLrSn//kBc5T8=\"}", + "bls_pubkey_0": "04c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de04fe0514283c1450f6ba79e70ec974751381a21e028958fa4e7a16b2aa813640ba2f48a9db1b4495559d112423ee4ac4", + "bls_pubkey_1": "03d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd04e9ad3f43268964312aeb740a80d590e21aa02bb8b838238555110230cd7cb15965e4ac058da1889ee90efcdf1019d0", + "bls_pubkey_2": "13d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a168328a12d18f7863277ae7fe349f81c3e3398e85e1b669b8342dd287c500fda77d8e34a3a1051afc6295df02cb2220d", + "bls_pubkey_3": "17bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e", + "bls_pubkey_compressed_0": "84c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de", + "bls_pubkey_compressed_1": "83d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd", + "bls_pubkey_compressed_2": "b3d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a", + "bls_pubkey_compressed_3": "97bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c", + "bls_validator0_bytes": "0a621a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e1028", + "bls_validator0_proto": "0a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f333831", + "bls_valset_hash": "96d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b10006314", + "bls_valset_proto": "0a90010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338310a87010a14e213983e829c4b946f0ac5ac3c41c4bb25b1361f181e201e2a6013d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a168328a12d18f7863277ae7fe349f81c3e3398e85e1b669b8342dd287c500fda77d8e34a3a1051afc6295df02cb2220d3209626c7331325f3338310a87010a14e6c734c2265f59ae5135de87c0602b811ac3b29c181420142a6003d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd04e9ad3f43268964312aeb740a80d590e21aa02bb8b838238555110230cd7cb15965e4ac058da1889ee90efcdf1019d03209626c7331325f3338310a87010a148ff7be3b769206c05bbdd81ddad369edb6d8ccd7180a200a2a6004c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de04fe0514283c1450f6ba79e70ec974751381a21e028958fa4e7a16b2aa813640ba2f48a9db1b4495559d112423ee4ac43209626c7331325f3338311290010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f333831", + "consensus_params_info_proto": "0a4d0a10088080803210ffffffffffffffffff01120e08a08d0612040880c60a188080401a0b0a09626c7331325f33383122020801320c0a0610c0e0e6f0011202080f3a0a0a00120208011a0208071032", + "data_hash": "303e3d08c4b8e61e7640853a93c47255a8b592e193af9588081f6d800e856b11", + "dve_bytes": "0ad801080210d209180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0b088092b8c398feffffff013214e6c734c2265f59ae5135de87c0602b811ac3b29c3802426083ae741b651e23e9697b351d5314b9914ca47b980531df1d6bdd9fbb3b8a291d287de32a11799c5030871fd09aef30540e7c3c7e9c069de0077af7a0ff20173c81db70b2d52fce4b16e06c8f989fd867546cf5dbd306dc40b8fe941ce3b44a3f12d801080210d209180222480a2090e48be2976c4756547456e5352caea7062dc88122d2ba59b21e354276f9db681224080312202fa5d2139445f42698d5a690f0a5f898c15e32f6bd0bc2b23d76528719f84f8d2a0b088092b8c398feffffff013214e6c734c2265f59ae5135de87c0602b811ac3b29c38024260ab4f1416d9466e2e7e178d8be83f92897ed2a9e375e5c0e73e111c6283a226f655e65a3ed08c3145bc9507eb37db94e208c96391bde822af995c56fd15a1f788316c94de3b595c6f79b801971e7e365ba839697112b8a815ee4b2c6d293d9ea5186420142a0c08a5ebdcca0610808c8d9e02", + "dve_hash": "f663c241f4cc765f3d56e38c259f231d50eb14ef6661df825f3d784468e73061", + "dve_proto": "0ac8030ad801080210d209180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0b088092b8c398feffffff013214e6c734c2265f59ae5135de87c0602b811ac3b29c3802426083ae741b651e23e9697b351d5314b9914ca47b980531df1d6bdd9fbb3b8a291d287de32a11799c5030871fd09aef30540e7c3c7e9c069de0077af7a0ff20173c81db70b2d52fce4b16e06c8f989fd867546cf5dbd306dc40b8fe941ce3b44a3f12d801080210d209180222480a2090e48be2976c4756547456e5352caea7062dc88122d2ba59b21e354276f9db681224080312202fa5d2139445f42698d5a690f0a5f898c15e32f6bd0bc2b23d76528719f84f8d2a0b088092b8c398feffffff013214e6c734c2265f59ae5135de87c0602b811ac3b29c38024260ab4f1416d9466e2e7e178d8be83f92897ed2a9e375e5c0e73e111c6283a226f655e65a3ed08c3145bc9507eb37db94e208c96391bde822af995c56fd15a1f788316c94de3b595c6f79b801971e7e365ba839697112b8a815ee4b2c6d293d9ea5186420142a0c08a5ebdcca0610808c8d9e02", + "ed25519_address": "2dde755c0b299c1ead82d72252cac4ea947cc604", + "ed25519_commit_hash": "96d7bf7893457779e9162962825ef549ddafa9ad908c65672ad5a1e99a3ca66e", + "ed25519_commit_proto": "08d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2267080212140580d060ff26da04445fc4033cb075b3c9d2fc2d1a0b088092b8c398feffffff01224092c9bce148d869729ff5b3096d58dd89bdc51a706340a804f976d2c54f5307b0c66b681a4ede26b64490010169b46f6737b1c8533a23a49677b2cbd3b1507c002267080312148bc90ed47eba17e84ad60b78a1861c309f9f51411a0b088092b8c398feffffff01224025703d730cb3d588377001f5cf692c1b2e64b4a9b2444dc04104b0e1bfa7b6488df7e754342ff3f144ea6cdc5f6a1ab00bf328cc67701b251517e1f48bde7501226708021214018b84ebe3e271f6a54aea4795cd393c0943a38a1a0b088092b8c398feffffff012240982e0e0fd4ef43d88c0c75e76ee55a995130b0ed6330a54f29132e7d94c615bf50717d13531193eeaf0c63259a394e151e764ab55eb6628420dbcc3e574604092267080212145e92162ce9b183d5f53c5989490ff5622f4ecc1f1a0b088092b8c398feffffff0122402b618b9793000f2ad7de1f7b0857bc4bacd9a146631b0d2495d1a31ae3a5e463762d9fc5f3efbc355a424672b89ec992e7e9dba6aff49d662cad579cf71d1c0f", + "ed25519_pubkey": "bf1cc0f7a5e910e632d73de020061c1383a97c51eb4cddc138cae4ba7c5d9e8c", + "ed25519_validator_proto": "0a142dde755c0b299c1ead82d72252cac4ea947cc60418052a20bf1cc0f7a5e910e632d73de020061c1383a97c51eb4cddc138cae4ba7c5d9e8c320765643235353139", + "ed25519_valset_hash": "7c2151dafee9db21842cb647e2898f09c29d3c4cb54309cb7d36547195fccdbe", + "evidence_hash_empty": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "evidence_list_hash": "39f2952ac612a5c69dd54088d21e384c843db45d1272a1b7a29e5b5160740abf", + "finalize_block_response_proto": "0a0d0a017412080a016b12017618011207120164280130011a3f104d1a3097bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c2209626c7331325f33383122160a040801100232080a020801120208033a041a0208092a20983f90f0fe1081a1611e12eeab5944daa6a66b308f161459f96e58fd76edfdfc320808011080cab5ee01", + "genesis_json": "{\n \"genesis_time\": \"2026-01-02T03:04:05.6Z\",\n \"chain_id\": \"bera-compat-1\",\n \"initial_height\": \"1\",\n \"consensus_params\": {\n \"block\": {\n \"max_bytes\": \"104857600\",\n \"max_gas\": \"-1\"\n },\n \"evidence\": {\n \"max_age_num_blocks\": \"100000\",\n \"max_age_duration\": \"172800000000000\",\n \"max_bytes\": \"1048576\"\n },\n \"validator\": {\n \"pub_key_types\": [\n \"bls12_381\"\n ]\n },\n \"version\": {\n \"app\": \"1\"\n },\n \"synchrony\": {\n \"precision\": \"505000000\",\n \"message_delay\": \"15000000000\"\n },\n \"feature\": {\n \"vote_extensions_enable_height\": \"0\",\n \"pbts_enable_height\": \"1\",\n \"sbt_enable_height\": \"7\"\n }\n },\n \"validators\": [\n {\n \"address\": \"95C39B91AF280535B0132107625C6262E2F83DD6\",\n \"pub_key\": {\n \"type\": \"cometbft/PubKeyBls12_381\",\n \"value\": \"F71KXfW8l48qR6tvEJkriKqNofaFKzzRMU7A8+KUSKMb7VpNWQK2oI3UEcWlfUVsDDGBh9xUinv3DbJv1Ch3NjrvMBkJeDNb4R0SefxvOxfj8OoFuut9mV/y0tk4v95O\"\n },\n \"power\": \"40\",\n \"name\": \"val0\"\n },\n {\n \"address\": \"E213983E829C4B946F0AC5AC3C41C4BB25B1361F\",\n \"pub_key\": {\n \"type\": \"cometbft/PubKeyBls12_381\",\n \"value\": \"E9ZuCq13HmymK7SHvCQ1YTKPSarntSAqRk1Ojp5i2mXzDW5B0X/V9K05ArvdVixaFoMooS0Y94Yyd65/40n4HD4zmOheG2abg0LdKHxQD9p32ONKOhBRr8YpXfAssiIN\"\n },\n \"power\": \"30\",\n \"name\": \"val1\"\n },\n {\n \"address\": \"E6C734C2265F59AE5135DE87C0602B811AC3B29C\",\n \"pub_key\": {\n \"type\": \"cometbft/PubKeyBls12_381\",\n \"value\": \"A9B2AFM45syg9U1+IfJDglEZsUJ39DxCyne9IEnQaVq9XqpDRwu9EBb8CGPPUeT9BOmtP0MmiWQxKut0CoDVkOIaoCu4uDgjhVURAjDNfLFZZeSsBY2hiJ7pDvzfEBnQ\"\n },\n \"power\": \"20\",\n \"name\": \"val2\"\n },\n {\n \"address\": \"8FF7BE3B769206C05BBDD81DDAD369EDB6D8CCD7\",\n \"pub_key\": {\n \"type\": \"cometbft/PubKeyBls12_381\",\n \"value\": \"BMSz0DUXqNnIJ1WnZ2b4grNENLf7eEt/EjWOQHV0qDFeUzsXIJf4sOH9TLwKK5jeBP4FFCg8FFD2unnnDsl0dROBoh4CiVj6TnoWsqqBNkC6L0ip2xtElVWdESQj7krE\"\n },\n \"power\": \"10\",\n \"name\": \"val3\"\n }\n ],\n \"app_hash\": \"69D4821FB36FCE2E5E2F5B5CFBC4BA2C0BF9612CBE5BD8AB4431D447EE698592\",\n \"app_state\": {\n \"k\": \"v\"\n }\n}", + "genesis_validator_hash": "96d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b10006314", + "header_hash": "53eb6c76d082062996efb85c11dde07969d54f69d088c5320fe88e000139a2e7", + "header_proto": "0a04080b1001120d626572612d636f6d7061742d3118d309220c08a7ebdcca0610808c8d9e022a480a20811c5967a1b7320bd1b26156be59801e65f77ab2af703976bf42bb9e443721621224080112209f24376f2cb0ea72f6f3d99634bfb80323f53e367ed0cd0cee9c7170c17b37ff322084a763b0d4704948493e446ef421570c73394c652781dadd11b230d93945bbb83a20303e3d08c4b8e61e7640853a93c47255a8b592e193af9588081f6d800e856b11422096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063144a2096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063145220ab619380cd4e115b18d6334918f00ba86403d95ebcb6eb2e4a2d457a86ff8ca35a20a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f3336220c099142bc3186ded72786ba27e9ea6d2da240fb9fd3fe79b479ecf8e734b28506a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214e213983e829c4b946f0ac5ac3c41c4bb25b1361f", + "p2p_block_part": "2a920708d30910021a8a0712e1060a96030a04080b1001120d626572612d636f6d7061742d3118d309220c08a7ebdcca0610808c8d9e022a480a20811c5967a1b7320bd1b26156be59801e65f77ab2af703976bf42bb9e443721621224080112209f24376f2cb0ea72f6f3d99634bfb80323f53e367ed0cd0cee9c7170c17b37ff322084a763b0d4704948493e446ef421570c73394c652781dadd11b230d93945bbb83a20303e3d08c4b8e61e7640853a93c47255a8b592e193af9588081f6d800e856b11422096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063144a2096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063145220ab619380cd4e115b18d6334918f00ba86403d95ebcb6eb2e4a2d457a86ff8ca35a20a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f3336220c099142bc3186ded72786ba27e9ea6d2da240fb9fd3fe79b479ecf8e734b28506a20e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b8557214e213983e829c4b946f0ac5ac3c41c4bb25b1361f12100a0674782d6f6e650a0674782d74776f1a0022b10308d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2287010804121495c39b91af280535b0132107625c6262e2f83dd61a0b088092b8c398feffffff0122608de8b5ff33540f1678428e38911de47fc3ab8d6ccd79f07c8b3ce469f8d99ecccaa7e6138d9d5016b41479ff0eb8f8fd19136435ed2bc1053555145c7eb0feee5ac0ab8c68ebb287628b4c994d999ffe47bb437f78f3fb6e0a909daa4adbff0c222508051214e213983e829c4b946f0ac5ac3c41c4bb25b1361f1a0b088092b8c398feffffff01222508051214e6c734c2265f59ae5135de87c0602b811ac3b29c1a0b088092b8c398feffffff01228701080612148ff7be3b769206c05bbdd81ddad369edb6d8ccd71a0b088092b8c398feffffff012260abb1bb002e2fee585cdeabcedd3398dc554558b3637f7e6a1ac4b2a9d67ea581ae5805d0c54a820f155c477ae58ac3ba03033db6f06a787dcdad46a4e60589149a6e8fbfedca5c648570badd0acf0feebc011adbe5781ade6ef71a02e210cf251a2408011a209f1327037b27d10cef34002c8e978c9167c9d62eb67813c2a4ce66525568f3d4", + "p2p_commit": "5ab4030ab10308d20910021a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2287010804121495c39b91af280535b0132107625c6262e2f83dd61a0b088092b8c398feffffff0122608de8b5ff33540f1678428e38911de47fc3ab8d6ccd79f07c8b3ce469f8d99ecccaa7e6138d9d5016b41479ff0eb8f8fd19136435ed2bc1053555145c7eb0feee5ac0ab8c68ebb287628b4c994d999ffe47bb437f78f3fb6e0a909daa4adbff0c222508051214e213983e829c4b946f0ac5ac3c41c4bb25b1361f1a0b088092b8c398feffffff01222508051214e6c734c2265f59ae5135de87c0602b811ac3b29c1a0b088092b8c398feffffff01228701080612148ff7be3b769206c05bbdd81ddad369edb6d8ccd71a0b088092b8c398feffffff012260abb1bb002e2fee585cdeabcedd3398dc554558b3637f7e6a1ac4b2a9d67ea581ae5805d0c54a820f155c477ae58ac3ba03033db6f06a787dcdad46a4e60589149a6e8fbfedca5c648570badd0acf0feebc011adbe5781ade6ef71a02e210cf25", + "p2p_has_proposal_block_part": "520508d3091002", + "p2p_has_vote": "3a0908d209100218022002", + "p2p_new_round_step": "0a0b08d2091002180320072801", + "p2p_new_valid_block": "123408d20910021a2408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea220508031201022801", + "p2p_proposal": "1ac6010ac301082010d209180220012a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320c08a5ebdcca0610808c8d9e023a6089c32ec1b8eef9447160a3b89b833880067b13e70e1435de8f278028100ac080d11f2717f250e43184b98359c686db9811714d99a8e47bc5172035dda5f02764922143a607ea90468513a8818df204f3bfe90580bea26c318eb926b6dad58a2f", + "p2p_proposal_pol": "220c08d20910011a050804120104", + "p2p_vote": "32d9010ad601080210d209180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0b088092b8c398feffffff01321495c39b91af280535b0132107625c6262e2f83dd64260b24e3ba8f8217e43bb62900617ae91f5c5aeb324677c26cf128b669e5df45b0d74c16e40e190747b167ca50a7298afc813d8fc26b439b6136e34137ed2ae6039d5d606802231f6af7935b746d6d513443274efff8648ed6eff470267647ff6e7", + "p2p_vote_set_bits": "4a5808d2091002180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a050804120108", + "p2p_vote_set_maj23": "425108d2091002180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea", + "params_hash": "ab619380cd4e115b18d6334918f00ba86403d95ebcb6eb2e4a2d457a86ff8ca3", + "params_json": "{\"block\":{\"max_bytes\":\"104857600\",\"max_gas\":\"-1\"},\"evidence\":{\"max_age_num_blocks\":\"100000\",\"max_age_duration\":\"172800000000000\",\"max_bytes\":\"1048576\"},\"validator\":{\"pub_key_types\":[\"bls12_381\"]},\"version\":{\"app\":\"1\"},\"synchrony\":{\"precision\":\"505000000\",\"message_delay\":\"15000000000\"},\"feature\":{\"vote_extensions_enable_height\":\"0\",\"pbts_enable_height\":\"1\",\"sbt_enable_height\":\"7\"}}", + "params_proto": "0a10088080803210ffffffffffffffffff01120e08a08d0612040880c60a188080401a0b0a09626c7331325f33383122020801320c0a0610c0e0e6f0011202080f3a0a0a00120208011a020807", + "params_update_proto": "0a040801100232080a020801120208033a041a020809", + "params_updated_hash": "f92048cfb1a10368bd183d3af4f21515a5c26aa8f3b6c46eab178dc28d47d152", + "params_updated_proto": "0a0408011002120e08a08d0612040880c60a188080401a0b0a09626c7331325f3338312202080132080a020801120208033a0a0a00120208011a020809", + "priv_validator_key_ed25519_json": "{\n \"address\": \"2DDE755C0B299C1EAD82D72252CAC4EA947CC604\",\n \"pub_key\": {\n \"type\": \"tendermint/PubKeyEd25519\",\n \"value\": \"vxzA96XpEOYy1z3gIAYcE4OpfFHrTN3BOMrkunxdnow=\"\n },\n \"priv_key\": {\n \"type\": \"tendermint/PrivKeyEd25519\",\n \"value\": \"OO9/AlR++0MD0QdnDocTT/+Aek45bRA8N5neaAoDrJS/HMD3pekQ5jLXPeAgBhwTg6l8UetM3cE4yuS6fF2ejA==\"\n }\n}", + "priv_validator_key_json": "{\n \"address\": \"95C39B91AF280535B0132107625C6262E2F83DD6\",\n \"pub_key\": {\n \"type\": \"cometbft/PubKeyBls12_381\",\n \"value\": \"F71KXfW8l48qR6tvEJkriKqNofaFKzzRMU7A8+KUSKMb7VpNWQK2oI3UEcWlfUVsDDGBh9xUinv3DbJv1Ch3NjrvMBkJeDNb4R0SefxvOxfj8OoFuut9mV/y0tk4v95O\"\n },\n \"priv_key\": {\n \"type\": \"cometbft/PrivKeyBls12_381\",\n \"value\": \"LMRBLE+B3QNZb/0PQ8bXc5/WUMLRIloLrSn//kBc5T8=\"\n }\n}", + "priv_validator_state_json": "{\n \"height\": \"0\",\n \"round\": 0,\n \"step\": 0\n}", + "priv_validator_state_json_after_signing": "{\n \"height\": \"1234\",\n \"round\": 2,\n \"step\": 3,\n \"signature\": \"sk47qPghfkO7YpAGF66R9cWusyRnfCbPEotmnl30Ww10wW5A4ZB0exZ8pQpymK/IE9j8JrQ5thNuNBN+0q5gOdXWBoAiMfaveTW3RtbVE0QydO//hkjtbv9HAmdkf/bn\",\n \"signbytes\": \"6D080211D20400000000000019020000000000000022480A20496ACA80E4D8F29FB8E8CD816C3AFB48D3F103970B3A2EE1600C08CA67326DEE122408031220D887DB09649DAB0D83951D8D5D69B2E7D8BB70E79DAA2A3A279B4FD6B8346CEA320D626572612D636F6D7061742D31\"\n}", + "privval_pubkey": "17bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e", + "privval_signed_proposal_signature": "8fe853f137ba3df14d8f8556c1ace9e73474d6221dcb2f0eff339d86afaafda5045aa91349dad30b7bbde0a762a6a2ae0dd08115a463b9e8a9e57c863b35d6cc78fac2e502077ffa3aa02646731664774f70a6a015cc3432826ee5192a573b15", + "privval_signed_vote_proto": "080210d209180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0b088092b8c398feffffff01321495c39b91af280535b0132107625c6262e2f83dd64260b24e3ba8f8217e43bb62900617ae91f5c5aeb324677c26cf128b669e5df45b0d74c16e40e190747b167ca50a7298afc813d8fc26b439b6136e34137ed2ae6039d5d606802231f6af7935b746d6d513443274efff8648ed6eff470267647ff6e7", + "privval_signed_vote_signature": "b24e3ba8f8217e43bb62900617ae91f5c5aeb324677c26cf128b669e5df45b0d74c16e40e190747b167ca50a7298afc813d8fc26b439b6136e34137ed2ae6039d5d606802231f6af7935b746d6d513443274efff8648ed6eff470267647ff6e7", + "process_proposal_request_proto": "12002220496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee28d209320c08a5ebdcca0610808c8d9e023a2096d173f12f618cbaf98738be85820f8eb040f557dfd2604e0ba6763b100063144214e213983e829c4b946f0ac5ac3c41c4bb25b1361f4a14e6c734c2265f59ae5135de87c0602b811ac3b29c", + "proposal_bls_signature_val1": "89c32ec1b8eef9447160a3b89b833880067b13e70e1435de8f278028100ac080d11f2717f250e43184b98359c686db9811714d99a8e47bc5172035dda5f02764922143a607ea90468513a8818df204f3bfe90580bea26c318eb926b6dad58a2f", + "proposal_proto_signed": "082010d209180220012a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320c08a5ebdcca0610808c8d9e023a6089c32ec1b8eef9447160a3b89b833880067b13e70e1435de8f278028100ac080d11f2717f250e43184b98359c686db9811714d99a8e47bc5172035dda5f02764922143a607ea90468513a8818df204f3bfe90580bea26c318eb926b6dad58a2f", + "proposal_sign_bytes": "7d082011d20400000000000019020000000000000020012a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320c08a5ebdcca0610808c8d9e023a0d626572612d636f6d7061742d31", + "state_proto": "0a0d0a04080b10011205312e302e31120d626572612d636f6d7061742d3118d20922480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0c08a5ebdcca0610808c8d9e0232c4050a90010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338310a87010a14e213983e829c4b946f0ac5ac3c41c4bb25b1361f181e201e2a6013d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a168328a12d18f7863277ae7fe349f81c3e3398e85e1b669b8342dd287c500fda77d8e34a3a1051afc6295df02cb2220d3209626c7331325f3338310a87010a14e6c734c2265f59ae5135de87c0602b811ac3b29c181420142a6003d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd04e9ad3f43268964312aeb740a80d590e21aa02bb8b838238555110230cd7cb15965e4ac058da1889ee90efcdf1019d03209626c7331325f3338310a87010a148ff7be3b769206c05bbdd81ddad369edb6d8ccd7180a200a2a6004c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de04fe0514283c1450f6ba79e70ec974751381a21e028958fa4e7a16b2aa813640ba2f48a9db1b4495559d112423ee4ac43209626c7331325f3338311290010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338313ac4050a90010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338310a87010a14e213983e829c4b946f0ac5ac3c41c4bb25b1361f181e201e2a6013d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a168328a12d18f7863277ae7fe349f81c3e3398e85e1b669b8342dd287c500fda77d8e34a3a1051afc6295df02cb2220d3209626c7331325f3338310a87010a14e6c734c2265f59ae5135de87c0602b811ac3b29c181420142a6003d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd04e9ad3f43268964312aeb740a80d590e21aa02bb8b838238555110230cd7cb15965e4ac058da1889ee90efcdf1019d03209626c7331325f3338310a87010a148ff7be3b769206c05bbdd81ddad369edb6d8ccd7180a200a2a6004c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de04fe0514283c1450f6ba79e70ec974751381a21e028958fa4e7a16b2aa813640ba2f48a9db1b4495559d112423ee4ac43209626c7331325f3338311290010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f33383142c4050a90010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338310a87010a14e213983e829c4b946f0ac5ac3c41c4bb25b1361f181e201e2a6013d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a168328a12d18f7863277ae7fe349f81c3e3398e85e1b669b8342dd287c500fda77d8e34a3a1051afc6295df02cb2220d3209626c7331325f3338310a87010a14e6c734c2265f59ae5135de87c0602b811ac3b29c181420142a6003d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd04e9ad3f43268964312aeb740a80d590e21aa02bb8b838238555110230cd7cb15965e4ac058da1889ee90efcdf1019d03209626c7331325f3338310a87010a148ff7be3b769206c05bbdd81ddad369edb6d8ccd7180a200a2a6004c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de04fe0514283c1450f6ba79e70ec974751381a21e028958fa4e7a16b2aa813640ba2f48a9db1b4495559d112423ee4ac43209626c7331325f3338311290010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338314864524d0a10088080803210ffffffffffffffffff01120e08a08d0612040880c60a188080401a0b0a09626c7331325f33383122020801320c0a0610c0e0e6f0011202080f3a0a0a00120208011a02080758326220c099142bc3186ded72786ba27e9ea6d2da240fb9fd3fe79b479ecf8e734b28506a20a172cedcae47474b615c54d510a5d84a8dea3032e958587430b413538be3f33370017a0808011080cab5ee01", + "validators_info_proto": "0ac4050a90010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338310a87010a14e213983e829c4b946f0ac5ac3c41c4bb25b1361f181e201e2a6013d66e0aad771e6ca62bb487bc243561328f49aae7b5202a464d4e8e9e62da65f30d6e41d17fd5f4ad3902bbdd562c5a168328a12d18f7863277ae7fe349f81c3e3398e85e1b669b8342dd287c500fda77d8e34a3a1051afc6295df02cb2220d3209626c7331325f3338310a87010a14e6c734c2265f59ae5135de87c0602b811ac3b29c181420142a6003d076005338e6cca0f54d7e21f243825119b14277f43c42ca77bd2049d0695abd5eaa43470bbd1016fc0863cf51e4fd04e9ad3f43268964312aeb740a80d590e21aa02bb8b838238555110230cd7cb15965e4ac058da1889ee90efcdf1019d03209626c7331325f3338310a87010a148ff7be3b769206c05bbdd81ddad369edb6d8ccd7180a200a2a6004c4b3d03517a8d9c82755a76766f882b34434b7fb784b7f12358e407574a8315e533b172097f8b0e1fd4cbc0a2b98de04fe0514283c1450f6ba79e70ec974751381a21e028958fa4e7a16b2aa813640ba2f48a9db1b4495559d112423ee4ac43209626c7331325f3338311290010a1495c39b91af280535b0132107625c6262e2f83dd6182820c4ffffffffffffffff012a6017bd4a5df5bc978f2a47ab6f10992b88aa8da1f6852b3cd1314ec0f3e29448a31bed5a4d5902b6a08dd411c5a57d456c0c318187dc548a7bf70db26fd42877363aef30190978335be11d1279fc6f3b17e3f0ea05baeb7d995ff2d2d938bfde4e3209626c7331325f3338311064", + "vote_bls_signature_val0_precommit": "b24e3ba8f8217e43bb62900617ae91f5c5aeb324677c26cf128b669e5df45b0d74c16e40e190747b167ca50a7298afc813d8fc26b439b6136e34137ed2ae6039d5d606802231f6af7935b746d6d513443274efff8648ed6eff470267647ff6e7", + "vote_proto_signed_precommit": "080210d209180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0b088092b8c398feffffff01321495c39b91af280535b0132107625c6262e2f83dd64260b24e3ba8f8217e43bb62900617ae91f5c5aeb324677c26cf128b669e5df45b0d74c16e40e190747b167ca50a7298afc813d8fc26b439b6136e34137ed2ae6039d5d606802231f6af7935b746d6d513443274efff8648ed6eff470267647ff6e7", + "vote_sign_bytes_nil_precommit": "23080211d204000000000000190200000000000000320d626572612d636f6d7061742d31", + "vote_sign_bytes_precommit": "6d080211d20400000000000019020000000000000022480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320d626572612d636f6d7061742d31", + "vote_sign_bytes_precommit_with_timestamp": "6d080211d20400000000000019020000000000000022480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320d626572612d636f6d7061742d31", + "vote_sign_bytes_prevote": "6d080111d20400000000000019020000000000000022480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320d626572612d636f6d7061742d31", + "wal_end_height_proto": "0a0c08a5ebdcca0610808c8d9e021205220308d209", + "wal_event_round_state_proto": "0a0c08a5ebdcca0610808c8d9e0212190a1708d20910021a10526f756e645374657050726f706f7365", + "wal_msginfo_proposal_with_receive_time_proto": "0a0c08a5ebdcca0610808c8d9e0212e40112e1010ac9011ac6010ac301082010d209180220012a480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea320c08a5ebdcca0610808c8d9e023a6089c32ec1b8eef9447160a3b89b833880067b13e70e1435de8f278028100ac080d11f2717f250e43184b98359c686db9811714d99a8e47bc5172035dda5f02764922143a607ea90468513a8818df204f3bfe90580bea26c318eb926b6dad58a2f120570656572311a0c08a7ebdcca0610808c8d9e02", + "wal_msginfo_vote_proto": "0a0c08a5ebdcca0610808c8d9e0212e20112df010adc0132d9010ad601080210d209180222480a20496aca80e4d8f29fb8e8cd816c3afb48d3f103970b3a2ee1600c08ca67326dee122408031220d887db09649dab0d83951d8d5d69b2e7d8bb70e79daa2a3a279b4fd6b8346cea2a0b088092b8c398feffffff01321495c39b91af280535b0132107625c6262e2f83dd64260b24e3ba8f8217e43bb62900617ae91f5c5aeb324677c26cf128b669e5df45b0d74c16e40e190747b167ca50a7298afc813d8fc26b439b6136e34137ed2ae6039d5d606802231f6af7935b746d6d513443274efff8648ed6eff470267647ff6e7", + "wal_timeout_proto": "0a0c08a5ebdcca0610808c8d9e02120d1a0b0a02080310d20918022004" +} diff --git a/test/compat/vectors_test.go b/test/compat/vectors_test.go new file mode 100644 index 00000000000..02c1281ecfa --- /dev/null +++ b/test/compat/vectors_test.go @@ -0,0 +1,973 @@ +//go:build bls12381 + +// Package compat holds the bera-v1.x <-> bera-v0.40.x compatibility tests. +// +// vectors_test.go replays a fixed set of inputs through this fork and asserts +// that every consensus-relevant byte string (sign bytes, signatures, hashes, +// persisted protos, p2p/WAL wire messages, key and genesis files) is identical +// to what the bera-v1.x line produces for the same inputs. The expected values +// live in vectors/bera_v1x_vectors.json and are generated by gen-v1x/ (which +// builds against bera-v1.x); the inputs below must stay in sync with gen-v1x. +package compat + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "path/filepath" + "testing" + "time" + + gogo "github.com/cosmos/gogoproto/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/bls12381" + "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/libs/bits" + cmtjson "github.com/cometbft/cometbft/libs/json" + "github.com/cometbft/cometbft/privval" + cmtcons "github.com/cometbft/cometbft/proto/tendermint/consensus" + cmtstate "github.com/cometbft/cometbft/proto/tendermint/state" + cmtstore "github.com/cometbft/cometbft/proto/tendermint/store" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmtversion "github.com/cometbft/cometbft/proto/tendermint/version" + sm "github.com/cometbft/cometbft/state" + "github.com/cometbft/cometbft/types" +) + +// Fixed inputs. Keep in sync with gen-v1x/main.go. +const ( + chainID = "bera-compat-1" + height = int64(1234) + round = int32(2) + numVals = 4 + blsSeed = "bera-compat-bls-val-" + ed25Seed = "bera-compat-ed25519-val" +) + +var ( + fixedTime = time.Date(2026, 1, 2, 3, 4, 5, 600000000, time.UTC) + fixedTimeNext = fixedTime.Add(2 * time.Second) + powers = []int64{10, 20, 30, 40} +) + +func h32(label string) []byte { s := sha256.Sum256([]byte(label)); return s[:] } + +type vectors map[string]any + +func loadVectors(t *testing.T) vectors { + t.Helper() + bz, err := os.ReadFile(filepath.Join("vectors", "bera_v1x_vectors.json")) + require.NoError(t, err) + v := vectors{} + require.NoError(t, json.Unmarshal(bz, &v)) + return v +} + +func (v vectors) hex(t *testing.T, key string) []byte { + t.Helper() + s, ok := v[key].(string) + require.Truef(t, ok, "vector %q missing or not a string", key) + bz, err := hex.DecodeString(s) + require.NoErrorf(t, err, "vector %q is not hex", key) + return bz +} + +func (v vectors) str(t *testing.T, key string) string { + t.Helper() + s, ok := v[key].(string) + require.Truef(t, ok, "vector %q missing or not a string", key) + return s +} + +func (v vectors) num(t *testing.T, key string) int { + t.Helper() + f, ok := v[key].(float64) + require.Truef(t, ok, "vector %q missing or not a number", key) + return int(f) +} + +// eqHex asserts that got equals the hex vector named key. +func (v vectors) eqHex(t *testing.T, key string, got []byte) { + t.Helper() + want := v.hex(t, key) + assert.Equalf(t, hex.EncodeToString(want), hex.EncodeToString(got), "vector %q differs from bera-v1.x", key) +} + +type marshaler interface{ Marshal() ([]byte, error) } + +func mustMarshal(t *testing.T, m marshaler) []byte { + t.Helper() + bz, err := m.Marshal() + require.NoError(t, err) + return bz +} + +func blsPubPtr(pk crypto.PubKey) *bls12381.PubKey { + switch k := pk.(type) { + case *bls12381.PubKey: + return k + case bls12381.PubKey: + return &k + default: + panic(fmt.Sprintf("not a BLS public key: %T", pk)) + } +} + +func sortKeys[K any](valSet *types.ValidatorSet, vals []*types.Validator, privs []K) ([]*types.Validator, []K) { + outVals := make([]*types.Validator, len(vals)) + outPrivs := make([]K, len(privs)) + for i, sv := range valSet.Validators { + found := false + for j, v := range vals { + if bytes.Equal(v.PubKey.Bytes(), sv.PubKey.Bytes()) { + outVals[i] = sv + outPrivs[i] = privs[j] + found = true + break + } + } + if !found { + panic("validator not found") + } + } + return outVals, outPrivs +} + +// fixture holds everything derived from the fixed inputs. +type fixture struct { + blsPrivs []*bls12381.PrivKey + blsVals []*types.Validator + blsValSet *types.ValidatorSet + edPriv ed25519.PrivKey + edPrivs []ed25519.PrivKey + edVals []*types.Validator + edValSet *types.ValidatorSet + blockID types.BlockID + params types.ConsensusParams +} + +func newFixture(t *testing.T) *fixture { + t.Helper() + f := &fixture{} + f.blsPrivs = make([]*bls12381.PrivKey, numVals) + f.blsVals = make([]*types.Validator, numVals) + for i := 0; i < numVals; i++ { + pk, err := bls12381.GenPrivKeyFromSecret([]byte(fmt.Sprintf("%s%d", blsSeed, i))) + require.NoError(t, err) + f.blsPrivs[i] = pk + f.blsVals[i] = types.NewValidator(pk.PubKey(), powers[i]) + } + f.blsValSet = types.NewValidatorSet(f.blsVals) + f.blsVals, f.blsPrivs = sortKeys(f.blsValSet, f.blsVals, f.blsPrivs) + + f.edPriv = ed25519.GenPrivKeyFromSecret([]byte(ed25Seed)) + f.edPrivs = make([]ed25519.PrivKey, numVals) + f.edVals = make([]*types.Validator, numVals) + for i := 0; i < numVals; i++ { + f.edPrivs[i] = ed25519.GenPrivKeyFromSecret([]byte(fmt.Sprintf("%s-%d", ed25Seed, i))) + f.edVals[i] = types.NewValidator(f.edPrivs[i].PubKey(), powers[i]) + } + f.edValSet = types.NewValidatorSet(f.edVals) + f.edVals, f.edPrivs = sortKeys(f.edValSet, f.edVals, f.edPrivs) + + f.blockID = types.BlockID{ + Hash: h32("block"), + PartSetHeader: types.PartSetHeader{Total: 3, Hash: h32("parts")}, + } + f.params = types.ConsensusParams{ + Block: types.BlockParams{MaxBytes: 104857600, MaxGas: -1}, + Evidence: types.EvidenceParams{MaxAgeNumBlocks: 100000, MaxAgeDuration: 172800 * time.Second, MaxBytes: 1048576}, + Validator: types.ValidatorParams{PubKeyTypes: []string{types.ABCIPubKeyTypeBls12381}}, + Version: types.VersionParams{App: 1}, + Synchrony: types.SynchronyParams{Precision: 505 * time.Millisecond, MessageDelay: 15 * time.Second}, + Feature: types.FeatureParams{VoteExtensionsEnableHeight: 0, PbtsEnableHeight: 1, SBTEnableHeight: 7}, + } + return f +} + +func (f *fixture) vote(tp cmtproto.SignedMsgType, bid types.BlockID, valIdx int32, addr []byte) *types.Vote { + return &types.Vote{ + Type: tp, + Height: height, + Round: round, + BlockID: bid, + ValidatorAddress: addr, + ValidatorIndex: valIdx, + Timestamp: time.Time{}, + } +} + +func (f *fixture) signedBLSVote(t *testing.T, bid types.BlockID, i int) *types.Vote { + t.Helper() + vote := f.vote(cmtproto.PrecommitType, bid, int32(i), f.blsVals[i].Address) + sig, err := f.blsPrivs[i].Sign(types.VoteSignBytes(chainID, vote.ToProto())) + require.NoError(t, err) + vote.Signature = sig + return vote +} + +// aggCommit builds the aggregated commit where validators 0..2 commit and 3 votes nil. +func (f *fixture) aggCommit(t *testing.T) *types.Commit { + t.Helper() + voteSet := types.NewVoteSet(chainID, height, round, cmtproto.PrecommitType, f.blsValSet) + for i := 0; i < numVals; i++ { + bid := f.blockID + if i == 3 { + bid = types.BlockID{} + } + added, err := voteSet.AddVote(f.signedBLSVote(t, bid, i)) + require.NoError(t, err) + require.True(t, added) + } + return voteSet.MakeBLSCommit().ToCommit() +} + +func TestVectorsKeysAndValidators(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + + // keyed by seed index (creation order), independent of validator-set order + for i := 0; i < numVals; i++ { + pk, err := bls12381.GenPrivKeyFromSecret([]byte(fmt.Sprintf("%s%d", blsSeed, i))) + require.NoError(t, err) + v.eqHex(t, fmt.Sprintf("bls_pubkey_%d", i), pk.PubKey().Bytes()) + v.eqHex(t, fmt.Sprintf("bls_pubkey_compressed_%d", i), blsPubPtr(pk.PubKey()).Compress()) + v.eqHex(t, fmt.Sprintf("bls_address_%d", i), pk.PubKey().Address()) + } + v.eqHex(t, "bls_valset_hash", f.blsValSet.Hash()) + vsp, err := f.blsValSet.ToProto() + require.NoError(t, err) + v.eqHex(t, "bls_valset_proto", mustMarshal(t, vsp)) + vp, err := f.blsVals[0].ToProto() + require.NoError(t, err) + v.eqHex(t, "bls_validator0_proto", mustMarshal(t, vp)) + v.eqHex(t, "bls_validator0_bytes", f.blsVals[0].Bytes()) + + // a validator set decoded from the bera-v1.x proto hashes identically + decoded := new(cmtproto.ValidatorSet) + require.NoError(t, decoded.Unmarshal(v.hex(t, "bls_valset_proto"))) + vs2, err := types.ValidatorSetFromProto(decoded) + require.NoError(t, err) + v.eqHex(t, "bls_valset_hash", vs2.Hash()) + + v.eqHex(t, "ed25519_pubkey", f.edPriv.PubKey().Bytes()) + v.eqHex(t, "ed25519_address", f.edPriv.PubKey().Address()) + edVal := types.NewValidator(f.edPriv.PubKey(), 5) + vp, err = edVal.ToProto() + require.NoError(t, err) + v.eqHex(t, "ed25519_validator_proto", mustMarshal(t, vp)) + v.eqHex(t, "ed25519_valset_hash", f.edValSet.Hash()) +} + +func TestVectorsSignBytes(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + + bidp := f.blockID.ToProto() + v.eqHex(t, "blockid_proto", mustMarshal(t, &bidp)) + + precommit := f.vote(cmtproto.PrecommitType, f.blockID, 0, f.blsVals[0].Address) + prevote := f.vote(cmtproto.PrevoteType, f.blockID, 0, f.blsVals[0].Address) + nilPrecommit := f.vote(cmtproto.PrecommitType, types.BlockID{}, 3, f.blsVals[3].Address) + v.eqHex(t, "vote_sign_bytes_precommit", types.VoteSignBytes(chainID, precommit.ToProto())) + v.eqHex(t, "vote_sign_bytes_prevote", types.VoteSignBytes(chainID, prevote.ToProto())) + v.eqHex(t, "vote_sign_bytes_nil_precommit", types.VoteSignBytes(chainID, nilPrecommit.ToProto())) + sig, err := f.blsPrivs[0].Sign(types.VoteSignBytes(chainID, precommit.ToProto())) + require.NoError(t, err) + v.eqHex(t, "vote_bls_signature_val0_precommit", sig) + precommit.Signature = sig + v.eqHex(t, "vote_proto_signed_precommit", mustMarshal(t, precommit.ToProto())) + require.NoError(t, precommit.ValidateBasic()) + require.NoError(t, precommit.Verify(chainID, f.blsVals[0].PubKey)) + + tsVote := f.vote(cmtproto.PrecommitType, f.blockID, 0, f.blsVals[0].Address) + tsVote.Timestamp = fixedTime + v.eqHex(t, "vote_sign_bytes_precommit_with_timestamp", types.VoteSignBytes(chainID, tsVote.ToProto())) + + proposal := types.NewProposal(height, round, 1, f.blockID, fixedTime) + v.eqHex(t, "proposal_sign_bytes", types.ProposalSignBytes(chainID, proposal.ToProto())) + psig, err := f.blsPrivs[1].Sign(types.ProposalSignBytes(chainID, proposal.ToProto())) + require.NoError(t, err) + v.eqHex(t, "proposal_bls_signature_val1", psig) + proposal.Signature = psig + v.eqHex(t, "proposal_proto_signed", mustMarshal(t, proposal.ToProto())) + require.NoError(t, proposal.ValidateBasic()) + + // a bera-v1.x signed vote decodes and verifies here + pv := new(cmtproto.Vote) + require.NoError(t, pv.Unmarshal(v.hex(t, "vote_proto_signed_precommit"))) + vote, err := types.VoteFromProto(pv) + require.NoError(t, err) + require.NoError(t, vote.ValidateBasic()) + require.NoError(t, vote.Verify(chainID, f.blsVals[0].PubKey)) +} + +func TestVectorsCommits(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + + agg := f.aggCommit(t) + v.eqHex(t, "agg_commit_proto", mustMarshal(t, agg.ToProto())) + v.eqHex(t, "agg_commit_hash", agg.Hash()) + for i, cs := range agg.Signatures { + assert.Equal(t, v.num(t, fmt.Sprintf("agg_commit_sig%d_flag", i)), int(cs.BlockIDFlag), "sig %d flag", i) + } + require.True(t, agg.HasAggregatedSignature()) + require.NoError(t, f.blsValSet.VerifyCommit(chainID, f.blockID, height, agg)) + require.NoError(t, types.VerifyCommitLight(chainID, f.blsValSet, f.blockID, height, agg)) + + // a bera-v1.x aggregated commit decodes, hashes and verifies here + pc := new(cmtproto.Commit) + require.NoError(t, pc.Unmarshal(v.hex(t, "agg_commit_proto"))) + c2, err := types.CommitFromProto(pc) + require.NoError(t, err) + require.NoError(t, c2.ValidateBasic()) + v.eqHex(t, "agg_commit_hash", c2.Hash()) + require.NoError(t, f.blsValSet.VerifyCommit(chainID, f.blockID, height, c2)) + + voteSet2 := types.NewVoteSet(chainID, height, round, cmtproto.PrecommitType, f.blsValSet) + for i := 0; i < numVals; i++ { + _, err := voteSet2.AddVote(f.signedBLSVote(t, f.blockID, i)) + require.NoError(t, err) + } + aggAll := voteSet2.MakeBLSCommit().ToCommit() + v.eqHex(t, "agg_commit_all_proto", mustMarshal(t, aggAll.ToProto())) + v.eqHex(t, "agg_commit_all_hash", aggAll.Hash()) + require.NoError(t, f.blsValSet.VerifyCommit(chainID, f.blockID, height, aggAll)) + + edVoteSet := types.NewVoteSet(chainID, height, round, cmtproto.PrecommitType, f.edValSet) + for i := 0; i < numVals; i++ { + bid := f.blockID + if i == 1 { + bid = types.BlockID{} + } + vote := f.vote(cmtproto.PrecommitType, bid, int32(i), f.edVals[i].Address) + sig, err := f.edPrivs[i].Sign(types.VoteSignBytes(chainID, vote.ToProto())) + require.NoError(t, err) + vote.Signature = sig + _, err = edVoteSet.AddVote(vote) + require.NoError(t, err) + } + edCommit := edVoteSet.MakeExtendedCommit(types.FeatureParams{}).ToCommit() + v.eqHex(t, "ed25519_commit_proto", mustMarshal(t, edCommit.ToProto())) + v.eqHex(t, "ed25519_commit_hash", edCommit.Hash()) + require.NoError(t, f.edValSet.VerifyCommit(chainID, f.blockID, height, edCommit)) +} + +func durPtr(d time.Duration) *time.Duration { return &d } + +func paramsUpdate() *cmtproto.ConsensusParams { + return &cmtproto.ConsensusParams{ + Block: &cmtproto.BlockParams{MaxBytes: 1, MaxGas: 2}, + Feature: &cmtproto.FeatureParams{SbtEnableHeight: &gogo.Int64Value{Value: 9}}, + Synchrony: &cmtproto.SynchronyParams{Precision: durPtr(1 * time.Second), MessageDelay: durPtr(3 * time.Second)}, + } +} + +func TestVectorsConsensusParams(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + + v.eqHex(t, "params_hash", f.params.Hash()) + pp := f.params.ToProto() + v.eqHex(t, "params_proto", mustMarshal(t, &pp)) + bz, err := cmtjson.Marshal(f.params) + require.NoError(t, err) + // The JSON form is what genesis files carry. This fork adds an "authority" + // object (upstream v0.39+), so compare the bera-v1.x keys as a subset and + // require the extra object to be empty. + var got, want map[string]json.RawMessage + require.NoError(t, json.Unmarshal(bz, &got)) + require.NoError(t, json.Unmarshal([]byte(v.str(t, "params_json")), &want)) + for k, wv := range want { + assert.JSONEqf(t, string(wv), string(got[k]), "consensus_params.%s JSON differs", k) + delete(got, k) + } + for k, gv := range got { + assert.Equalf(t, "authority", k, "unexpected extra consensus_params key %q", k) + assert.JSONEq(t, `{"authority":""}`, string(gv)) + } + + // params decoded from the bera-v1.x proto are identical + pbp := new(cmtproto.ConsensusParams) + require.NoError(t, pbp.Unmarshal(v.hex(t, "params_proto"))) + decoded := types.ConsensusParamsFromProto(*pbp) + assert.Equal(t, f.params, decoded) + v.eqHex(t, "params_hash", decoded.Hash()) + require.NoError(t, decoded.ValidateBasic()) + + upd := paramsUpdate() + v.eqHex(t, "params_update_proto", mustMarshal(t, upd)) + updated := f.params.Update(upd) + v.eqHex(t, "params_updated_hash", updated.Hash()) + up := updated.ToProto() + v.eqHex(t, "params_updated_proto", mustMarshal(t, &up)) + assert.EqualValues(t, 9, updated.Feature.SBTEnableHeight) + assert.Equal(t, 3*time.Second, updated.Synchrony.MessageDelay) + assert.EqualValues(t, 1, updated.Feature.PbtsEnableHeight) +} + +func (f *fixture) block(t *testing.T, agg *types.Commit) (*types.Block, *types.PartSet) { + t.Helper() + lastBlockID := types.BlockID{Hash: h32("last-block"), PartSetHeader: types.PartSetHeader{Total: 1, Hash: h32("last-parts")}} + txs := []types.Tx{types.Tx("tx-one"), types.Tx("tx-two")} + block := types.MakeBlock(height+1, txs, agg, nil) + block.Header.Version = cmtversion.Consensus{Block: 11, App: 1} + block.Header.ChainID = chainID + block.Header.Time = fixedTimeNext + block.Header.LastBlockID = lastBlockID + block.Header.ValidatorsHash = f.blsValSet.Hash() + block.Header.NextValidatorsHash = f.blsValSet.Hash() + block.Header.ConsensusHash = f.params.Hash() + block.Header.AppHash = h32("app") + block.Header.LastResultsHash = h32("results") + block.Header.ProposerAddress = f.blsVals[1].Address + block.Header.EvidenceHash = block.Evidence.Hash() + block.Header.DataHash = block.Data.Hash() + block.Header.LastCommitHash = agg.Hash() + ps, err := block.MakePartSet(types.BlockPartSizeBytes) + require.NoError(t, err) + return block, ps +} + +func TestVectorsBlock(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + agg := f.aggCommit(t) + block, ps := f.block(t, agg) + + v.eqHex(t, "header_hash", block.Header.Hash()) + hp := block.Header.ToProto() + v.eqHex(t, "header_proto", mustMarshal(t, hp)) + v.eqHex(t, "block_hash", block.Hash()) + bp, err := block.ToProto() + require.NoError(t, err) + v.eqHex(t, "block_proto", mustMarshal(t, bp)) + assert.Equal(t, v.num(t, "block_partset_total"), int(ps.Total())) + v.eqHex(t, "block_partset_hash", ps.Hash()) + partp, err := ps.GetPart(0).ToProto() + require.NoError(t, err) + v.eqHex(t, "block_part0_proto", mustMarshal(t, partp)) + v.eqHex(t, "data_hash", block.Data.Hash()) + v.eqHex(t, "evidence_hash_empty", block.Evidence.Hash()) + meta := types.NewBlockMeta(block, ps) + v.eqHex(t, "block_meta_proto", mustMarshal(t, meta.ToProto())) + + // a bera-v1.x block decodes, validates and hashes identically + pbb := new(cmtproto.Block) + require.NoError(t, pbb.Unmarshal(v.hex(t, "block_proto"))) + b2, err := types.BlockFromProto(pbb) + require.NoError(t, err) + require.NoError(t, b2.ValidateBasic()) + v.eqHex(t, "block_hash", b2.Hash()) + v.eqHex(t, "agg_commit_hash", b2.LastCommit.Hash()) + require.NoError(t, f.blsValSet.VerifyCommit(chainID, f.blockID, height, b2.LastCommit)) + pbm := new(cmtproto.BlockMeta) + require.NoError(t, pbm.Unmarshal(v.hex(t, "block_meta_proto"))) + m2, err := types.BlockMetaFromProto(pbm) + require.NoError(t, err) + v.eqHex(t, "block_hash", m2.BlockID.Hash) + v.eqHex(t, "block_partset_hash", m2.BlockID.PartSetHeader.Hash) +} + +func TestVectorsEvidence(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + + voteA := f.signedBLSVote(t, f.blockID, 2) + otherBlockID := types.BlockID{Hash: h32("other-block"), PartSetHeader: types.PartSetHeader{Total: 3, Hash: h32("other-parts")}} + voteB := f.signedBLSVote(t, otherBlockID, 2) + dve, err := types.NewDuplicateVoteEvidence(voteA, voteB, fixedTime, f.blsValSet) + require.NoError(t, err) + require.NoError(t, dve.ValidateBasic()) + v.eqHex(t, "dve_hash", dve.Hash()) + evp, err := types.EvidenceToProto(dve) + require.NoError(t, err) + v.eqHex(t, "dve_proto", mustMarshal(t, evp)) + v.eqHex(t, "dve_bytes", dve.Bytes()) + v.eqHex(t, "evidence_list_hash", types.EvidenceList{dve}.Hash()) + + // bera-v1.x evidence decodes and hashes identically + pe := new(cmtproto.Evidence) + require.NoError(t, pe.Unmarshal(v.hex(t, "dve_proto"))) + ev, err := types.EvidenceFromProto(pe) + require.NoError(t, err) + require.NoError(t, ev.ValidateBasic()) + v.eqHex(t, "dve_hash", ev.Hash()) +} + +func TestVectorsPersistedState(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + + state := sm.State{ + Version: cmtstate.Version{ + Consensus: cmtversion.Consensus{Block: 11, App: 1}, + Software: "1.0.1", + }, + ChainID: chainID, + InitialHeight: 1, + LastBlockHeight: height, + LastBlockID: f.blockID, + LastBlockTime: fixedTime, + NextValidators: f.blsValSet.Copy(), + Validators: f.blsValSet.Copy(), + LastValidators: f.blsValSet.Copy(), + LastHeightValidatorsChanged: 100, + ConsensusParams: f.params, + LastHeightConsensusParamsChanged: 50, + LastResultsHash: h32("results"), + AppHash: h32("app"), + NextBlockDelay: 1500 * time.Millisecond, + } + sp, err := state.ToProto() + require.NoError(t, err) + v.eqHex(t, "state_proto", mustMarshal(t, sp)) + + // the bera-v1.x persisted state loads into an identical State and + // round-trips byte-for-byte + psm := new(cmtstate.State) + require.NoError(t, psm.Unmarshal(v.hex(t, "state_proto"))) + loaded, err := sm.FromProto(psm) + require.NoError(t, err) + assert.Equal(t, state.ChainID, loaded.ChainID) + assert.Equal(t, state.LastBlockHeight, loaded.LastBlockHeight) + assert.Equal(t, state.LastBlockID, loaded.LastBlockID) + assert.True(t, state.LastBlockTime.Equal(loaded.LastBlockTime)) + assert.Equal(t, state.NextBlockDelay, loaded.NextBlockDelay) + assert.Equal(t, state.ConsensusParams, loaded.ConsensusParams) + assert.Equal(t, state.Validators.Hash(), loaded.Validators.Hash()) + assert.Equal(t, state.LastResultsHash, loaded.LastResultsHash) + assert.Equal(t, state.AppHash, loaded.AppHash) + rt, err := loaded.ToProto() + require.NoError(t, err) + v.eqHex(t, "state_proto", mustMarshal(t, rt)) + + vsp, err := f.blsValSet.ToProto() + require.NoError(t, err) + valInfo := &cmtstate.ValidatorsInfo{ValidatorSet: vsp, LastHeightChanged: 100} + v.eqHex(t, "validators_info_proto", mustMarshal(t, valInfo)) + ppp := f.params.ToProto() + cpInfo := &cmtstate.ConsensusParamsInfo{ConsensusParams: ppp, LastHeightChanged: 50} + v.eqHex(t, "consensus_params_info_proto", mustMarshal(t, cpInfo)) + + bss := &cmtstore.BlockStoreState{Base: 1, Height: height} + v.eqHex(t, "block_store_state_proto", mustMarshal(t, bss)) +} + +func TestVectorsABCIResponses(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + + // Build the response exactly as bera-v1.x persists it (validator updates + // with pub_key_bytes + pub_key_type) and check the bytes match. + fbr := &abci.ResponseFinalizeBlock{ + Events: []abci.Event{{Type: "t", Attributes: []abci.EventAttribute{{Key: "k", Value: "v", Index: true}}}}, + TxResults: []*abci.ExecTxResult{{Code: 0, Data: []byte("d"), GasWanted: 1, GasUsed: 1}}, + ValidatorUpdates: []abci.ValidatorUpdate{{ + PubKeyBytes: blsPubPtr(f.blsPrivs[0].PubKey()).Compress(), + PubKeyType: bls12381.KeyType, + Power: 77, + }}, + ConsensusParamUpdates: paramsUpdate(), + AppHash: h32("app-next"), + NextBlockDelay: 1500 * time.Millisecond, + } + // This fork's ValidatorUpdate.pub_key is a non-nullable message, so an + // unset key is still written as an empty field 1 (bytes 0a 00); bera-v1.x + // reserves field 1 and skips it. Everything else must be byte-identical, + // so compare after removing that empty field from this fork's encoding. + stripEmptyPubKey := func(bz []byte) []byte { + return bytes.Replace(bz, []byte{0x1a, 0x41, 0x0a, 0x00, 0x10, 0x4d}, []byte{0x1a, 0x3f, 0x10, 0x4d}, 1) + } + v.eqHex(t, "finalize_block_response_proto", stripEmptyPubKey(mustMarshal(t, fbr))) + respInfo := &cmtstate.ABCIResponsesInfo{ResponseFinalizeBlock: fbr, Height: height} + v.eqHex(t, "abci_responses_info_proto", bytes.Replace(stripEmptyPubKey(mustMarshal(t, respInfo)), []byte{0x1a, 0x9f, 0x01}, []byte{0x1a, 0x9d, 0x01}, 1)) + + // A bera-v1.x persisted response decodes fully: NextBlockDelay, params and + // validator updates (including the public key) are recovered. + got := new(cmtstate.ABCIResponsesInfo) + require.NoError(t, got.Unmarshal(v.hex(t, "abci_responses_info_proto"))) + require.NotNil(t, got.ResponseFinalizeBlock) + assert.Equal(t, height, got.Height) + assert.Equal(t, 1500*time.Millisecond, got.ResponseFinalizeBlock.NextBlockDelay) + assert.Equal(t, h32("app-next"), got.ResponseFinalizeBlock.AppHash) + vals, err := types.PB2TM.ValidatorUpdates(got.ResponseFinalizeBlock.ValidatorUpdates) + require.NoError(t, err) + require.Len(t, vals, 1) + assert.True(t, f.blsVals[0].PubKey.Equals(vals[0].PubKey)) + assert.EqualValues(t, 77, vals[0].VotingPower) + updated := f.params.Update(got.ResponseFinalizeBlock.ConsensusParamUpdates) + v.eqHex(t, "params_updated_hash", updated.Hash()) + + ppr := &abci.RequestProcessProposal{ + Hash: h32("block"), + Height: height, + Time: fixedTime, + ProposerAddress: f.blsVals[1].Address, + NextValidatorsHash: f.blsValSet.Hash(), + NextProposerAddress: f.blsVals[2].Address, + } + v.eqHex(t, "process_proposal_request_proto", mustMarshal(t, ppr)) +} + +func (f *fixture) p2pMessages(t *testing.T) map[string]*cmtcons.Message { + t.Helper() + agg := f.aggCommit(t) + _, ps := f.block(t, agg) + partp, err := ps.GetPart(0).ToProto() + require.NoError(t, err) + precommit := f.signedBLSVote(t, f.blockID, 0) + proposal := types.NewProposal(height, round, 1, f.blockID, fixedTime) + psig, err := f.blsPrivs[1].Sign(types.ProposalSignBytes(chainID, proposal.ToProto())) + require.NoError(t, err) + proposal.Signature = psig + ba := func(n, set int) *bits.BitArray { + b := bits.NewBitArray(n) + b.SetIndex(set, true) + return b + } + return map[string]*cmtcons.Message{ + "p2p_new_round_step": {Sum: &cmtcons.Message_NewRoundStep{NewRoundStep: &cmtcons.NewRoundStep{ + Height: height, Round: round, Step: 3, SecondsSinceStartTime: 7, LastCommitRound: 1, + }}}, + "p2p_new_valid_block": {Sum: &cmtcons.Message_NewValidBlock{NewValidBlock: &cmtcons.NewValidBlock{ + Height: height, Round: round, BlockPartSetHeader: f.blockID.PartSetHeader.ToProto(), + BlockParts: ba(3, 1).ToProto(), IsCommit: true, + }}}, + "p2p_proposal": {Sum: &cmtcons.Message_Proposal{Proposal: &cmtcons.Proposal{Proposal: *proposal.ToProto()}}}, + "p2p_proposal_pol": {Sum: &cmtcons.Message_ProposalPol{ProposalPol: &cmtcons.ProposalPOL{Height: height, ProposalPolRound: 1, ProposalPol: *ba(4, 2).ToProto()}}}, + "p2p_block_part": {Sum: &cmtcons.Message_BlockPart{BlockPart: &cmtcons.BlockPart{Height: height + 1, Round: round, Part: *partp}}}, + "p2p_vote": {Sum: &cmtcons.Message_Vote{Vote: &cmtcons.Vote{Vote: precommit.ToProto()}}}, + "p2p_has_vote": {Sum: &cmtcons.Message_HasVote{HasVote: &cmtcons.HasVote{Height: height, Round: round, Type: cmtproto.PrecommitType, Index: 2}}}, + "p2p_vote_set_maj23": {Sum: &cmtcons.Message_VoteSetMaj23{VoteSetMaj23: &cmtcons.VoteSetMaj23{ + Height: height, Round: round, Type: cmtproto.PrecommitType, BlockID: f.blockID.ToProto(), + }}}, + "p2p_vote_set_bits": {Sum: &cmtcons.Message_VoteSetBits{VoteSetBits: &cmtcons.VoteSetBits{ + Height: height, Round: round, Type: cmtproto.PrecommitType, BlockID: f.blockID.ToProto(), Votes: *ba(4, 3).ToProto(), + }}}, + "p2p_has_proposal_block_part": {Sum: &cmtcons.Message_HasProposalBlockPart{HasProposalBlockPart: &cmtcons.HasProposalBlockPart{ + Height: height + 1, Round: round, Index: 0, + }}}, + "p2p_commit": {Sum: &cmtcons.Message_Commit{Commit: &cmtcons.Commit{Commit: agg.ToProto()}}}, + } +} + +func TestVectorsConsensusP2PMessages(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + msgs := f.p2pMessages(t) + for name, m := range msgs { + v.eqHex(t, name, mustMarshal(t, m)) + // and every bera-v1.x message decodes into the same message here + decoded := new(cmtcons.Message) + require.NoErrorf(t, decoded.Unmarshal(v.hex(t, name)), "decode %s", name) + v.eqHex(t, name, mustMarshal(t, decoded)) + } +} + +func TestVectorsWAL(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + msgs := f.p2pMessages(t) + + walVote := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_MsgInfo{MsgInfo: &cmtcons.MsgInfo{Msg: *msgs["p2p_vote"], PeerID: ""}}}, + } + v.eqHex(t, "wal_msginfo_vote_proto", mustMarshal(t, walVote)) + walTimeout := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_TimeoutInfo{TimeoutInfo: &cmtcons.TimeoutInfo{ + Duration: 3 * time.Second, Height: height, Round: round, Step: 4, + }}}, + } + v.eqHex(t, "wal_timeout_proto", mustMarshal(t, walTimeout)) + walEnd := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_EndHeight{EndHeight: &cmtcons.EndHeight{Height: height}}}, + } + v.eqHex(t, "wal_end_height_proto", mustMarshal(t, walEnd)) + walRS := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_EventDataRoundState{EventDataRoundState: &cmtproto.EventDataRoundState{ + Height: height, Round: round, Step: "RoundStepPropose", + }}}, + } + v.eqHex(t, "wal_event_round_state_proto", mustMarshal(t, walRS)) + + // MsgInfo.receive_time (field 3) is persisted exactly as on bera-v1.x, so + // a proposal replayed from a WAL written by either line keeps its PBTS + // timeliness verdict. + rt := fixedTimeNext + walProposalRT := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_MsgInfo{MsgInfo: &cmtcons.MsgInfo{ + Msg: *msgs["p2p_proposal"], PeerID: "peer1", ReceiveTime: &rt, + }}}, + } + v.eqHex(t, "wal_msginfo_proposal_with_receive_time_proto", mustMarshal(t, walProposalRT)) + withRT := new(cmtcons.TimedWALMessage) + require.NoError(t, withRT.Unmarshal(v.hex(t, "wal_msginfo_proposal_with_receive_time_proto"))) + mi, ok := withRT.Msg.Sum.(*cmtcons.WALMessage_MsgInfo) + require.True(t, ok) + assert.Equal(t, "peer1", mi.MsgInfo.PeerID) + require.NotNil(t, mi.MsgInfo.ReceiveTime) + assert.True(t, fixedTimeNext.Equal(*mi.MsgInfo.ReceiveTime)) + assert.True(t, fixedTime.Equal(withRT.Time)) + v.eqHex(t, "p2p_proposal", mustMarshal(t, &mi.MsgInfo.Msg)) +} + +func TestVectorsGenesisAndPrivval(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + + // genesis.json written by bera-v1.x + genDoc, err := types.GenesisDocFromJSON([]byte(v.str(t, "genesis_json"))) + require.NoError(t, err) + require.NoError(t, genDoc.ValidateAndComplete()) + assert.Equal(t, chainID, genDoc.ChainID) + assert.True(t, fixedTime.Equal(genDoc.GenesisTime)) + assert.EqualValues(t, 1, genDoc.InitialHeight) + assert.Equal(t, h32("genesis-app"), []byte(genDoc.AppHash)) + assert.JSONEq(t, `{"k":"v"}`, string(genDoc.AppState)) + require.NotNil(t, genDoc.ConsensusParams) + assert.Equal(t, f.params, *genDoc.ConsensusParams) + v.eqHex(t, "params_hash", genDoc.ConsensusParams.Hash()) + v.eqHex(t, "genesis_validator_hash", genDoc.ValidatorHash()) + v.eqHex(t, "bls_valset_hash", genDoc.ValidatorHash()) + require.Len(t, genDoc.Validators, numVals) + for i, gv := range genDoc.Validators { + assert.True(t, f.blsVals[i].PubKey.Equals(gv.PubKey), "genesis validator %d pubkey", i) + assert.Equal(t, f.blsVals[i].Address, gv.Address) + assert.Equal(t, f.blsVals[i].VotingPower, gv.Power) + } + // and it re-serializes to the same document apart from the extra + // "authority" consensus param object this fork carries + tmp := t.TempDir() + require.NoError(t, genDoc.SaveAs(filepath.Join(tmp, "genesis.json"))) + reread, err := os.ReadFile(filepath.Join(tmp, "genesis.json")) + require.NoError(t, err) + var got, want map[string]json.RawMessage + require.NoError(t, json.Unmarshal(reread, &got)) + require.NoError(t, json.Unmarshal([]byte(v.str(t, "genesis_json")), &want)) + for k := range want { + if k == "consensus_params" { + continue // checked above (modulo "authority") + } + assert.JSONEqf(t, string(want[k]), string(got[k]), "genesis.%s differs", k) + } + + // priv_validator_key.json / priv_validator_state.json written by bera-v1.x + keyFile := filepath.Join(tmp, "priv_validator_key.json") + stateFile := filepath.Join(tmp, "priv_validator_state.json") + require.NoError(t, os.WriteFile(keyFile, []byte(v.str(t, "priv_validator_key_json")), 0o600)) + require.NoError(t, os.WriteFile(stateFile, []byte(v.str(t, "priv_validator_state_json")), 0o600)) + pv := privval.LoadFilePV(keyFile, stateFile) + pub, err := pv.GetPubKey() + require.NoError(t, err) + v.eqHex(t, "privval_pubkey", pub.Bytes()) + assert.True(t, f.blsVals[0].PubKey.Equals(pub)) + pvProp := types.NewProposal(height, round, -1, f.blockID, fixedTime).ToProto() + require.NoError(t, pv.SignProposal(chainID, pvProp)) + v.eqHex(t, "privval_signed_proposal_signature", pvProp.Signature) + pvVote := f.vote(cmtproto.PrecommitType, f.blockID, 0, f.blsVals[0].Address).ToProto() + require.NoError(t, pv.SignVote(chainID, pvVote)) + v.eqHex(t, "privval_signed_vote_signature", pvVote.Signature) + // FilePV on this fork always signs the (empty) vote extension of a non-nil + // precommit; bera-v1.x's FilePV was asked not to. Consensus strips the + // extension signature when vote extensions are disabled (see + // types.SignAndCheckVote), so the vote that reaches the wire is identical. + require.NotEmpty(t, pvVote.ExtensionSignature) + pvVote.ExtensionSignature = nil + v.eqHex(t, "privval_signed_vote_proto", mustMarshal(t, pvVote)) + // the last-signed state file matches bera-v1.x's (same height/round/step, + // signature and sign bytes) + stateAfter, err := os.ReadFile(stateFile) + require.NoError(t, err) + assert.JSONEq(t, v.str(t, "priv_validator_state_json_after_signing"), string(stateAfter)) + // a key file written by this fork is byte-identical to the bera-v1.x one + pv2 := privval.NewFilePV(f.blsPrivs[0], filepath.Join(tmp, "k2.json"), filepath.Join(tmp, "s2.json")) + pv2.Save() + k2, err := os.ReadFile(filepath.Join(tmp, "k2.json")) + require.NoError(t, err) + assert.JSONEq(t, v.str(t, "priv_validator_key_json"), string(k2)) + s2, err := os.ReadFile(filepath.Join(tmp, "s2.json")) + require.NoError(t, err) + assert.JSONEq(t, v.str(t, "priv_validator_state_json"), string(s2)) + bz, err := cmtjson.Marshal(f.blsPrivs[0]) + require.NoError(t, err) + assert.JSONEq(t, v.str(t, "bls_privkey0_cmtjson"), string(bz)) + v.eqHex(t, "bls_privkey0_bytes", f.blsPrivs[0].Bytes()) + pvEd := privval.NewFilePV(f.edPriv, filepath.Join(tmp, "ed.json"), filepath.Join(tmp, "ed_state.json")) + pvEd.Save() + ed, err := os.ReadFile(filepath.Join(tmp, "ed.json")) + require.NoError(t, err) + assert.JSONEq(t, v.str(t, "priv_validator_key_ed25519_json"), string(ed)) +} + +// TestWriteV040Outputs writes this fork's values for the shared inputs (same +// schema as vectors/bera_v1x_vectors.json, plus a few fork-specific extras) to +// the path in BERA_COMPAT_WRITE_OUTPUTS, for the reverse check performed by +// "gen-v1x -verify" (bera-v1.x decoding what this fork produces). It is a +// no-op unless the variable is set. See README.md. +func TestWriteV040Outputs(t *testing.T) { + path := os.Getenv("BERA_COMPAT_WRITE_OUTPUTS") + if path == "" { + t.Skip("BERA_COMPAT_WRITE_OUTPUTS not set") + } + f := newFixture(t) + out := map[string]any{} + hx := hex.EncodeToString + + agg := f.aggCommit(t) + out["agg_commit_proto"] = hx(mustMarshal(t, agg.ToProto())) + out["agg_commit_hash"] = hx(agg.Hash()) + block, ps := f.block(t, agg) + bp, err := block.ToProto() + require.NoError(t, err) + out["block_proto"] = hx(mustMarshal(t, bp)) + out["block_hash"] = hx(block.Hash()) + out["header_hash"] = hx(block.Header.Hash()) + out["block_partset_hash"] = hx(ps.Hash()) + out["block_meta_proto"] = hx(mustMarshal(t, types.NewBlockMeta(block, ps).ToProto())) + precommit := f.signedBLSVote(t, f.blockID, 0) + out["vote_proto_signed_precommit"] = hx(mustMarshal(t, precommit.ToProto())) + out["vote_sign_bytes_precommit"] = hx(types.VoteSignBytes(chainID, precommit.ToProto())) + proposal := types.NewProposal(height, round, 1, f.blockID, fixedTime) + psig, err := f.blsPrivs[1].Sign(types.ProposalSignBytes(chainID, proposal.ToProto())) + require.NoError(t, err) + proposal.Signature = psig + out["proposal_proto_signed"] = hx(mustMarshal(t, proposal.ToProto())) + out["proposal_sign_bytes"] = hx(types.ProposalSignBytes(chainID, proposal.ToProto())) + pp := f.params.ToProto() + out["params_proto"] = hx(mustMarshal(t, &pp)) + out["params_hash"] = hx(f.params.Hash()) + bz, err := cmtjson.Marshal(f.params) + require.NoError(t, err) + out["params_json"] = string(bz) + vsp, err := f.blsValSet.ToProto() + require.NoError(t, err) + out["bls_valset_proto"] = hx(mustMarshal(t, vsp)) + out["bls_valset_hash"] = hx(f.blsValSet.Hash()) + state := sm.State{ + Version: cmtstate.Version{Consensus: cmtversion.Consensus{Block: 11, App: 1}, Software: "1.0.1"}, + ChainID: chainID, + InitialHeight: 1, + LastBlockHeight: height, + LastBlockID: f.blockID, + LastBlockTime: fixedTime, + NextValidators: f.blsValSet.Copy(), + Validators: f.blsValSet.Copy(), + LastValidators: f.blsValSet.Copy(), + LastHeightValidatorsChanged: 100, + ConsensusParams: f.params, + LastHeightConsensusParamsChanged: 50, + LastResultsHash: h32("results"), + AppHash: h32("app"), + NextBlockDelay: 1500 * time.Millisecond, + } + sp, err := state.ToProto() + require.NoError(t, err) + out["state_proto"] = hx(mustMarshal(t, sp)) + for name, m := range f.p2pMessages(t) { + out[name] = hx(mustMarshal(t, m)) + } + walVote := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_MsgInfo{MsgInfo: &cmtcons.MsgInfo{Msg: *f.p2pMessages(t)["p2p_vote"], PeerID: ""}}}, + } + out["wal_msginfo_vote_proto"] = hx(mustMarshal(t, walVote)) + rt := fixedTimeNext + walProposalRT := &cmtcons.TimedWALMessage{ + Time: fixedTime, + Msg: &cmtcons.WALMessage{Sum: &cmtcons.WALMessage_MsgInfo{MsgInfo: &cmtcons.MsgInfo{ + Msg: *f.p2pMessages(t)["p2p_proposal"], PeerID: "peer1", ReceiveTime: &rt, + }}}, + } + out["wal_msginfo_proposal_with_receive_time_proto"] = hx(mustMarshal(t, walProposalRT)) + voteA := f.signedBLSVote(t, f.blockID, 2) + otherBlockID := types.BlockID{Hash: h32("other-block"), PartSetHeader: types.PartSetHeader{Total: 3, Hash: h32("other-parts")}} + voteB := f.signedBLSVote(t, otherBlockID, 2) + dve, err := types.NewDuplicateVoteEvidence(voteA, voteB, fixedTime, f.blsValSet) + require.NoError(t, err) + evp, err := types.EvidenceToProto(dve) + require.NoError(t, err) + out["dve_proto"] = hx(mustMarshal(t, evp)) + out["dve_hash"] = hx(dve.Hash()) + + // Fork-specific extras, for bera-v1.x to decode: + // the FinalizeBlock response exactly as this fork persists it (pub_key set + // by the app, pub_key_bytes/pub_key_type added on save) ... + fbr := &abci.ResponseFinalizeBlock{ + ValidatorUpdates: []abci.ValidatorUpdate{types.TM2PB.NewValidatorUpdate(f.blsVals[0].PubKey, 77)}, + ConsensusParamUpdates: paramsUpdate(), + AppHash: h32("app-next"), + NextBlockDelay: 1500 * time.Millisecond, + } + fbr.ValidatorUpdates = types.NormalizeValidatorUpdates(fbr.ValidatorUpdates) + out["v040_finalize_block_response_proto"] = hx(mustMarshal(t, fbr)) + out["v040_abci_responses_info_proto"] = hx(mustMarshal(t, &cmtstate.ABCIResponsesInfo{ResponseFinalizeBlock: fbr, Height: height})) + // ... a genesis file written by this fork (carries the extra "authority" object) ... + genDoc := types.GenesisDoc{ + GenesisTime: fixedTime, + ChainID: chainID, + InitialHeight: 1, + ConsensusParams: &f.params, + AppHash: h32("genesis-app"), + AppState: json.RawMessage(`{"k":"v"}`), + } + for i := 0; i < numVals; i++ { + genDoc.Validators = append(genDoc.Validators, types.GenesisValidator{ + Address: f.blsVals[i].Address, PubKey: f.blsVals[i].PubKey, Power: f.blsVals[i].VotingPower, Name: fmt.Sprintf("val%d", i), + }) + } + require.NoError(t, genDoc.ValidateAndComplete()) + tmp := t.TempDir() + require.NoError(t, genDoc.SaveAs(filepath.Join(tmp, "genesis.json"))) + gj, err := os.ReadFile(filepath.Join(tmp, "genesis.json")) + require.NoError(t, err) + out["v040_genesis_json"] = string(gj) + out["genesis_validator_hash"] = hx(genDoc.ValidatorHash()) + // ... and the key/state files written by this fork. + pv := privval.NewFilePV(f.blsPrivs[0], filepath.Join(tmp, "k.json"), filepath.Join(tmp, "s.json")) + pv.Save() + kj, err := os.ReadFile(filepath.Join(tmp, "k.json")) + require.NoError(t, err) + out["v040_priv_validator_key_json"] = string(kj) + sj, err := os.ReadFile(filepath.Join(tmp, "s.json")) + require.NoError(t, err) + out["v040_priv_validator_state_json"] = string(sj) + + out["_source"] = "generated by test/compat TestWriteV040Outputs against bera-v0.40.x" + bz, err = json.MarshalIndent(out, "", " ") + require.NoError(t, err) + require.NoError(t, os.WriteFile(path, append(bz, '\n'), 0o644)) + t.Logf("wrote %s", path) +} + +func TestVectorsBLSAggregation(t *testing.T) { + v := loadVectors(t) + f := newFixture(t) + msg := []byte(v.str(t, "bls_aggregate_message")) + sigs := make([][]byte, numVals) + pubs := make([]*bls12381.PubKey, numVals) + for i := 0; i < numVals; i++ { + sig, err := f.blsPrivs[i].Sign(msg) + require.NoError(t, err) + sigs[i] = sig + pubs[i] = blsPubPtr(f.blsPrivs[i].PubKey()) + } + agg, err := bls12381.AggregateSignatures(sigs) + require.NoError(t, err) + v.eqHex(t, "bls_aggregate_signature", agg) + assert.True(t, bls12381.VerifyAggregateSignature(agg, pubs, msg)) + assert.True(t, bls12381.VerifyAggregateSignature(v.hex(t, "bls_aggregate_signature"), pubs, msg)) +} diff --git a/test/e2e/app/app.go b/test/e2e/app/app.go index 5ce4e204f3b..c5767658a18 100644 --- a/test/e2e/app/app.go +++ b/test/e2e/app/app.go @@ -16,6 +16,8 @@ import ( "strings" "time" + gogo "github.com/cosmos/gogoproto/types" + "github.com/cometbft/cometbft/abci/example/kvstore" abci "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/crypto" @@ -167,8 +169,8 @@ func (app *Application) updateVoteExtensionEnableHeight(currentHeight int64) *cm "current_height", currentHeight, "enable_height", app.cfg.VoteExtensionsEnableHeight) params = &cmtproto.ConsensusParams{ - Abci: &cmtproto.ABCIParams{ - VoteExtensionsEnableHeight: app.cfg.VoteExtensionsEnableHeight, + Feature: &cmtproto.FeatureParams{ + VoteExtensionsEnableHeight: &gogo.Int64Value{Value: app.cfg.VoteExtensionsEnableHeight}, }, } app.logger.Info("updating VoteExtensionsHeight in app_state", "height", app.cfg.VoteExtensionsEnableHeight) @@ -189,8 +191,8 @@ func (app *Application) InitChain(_ context.Context, req *abci.RequestInitChain) } app.logger.Info("setting ChainID in app_state", "chainId", req.ChainId) app.state.Set(prefixReservedKey+suffixChainID, req.ChainId) - app.logger.Info("setting VoteExtensionsHeight in app_state", "height", req.ConsensusParams.Abci.VoteExtensionsEnableHeight) - app.state.Set(prefixReservedKey+suffixVoteExtHeight, strconv.FormatInt(req.ConsensusParams.Abci.VoteExtensionsEnableHeight, 10)) + app.logger.Info("setting VoteExtensionsHeight in app_state", "height", req.ConsensusParams.Feature.VoteExtensionsEnableHeight.GetValue()) + app.state.Set(prefixReservedKey+suffixVoteExtHeight, strconv.FormatInt(req.ConsensusParams.Feature.VoteExtensionsEnableHeight.GetValue(), 10)) app.logger.Info("setting initial height in app_state", "initial_height", req.InitialHeight) app.state.Set(prefixReservedKey+suffixInitialHeight, strconv.FormatInt(req.InitialHeight, 10)) // Get validators from genesis diff --git a/test/e2e/generator/generate.go b/test/e2e/generator/generate.go index 6cae9be6660..73bca1df89f 100644 --- a/test/e2e/generator/generate.go +++ b/test/e2e/generator/generate.go @@ -71,7 +71,7 @@ var ( "upgrade": 0.3, } voteExtensionUpdateHeight = uniformChoice{int64(-1), int64(0), int64(1)} // -1: genesis, 0: InitChain, 1: (use offset) - voteExtensionEnabled = weightedChoice{true: 3, false: 1} + voteExtensionEnabled = weightedChoice{false: 1} // vote extensions are disabled on the Berachain fork voteExtensionHeightOffset = uniformChoice{int64(0), int64(10), int64(100)} voteExtensionSize = uniformChoice{uint(128), uint(512), uint(2048), uint(8192)} //TODO: define the right values depending on experiment results. keyType = uniformChoice{ed25519.KeyType, secp256k1.KeyType, bls12381.KeyType, mldsa65.KeyType, secp256k1eth.KeyType} diff --git a/test/e2e/networks/ci.toml b/test/e2e/networks/ci.toml index 98a1a935936..142840f9bcd 100644 --- a/test/e2e/networks/ci.toml +++ b/test/e2e/networks/ci.toml @@ -3,8 +3,8 @@ ipv6 = true initial_height = 1000 -vote_extensions_update_height = 1004 -vote_extensions_enable_height = 1007 +vote_extensions_update_height = -1 +vote_extensions_enable_height = 0 evidence = 5 initial_state = { initial01 = "a", initial02 = "b", initial03 = "c" } prepare_proposal_delay = "100ms" diff --git a/test/e2e/networks_regressions/blocksync_blocked.toml b/test/e2e/networks_regressions/blocksync_blocked.toml index c84e610b5a8..b282b947275 100644 --- a/test/e2e/networks_regressions/blocksync_blocked.toml +++ b/test/e2e/networks_regressions/blocksync_blocked.toml @@ -1,4 +1,4 @@ -vote_extensions_enable_height = 1 +vote_extensions_enable_height = 0 pbts_enable_height = 1 [validators] diff --git a/test/e2e/runner/setup.go b/test/e2e/runner/setup.go index 16a1fef61b2..b2cb78a8195 100644 --- a/test/e2e/runner/setup.go +++ b/test/e2e/runner/setup.go @@ -142,7 +142,7 @@ func MakeGenesis(testnet *e2e.Testnet) (types.GenesisDoc, error) { genesis.ConsensusParams.Block.MaxBytes = testnet.BlockMaxBytes } if testnet.VoteExtensionsUpdateHeight == -1 { - genesis.ConsensusParams.ABCI.VoteExtensionsEnableHeight = testnet.VoteExtensionsEnableHeight + genesis.ConsensusParams.Feature.VoteExtensionsEnableHeight = testnet.VoteExtensionsEnableHeight } for validator, power := range testnet.Validators { genesis.Validators = append(genesis.Validators, types.GenesisValidator{ diff --git a/tests.mk b/tests.mk index 9c6b5de65b7..fa8a3939baf 100644 --- a/tests.mk +++ b/tests.mk @@ -57,7 +57,7 @@ NUM_SPLIT ?= 4 # Note we need to check for both in-package tests (.TestGoFiles) and # out-of-package tests (.XTestGoFiles). $(BUILDDIR)/packages.txt:$(GO_TEST_FILES) $(BUILDDIR) - go list -f "{{ if (or .TestGoFiles .XTestGoFiles) }}{{ .ImportPath }}{{ end }}" ./... | sort > $@ + go list -tags 'bls12381,secp256k1eth' -f "{{ if (or .TestGoFiles .XTestGoFiles) }}{{ .ImportPath }}{{ end }}" ./... | sort > $@ split-test-packages:$(BUILDDIR)/packages.txt split -d -n l/$(NUM_SPLIT) $< $<. diff --git a/types/block.go b/types/block.go index efdd4b6fcac..ef6c8f43cee 100644 --- a/types/block.go +++ b/types/block.go @@ -15,6 +15,7 @@ import ( "github.com/cometbft/cometbft/crypto/tmhash" "github.com/cometbft/cometbft/libs/bits" cmtbytes "github.com/cometbft/cometbft/libs/bytes" + cmtjson "github.com/cometbft/cometbft/libs/json" cmtmath "github.com/cometbft/cometbft/libs/math" cmtsync "github.com/cometbft/cometbft/libs/sync" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" @@ -39,6 +40,13 @@ const ( MaxOverheadForBlock int64 = 11 ) +func init() { + // RoundState.LastCommit is an interface (VoteSetReader), so the concrete + // types it can hold must be registered for JSON encoding (e.g. the + // /dump_consensus_state RPC endpoint). + cmtjson.RegisterType(&Commit{}, "cometbft/Commit") +} + // Block defines the atomic unit of a CometBFT blockchain. type Block struct { mtx cmtsync.Mutex @@ -591,6 +599,14 @@ const ( BlockIDFlagCommit // BlockIDFlagNil - voted for nil. BlockIDFlagNil + // BlockIDFlagAggCommit - voted for the Commit.BlockID and contains aggregated signature.. + BlockIDFlagAggCommit + // BlockIDFlagAggCommitAbsent - voted for the Commit.BlockID; aggregated signature in another entry. + BlockIDFlagAggCommitAbsent + // BlockIDFlagAggNil - voted for nil and contains aggregated signature.. + BlockIDFlagAggNil + // BlockIDFlagAggNilAbsent - voted for nil; aggregated signature in another entry. + BlockIDFlagAggNilAbsent ) // MaxCommitOverheadBytes is the max size of commit without any commitSigs -> 82 for BlockID, 8 for Height, 4 for Round. @@ -648,12 +664,10 @@ func (cs CommitSig) String() string { func (cs CommitSig) BlockID(commitBlockID BlockID) BlockID { var blockID BlockID switch cs.BlockIDFlag { - case BlockIDFlagAbsent: + case BlockIDFlagAbsent, BlockIDFlagNil, BlockIDFlagAggNil, BlockIDFlagAggNilAbsent: blockID = BlockID{} - case BlockIDFlagCommit: + case BlockIDFlagCommit, BlockIDFlagAggCommit, BlockIDFlagAggCommitAbsent: blockID = commitBlockID - case BlockIDFlagNil: - blockID = BlockID{} default: panic(fmt.Sprintf("Unknown BlockIDFlag: %v", cs.BlockIDFlag)) } @@ -666,18 +680,23 @@ func (cs CommitSig) ValidateBasic() error { case BlockIDFlagAbsent: case BlockIDFlagCommit: case BlockIDFlagNil: + case BlockIDFlagAggCommit: + case BlockIDFlagAggCommitAbsent: + case BlockIDFlagAggNil: + case BlockIDFlagAggNilAbsent: default: return fmt.Errorf("unknown BlockIDFlag: %v", cs.BlockIDFlag) } + if !cs.Timestamp.IsZero() { + return errors.New("time is present") + } + switch cs.BlockIDFlag { case BlockIDFlagAbsent: if len(cs.ValidatorAddress) != 0 { return errors.New("validator address is present") } - if !cs.Timestamp.IsZero() { - return errors.New("time is present") - } if len(cs.Signature) != 0 { return errors.New("signature is present") } @@ -689,9 +708,7 @@ func (cs CommitSig) ValidateBasic() error { ) } // NOTE: Timestamp validation is subtle and handled elsewhere. - if len(cs.Signature) == 0 { - return errors.New("signature is missing") - } + // NOTE: Signature can be empty when using BLS aggregation. if len(cs.Signature) > MaxSignatureSize { return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize) } @@ -780,19 +797,20 @@ func (ecs ExtendedCommitSig) ValidateBasic() error { // this ExtendedCommitSig. func (ecs ExtendedCommitSig) EnsureExtension(extEnabled bool) error { if extEnabled { - if ecs.BlockIDFlag == BlockIDFlagCommit && len(ecs.ExtensionSignature) == 0 { + sigForBlock := ecs.BlockIDFlag == BlockIDFlagCommit || ecs.BlockIDFlag == BlockIDFlagAggCommit || ecs.BlockIDFlag == BlockIDFlagAggCommitAbsent + if sigForBlock && len(ecs.ExtensionSignature) == 0 { return fmt.Errorf("vote extension signature is missing; validator addr %s, timestamp %v", ecs.ValidatorAddress.String(), ecs.Timestamp, ) } - if ecs.BlockIDFlag != BlockIDFlagCommit && len(ecs.Extension) != 0 { + if !sigForBlock && len(ecs.Extension) != 0 { return fmt.Errorf("non-commit vote extension present; validator addr %s, timestamp %v", ecs.ValidatorAddress.String(), ecs.Timestamp, ) } - if ecs.BlockIDFlag != BlockIDFlagCommit && len(ecs.ExtensionSignature) != 0 { + if !sigForBlock && len(ecs.ExtensionSignature) != 0 { return fmt.Errorf("non-commit vote extension signature present; validator addr %s, timestamp %v", ecs.ValidatorAddress.String(), ecs.Timestamp, @@ -862,18 +880,72 @@ type Commit struct { // Memoized in first call to corresponding method. // NOTE: can't memoize in constructor because constructor isn't used for // unmarshaling. - hash cmtbytes.HexBytes + hash cmtbytes.HexBytes + bitArray *bits.BitArray + mtx cmtsync.Mutex // Protects hash and bitArray. } +// A whole aggregated Commit can stand in for a VoteSet as the source of the +// last commit (individual precommit votes cannot be reconstructed from an +// aggregated commit). +var _ VoteSetReader = (*Commit)(nil) + // Clone creates a deep copy of this commit. func (commit *Commit) Clone() *Commit { sigs := make([]CommitSig, len(commit.Signatures)) copy(sigs, commit.Signatures) - commCopy := *commit - commCopy.Signatures = sigs + commCopy := Commit{ + Height: commit.Height, + Round: commit.Round, + BlockID: commit.BlockID, + Signatures: sigs, + } return &commCopy } +// Type returns the vote type of the commit, which is always +// VoteTypePrecommit +// Implements VoteSetReader. +func (*Commit) Type() byte { return byte(cmtproto.PrecommitType) } + +// BitArray returns a BitArray of which validators voted for BlockID or nil in +// this commit. +// Implements VoteSetReader. +func (commit *Commit) BitArray() *bits.BitArray { + commit.mtx.Lock() + defer commit.mtx.Unlock() + if commit.bitArray == nil { + commit.bitArray = bits.NewBitArray(len(commit.Signatures)) + for i, commitSig := range commit.Signatures { + // TODO: need to check the BlockID otherwise we could be counting conflicts, + // not just the one with +2/3 ! + commit.bitArray.SetIndex(i, commitSig.BlockIDFlag != BlockIDFlagAbsent) + } + } + return commit.bitArray +} + +// GetByIndex returns nil: individual votes cannot be reconstructed from an +// aggregated Commit. +// Implements VoteSetReader. +func (*Commit) GetByIndex(int32) *Vote { + return nil +} + +// IsCommit returns true if there is at least one signature. +// Implements VoteSetReader. +func (commit *Commit) IsCommit() bool { + return len(commit.Signatures) != 0 +} + +// GetHeight returns height of the commit. +// Implements VoteSetReader. +func (commit *Commit) GetHeight() int64 { return commit.Height } + +// GetRound returns round of the commit. +// Implements VoteSetReader. +func (commit *Commit) GetRound() int32 { return commit.Round } + // GetVote converts the CommitSig for the given valIdx to a Vote. Commits do // not contain vote extensions, so the vote extension and vote extension // signature will not be present in the returned vote. @@ -886,10 +958,10 @@ func (commit *Commit) GetVote(valIdx int32) *Vote { Height: commit.Height, Round: commit.Round, BlockID: commitSig.BlockID(commit.BlockID), - Timestamp: commitSig.Timestamp, ValidatorAddress: commitSig.ValidatorAddress, ValidatorIndex: valIdx, Signature: commitSig.Signature, + Timestamp: time.Time{}, } } @@ -942,11 +1014,24 @@ func (commit *Commit) ValidateBasic() error { return nil } +// HasAggregatedSignature returns true if the commit contains an aggregated signature. +func (commit *Commit) HasAggregatedSignature() bool { + for _, sig := range commit.Signatures { + if sig.BlockIDFlag == BlockIDFlagAggCommit || sig.BlockIDFlag == BlockIDFlagAggNil || + sig.BlockIDFlag == BlockIDFlagAggCommitAbsent || sig.BlockIDFlag == BlockIDFlagAggNilAbsent { + return true + } + } + return false +} + // Hash returns the hash of the commit. func (commit *Commit) Hash() cmtbytes.HexBytes { if commit == nil { return nil } + commit.mtx.Lock() + defer commit.mtx.Unlock() if commit.hash == nil { bs := make([][]byte, len(commit.Signatures)) for i, commitSig := range commit.Signatures { @@ -983,6 +1068,16 @@ func (commit *Commit) WrappedExtendedCommit() *ExtendedCommit { } } +// String returns a string representation of the commit. +// +// Commit must not be rendered through fmt's default struct formatting (it +// reflects over the lazily-initialized hash, bitArray, and mtx fields, which +// races with concurrent Hash/BitArray callers), so all printing goes through +// here. +func (commit *Commit) String() string { + return commit.StringIndented("") +} + // StringIndented returns a string representation of the commit. func (commit *Commit) StringIndented(indent string) string { if commit == nil { @@ -992,6 +1087,9 @@ func (commit *Commit) StringIndented(indent string) string { for i, commitSig := range commit.Signatures { commitSigStrings[i] = commitSig.String() } + commit.mtx.Lock() + hash := commit.hash + commit.mtx.Unlock() return fmt.Sprintf(`Commit{ %s Height: %d %s Round: %d @@ -1004,7 +1102,7 @@ func (commit *Commit) StringIndented(indent string) string { indent, commit.BlockID, indent, indent, strings.Join(commitSigStrings, "\n"+indent+" "), - indent, commit.hash) + indent, hash) } // ToProto converts Commit to protobuf @@ -1162,12 +1260,12 @@ func (ec *ExtendedCommit) GetExtendedVote(valIndex int32) *Vote { Height: ec.Height, Round: ec.Round, BlockID: ecs.BlockID(ec.BlockID), - Timestamp: ecs.Timestamp, ValidatorAddress: ecs.ValidatorAddress, ValidatorIndex: valIndex, Signature: ecs.Signature, Extension: ecs.Extension, ExtensionSignature: ecs.ExtensionSignature, + Timestamp: time.Time{}, } } @@ -1510,6 +1608,11 @@ func (blockID BlockID) IsZero() bool { blockID.PartSetHeader.IsZero() } +// IsNil returns true if this is the BlockID of a nil block. +func (blockID BlockID) IsNil() bool { + return blockID.IsZero() +} + // IsComplete returns true if this is a valid BlockID of a non-nil block. func (blockID BlockID) IsComplete() bool { return len(blockID.Hash) == tmhash.Size && diff --git a/types/block_test.go b/types/block_test.go index 67c038833d3..59999d7545c 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -24,7 +24,6 @@ import ( cmtrand "github.com/cometbft/cometbft/libs/rand" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" cmtversion "github.com/cometbft/cometbft/proto/tendermint/version" - cmttime "github.com/cometbft/cometbft/types/time" "github.com/cometbft/cometbft/version" ) @@ -548,7 +547,6 @@ func TestVoteSetToExtendedCommit(t *testing.T) { Round: 1, Type: cmtproto.PrecommitType, BlockID: blockID, - Timestamp: time.Now(), } v := vote.ToProto() err = vals[i].SignVote(voteSet.ChainID(), v) @@ -565,7 +563,7 @@ func TestVoteSetToExtendedCommit(t *testing.T) { if testCase.includeExtension { veHeight = 1 } - ec := voteSet.MakeExtendedCommit(ABCIParams{VoteExtensionsEnableHeight: veHeight}) + ec := voteSet.MakeExtendedCommit(FeatureParams{VoteExtensionsEnableHeight: veHeight}) for i := int32(0); int(i) < len(vals); i++ { vote1 := voteSet.GetByIndex(i) @@ -686,7 +684,6 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { Round: round, Type: cmtproto.PrecommitType, BlockID: tc.blockIDs[n], - Timestamp: cmttime.Now(), } added, err := signAddVote(vals[vi], vote, voteSet) @@ -697,7 +694,7 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { } } - veHeightParam := ABCIParams{VoteExtensionsEnableHeight: 0} + veHeightParam := FeatureParams{VoteExtensionsEnableHeight: 0} if tc.valid { extCommit := voteSet.MakeExtendedCommit(veHeightParam) // panics without > 2/3 valid votes assert.NotNil(t, extCommit) @@ -789,7 +786,7 @@ func TestBlockProtoBuf(t *testing.T) { require.EqualValues(t, tc.b1.Header, block.Header, tc.msg) require.EqualValues(t, tc.b1.Data, block.Data, tc.msg) require.EqualValues(t, tc.b1.Evidence.Evidence, block.Evidence.Evidence, tc.msg) - require.EqualValues(t, *tc.b1.LastCommit, *block.LastCommit, tc.msg) + require.EqualValues(t, tc.b1.LastCommit, block.LastCommit, tc.msg) } else { require.Error(t, err, tc.msg) } diff --git a/types/canonical.go b/types/canonical.go index ee82f660f58..8ff3dedf896 100644 --- a/types/canonical.go +++ b/types/canonical.go @@ -54,14 +54,17 @@ func CanonicalizeProposal(chainID string, proposal *cmtproto.Proposal) cmtproto. // CanonicalizeVote transforms the given Vote to a CanonicalVote, which does // not contain ValidatorIndex and ValidatorAddress fields, or any fields // relating to vote extensions. +// +// The per-vote Timestamp is deliberately excluded from the canonical +// (signed) representation: all validators must sign identical bytes for a +// given block so that their BLS signatures can be aggregated. func CanonicalizeVote(chainID string, vote *cmtproto.Vote) cmtproto.CanonicalVote { return cmtproto.CanonicalVote{ - Type: vote.Type, - Height: vote.Height, // encoded as sfixed64 - Round: int64(vote.Round), // encoded as sfixed64 - BlockID: CanonicalizeBlockID(vote.BlockID), - Timestamp: vote.Timestamp, - ChainID: chainID, + Type: vote.Type, + Height: vote.Height, // encoded as sfixed64 + Round: int64(vote.Round), // encoded as sfixed64 + BlockID: CanonicalizeBlockID(vote.BlockID), + ChainID: chainID, } } diff --git a/types/events.go b/types/events.go index 7b40178aa53..92092446093 100644 --- a/types/events.go +++ b/types/events.go @@ -31,6 +31,7 @@ const ( EventNewRound = "NewRound" EventNewRoundStep = "NewRoundStep" EventPolka = "Polka" + EventProposalBlockPart = "ProposalBlockPart" EventRelock = "Relock" EventTimeoutPropose = "TimeoutPropose" EventTimeoutWait = "TimeoutWait" diff --git a/types/evidence.go b/types/evidence.go index faa6873c7c3..8fb04216c95 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -603,14 +603,14 @@ func NewMockDuplicateVoteEvidenceWithValidator(height int64, time time.Time, return nil, err } val := NewValidator(pubKey, 10) - voteA := makeMockVote(height, 0, 0, pubKey.Address(), randBlockID(), time) + voteA := makeMockVote(height, 0, 0, pubKey.Address(), randBlockID()) vA := voteA.ToProto() err = pv.SignVote(chainID, vA) if err != nil { return nil, err } voteA.Signature = vA.Signature - voteB := makeMockVote(height, 0, 0, pubKey.Address(), randBlockID(), time) + voteB := makeMockVote(height, 0, 0, pubKey.Address(), randBlockID()) vB := voteB.ToProto() err = pv.SignVote(chainID, vB) if err != nil { @@ -621,14 +621,13 @@ func NewMockDuplicateVoteEvidenceWithValidator(height int64, time time.Time, } func makeMockVote(height int64, round, index int32, addr Address, - blockID BlockID, time time.Time, + blockID BlockID, ) *Vote { return &Vote{ Type: cmtproto.SignedMsgType(2), Height: height, Round: round, BlockID: blockID, - Timestamp: time, ValidatorAddress: addr, ValidatorIndex: index, } diff --git a/types/params.go b/types/params.go index 5278ff141d4..2dbd54d0710 100644 --- a/types/params.go +++ b/types/params.go @@ -3,8 +3,11 @@ package types import ( "errors" "fmt" + "math" "time" + gogo "github.com/cosmos/gogoproto/types" + "github.com/cometbft/cometbft/crypto/bls12381" "github.com/cometbft/cometbft/crypto/ed25519" "github.com/cometbft/cometbft/crypto/mldsa65" @@ -29,6 +32,17 @@ const ( ABCIPubKeyTypeBls12381 = bls12381.KeyType ABCIPubKeyTypeMlDsa65 = mldsa65.KeyType ABCIPubKeyTypeSecp256k1Eth = secp256k1eth.KeyType + + // MaxMessageDelay is the maximum allowed value for SynchronyParams.MessageDelay. + // + // It ensures that the SynchronyParams.MessageDelay does not overflow int64. + // The 24hr value was chosen based on common sense. + MaxMessageDelay = 24 * time.Hour + // MaxPrecision is the maximum allowed value for SynchronyParams.Precision. + // + // It ensures that the SynchronyParams.Precision does not overflow int64. The + // 30s value was chosen based on common sense. + MaxPrecision = 30 * time.Second ) var ABCIPubKeyTypesToNames = map[string]string{ @@ -51,7 +65,8 @@ type ConsensusParams struct { Evidence EvidenceParams `json:"evidence"` Validator ValidatorParams `json:"validator"` Version VersionParams `json:"version"` - ABCI ABCIParams `json:"abci"` + Synchrony SynchronyParams `json:"synchrony"` + Feature FeatureParams `json:"feature"` Authority AuthorityParams `json:"authority"` } @@ -90,22 +105,89 @@ type VersionParams struct { App uint64 `json:"app"` } -// ABCIParams configure ABCI functionality specific to the Application Blockchain -// Interface. -type ABCIParams struct { +// FeatureParams configure the height from which features of CometBFT are enabled. +// A value of 0 means the feature is disabled. A value > 0 denotes +// the height at which the feature will be (or has been) enabled. +type FeatureParams struct { VoteExtensionsEnableHeight int64 `json:"vote_extensions_enable_height"` + PbtsEnableHeight int64 `json:"pbts_enable_height"` + SBTEnableHeight int64 `json:"sbt_enable_height"` } // VoteExtensionsEnabled returns true if vote extensions are enabled at height h // and false otherwise. -func (a ABCIParams) VoteExtensionsEnabled(h int64) bool { - if h < 1 { - panic(fmt.Errorf("cannot check if vote extensions enabled for height %d (< 1)", h)) +func (p FeatureParams) VoteExtensionsEnabled(h int64) bool { + enabledHeight := p.VoteExtensionsEnableHeight + + return featureEnabled(enabledHeight, h, "Vote Extensions") +} + +// PbtsEnabled returns true if PBTS is enabled at height h and false otherwise. +func (p FeatureParams) PbtsEnabled(h int64) bool { + enabledHeight := p.PbtsEnableHeight + + return featureEnabled(enabledHeight, h, "PBTS") +} + +// SBTEnabled returns true if SBT is enabled at height h and false otherwise. +func (p FeatureParams) SBTEnabled(h int64) bool { + enabledHeight := p.SBTEnableHeight + + return featureEnabled(enabledHeight, h, "SBT") +} + +// featureEnabled returns true if `enabledHeight` points to a height that is smaller than `currentHeight`. +func featureEnabled(enableHeight int64, currentHeight int64, f string) bool { + if currentHeight < 1 { + panic(fmt.Errorf("cannot check if %s is enabled for height %d (< 1)", f, currentHeight)) } - if a.VoteExtensionsEnableHeight == 0 { + + if enableHeight <= 0 { return false } - return a.VoteExtensionsEnableHeight <= h + + return enableHeight <= currentHeight +} + +// SynchronyParams determine the validity of block timestamps. +// +// These parameters are part of the Proposer-Based Timestamps (PBTS) algorithm. +// For more information on the relationship of the synchrony parameters to +// block timestamps validity, refer to the PBTS specification: +// https://github.com/cometbft/cometbft/tree/main/spec/consensus/proposer-based-timestamp +type SynchronyParams struct { + // Maximum allowed value: MaxPrecision. + Precision time.Duration `json:"precision,string"` + // Maximum allowed value: MaxMessageDelay. + MessageDelay time.Duration `json:"message_delay,string"` +} + +// InRound ensures an exponential back-off of SynchronyParams.MessageDelay for +// block timestamps validation, as the associated proposal rounds increase. +// +// The adaptation is achieve by increasing MessageDelay by a factor of 10% each +// subsequent round a proposal's timeliness is calculated, namely: +// +// MessageDelay(round) == MessageDelay * (1.1)^round +// +// The goal is facilitate the progression of consensus when improper synchrony +// parameters are set or become insufficient to preserve liveness. Refer to +// https://github.com/cometbft/cometbft/issues/2184 for more details. +// +// There's a cap (MaxMessageDelay) on the MessageDelay to prevent overflow. +func (sp SynchronyParams) InRound(round int32) SynchronyParams { + if round <= 0 { + return sp + } + + d := time.Duration(math.Min( + float64(MaxMessageDelay), + math.Pow(1.1, float64(round))*float64(sp.MessageDelay), + )) + return SynchronyParams{ + Precision: sp.Precision, + MessageDelay: d, + } } // AuthorityParams holds an opaque authority string to be configured and @@ -122,7 +204,8 @@ func DefaultConsensusParams() *ConsensusParams { Evidence: DefaultEvidenceParams(), Validator: DefaultValidatorParams(), Version: DefaultVersionParams(), - ABCI: DefaultABCIParams(), + Feature: DefaultFeatureParams(), + Synchrony: DefaultSynchronyParams(), Authority: DefaultAuthorityParams(), } } @@ -161,10 +244,24 @@ func DefaultVersionParams() VersionParams { } } -func DefaultABCIParams() ABCIParams { - return ABCIParams{ - // When set to 0, vote extensions are not required. +// DefaultFeatureParams returns the default FeatureParams. PBTS is enabled +// from the first height; with BLS signature aggregation the per-vote +// timestamps that BFT Time medians over are not signed, so PBTS is the only +// viable block-time source on this fork. +func DefaultFeatureParams() FeatureParams { + return FeatureParams{ VoteExtensionsEnableHeight: 0, + PbtsEnableHeight: 1, // PBTS from start + SBTEnableHeight: 0, // SBT disabled by default + } +} + +func DefaultSynchronyParams() SynchronyParams { + // Default values determined based on experimental results and on + // https://github.com/cometbft/cometbft/issues/4246 + return SynchronyParams{ + Precision: 505 * time.Millisecond, + MessageDelay: 15 * time.Second, } } @@ -231,8 +328,34 @@ func (params ConsensusParams) ValidateBasic() error { params.Evidence.MaxBytes) } - if params.ABCI.VoteExtensionsEnableHeight < 0 { - return fmt.Errorf("ABCI.VoteExtensionsEnableHeight cannot be negative. Got: %d", params.ABCI.VoteExtensionsEnableHeight) + if params.Feature.VoteExtensionsEnableHeight < 0 { + return fmt.Errorf("Feature.VoteExtensionsEnableHeight cannot be negative. Got: %d", params.Feature.VoteExtensionsEnableHeight) + } + + if params.Feature.SBTEnableHeight < 0 { + return fmt.Errorf("Feature.SBTEnableHeight cannot be negative. Got: %d", params.Feature.SBTEnableHeight) + } + + if params.Feature.PbtsEnableHeight < 0 { + return fmt.Errorf("Feature.PbtsEnableHeight cannot be negative. Got: %d", params.Feature.PbtsEnableHeight) + } + + // Synchrony params are only relevant when PBTS is enabled + if params.Feature.PbtsEnableHeight > 0 { + if params.Synchrony.MessageDelay <= 0 { + return fmt.Errorf("synchrony.MessageDelay must be greater than 0. Got: %d", + params.Synchrony.MessageDelay) + } + if params.Synchrony.Precision <= 0 { + return fmt.Errorf("synchrony.Precision must be greater than 0. Got: %d", + params.Synchrony.Precision) + } + if params.Synchrony.MessageDelay > MaxMessageDelay { + return fmt.Errorf("synchrony.MessageDelay is too big, must be less than or equal to %v", MaxMessageDelay) + } + if params.Synchrony.Precision > MaxPrecision { + return fmt.Errorf("synchrony.Precision is too big, must be less than or equal to %v", MaxPrecision) + } } if len(params.Validator.PubKeyTypes) == 0 { @@ -258,63 +381,98 @@ func (params ConsensusParams) ValidateBasic() error { return nil } -// ValidateUpdate validates the updated VoteExtensionsEnableHeight. +// ValidateUpdate validates the updated Consensus Params +// if updated == nil, then pass. +func (params ConsensusParams) ValidateUpdate(updated *cmtproto.ConsensusParams, h int64) error { + if updated == nil { + return nil + } + + var err error + // Validate feature update parameters. + if updated.Feature != nil { + err = validateUpdateFeatures(params.Feature, *updated.Feature, h) + } + return err +} + +// validateUpdateFeatures validates the updated PBTSEnableHeight. // | r | params...EnableHeight | updated...EnableHeight | result (nil == pass) -// | 1 | * | (nil) | nil -// | 2 | * | < 0 | VoteExtensionsEnableHeight must be positive +// | 2 | * | < 0 | EnableHeight must be positive // | 3 | <=0 | 0 | nil // | 4 | X | X (>=0) | nil -// | 5 | > 0; <=height | 0 | vote extensions cannot be disabled once enabled +// | 5 | > 0; <=height | 0 | Feature cannot be disabled once enabled // | 6 | > 0; > height | 0 | nil (disable a previous proposal) -// | 7 | * | <=height | vote extensions cannot be updated to a past height +// | 7 | * | <=height | Feature cannot be updated to a past height // | 8 | <=0 | > height (*) | nil -// | 9 | (> 0) <=height | > height (*) | vote extensions cannot be modified once enabled +// | 9 | (> 0) <=height | > height (*) | Feature cannot be modified once enabled // | 10 | (> 0) > height | > height (*) | nil -func (params ConsensusParams) ValidateUpdate(updated *cmtproto.ConsensusParams, h int64) error { - // 1 - if updated == nil || updated.Abci == nil { - return nil +// The table above reflects all cases covered. +func validateUpdateFeatures(params FeatureParams, updated cmtproto.FeatureParams, h int64) error { + if updated.VoteExtensionsEnableHeight != nil { + err := validateUpdateFeatureEnableHeight(params.VoteExtensionsEnableHeight, updated.VoteExtensionsEnableHeight.Value, h, "Vote Extensions") + if err != nil { + return err + } + } + + if updated.PbtsEnableHeight != nil { + err := validateUpdateFeatureEnableHeight(params.PbtsEnableHeight, updated.PbtsEnableHeight.Value, h, "PBTS") + if err != nil { + return err + } } + + if updated.SbtEnableHeight != nil { + err := validateUpdateFeatureEnableHeight(params.SBTEnableHeight, updated.SbtEnableHeight.Value, h, "SBT") + if err != nil { + return err + } + } + return nil +} + +func validateUpdateFeatureEnableHeight(param int64, updated int64, h int64, featureName string) error { // 2 - if updated.Abci.VoteExtensionsEnableHeight < 0 { - return errors.New("VoteExtensionsEnableHeight must be positive") + if updated < 0 { + return fmt.Errorf("%s EnableHeight must be positive", featureName) } // 3 - if params.ABCI.VoteExtensionsEnableHeight <= 0 && updated.Abci.VoteExtensionsEnableHeight == 0 { + if param <= 0 && updated == 0 { return nil } - // 4 (implicit: updated.Abci.VoteExtensionsEnableHeight >= 0) - if params.ABCI.VoteExtensionsEnableHeight == updated.Abci.VoteExtensionsEnableHeight { + // 4 (implicit: updated >= 0) + if param == updated { return nil } // 5 & 6 - if params.ABCI.VoteExtensionsEnableHeight > 0 && updated.Abci.VoteExtensionsEnableHeight == 0 { + if param > 0 && updated == 0 { // 5 - if params.ABCI.VoteExtensionsEnableHeight <= h { - return fmt.Errorf("vote extensions cannot be disabled once enabled"+ - "old enable height: %d, current height %d", - params.ABCI.VoteExtensionsEnableHeight, h) + if param <= h { + return fmt.Errorf("%s cannot be disabled once enabled"+ + "enabled height: %d, current height: %d", + featureName, param, h) } // 6 return nil } - // 7 (implicit: updated.Abci.VoteExtensionsEnableHeight > 0) - if updated.Abci.VoteExtensionsEnableHeight <= h { - return fmt.Errorf("vote extensions cannot be updated to a past or current height, "+ - "enable height: %d, current height %d", - updated.Abci.VoteExtensionsEnableHeight, h) + // 7 (implicit: updated > 0) + if updated <= h { + return fmt.Errorf("%s cannot be updated to a past or current height, "+ + "enabled height: %d, enable height: %d, current height %d", + featureName, param, updated, h) } - // 8 (implicit: updated.Abci.VoteExtensionsEnableHeight > h) - if params.ABCI.VoteExtensionsEnableHeight <= 0 { + // 8 (implicit: updated > h) + if param <= 0 { return nil } - // 9 (implicit: params.ABCI.VoteExtensionsEnableHeight > 0 && updated.Abci.VoteExtensionsEnableHeight > h) - if params.ABCI.VoteExtensionsEnableHeight <= h { - return fmt.Errorf("vote extensions cannot be modified once enabled"+ - "enable height: %d, current height %d", - params.ABCI.VoteExtensionsEnableHeight, h) + // 9 (implicit: param > 0 && updated > h) + if param <= h { + return fmt.Errorf("%s cannot be modified once enabled"+ + "enabled height: %d, current height: %d", + featureName, param, h) } - // 10 (implicit: params.ABCI.VoteExtensionsEnableHeight > h && updated.Abci.VoteExtensionsEnableHeight > h) + // 10 (implicit: param > h && updated > h) return nil } @@ -369,8 +527,29 @@ func (params ConsensusParams) Update(params2 *cmtproto.ConsensusParams) Consensu if params2.Version != nil { res.Version.App = params2.Version.App } - if params2.Abci != nil { - res.ABCI.VoteExtensionsEnableHeight = params2.Abci.GetVoteExtensionsEnableHeight() + if params2.Feature != nil { + if params2.Feature.VoteExtensionsEnableHeight != nil { + res.Feature.VoteExtensionsEnableHeight = 0 // disable vote extensions in Berachain + } + + if params2.Feature.PbtsEnableHeight != nil { + // Disabled because it should be enabled from the beginning + if res.Feature.PbtsEnableHeight != 1 { + panic("PBTS has to be enabled") + } + } + + if params2.Feature.SbtEnableHeight != nil { + res.Feature.SBTEnableHeight = params2.Feature.GetSbtEnableHeight().GetValue() + } + } + if params2.Synchrony != nil { + if params2.Synchrony.MessageDelay != nil { + res.Synchrony.MessageDelay = *params2.Synchrony.GetMessageDelay() + } + if params2.Synchrony.Precision != nil { + res.Synchrony.Precision = *params2.Synchrony.GetPrecision() + } } if params2.Authority != nil { res.Authority.Authority = params2.Authority.Authority @@ -379,7 +558,7 @@ func (params ConsensusParams) Update(params2 *cmtproto.ConsensusParams) Consensu } func (params *ConsensusParams) ToProto() cmtproto.ConsensusParams { - return cmtproto.ConsensusParams{ + pb := cmtproto.ConsensusParams{ Block: &cmtproto.BlockParams{ MaxBytes: params.Block.MaxBytes, MaxGas: params.Block.MaxGas, @@ -395,13 +574,24 @@ func (params *ConsensusParams) ToProto() cmtproto.ConsensusParams { Version: &cmtproto.VersionParams{ App: params.Version.App, }, - Abci: &cmtproto.ABCIParams{ - VoteExtensionsEnableHeight: params.ABCI.VoteExtensionsEnableHeight, + Feature: &cmtproto.FeatureParams{ + PbtsEnableHeight: &gogo.Int64Value{Value: params.Feature.PbtsEnableHeight}, + VoteExtensionsEnableHeight: &gogo.Int64Value{Value: params.Feature.VoteExtensionsEnableHeight}, + SbtEnableHeight: &gogo.Int64Value{Value: params.Feature.SBTEnableHeight}, }, - Authority: &cmtproto.AuthorityParams{ - Authority: params.Authority.Authority, + Synchrony: &cmtproto.SynchronyParams{ + MessageDelay: ¶ms.Synchrony.MessageDelay, + Precision: ¶ms.Synchrony.Precision, }, } + // Emitted only when set, so that the serialized form of params written by + // this fork matches what bera-v1.x wrote (it has no authority field). + if params.Authority.Authority != "" { + pb.Authority = &cmtproto.AuthorityParams{ + Authority: params.Authority.Authority, + } + } + return pb } func ConsensusParamsFromProto(pbParams cmtproto.ConsensusParams) ConsensusParams { @@ -421,9 +611,25 @@ func ConsensusParamsFromProto(pbParams cmtproto.ConsensusParams) ConsensusParams Version: VersionParams{ App: pbParams.Version.App, }, + Feature: FeatureParams{ + VoteExtensionsEnableHeight: pbParams.GetFeature().GetVoteExtensionsEnableHeight().GetValue(), + PbtsEnableHeight: pbParams.GetFeature().GetPbtsEnableHeight().GetValue(), + SBTEnableHeight: pbParams.GetFeature().GetSbtEnableHeight().GetValue(), + }, + } + if pbParams.GetSynchrony().GetMessageDelay() != nil { + c.Synchrony.MessageDelay = *pbParams.GetSynchrony().GetMessageDelay() } - if pbParams.Abci != nil { - c.ABCI.VoteExtensionsEnableHeight = pbParams.Abci.GetVoteExtensionsEnableHeight() + if pbParams.GetSynchrony().GetPrecision() != nil { + c.Synchrony.Precision = *pbParams.GetSynchrony().GetPrecision() + } + if pbParams.GetAbci().GetVoteExtensionsEnableHeight() > 0 { + // Value set before the upgrade to V1. We can safely overwrite here because + // ABCIParams and FeatureParams being set is mutually exclusive (=V1). + if pbParams.GetFeature().GetVoteExtensionsEnableHeight().GetValue() > 0 { + panic("vote_extension_enable_height is set in two different places") + } + c.Feature.VoteExtensionsEnableHeight = pbParams.Abci.VoteExtensionsEnableHeight } if pbParams.Authority != nil { c.Authority.Authority = pbParams.Authority.Authority diff --git a/types/params_test.go b/types/params_test.go index 4c0fbe5672f..18cfeacfc36 100644 --- a/types/params_test.go +++ b/types/params_test.go @@ -6,6 +6,8 @@ import ( "testing" "time" + gogo "github.com/cosmos/gogoproto/types" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -78,9 +80,11 @@ func makeParams( Validator: ValidatorParams{ PubKeyTypes: pubkeyTypes, }, - ABCI: ABCIParams{ + Feature: FeatureParams{ VoteExtensionsEnableHeight: abciExtensionHeight, + PbtsEnableHeight: 1, }, + Synchrony: DefaultSynchronyParams(), Authority: auth, } } @@ -230,12 +234,12 @@ func TestConsensusParamsUpdate_VoteExtensionsEnableHeight(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(*testing.T) { initialParams := makeParams(1, 0, 2, 0, valEd25519, tc.from, "") - update := &cmtproto.ConsensusParams{} + update := &cmtproto.ConsensusParams{Feature: &cmtproto.FeatureParams{}} if tc.to == nilTest { - update.Abci = nil + update.Feature.VoteExtensionsEnableHeight = nil } else { - update.Abci = &cmtproto.ABCIParams{ - VoteExtensionsEnableHeight: tc.to, + update.Feature = &cmtproto.FeatureParams{ + VoteExtensionsEnableHeight: &gogo.Int64Value{Value: tc.to}, } } if tc.expectedErr { diff --git a/types/priv_validator.go b/types/priv_validator.go index 1b33f46f00a..00cfa66f8a4 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -6,7 +6,9 @@ import ( "fmt" "github.com/cometbft/cometbft/crypto" + "github.com/cometbft/cometbft/crypto/bls12381" "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/crypto/secp256k1" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" ) @@ -17,6 +19,9 @@ type PrivValidator interface { SignVote(chainID string, vote *cmtproto.Vote) error SignProposal(chainID string, proposal *cmtproto.Proposal) error + + // SignBytes signs an arbitrary array of bytes. + SignBytes(bytes []byte) ([]byte, error) } type PrivValidatorsByAddress []PrivValidator @@ -57,6 +62,25 @@ func NewMockPV() MockPV { return MockPV{ed25519.GenPrivKey(), false, false} } +func NewMockPVWithKeyType(keyType string) MockPV { + var pk crypto.PrivKey + switch keyType { + case "", ed25519.KeyType: + pk = ed25519.GenPrivKey() + case secp256k1.KeyType: + pk = secp256k1.GenPrivKey() + case bls12381.KeyType: + blsPk, err := bls12381.GenPrivKey() + if err != nil { + panic(err) + } + pk = blsPk + default: + panic("unexpected key type: " + keyType) + } + return MockPV{pk, false, false} +} + // NewMockPVWithParams allows one to create a MockPV instance, but with finer // grained control over the operation of the mock validator. This is useful for // mocking test failures. @@ -114,6 +138,11 @@ func (pv MockPV) SignProposal(chainID string, proposal *cmtproto.Proposal) error return nil } +// SignBytes implements PrivValidator. +func (pv MockPV) SignBytes(bytes []byte) ([]byte, error) { + return pv.PrivKey.Sign(bytes) +} + func (pv MockPV) ExtractIntoValidator(votingPower int64) *Validator { pubKey, _ := pv.GetPubKey() return &Validator{ diff --git a/types/proposal.go b/types/proposal.go index b66a0d6dfa9..5ead8e9114f 100644 --- a/types/proposal.go +++ b/types/proposal.go @@ -34,14 +34,16 @@ type Proposal struct { // NewProposal returns a new Proposal. // If there is no POLRound, polRound should be -1. -func NewProposal(height int64, round int32, polRound int32, blockID BlockID) *Proposal { +// Under PBTS the proposal timestamp must equal the proposed block's time, so +// the caller provides it (it is canonicalized here). +func NewProposal(height int64, round int32, polRound int32, blockID BlockID, ts time.Time) *Proposal { return &Proposal{ Type: cmtproto.ProposalType, Height: height, Round: round, BlockID: blockID, POLRound: polRound, - Timestamp: cmttime.Now(), + Timestamp: cmttime.Canonical(ts), } } @@ -50,8 +52,8 @@ func (p *Proposal) ValidateBasic() error { if p.Type != cmtproto.ProposalType { return errors.New("invalid Type") } - if p.Height < 0 { - return errors.New("negative Height") + if p.Height <= 0 { + return errors.New("non positive Height") } if p.Round < 0 { return errors.New("negative Round") @@ -59,6 +61,9 @@ func (p *Proposal) ValidateBasic() error { if p.POLRound < -1 { return errors.New("negative POLRound (exception: -1)") } + if p.POLRound >= p.Round { + return errors.New("POLRound >= Round") + } if err := p.BlockID.ValidateBasic(); err != nil { return fmt.Errorf("wrong BlockID: %v", err) } @@ -67,7 +72,10 @@ func (p *Proposal) ValidateBasic() error { return fmt.Errorf("expected a complete, non-empty BlockID, got: %v", p.BlockID) } - // NOTE: Timestamp validation is subtle and handled elsewhere. + // Times must be canonical + if cmttime.Canonical(p.Timestamp) != p.Timestamp { + return fmt.Errorf("expected a canonical timestamp, got: %v", p.Timestamp) + } if len(p.Signature) == 0 { return errors.New("signature is missing") @@ -79,6 +87,30 @@ func (p *Proposal) ValidateBasic() error { return nil } +// IsTimely validates that the proposal timestamp is 'timely' according to the +// proposer-based timestamp algorithm. To evaluate if a proposal is timely, its +// timestamp is compared to the local time of the validator when it receives +// the proposal along with the configured Precision and MessageDelay +// parameters. Specifically, a proposed proposal timestamp is considered timely +// if it is satisfies the following inequalities: +// +// proposalReceiveTime >= proposalTimestamp - Precision +// proposalReceiveTime <= proposalTimestamp + MessageDelay + Precision +// +// For more information on the meaning of 'timely', refer to the specification: +// https://github.com/cometbft/cometbft/tree/main/spec/consensus/proposer-based-timestamp +func (p *Proposal) IsTimely(recvTime time.Time, sp SynchronyParams) bool { + // lhs is `proposalTimestamp - Precision` in the first inequality + lhs := p.Timestamp.Add(-sp.Precision) + // rhs is `proposalTimestamp + MessageDelay + Precision` in the second inequality + rhs := p.Timestamp.Add(sp.MessageDelay).Add(sp.Precision) + + if recvTime.Before(lhs) || recvTime.After(rhs) { + return false + } + return true +} + // ValidateBlockSize block size ensures that a proposal block is not larger // than a maximum number of bytes, based on the total amount of parts reported // in the PartSetHeader. If -1 is passed as the maxBlockSizeBytes, diff --git a/types/proposal_test.go b/types/proposal_test.go index c2c50d82881..8782e744c01 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -13,6 +13,7 @@ import ( "github.com/cometbft/cometbft/libs/protoio" cmtrand "github.com/cometbft/cometbft/libs/rand" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttime "github.com/cometbft/cometbft/types/time" ) var ( @@ -62,8 +63,8 @@ func TestProposalVerifySignature(t *testing.T) { require.NoError(t, err) prop := NewProposal( - 4, 2, 2, - BlockID{cmtrand.Bytes(tmhash.Size), PartSetHeader{777, cmtrand.Bytes(tmhash.Size)}}) + 4, 2, 1, + BlockID{cmtrand.Bytes(tmhash.Size), PartSetHeader{777, cmtrand.Bytes(tmhash.Size)}}, cmttime.Now()) p := prop.ToProto() signBytes := ProposalSignBytes("test_chain_id", p) @@ -151,8 +152,8 @@ func TestProposalValidateBasic(t *testing.T) { for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { prop := NewProposal( - 4, 2, 2, - blockID) + 4, 2, 1, + blockID, cmttime.Now()) p := prop.ToProto() err := privVal.SignProposal("test_chain_id", p) prop.Signature = p.Signature @@ -164,9 +165,9 @@ func TestProposalValidateBasic(t *testing.T) { } func TestProposalProtoBuf(t *testing.T) { - proposal := NewProposal(1, 2, 3, makeBlockID([]byte("hash"), 2, []byte("part_set_hash"))) + proposal := NewProposal(1, 2, 1, makeBlockID([]byte("hash"), 2, []byte("part_set_hash")), cmttime.Now()) proposal.Signature = []byte("sig") - proposal2 := NewProposal(1, 2, 3, BlockID{}) + proposal2 := NewProposal(1, 2, 3, BlockID{}, cmttime.Now()) testCases := []struct { msg string @@ -198,12 +199,12 @@ func TestProposalValidateBlockSize(t *testing.T) { proposal *Proposal expectPass bool }{ - {"10 chunk max, 5 chunk proposal, success", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 5}}), true}, - {"10 chunk max, 20 chunk proposal, fail", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 20}}), false}, - {"10 chunk max, max uint32 chunk proposal, fail", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}), false}, - {"-1 chunk max, max uint32 chunk proposal, fail", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}), false}, - {"0 chunk max, max uint32 chunk proposal, fail", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}), false}, - {"total parts equals chunk max, success", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 1600}}), true}, + {"10 chunk max, 5 chunk proposal, success", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 5}}, cmttime.Now()), true}, + {"10 chunk max, 20 chunk proposal, fail", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 20}}, cmttime.Now()), false}, + {"10 chunk max, max uint32 chunk proposal, fail", int64(10 * BlockPartSizeBytes), NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}, cmttime.Now()), false}, + {"-1 chunk max, max uint32 chunk proposal, fail", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}, cmttime.Now()), false}, + {"0 chunk max, max uint32 chunk proposal, fail", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: math.MaxUint32}}, cmttime.Now()), false}, + {"total parts equals chunk max, success", -1, NewProposal(0, 0, 0, BlockID{PartSetHeader: PartSetHeader{Total: 1600}}, cmttime.Now()), true}, } for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { diff --git a/types/protobuf.go b/types/protobuf.go index 4393f571c6e..1fd60b96044 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -103,7 +103,7 @@ type pb2tm struct{} func (pb2tm) ValidatorUpdates(vals []abci.ValidatorUpdate) ([]*Validator, error) { cmtVals := make([]*Validator, len(vals)) for i, v := range vals { - pub, err := cryptoenc.PubKeyFromProto(v.PubKey) + pub, err := PubKeyFromValidatorUpdate(v) if err != nil { return nil, err } @@ -111,3 +111,43 @@ func (pb2tm) ValidatorUpdates(vals []abci.ValidatorUpdate) ([]*Validator, error) } return cmtVals, nil } + +// PubKeyFromValidatorUpdate extracts the public key from an ABCI validator +// update. It accepts both encodings: the proto PublicKey in pub_key (upstream +// and this fork) and the raw pub_key_bytes + pub_key_type pair written by the +// bera-v1.x line. pub_key takes precedence when both are set. +func PubKeyFromValidatorUpdate(v abci.ValidatorUpdate) (crypto.PubKey, error) { + if v.PubKey.Sum == nil && v.PubKeyType != "" { + return cryptoenc.PubKeyFromTypeAndBytes(v.PubKeyType, v.PubKeyBytes) + } + return cryptoenc.PubKeyFromProto(v.PubKey) +} + +// NormalizeValidatorUpdates returns a copy of the validator updates in which +// every entry carries both public key encodings (pub_key, and +// pub_key_bytes + pub_key_type) so that the persisted form can be read by both +// this fork and the bera-v1.x line. Entries whose key cannot be decoded are +// left untouched. +func NormalizeValidatorUpdates(vals []abci.ValidatorUpdate) []abci.ValidatorUpdate { + if len(vals) == 0 { + return vals + } + out := make([]abci.ValidatorUpdate, len(vals)) + copy(out, vals) + for i := range out { + pub, err := PubKeyFromValidatorUpdate(out[i]) + if err != nil { + continue + } + if out[i].PubKey.Sum == nil { + if pk, err := cryptoenc.PubKeyToProto(pub); err == nil { + out[i].PubKey = pk + } + } + if out[i].PubKeyType == "" { + out[i].PubKeyType = pub.Type() + out[i].PubKeyBytes = pub.Bytes() + } + } + return out +} diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 73a2c02a16b..74a2cb80325 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -84,3 +84,67 @@ func TestABCIValidatorWithoutPubKey(t *testing.T) { assert.Equal(t, cmtValExpected, abciVal) } + +// The bera-v1.x line encodes ValidatorUpdate public keys as pub_key_bytes + +// pub_key_type; this fork (like upstream) uses the proto PublicKey in pub_key. +// Both must be readable, and persisted updates carry both. +func TestABCIValidatorUpdatesAcceptBothPubKeyEncodings(t *testing.T) { + pubkey := ed25519.GenPrivKey().PubKey() + pkProto, err := cryptoenc.PubKeyToProto(pubkey) + require.NoError(t, err) + + // upstream / bera-v0.40.x encoding + vals, err := PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{PubKey: pkProto, Power: 10}}) + require.NoError(t, err) + require.Len(t, vals, 1) + assert.True(t, pubkey.Equals(vals[0].PubKey)) + + // bera-v1.x encoding + vals, err = PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{ + PubKeyBytes: pubkey.Bytes(), + PubKeyType: pubkey.Type(), + Power: 10, + }}) + require.NoError(t, err) + require.Len(t, vals, 1) + assert.True(t, pubkey.Equals(vals[0].PubKey)) + + // pub_key wins when both are present and disagree + other := ed25519.GenPrivKey().PubKey() + vals, err = PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{ + PubKey: pkProto, + PubKeyBytes: other.Bytes(), + PubKeyType: other.Type(), + Power: 10, + }}) + require.NoError(t, err) + assert.True(t, pubkey.Equals(vals[0].PubKey)) + + // neither present is still an error + _, err = PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{{Power: 10}}) + require.Error(t, err) +} + +func TestNormalizeValidatorUpdates(t *testing.T) { + pubkey := ed25519.GenPrivKey().PubKey() + pkProto, err := cryptoenc.PubKeyToProto(pubkey) + require.NoError(t, err) + + in := []abci.ValidatorUpdate{ + {PubKey: pkProto, Power: 1}, + {PubKeyBytes: pubkey.Bytes(), PubKeyType: pubkey.Type(), Power: 2}, + {Power: 3}, // undecodable, left untouched + } + out := NormalizeValidatorUpdates(in) + require.Len(t, out, 3) + for _, v := range out[:2] { + assert.Equal(t, pkProto, v.PubKey) + assert.Equal(t, pubkey.Bytes(), v.PubKeyBytes) + assert.Equal(t, pubkey.Type(), v.PubKeyType) + } + assert.Equal(t, in[2], out[2]) + // input is not mutated + assert.Nil(t, in[0].PubKeyBytes) + assert.Nil(t, in[1].PubKey.Sum) + assert.Nil(t, NormalizeValidatorUpdates(nil)) +} diff --git a/types/test_util.go b/types/test_util.go index 2e5ed414a82..ef073f833ca 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -12,7 +12,7 @@ import ( ) func MakeExtCommit(blockID BlockID, height int64, round int32, - voteSet *VoteSet, validators []PrivValidator, now time.Time, extEnabled bool, + voteSet *VoteSet, validators []PrivValidator, _ time.Time, extEnabled bool, ) (*ExtendedCommit, error) { // all sign for i := 0; i < len(validators); i++ { @@ -27,7 +27,6 @@ func MakeExtCommit(blockID BlockID, height int64, round int32, Round: round, Type: cmtproto.PrecommitType, BlockID: blockID, - Timestamp: now, } _, err = signAddVote(validators[i], vote, voteSet) @@ -41,7 +40,7 @@ func MakeExtCommit(blockID BlockID, height int64, round int32, enableHeight = height } - return voteSet.MakeExtendedCommit(ABCIParams{VoteExtensionsEnableHeight: enableHeight}), nil + return voteSet.MakeExtendedCommit(FeatureParams{VoteExtensionsEnableHeight: enableHeight}), nil } func signAddVote(privVal PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) { @@ -62,7 +61,7 @@ func MakeVote( round int32, step cmtproto.SignedMsgType, blockID BlockID, - time time.Time, + _ time.Time, ) (*Vote, error) { pubKey, err := val.GetPubKey() if err != nil { @@ -76,7 +75,6 @@ func MakeVote( Round: round, Type: step, BlockID: blockID, - Timestamp: time, } extensionsEnabled := step == cmtproto.PrecommitType @@ -96,9 +94,9 @@ func MakeVoteNoError( round int32, step cmtproto.SignedMsgType, blockID BlockID, - time time.Time, + ts time.Time, ) *Vote { - vote, err := MakeVote(val, chainID, valIndex, height, round, step, blockID, time) + vote, err := MakeVote(val, chainID, valIndex, height, round, step, blockID, ts) require.NoError(t, err) return vote } diff --git a/types/time/mocks/source.go b/types/time/mocks/source.go new file mode 100644 index 00000000000..3f5d153ba75 --- /dev/null +++ b/types/time/mocks/source.go @@ -0,0 +1,46 @@ +// Code generated by mockery v2.53.6. DO NOT EDIT. + +package mocks + +import ( + time "time" + + mock "github.com/stretchr/testify/mock" +) + +// Source is an autogenerated mock type for the Source type +type Source struct { + mock.Mock +} + +// Now provides a mock function with no fields +func (_m *Source) Now() time.Time { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Now") + } + + var r0 time.Time + if rf, ok := ret.Get(0).(func() time.Time); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Time) + } + + return r0 +} + +// NewSource creates a new instance of Source. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSource(t interface { + mock.TestingT + Cleanup(func()) +}) *Source { + mock := &Source{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/types/time/time.go b/types/time/time.go index 6f7d3dfa634..9d9c296e972 100644 --- a/types/time/time.go +++ b/types/time/time.go @@ -17,6 +17,20 @@ func Canonical(t time.Time) time.Time { return t.Round(0).UTC() } +//go:generate ../../scripts/mockery_generate.sh Source + +// Source is an interface that defines a way to fetch the current time. +type Source interface { + Now() time.Time +} + +// DefaultSource implements the Source interface using the system clock provided by the standard library. +type DefaultSource struct{} + +func (DefaultSource) Now() time.Time { + return Now() +} + // WeightedTime for computing a median. type WeightedTime struct { Time time.Time diff --git a/types/validation.go b/types/validation.go index 4211903cf2a..9686c74b40b 100644 --- a/types/validation.go +++ b/types/validation.go @@ -7,11 +7,16 @@ import ( "github.com/cometbft/cometbft/crypto" "github.com/cometbft/cometbft/crypto/batch" + "github.com/cometbft/cometbft/crypto/bls12381" "github.com/cometbft/cometbft/crypto/tmhash" cmtmath "github.com/cometbft/cometbft/libs/math" cmterrors "github.com/cometbft/cometbft/types/errors" ) +// ErrInvalidAggCommit is returned when a commit carries aggregated signature +// flags but the validator set is not made of BLS12-381 keys. +var ErrInvalidAggCommit = errors.New("commit has aggregated signature flags, but was not created by vals with BLS12-381 keys") + const batchVerifyThreshold = 2 func shouldBatchVerify(vals *ValidatorSet, commit *Commit) bool { @@ -20,6 +25,13 @@ func shouldBatchVerify(vals *ValidatorSet, commit *Commit) bool { vals.AllKeysHaveSameType() } +// isAggregatedCommit returns true if the commit is an aggregated one. +func isAggregatedCommit(vals *ValidatorSet, commit *Commit) bool { + _, ok := vals.GetProposer().PubKey.(*bls12381.PubKey) + _, ok2 := vals.GetProposer().PubKey.(bls12381.PubKey) + return (ok || ok2) && vals.AllKeysHaveSameType() && commit.HasAggregatedSignature() +} + // VerifyCommit verifies +2/3 of the set had signed the given commit. // // It checks all the signatures! While it's safe to exit as soon as we have @@ -42,6 +54,22 @@ func VerifyCommit(chainID string, vals *ValidatorSet, blockID BlockID, // ignore all absent signatures ignore := func(c CommitSig) bool { return c.BlockIDFlag == BlockIDFlagAbsent } + // attempt to verify aggregated commit + if isAggregatedCommit(vals, commit) { + // only count the signatures that are for the block + count := func(c CommitSig) bool { + return c.BlockIDFlag == BlockIDFlagAggCommit || c.BlockIDFlag == BlockIDFlagAggCommitAbsent + } + + return verifyAggregatedCommit(chainID, vals, commit, + votingPowerNeeded, ignore, count, true) + } + + // Guard against invalid aggregated commits. + if commit.HasAggregatedSignature() { + return ErrInvalidAggCommit + } + // only count the signatures that are for the block count := func(c CommitSig) bool { return c.BlockIDFlag == BlockIDFlagCommit } @@ -120,12 +148,28 @@ func verifyCommitLightInternal( // calculate voting power needed votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 + // count all the remaining signatures + count := func(_ CommitSig) bool { return true } + + // attempt to verify aggregated commit + if isAggregatedCommit(vals, commit) { + // ignore all commit signatures that are not for the block + ignore := func(c CommitSig) bool { + return c.BlockIDFlag != BlockIDFlagAggCommit && c.BlockIDFlag != BlockIDFlagAggCommitAbsent + } + + return verifyAggregatedCommit(chainID, vals, commit, + votingPowerNeeded, ignore, count, true) + } + + // Guard against invalid aggregated commits. + if commit.HasAggregatedSignature() { + return ErrInvalidAggCommit + } + // ignore all commit signatures that are not for the block ignore := func(c CommitSig) bool { return c.BlockIDFlag != BlockIDFlagCommit } - // count all the remaining signatures - count := func(c CommitSig) bool { return true } - // attempt to batch verify if shouldBatchVerify(vals, commit) { return verifyCommitBatch(chainID, vals, commit, @@ -219,12 +263,28 @@ func verifyCommitLightTrustingInternal( } votingPowerNeeded := totalVotingPowerMulByNumerator / int64(trustLevel.Denominator) + // count all the remaining signatures + count := func(_ CommitSig) bool { return true } + + // attempt to verify aggregated commit + if isAggregatedCommit(vals, commit) { + // ignore all commit signatures that are not for the block + ignore := func(c CommitSig) bool { + return c.BlockIDFlag != BlockIDFlagAggCommit && c.BlockIDFlag != BlockIDFlagAggCommitAbsent + } + + return verifyAggregatedCommit(chainID, vals, commit, + votingPowerNeeded, ignore, count, false) + } + + // Guard against invalid aggregated commits. + if commit.HasAggregatedSignature() { + return ErrInvalidAggCommit + } + // ignore all commit signatures that are not for the block ignore := func(c CommitSig) bool { return c.BlockIDFlag != BlockIDFlagCommit } - // count all the remaining signatures - count := func(c CommitSig) bool { return true } - // attempt to batch verify commit. As the validator set doesn't necessarily // correspond with the validator set that signed the block we need to look // up by address rather than index. @@ -531,3 +591,168 @@ func verifyBasicValsAndCommit(vals *ValidatorSet, commit *Commit, height int64, return nil } + +// verifyAggregatedCommit verifies an aggregated commit, in which the +// individual BLS signatures have been collapsed into (at most) one aggregated +// signature for the block and one for nil. +// +// The aggregated signature is carried by the first CommitSig with +// BlockIDFlagAggCommit (respectively BlockIDFlagAggNil); the remaining +// signers for the same message carry BlockIDFlagAggCommitAbsent +// (BlockIDFlagAggNilAbsent) with an empty signature. +// +// If lookUpByIndex is true the commit signatures must have a 1-to-1 +// correspondence with the validator set (and the validator address is checked +// against the commit entry); otherwise validators are looked up by address. +func verifyAggregatedCommit( + chainID string, + vals *ValidatorSet, + commit *Commit, + votingPowerNeeded int64, + ignoreSig func(CommitSig) bool, + countSig func(CommitSig) bool, + lookUpByIndex bool, +) error { + var ( + val *Validator + valIdx int32 + seenVals = make(map[int32]int, len(commit.Signatures)) + talliedVotingPower int64 + aggSig1, aggSig2 []byte + msg1, msg2 []byte + + aggSig1Found, aggSig2Found bool + ) + + pubkeys1 := make([]*bls12381.PubKey, 0, len(commit.Signatures)) + pubkeys2 := make([]*bls12381.PubKey, 0, len(commit.Signatures)) + + for idx, commitSig := range commit.Signatures { + // skip over signatures that should be ignored + if ignoreSig(commitSig) { + continue + } + + // If the vals and commit have a 1-to-1 correspondence we can retrieve + // them by index else we need to retrieve them by address + if lookUpByIndex { + val = vals.Validators[idx] + if !bytes.Equal(val.Address, commitSig.ValidatorAddress) { + return fmt.Errorf("validator address mismatch at index %d: expected %X, got %X", + idx, val.Address, commitSig.ValidatorAddress) + } + } else { + valIdx, val = vals.GetByAddressMut(commitSig.ValidatorAddress) + + // if the signature doesn't belong to anyone in the validator set + // then we just skip over it + if val == nil { + continue + } + + // because we are getting validators by address we need to make sure + // that the same validator doesn't commit twice + if firstIndex, ok := seenVals[valIdx]; ok { + secondIndex := idx + return fmt.Errorf("double vote from %v (%d and %d)", val, firstIndex, secondIndex) + } + seenVals[valIdx] = idx + } + + if val.PubKey == nil { + return fmt.Errorf("validator %v has a nil PubKey at index %d", val, idx) + } + + switch commitSig.BlockIDFlag { + case BlockIDFlagAggCommit: + if aggSig1Found { + return errors.New("multiple aggregated signatures for block") + } + + aggSig1 = commitSig.Signature + msg1 = commit.VoteSignBytes(chainID, int32(idx)) + aggSig1Found = true + + pk, ok := val.PubKey.(*bls12381.PubKey) + if !ok { + pk2 := val.PubKey.(bls12381.PubKey) + pk = &pk2 + } + pubkeys1 = append(pubkeys1, pk) + case BlockIDFlagAggCommitAbsent: + if !aggSig1Found { + return fmt.Errorf("aggregated signature for block absent %d but no previously defined aggregated signature", idx) + } + + pk, ok := val.PubKey.(*bls12381.PubKey) + if !ok { + pk2 := val.PubKey.(bls12381.PubKey) + pk = &pk2 + } + pubkeys1 = append(pubkeys1, pk) + case BlockIDFlagAggNil: + if aggSig2Found { + return errors.New("multiple aggregated signatures for nil") + } + + aggSig2 = commitSig.Signature + msg2 = commit.VoteSignBytes(chainID, int32(idx)) + aggSig2Found = true + + pk, ok := val.PubKey.(*bls12381.PubKey) + if !ok { + pk2 := val.PubKey.(bls12381.PubKey) + pk = &pk2 + } + pubkeys2 = append(pubkeys2, pk) + case BlockIDFlagAggNilAbsent: + if !aggSig2Found { + return fmt.Errorf("aggregated signature for nil absent %d but no previously defined aggregated signature", idx) + } + pk, ok := val.PubKey.(*bls12381.PubKey) + if !ok { + pk2 := val.PubKey.(bls12381.PubKey) + pk = &pk2 + } + pubkeys2 = append(pubkeys2, pk) + default: + return fmt.Errorf("wrong block ID flag %d for sig %d (expected aggregated signature flags)", commitSig.BlockIDFlag, idx) + } + + if countSig(commitSig) { + talliedVotingPower += val.VotingPower + } + } + + // ensure that we have batched together enough signatures to exceed the + // voting power needed else there is no need to even verify + if got, needed := talliedVotingPower, votingPowerNeeded; got <= needed { + return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} + } + + // Additional guard. + if !aggSig1Found { + return errors.New("missing aggregated signature for block") + } + + // Since we are above the voting power threshold needed, we know `aggSig1`, + // `pubkeys1`, and `msg1` are not `nil`. + ok := bls12381.VerifyAggregateSignature(aggSig1, pubkeys1, msg1) + if !ok { + return fmt.Errorf("wrong aggregated signature for block: %X (pubkeys: %v)", aggSig1, pubkeys1) + } + + // If we have to verify the aggregated signature for nil and there are + // signatures for nil, then verify the aggregated signature for nil. + if len(pubkeys2) > 0 { + if aggSig2 == nil { + return errors.New("missing aggregated signature for nil") + } + ok = bls12381.VerifyAggregateSignature(aggSig2, pubkeys2, msg2) + if !ok { + return fmt.Errorf("wrong aggregated signature for nil: %X (pubkeys: %v)", aggSig2, pubkeys2) + } + } + + return nil +} diff --git a/types/validator.go b/types/validator.go index 77d62724788..4e5673742a4 100644 --- a/types/validator.go +++ b/types/validator.go @@ -138,20 +138,22 @@ func (v *Validator) Bytes() []byte { return bz } -// ToProto converts Validator to protobuf +// ToProto converts Validator to protobuf. +// The key is serialized via pub_key_bytes/pub_key_type (the bera-v1.x +// layout); the deprecated pub_key field is left unset. func (v *Validator) ToProto() (*cmtproto.Validator, error) { if v == nil { return nil, errors.New("nil validator") } - pk, err := ce.PubKeyToProto(v.PubKey) - if err != nil { - return nil, err + if v.PubKey == nil { + return nil, errors.New("nil pubkey") } vp := cmtproto.Validator{ Address: v.Address, - PubKey: pk, + PubKeyType: v.PubKey.Type(), + PubKeyBytes: v.PubKey.Bytes(), VotingPower: v.VotingPower, ProposerPriority: v.ProposerPriority, } @@ -166,9 +168,15 @@ func ValidatorFromProto(vp *cmtproto.Validator) (*Validator, error) { return nil, errors.New("nil validator") } - pk, err := ce.PubKeyFromProto(vp.PubKey) + pk, err := ce.PubKeyFromTypeAndBytes(vp.PubKeyType, vp.PubKeyBytes) if err != nil { - return nil, err + if vp.PubKey == nil { + return nil, fmt.Errorf("validator pubkey decode failed and legacy pub_key is nil: %w", err) + } + pk, err = ce.PubKeyFromProto(*vp.PubKey) + if err != nil { + return nil, err + } } v := new(Validator) v.Address = vp.GetAddress() diff --git a/types/validator_set_test.go b/types/validator_set_test.go index 2d0c4e18712..52f66b0dc35 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -523,18 +523,16 @@ func TestValidatorSetTotalVotingPowerPanicsOnOverflow(t *testing.T) { func TestValidatorSetFromProtoReturnsErrorOnOverflow(t *testing.T) { // ValidatorSetFromProto should return an error instead of panicking when total voting power exceeds MaxTotalVotingPower. pubKey := ed25519.GenPrivKey().PubKey() - pkProto, err := cryptoenc.PubKeyToProto(pubKey) - require.NoError(t, err) protoVals := &cmtproto.ValidatorSet{ Validators: []*cmtproto.Validator{ - {Address: pubKey.Address(), PubKey: pkProto, VotingPower: math.MaxInt64, ProposerPriority: 0}, - {Address: pubKey.Address(), PubKey: pkProto, VotingPower: math.MaxInt64, ProposerPriority: 0}, + {Address: pubKey.Address(), PubKeyBytes: pubKey.Bytes(), PubKeyType: pubKey.Type(), VotingPower: math.MaxInt64, ProposerPriority: 0}, + {Address: pubKey.Address(), PubKeyBytes: pubKey.Bytes(), PubKeyType: pubKey.Type(), VotingPower: math.MaxInt64, ProposerPriority: 0}, }, - Proposer: &cmtproto.Validator{Address: pubKey.Address(), PubKey: pkProto, VotingPower: math.MaxInt64, ProposerPriority: 0}, + Proposer: &cmtproto.Validator{Address: pubKey.Address(), PubKeyBytes: pubKey.Bytes(), PubKeyType: pubKey.Type(), VotingPower: math.MaxInt64, ProposerPriority: 0}, } - _, err = ValidatorSetFromProto(protoVals) + _, err := ValidatorSetFromProto(protoVals) require.Error(t, err) assert.Contains(t, err.Error(), "exceeds maximum") } @@ -1754,7 +1752,6 @@ func TestVerifyCommitExtended(t *testing.T) { Round: round, Type: cmtproto.PrecommitType, BlockID: blockID, - Timestamp: now, } added, err := signAddVote(vals[i], vote, voteSet) @@ -1768,7 +1765,7 @@ func TestVerifyCommitExtended(t *testing.T) { require.NoError(t, err) require.True(t, added) - extCommit := voteSet.MakeExtendedCommit(ABCIParams{VoteExtensionsEnableHeight: height}) + extCommit := voteSet.MakeExtendedCommit(FeatureParams{VoteExtensionsEnableHeight: height}) require.Equal(t, BlockIDFlagNil, extCommit.ExtendedSignatures[5].BlockIDFlag) require.Equal(t, BlockIDFlagAbsent, extCommit.ExtendedSignatures[6].BlockIDFlag) require.Empty(t, extCommit.ExtendedSignatures[5].Extension) diff --git a/types/vote.go b/types/vote.go index 7ea9930a777..af11365d43d 100644 --- a/types/vote.go +++ b/types/vote.go @@ -27,10 +27,23 @@ var ( ErrVoteInvalidBlockHash = errors.New("invalid block hash") ErrVoteNonDeterministicSignature = errors.New("non-deterministic signature") ErrVoteNil = errors.New("nil vote") + ErrVoteNoSignature = errors.New("no signature") ErrVoteExtensionAbsent = errors.New("vote extension absent") ErrInvalidVoteExtension = errors.New("invalid vote extension") ) +// ErrVoteTimestampNotZero is returned when a vote carries a non-zero +// timestamp. Vote timestamps are pinned to zero on this fork because they are +// excluded from the signed bytes (BLS aggregation requires all validators to +// sign identical bytes). +type ErrVoteTimestampNotZero struct { + Timestamp time.Time +} + +func (err *ErrVoteTimestampNotZero) Error() string { + return fmt.Sprintf("Vote time stamp should be 0 and not set: %s", err.Timestamp) +} + type ErrVoteConflictingVotes struct { VoteA *Vote VoteB *Vote @@ -50,10 +63,11 @@ func NewConflictingVoteError(vote1, vote2 *Vote) *ErrVoteConflictingVotes { // The vote extension is only valid for non-nil precommits. type ErrVoteExtensionInvalid struct { ExtSignature []byte + Reason string } func (err *ErrVoteExtensionInvalid) Error() string { - return fmt.Sprintf("extensions must be present IFF vote is a non-nil Precommit; extension signature: %X", err.ExtSignature) + return fmt.Sprintf("invalid vote extension: %s; extension signature: %X", err.Reason, err.ExtSignature) } // Address is hex bytes. @@ -93,12 +107,15 @@ func VoteFromProto(pv *cmtproto.Vote) (*Vote, error) { Height: pv.Height, Round: pv.Round, BlockID: *blockID, - Timestamp: pv.Timestamp, ValidatorAddress: pv.ValidatorAddress, ValidatorIndex: pv.ValidatorIndex, Signature: pv.Signature, Extension: pv.Extension, ExtensionSignature: pv.ExtensionSignature, + // The vote timestamp is not part of the signed (canonical) bytes and + // is pinned to zero on this fork (BLS aggregation requires identical + // sign bytes across validators). + Timestamp: time.Time{}, }, nil } @@ -121,7 +138,6 @@ func (vote *Vote) CommitSig() CommitSig { return CommitSig{ BlockIDFlag: blockIDFlag, ValidatorAddress: vote.ValidatorAddress, - Timestamp: vote.Timestamp, Signature: vote.Signature, } } @@ -206,7 +222,7 @@ func (vote *Vote) String() string { panic("Unknown vote type") } - return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X %X @ %s}", + return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X %X @}", vote.ValidatorIndex, cmtbytes.Fingerprint(vote.ValidatorAddress), vote.Height, @@ -216,7 +232,6 @@ func (vote *Vote) String() string { cmtbytes.Fingerprint(vote.BlockID.Hash), cmtbytes.Fingerprint(vote.Signature), cmtbytes.Fingerprint(vote.Extension), - CanonicalTime(vote.Timestamp), ) } @@ -429,7 +444,10 @@ func SignAndCheckVote( isPrecommit := vote.Type == cmtproto.PrecommitType if !isPrecommit && extensionsEnabled { // Non-recoverable because the caller passed parameters that don't make sense - return false, &ErrVoteExtensionInvalid{ExtSignature: v.ExtensionSignature} + return false, &ErrVoteExtensionInvalid{ + Reason: "inconsistent values of `isPrecommit` and `extensionsEnabled`", + ExtSignature: v.ExtensionSignature, + } } isNil := vote.BlockID.IsZero() @@ -438,7 +456,10 @@ func SignAndCheckVote( // Error if prevote contains an extension signature if extSignature && (!isPrecommit || isNil) { // Non-recoverable because the vote is malformed - return false, &ErrVoteExtensionInvalid{ExtSignature: v.ExtensionSignature} + return false, &ErrVoteExtensionInvalid{ + Reason: "vote extension signature must not be present in prevotes or nil-precommits", + ExtSignature: v.ExtensionSignature, + } } vote.ExtensionSignature = nil @@ -446,13 +467,18 @@ func SignAndCheckVote( // Error if missing extension signature for non-nil Precommit if !extSignature && isPrecommit && !isNil { // Non-recoverable because the vote is malformed - return false, &ErrVoteExtensionInvalid{ExtSignature: v.ExtensionSignature} + return false, &ErrVoteExtensionInvalid{ + Reason: "vote extension signature must be present if extensions are enabled", + ExtSignature: v.ExtensionSignature, + } } vote.ExtensionSignature = v.ExtensionSignature } - vote.Timestamp = v.Timestamp + if !v.Timestamp.Equal(time.Time{}) { + return false, &ErrVoteTimestampNotZero{Timestamp: v.Timestamp} + } return true, nil } diff --git a/types/vote_set.go b/types/vote_set.go index ac70c128a1b..3e9d99deb31 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -5,6 +5,7 @@ import ( "fmt" "strings" + "github.com/cometbft/cometbft/crypto/bls12381" "github.com/cometbft/cometbft/libs/bits" cmtjson "github.com/cometbft/cometbft/libs/json" cmtsync "github.com/cometbft/cometbft/libs/sync" @@ -18,6 +19,13 @@ const ( MaxVotesCount = 10000 ) +func init() { + // RoundState.LastCommit is an interface (VoteSetReader), so the concrete + // types it can hold must be registered for JSON encoding (e.g. the + // /dump_consensus_state RPC endpoint). + cmtjson.RegisterType(&VoteSet{}, "cometbft/VoteSet") +} + // UNSTABLE // XXX: duplicate of p2p.ID to avoid dependence between packages. // Perhaps we can have a minimal types package containing this (and other things?) @@ -633,7 +641,7 @@ func (voteSet *VoteSet) sumTotalFrac() (int64, int64, float64) { // // Panics if the vote type is not PrecommitType or if there's no +2/3 votes for // a single block. -func (voteSet *VoteSet) MakeExtendedCommit(ap ABCIParams) *ExtendedCommit { +func (voteSet *VoteSet) MakeExtendedCommit(fp FeatureParams) *ExtendedCommit { voteSet.mtx.Lock() defer voteSet.mtx.Unlock() @@ -664,13 +672,124 @@ func (voteSet *VoteSet) MakeExtendedCommit(ap ABCIParams) *ExtendedCommit { BlockID: *voteSet.maj23, ExtendedSignatures: sigs, } - if err := ec.EnsureExtensions(ap.VoteExtensionsEnabled(ec.Height)); err != nil { + if err := ec.EnsureExtensions(fp.VoteExtensionsEnabled(ec.Height)); err != nil { panic(fmt.Errorf("problem with vote extension data when making extended commit of height %d; %w", ec.Height, err)) } return ec } +// MakeBLSCommit is a variant of MakeExtendedCommit that aggregates BLS signatures. +// +// It additionally aggregates the BLS signatures for the block and nil. The +// resulting commit contains only two aggregated signatures: +// +// 1 - aggregated signature for the block (compressed) +// 2 - aggregated signature for nil (compressed). +// +// Note the signatures count is preserved, but only the first signature in the +// each category (block, nil) is non-empty. +// +// NOTE: it doesn't aggregate vote extension signatures since the extensions +// are all different. +func (voteSet *VoteSet) MakeBLSCommit() *ExtendedCommit { + voteSet.mtx.Lock() + defer voteSet.mtx.Unlock() + + if voteSet.signedMsgType != cmtproto.PrecommitType { + panic("Cannot MakeBLSCommit() unless VoteSet.Type is PrecommitType") + } + + // Make sure we have a 2/3 majority + if voteSet.maj23 == nil { + panic("Cannot MakeBLSCommit() unless a blockhash has +2/3") + } + + // 1. Aggregate the signatures for the block. + sigsToAgg := make([][]byte, 0, len(voteSet.votes)) + for _, v := range voteSet.votes { + if v != nil && v.BlockID.IsComplete() { + // if block ID exists but doesn't match, exclude sig + if !v.BlockID.Equals(*voteSet.maj23) { + continue + } + sigsToAgg = append(sigsToAgg, v.Signature) + } + } + agSig1, err := bls12381.AggregateSignatures(sigsToAgg) + if err != nil { + panic(fmt.Errorf("BLS aggregation error: %w", err)) + } + + // 2. Aggregate the signatures for nil, if any. + sigsToAgg = make([][]byte, 0, len(voteSet.votes)) + for _, v := range voteSet.votes { + if v != nil && v.BlockID.IsNil() { + sigsToAgg = append(sigsToAgg, v.Signature) + } + } + var agSig2 []byte + if len(sigsToAgg) > 0 { + agSig2, err = bls12381.AggregateSignatures(sigsToAgg) + if err != nil { + panic(fmt.Errorf("BLS aggregation error: %w", err)) + } + } + + // For every validator, get the precommit without extensions + sigs := make([]ExtendedCommitSig, len(voteSet.votes)) + for i, v := range voteSet.votes { + sig := v.ExtendedCommitSig() + sig.Signature = []byte{} // clear the signature + + // if block ID exists but doesn't match, exclude sig + if sig.BlockIDFlag == BlockIDFlagCommit && !v.BlockID.Equals(*voteSet.maj23) { + sig = NewExtendedCommitSigAbsent() + } else { + switch sig.BlockIDFlag { + case BlockIDFlagCommit: + sig.BlockIDFlag = BlockIDFlagAggCommitAbsent + case BlockIDFlagNil: + sig.BlockIDFlag = BlockIDFlagAggNilAbsent + default: + } + } + + sigs[i] = sig + } + + // Add agSig1 to the first validator who voted for block. + for i, v := range voteSet.votes { + if v != nil && v.BlockID.IsComplete() { + // if block ID exists but doesn't match, exclude sig + if !v.BlockID.Equals(*voteSet.maj23) { + continue + } + sigs[i].Signature = agSig1 + sigs[i].BlockIDFlag = BlockIDFlagAggCommit + break + } + } + + // Add agSig2 to the first validator who voted for nil. + if agSig2 != nil { + for i, v := range voteSet.votes { + if v != nil && v.BlockID.IsNil() { + sigs[i].Signature = agSig2 + sigs[i].BlockIDFlag = BlockIDFlagAggNil + break + } + } + } + + return &ExtendedCommit{ + Height: voteSet.GetHeight(), + Round: voteSet.GetRound(), + BlockID: *voteSet.maj23, + ExtendedSignatures: sigs, + } +} + //-------------------------------------------------------------------------------- /* diff --git a/types/vote_set_test.go b/types/vote_set_test.go index 01ca884b8d6..6f0663a5283 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -10,7 +10,6 @@ import ( "github.com/cometbft/cometbft/crypto" cmtrand "github.com/cometbft/cometbft/libs/rand" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - cmttime "github.com/cometbft/cometbft/types/time" ) func TestVoteSet_AddVote_Good(t *testing.T) { @@ -33,7 +32,6 @@ func TestVoteSet_AddVote_Good(t *testing.T) { Height: height, Round: round, Type: cmtproto.PrevoteType, - Timestamp: cmttime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, } _, err = signAddVote(val0, vote, voteSet) @@ -54,7 +52,6 @@ func TestVoteSet_AddVote_Bad(t *testing.T) { ValidatorIndex: -1, Height: height, Round: round, - Timestamp: cmttime.Now(), Type: cmtproto.PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, } @@ -129,7 +126,6 @@ func Benchmark_2_3_Maj(b *testing.B) { Height: height, Round: round, Type: cmtproto.PrevoteType, - Timestamp: cmttime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, } blockPartsTotal := uint32(123) @@ -180,7 +176,6 @@ func TestVoteSet_2_3Majority(t *testing.T) { Height: height, Round: round, Type: cmtproto.PrevoteType, - Timestamp: cmttime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, } // 6 out of 10 voted for nil. @@ -233,7 +228,6 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) { ValidatorIndex: -1, // NOTE: must fill in Height: height, Round: round, - Timestamp: cmttime.Now(), Type: cmtproto.PrevoteType, BlockID: BlockID{blockHash, blockPartSetHeader}, } @@ -330,7 +324,6 @@ func TestVoteSet_Conflicts(t *testing.T) { ValidatorIndex: -1, Height: height, Round: round, - Timestamp: cmttime.Now(), Type: cmtproto.PrevoteType, BlockID: BlockID{nil, PartSetHeader{}}, } @@ -458,7 +451,6 @@ func TestVoteSet_MakeCommit(t *testing.T) { ValidatorIndex: -1, Height: height, Round: round, - Timestamp: cmttime.Now(), Type: cmtproto.PrecommitType, BlockID: BlockID{blockHash, blockPartSetHeader}, } @@ -476,7 +468,7 @@ func TestVoteSet_MakeCommit(t *testing.T) { } // MakeCommit should fail. - veHeightParam := ABCIParams{VoteExtensionsEnableHeight: height} + veHeightParam := FeatureParams{VoteExtensionsEnableHeight: height} assert.Panics(t, func() { voteSet.MakeExtendedCommit(veHeightParam) }, "Doesn't have +2/3 majority") // 7th voted for some other block. @@ -584,7 +576,6 @@ func TestVoteSet_VoteExtensionsEnabled(t *testing.T) { Height: height, Round: round, Type: cmtproto.PrecommitType, - Timestamp: cmttime.Now(), BlockID: BlockID{blockHash, blockPartSetHeader}, } v := vote.ToProto() diff --git a/types/vote_test.go b/types/vote_test.go index ce7afd1e577..4e270db8d12 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -3,7 +3,6 @@ package types import ( "fmt" "testing" - "time" "github.com/cosmos/gogoproto/proto" "github.com/stretchr/testify/assert" @@ -14,7 +13,6 @@ import ( "github.com/cometbft/cometbft/crypto/tmhash" "github.com/cometbft/cometbft/libs/protoio" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - cmttime "github.com/cometbft/cometbft/types/time" ) func examplePrevote() *Vote { @@ -29,16 +27,10 @@ func examplePrecommit() *Vote { } func exampleVote(t byte) *Vote { - stamp, err := time.Parse(TimeFormat, "2017-12-25T03:00:01.234Z") - if err != nil { - panic(err) - } - return &Vote{ - Type: cmtproto.SignedMsgType(t), - Height: 12345, - Round: 2, - Timestamp: stamp, + Type: cmtproto.SignedMsgType(t), + Height: 12345, + Round: 2, BlockID: BlockID{ Hash: tmhash.Sum([]byte("blockID_hash")), PartSetHeader: PartSetHeader{ @@ -71,65 +63,53 @@ func TestVoteSignBytesTestVectors(t *testing.T) { 0: { "", &Vote{}, // NOTE: Height and Round are skipped here. This case needs to be considered while parsing. - []byte{0xd, 0x2a, 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, + []byte{0x0}, }, // with proper (fixed size) height and round (PreCommit): 1: { "", &Vote{Height: 1, Round: 1, Type: cmtproto.PrecommitType}, []byte{ - 0x21, // length + 0x14, // length 0x8, // (field_number << 3) | wire_type - 0x2, // PrecommitType + 0x2, // cmtproto.PrecommitType 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round - 0x2a, // (field_number << 3) | wire_type - // remaining fields (timestamp): - 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, }, }, // with proper (fixed size) height and round (PreVote): 2: { "", &Vote{Height: 1, Round: 1, Type: cmtproto.PrevoteType}, []byte{ - 0x21, // length + 0x14, // length 0x8, // (field_number << 3) | wire_type - 0x1, // PrevoteType + 0x1, // cmtproto.PrevoteType 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round - 0x2a, // (field_number << 3) | wire_type - // remaining fields (timestamp): - 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, }, }, 3: { "", &Vote{Height: 1, Round: 1}, []byte{ - 0x1f, // length + 0x12, // length 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round - // remaining fields (timestamp): - 0x2a, - 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, }, }, // containing non-empty chain_id: 4: { "test_chain_id", &Vote{Height: 1, Round: 1}, []byte{ - 0x2e, // length + 0x21, // length 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round - // remaining fields: - 0x2a, // (field_number << 3) | wire_type - 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, // timestamp // (field_number << 3) | wire_type 0x32, 0xd, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, @@ -143,15 +123,12 @@ func TestVoteSignBytesTestVectors(t *testing.T) { Extension: []byte("extension"), }, []byte{ - 0x2e, // length + 0x21, // length 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round // remaining fields: - 0x2a, // (field_number << 3) | wire_type - 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1, // timestamp - // (field_number << 3) | wire_type 0x32, 0xd, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, // chainID }, // chainID @@ -164,7 +141,6 @@ func TestVoteSignBytesTestVectors(t *testing.T) { assert.Equal(t, tc.want, got, "test case #%v: got unexpected sign bytes for Vote.", i) } } - func TestVoteProposalNotEq(t *testing.T) { cv := CanonicalizeVote("", &cmtproto.Vote{Height: 1, Round: 1}) p := CanonicalizeProposal("", &cmtproto.Proposal{Height: 1, Round: 1}) @@ -250,7 +226,6 @@ func TestVoteExtension(t *testing.T) { ValidatorIndex: 0, Height: height, Round: round, - Timestamp: cmttime.Now(), Type: cmtproto.PrecommitType, BlockID: makeBlockIDRandom(), } @@ -313,13 +288,13 @@ func TestVoteVerify(t *testing.T) { func TestVoteString(t *testing.T) { str := examplePrecommit().String() - expected := `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PRECOMMIT(Precommit) 8B01023386C3 000000000000 657874656E73 @ 2017-12-25T03:00:01.234Z}` //nolint:lll //ignore line length for tests + expected := `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PRECOMMIT(Precommit) 8B01023386C3 000000000000 657874656E73 @}` //nolint:lll //ignore line length for tests if str != expected { t.Errorf("got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str) } str2 := examplePrevote().String() - expected = `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PREVOTE(Prevote) 8B01023386C3 000000000000 000000000000 @ 2017-12-25T03:00:01.234Z}` //nolint:lll //ignore line length for tests + expected = `Vote{56789:6AF1F4111082 12345/02/SIGNED_MSG_TYPE_PREVOTE(Prevote) 8B01023386C3 000000000000 000000000000 @}` //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) } diff --git a/version/version.go b/version/version.go index d7b8fc0ef4d..1c4d3756329 100644 --- a/version/version.go +++ b/version/version.go @@ -1,9 +1,14 @@ package version const ( - // TMCoreSemVer is the used as the fallback version of CometBFT - // when not using git describe. It is formatted with semantic versioning. - TMCoreSemVer = "0.39.0" + // TMCoreSemVer is used as the fallback version of CometBFT when not using + // git describe. It is formatted with semantic versioning. + // + // Berachain fork scheme: -bera. (e.g. 0.40.0-bera.1). + // The base tracks the upstream CometBFT release this fork is built on; the + // -bera.N counter is bumped per fork release and reset to 1 when the base + // moves to a newer upstream release. + TMCoreSemVer = "0.40.0-bera.1" // ABCISemVer is the semantic version of the ABCI protocol ABCISemVer = "2.0.0" ABCIVersion = ABCISemVer