mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-13 16:22:53 +00:00
Compare commits
70 Commits
wb/release
...
v0.33.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
174783220e | ||
|
|
4700103f86 | ||
|
|
86b89e4ae1 | ||
|
|
39c703ce4a | ||
|
|
e690338b75 | ||
|
|
be5baf9b9e | ||
|
|
903c389b76 | ||
|
|
544cf26ee9 | ||
|
|
41cb130a2d | ||
|
|
9140860462 | ||
|
|
6f28dd89fd | ||
|
|
a341fc8c2e | ||
|
|
84904ecef1 | ||
|
|
2e406e0d78 | ||
|
|
99b337c5d8 | ||
|
|
42db397a9f | ||
|
|
f32207db15 | ||
|
|
07113abd9e | ||
|
|
0aaaa34038 | ||
|
|
ed6fd81fbf | ||
|
|
4cc4a882f6 | ||
|
|
b0d431cc08 | ||
|
|
26f3211a8a | ||
|
|
059d43f626 | ||
|
|
1baf670c60 | ||
|
|
b59ecd68ab | ||
|
|
24193dd817 | ||
|
|
b1de0c24f6 | ||
|
|
6e375c2923 | ||
|
|
1a8e42d41e | ||
|
|
1b1fe4d7cc | ||
|
|
8026fd3cc9 | ||
|
|
606d0a89cc | ||
|
|
cefeab0fbb | ||
|
|
8ccfdb96d0 | ||
|
|
5e52a6ec55 | ||
|
|
e22acc0f45 | ||
|
|
d9c2f01bb5 | ||
|
|
64c7771966 | ||
|
|
e40602502f | ||
|
|
69b6e0eccb | ||
|
|
e8b4226b17 | ||
|
|
7772cdc4a4 | ||
|
|
cba24382ac | ||
|
|
e4e4bf6d6d | ||
|
|
62018d90df | ||
|
|
29035985c6 | ||
|
|
e1b3903a7f | ||
|
|
901ce19cc6 | ||
|
|
e080280d34 | ||
|
|
642dcd4a12 | ||
|
|
24c39aa3c3 | ||
|
|
9550e8a54d | ||
|
|
4ddf549e36 | ||
|
|
ea2d3f4889 | ||
|
|
8f4080e0cc | ||
|
|
70a4b5ea08 | ||
|
|
6018f2d1a5 | ||
|
|
31a0f48c4a | ||
|
|
13eff7f7ed | ||
|
|
62c1a1399c | ||
|
|
9821af83e7 | ||
|
|
0c0cb9f200 | ||
|
|
2544a5cf3a | ||
|
|
b215e07ecd | ||
|
|
ec9424c6ce | ||
|
|
30e5619ac0 | ||
|
|
e62b21c9bd | ||
|
|
42d8bc5124 | ||
|
|
af99236105 |
1550
.circleci/codecov.sh
1550
.circleci/codecov.sh
File diff suppressed because it is too large
Load Diff
@@ -1,421 +0,0 @@
|
||||
version: 2.1
|
||||
|
||||
executors:
|
||||
golang:
|
||||
docker:
|
||||
- image: tendermintdev/docker-tendermint-build
|
||||
working_directory: /go/src/github.com/tendermint/tendermint
|
||||
environment:
|
||||
GOBIN: /tmp/bin
|
||||
release:
|
||||
machine: true
|
||||
docs:
|
||||
docker:
|
||||
- image: tendermintdev/docker-website-deployment
|
||||
environment:
|
||||
AWS_REGION: us-east-1
|
||||
|
||||
commands:
|
||||
run_test:
|
||||
parameters:
|
||||
script_path:
|
||||
type: string
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/bin
|
||||
- restore_cache:
|
||||
name: "Restore source code cache"
|
||||
keys:
|
||||
- go-src-v1-{{ .Revision }}
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: "Restore go modules cache"
|
||||
keys:
|
||||
- go-mod-v1-{{ checksum "go.sum" }}
|
||||
- run:
|
||||
name: "Running test"
|
||||
command: |
|
||||
bash << parameters.script_path >>
|
||||
|
||||
jobs:
|
||||
setup_dependencies:
|
||||
executor: golang
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: "Restore go modules cache"
|
||||
keys:
|
||||
- go-mod-v1-{{ checksum "go.sum" }}
|
||||
- run:
|
||||
command: |
|
||||
mkdir -p /tmp/bin
|
||||
- run:
|
||||
name: Cache go modules
|
||||
command: make go-mod-cache
|
||||
- run:
|
||||
name: tools
|
||||
command: make tools
|
||||
- run:
|
||||
name: "Build binaries"
|
||||
command: make install install_abci
|
||||
- save_cache:
|
||||
name: "Save go modules cache"
|
||||
key: go-mod-v1-{{ checksum "go.sum" }}
|
||||
paths:
|
||||
- "/go/pkg/mod"
|
||||
- save_cache:
|
||||
name: "Save source code cache"
|
||||
key: go-src-v1-{{ .Revision }}
|
||||
paths:
|
||||
- ".git"
|
||||
- persist_to_workspace:
|
||||
root: "/tmp/bin"
|
||||
paths:
|
||||
- "."
|
||||
|
||||
test_persistence:
|
||||
executor: golang
|
||||
steps:
|
||||
- run_test:
|
||||
script_path: test/persist/test_failure_indices.sh
|
||||
|
||||
test_cover:
|
||||
executor: golang
|
||||
parallelism: 4
|
||||
steps:
|
||||
- restore_cache:
|
||||
name: "Restore source code cache"
|
||||
keys:
|
||||
- go-src-v1-{{ .Revision }}
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: "Restore go module cache"
|
||||
keys:
|
||||
- go-mod-v2-{{ checksum "go.sum" }}
|
||||
- run:
|
||||
name: "Run tests"
|
||||
command: |
|
||||
export VERSION="$(git describe --tags --long | sed 's/v\(.*\)/\1/')"
|
||||
export GO111MODULE=on
|
||||
mkdir -p /tmp/logs /tmp/workspace/profiles
|
||||
for pkg in $(go list github.com/tendermint/tendermint/... | circleci tests split --split-by=timings); do
|
||||
id=$(basename "$pkg")
|
||||
go test -v -timeout 5m -mod=readonly -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
|
||||
done
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
- "profiles/*"
|
||||
- store_artifacts:
|
||||
path: /tmp/logs
|
||||
|
||||
localnet:
|
||||
working_directory: /home/circleci/.go_workspace/src/github.com/tendermint/tendermint
|
||||
machine:
|
||||
image: circleci/classic:latest
|
||||
environment:
|
||||
GOBIN: /home/circleci/.go_workspace/bin
|
||||
GOPATH: /home/circleci/.go_workspace/
|
||||
GOOS: linux
|
||||
GOARCH: amd64
|
||||
parallelism: 1
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: run localnet and exit on failure
|
||||
command: |
|
||||
set -x
|
||||
docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang make build-linux
|
||||
make localnet-start &
|
||||
./scripts/localnet-blocks-test.sh 40 5 10 localhost
|
||||
|
||||
test_p2p:
|
||||
environment:
|
||||
GOBIN: /home/circleci/.go_workspace/bin
|
||||
GOPATH: /home/circleci/.go_workspace
|
||||
machine:
|
||||
image: circleci/classic:latest
|
||||
parameters:
|
||||
ipv:
|
||||
type: integer
|
||||
default: 4
|
||||
steps:
|
||||
- checkout
|
||||
- run: mkdir -p $GOPATH/src/github.com/tendermint
|
||||
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
|
||||
- run: bash test/p2p/circleci.sh << parameters.ipv >>
|
||||
- store_artifacts:
|
||||
path: /home/circleci/project/test/p2p/logs
|
||||
|
||||
upload_coverage:
|
||||
executor: golang
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
name: "Restore source code cache"
|
||||
keys:
|
||||
- go-src-v1-{{ .Revision }}
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: "Restore go module cache"
|
||||
keys:
|
||||
- go-mod-v2-{{ checksum "go.sum" }}
|
||||
- run:
|
||||
name: gather
|
||||
command: |
|
||||
echo "mode: atomic" > coverage.txt
|
||||
for prof in $(ls /tmp/workspace/profiles/); do
|
||||
tail -n +2 /tmp/workspace/profiles/"$prof" >> coverage.txt
|
||||
done
|
||||
- run:
|
||||
name: upload
|
||||
command: bash .circleci/codecov.sh -f coverage.txt
|
||||
|
||||
deploy_docs:
|
||||
executor: docs
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Build docs"
|
||||
command: make build-docs
|
||||
- run:
|
||||
name: "Sync to S3"
|
||||
command: make sync-docs
|
||||
|
||||
prepare_build:
|
||||
executor: golang
|
||||
steps:
|
||||
- restore_cache:
|
||||
name: "Restore source code cache"
|
||||
keys:
|
||||
- go-src-v1-{{ .Revision }}
|
||||
- checkout
|
||||
- run:
|
||||
name: Get next release number
|
||||
command: |
|
||||
export LAST_TAG="`git describe --tags --abbrev=0 --match "${CIRCLE_BRANCH}.*"`"
|
||||
echo "Last tag: ${LAST_TAG}"
|
||||
if [ -z "${LAST_TAG}" ]; then
|
||||
export LAST_TAG="${CIRCLE_BRANCH}"
|
||||
echo "Last tag not found. Possibly fresh branch or feature branch. Setting ${LAST_TAG} as tag."
|
||||
fi
|
||||
export NEXT_TAG="`python -u scripts/release_management/bump-semver.py --version "${LAST_TAG}"`"
|
||||
echo "Next tag: ${NEXT_TAG}"
|
||||
echo "export CIRCLE_TAG=\"${NEXT_TAG}\"" > release-version.source
|
||||
- run:
|
||||
name: Build dependencies
|
||||
command: make tools
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- "release-version.source"
|
||||
- save_cache:
|
||||
key: v2-release-deps-{{ checksum "go.sum" }}
|
||||
paths:
|
||||
- "/go/pkg/mod"
|
||||
|
||||
build_artifacts:
|
||||
executor: golang
|
||||
parallelism: 4
|
||||
steps:
|
||||
- restore_cache:
|
||||
name: "Restore source code cache"
|
||||
keys:
|
||||
- go-src-v1-{{ .Revision }}
|
||||
- checkout
|
||||
- restore_cache:
|
||||
name: "Restore release dependencies cache"
|
||||
keys:
|
||||
- v2-release-deps-{{ checksum "go.sum" }}
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
name: Build artifact
|
||||
command: |
|
||||
# Setting CIRCLE_TAG because we do not tag the release ourselves.
|
||||
source /tmp/workspace/release-version.source
|
||||
if test ${CIRCLE_NODE_INDEX:-0} == 0 ;then export GOOS=linux GOARCH=amd64 && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi
|
||||
if test ${CIRCLE_NODE_INDEX:-0} == 1 ;then export GOOS=darwin GOARCH=amd64 && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi
|
||||
if test ${CIRCLE_NODE_INDEX:-0} == 2 ;then export GOOS=windows GOARCH=amd64 && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi
|
||||
if test ${CIRCLE_NODE_INDEX:-0} == 3 ;then export GOOS=linux GOARCH=arm && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi
|
||||
- persist_to_workspace:
|
||||
root: build
|
||||
paths:
|
||||
- "*.zip"
|
||||
- "tendermint_linux_amd64"
|
||||
|
||||
release_artifacts:
|
||||
executor: golang
|
||||
steps:
|
||||
- restore_cache:
|
||||
name: "Restore source code cache"
|
||||
keys:
|
||||
- go-src-v1-{{ .Revision }}
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
name: "Deploy to GitHub"
|
||||
command: |
|
||||
# Setting CIRCLE_TAG because we do not tag the release ourselves.
|
||||
source /tmp/workspace/release-version.source
|
||||
echo "---"
|
||||
ls -la /tmp/workspace/*.zip
|
||||
echo "---"
|
||||
python -u scripts/release_management/sha-files.py
|
||||
echo "---"
|
||||
cat /tmp/workspace/SHA256SUMS
|
||||
echo "---"
|
||||
export RELEASE_ID="`python -u scripts/release_management/github-draft.py`"
|
||||
echo "Release ID: ${RELEASE_ID}"
|
||||
#Todo: Parallelize uploads
|
||||
export GOOS=linux GOARCH=amd64 && python -u scripts/release_management/github-upload.py --id "${RELEASE_ID}"
|
||||
export GOOS=darwin GOARCH=amd64 && python -u scripts/release_management/github-upload.py --id "${RELEASE_ID}"
|
||||
export GOOS=windows GOARCH=amd64 && python -u scripts/release_management/github-upload.py --id "${RELEASE_ID}"
|
||||
export GOOS=linux GOARCH=arm && python -u scripts/release_management/github-upload.py --id "${RELEASE_ID}"
|
||||
python -u scripts/release_management/github-upload.py --file "/tmp/workspace/SHA256SUMS" --id "${RELEASE_ID}"
|
||||
python -u scripts/release_management/github-publish.py --id "${RELEASE_ID}"
|
||||
|
||||
release_docker:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
name: "Deploy to Docker Hub"
|
||||
command: |
|
||||
# Setting CIRCLE_TAG because we do not tag the release ourselves.
|
||||
source /tmp/workspace/release-version.source
|
||||
cp /tmp/workspace/tendermint_linux_amd64 DOCKER/tendermint
|
||||
docker build --label="tendermint" --tag="tendermint/tendermint:${CIRCLE_TAG}" --tag="tendermint/tendermint:latest" "DOCKER"
|
||||
docker login -u "${DOCKERHUB_USER}" --password-stdin \<<< "${DOCKERHUB_PASS}"
|
||||
docker push "tendermint/tendermint"
|
||||
docker logout
|
||||
|
||||
reproducible_builds:
|
||||
executor: golang
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
docker_layer_caching: true
|
||||
- run:
|
||||
name: Build tendermint
|
||||
no_output_timeout: 20m
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ruby
|
||||
bash -x ./scripts/gitian-build.sh all
|
||||
for os in darwin linux windows; do
|
||||
cp gitian-build-${os}/result/tendermint-${os}-res.yml .
|
||||
cp gitian-build-${os}/build/out/tendermint-*.tar.gz .
|
||||
rm -rf gitian-build-${os}/
|
||||
done
|
||||
- store_artifacts:
|
||||
path: /go/src/github.com/tendermint/tendermint/tendermint-darwin-res.yml
|
||||
- store_artifacts:
|
||||
path: /go/src/github.com/tendermint/tendermint/tendermint-linux-res.yml
|
||||
- store_artifacts:
|
||||
path: /go/src/github.com/tendermint/tendermint/tendermint-windows-res.yml
|
||||
- store_artifacts:
|
||||
path: /go/src/github.com/tendermint/tendermint/tendermint-*.tar.gz
|
||||
|
||||
# # Test RPC implementation against the swagger documented specs
|
||||
# contract_tests:
|
||||
# working_directory: /home/circleci/.go_workspace/src/github.com/tendermint/tendermint
|
||||
# machine:
|
||||
# image: circleci/classic:latest
|
||||
# environment:
|
||||
# GOBIN: /home/circleci/.go_workspace/bin
|
||||
# GOPATH: /home/circleci/.go_workspace/
|
||||
# GOOS: linux
|
||||
# GOARCH: amd64
|
||||
# parallelism: 1
|
||||
# steps:
|
||||
# - checkout
|
||||
# - run:
|
||||
# name: Test RPC endpoints against swagger documentation
|
||||
# command: |
|
||||
# set -x
|
||||
# export PATH=~/.local/bin:$PATH
|
||||
# # install node and dredd
|
||||
# ./scripts/get_nodejs.sh
|
||||
# # build the binaries with a proper version of Go
|
||||
# docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang make build-linux build-contract-tests-hooks
|
||||
# # This docker image works with go 1.7, we can install here the hook handler that contract-tests is going to use
|
||||
# go get github.com/snikch/goodman/cmd/goodman
|
||||
# make contract-tests
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
test-suite:
|
||||
jobs:
|
||||
- deploy_docs:
|
||||
context: tendermint-docs
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
tags:
|
||||
only:
|
||||
- /^v.*/
|
||||
- deploy_docs:
|
||||
context: tendermint-docs-staging
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- docs-staging
|
||||
- setup_dependencies
|
||||
- test_cover:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- test_persistence:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- localnet:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- test_p2p
|
||||
- test_p2p:
|
||||
name: test_p2p_ipv6
|
||||
ipv: 6
|
||||
- upload_coverage:
|
||||
requires:
|
||||
- test_cover
|
||||
- reproducible_builds:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /v[0-9]+\.[0-9]+/
|
||||
# - contract_tests:
|
||||
# requires:
|
||||
# - setup_dependencies
|
||||
|
||||
release:
|
||||
jobs:
|
||||
- prepare_build
|
||||
- build_artifacts:
|
||||
requires:
|
||||
- prepare_build
|
||||
- release_artifacts:
|
||||
requires:
|
||||
- prepare_build
|
||||
- build_artifacts
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- /v[0-9]+\.[0-9]+/
|
||||
- release_docker:
|
||||
requires:
|
||||
- prepare_build
|
||||
- build_artifacts
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- /v[0-9]+\.[0-9]+/
|
||||
- master
|
||||
15
.github/CODEOWNERS
vendored
15
.github/CODEOWNERS
vendored
@@ -1,9 +1,10 @@
|
||||
# CODEOWNERS: https://help.github.com/articles/about-codeowners/
|
||||
|
||||
# Everything goes through Bucky, Anton, Tess. For now.
|
||||
* @ebuchman @melekes @tessr
|
||||
|
||||
# Precious documentation
|
||||
/docs/README.md @zramsay
|
||||
/docs/DOCS_README.md @zramsay
|
||||
/docs/.vuepress/ @zramsay
|
||||
# Everything goes through the following "global owners" by default.
|
||||
# Unless a later match takes precedence, these three will be
|
||||
# requested for review when someone opens a PR.
|
||||
# Note that the last matching pattern takes precedence, so
|
||||
# global owners are only requested if there isn't a more specific
|
||||
# codeowner specified below. For this reason, the global codeowners
|
||||
# are often repeated in package-level definitions.
|
||||
* @ebuchman @tendermint/tendermint-engineering
|
||||
|
||||
127
.github/workflows/coverage.yml
vendored
Normal file
127
.github/workflows/coverage.yml
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
name: Test Coverage
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- release/**
|
||||
|
||||
jobs:
|
||||
split-test-files:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Create a file with all the pkgs
|
||||
run: go list ./... > pkgs.txt
|
||||
- name: Split pkgs into 4 files
|
||||
run: split -d -n l/4 pkgs.txt pkgs.txt.part.
|
||||
# cache multiple
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "${{ github.sha }}-00"
|
||||
path: ./pkgs.txt.part.00
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "${{ github.sha }}-01"
|
||||
path: ./pkgs.txt.part.01
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "${{ github.sha }}-02"
|
||||
path: ./pkgs.txt.part.02
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "${{ github.sha }}-03"
|
||||
path: ./pkgs.txt.part.03
|
||||
|
||||
build-linux:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
goarch: ["arm", "amd64"]
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.15"
|
||||
- uses: actions/checkout@v2
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: install
|
||||
run: GOOS=linux GOARCH=${{ matrix.goarch }} make build
|
||||
if: "env.GIT_DIFF != ''"
|
||||
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
needs: split-test-files
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
part: ["00", "01", "02", "03"]
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.15"
|
||||
- uses: actions/checkout@v2
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: "${{ github.sha }}-${{ matrix.part }}"
|
||||
if: env.GIT_DIFF
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
- name: test & coverage report creation
|
||||
run: |
|
||||
cat pkgs.txt.part.${{ matrix.part }} | xargs go test -mod=readonly -timeout 8m -race -coverprofile=${{ matrix.part }}profile.out -covermode=atomic
|
||||
if: env.GIT_DIFF
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: "${{ github.sha }}-${{ matrix.part }}-coverage"
|
||||
path: ./${{ matrix.part }}profile.out
|
||||
|
||||
upload-coverage-report:
|
||||
runs-on: ubuntu-latest
|
||||
needs: tests
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: "${{ github.sha }}-00-coverage"
|
||||
if: env.GIT_DIFF
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: "${{ github.sha }}-01-coverage"
|
||||
if: env.GIT_DIFF
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: "${{ github.sha }}-02-coverage"
|
||||
if: env.GIT_DIFF
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: "${{ github.sha }}-03-coverage"
|
||||
if: env.GIT_DIFF
|
||||
- run: |
|
||||
cat ./*profile.out | grep -v "mode: atomic" >> coverage.txt
|
||||
if: env.GIT_DIFF
|
||||
- uses: codecov/codecov-action@v1.0.15
|
||||
with:
|
||||
file: ./coverage.txt
|
||||
if: env.GIT_DIFF
|
||||
59
.github/workflows/docker.yml
vendored
Normal file
59
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Build & Push
|
||||
# Build & Push rebuilds the tendermint docker image on every push to master and creation of tags
|
||||
# and pushes the image to https://hub.docker.com/r/interchainio/simapp/tags
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- "v[0-9]+.[0-9]+.[0-9]+" # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
- "v[0-9]+.[0-9]+.[0-9]+-rc*" # Push events to matching v*, i.e. v1.0-rc1, v20.15.10-rc5
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.15"
|
||||
- uses: actions/checkout@master
|
||||
- name: Prepare
|
||||
id: prep
|
||||
run: |
|
||||
DOCKER_IMAGE=tendermint/tendermint
|
||||
VERSION=noop
|
||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
elif [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g')
|
||||
if [ "${{ github.event.repository.default_branch }}" = "$VERSION" ]; then
|
||||
VERSION=latest
|
||||
fi
|
||||
fi
|
||||
TAGS="${DOCKER_IMAGE}:${VERSION}"
|
||||
if [[ $VERSION =~ ^v[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||
TAGS="$TAGS,${DOCKER_IMAGE}:${VERSION}"
|
||||
fi
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build Tendermint
|
||||
run: |
|
||||
make build-linux && cp build/tendermint DOCKER/tendermint
|
||||
|
||||
- name: Publish to Docker Hub
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./DOCKER
|
||||
file: ./DOCKER/Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
2
.github/workflows/linkchecker.yml
vendored
2
.github/workflows/linkchecker.yml
vendored
@@ -7,6 +7,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@0.6.0
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@1.0.8
|
||||
with:
|
||||
folder-path: "docs"
|
||||
|
||||
31
.github/workflows/lint.yaml
vendored
31
.github/workflows/lint.yaml
vendored
@@ -1,12 +1,29 @@
|
||||
name: Lint
|
||||
on: [pull_request]
|
||||
# Lint runs golangci-lint over the entire Tendermint repository
|
||||
# This workflow is run on every pull request and push to master
|
||||
# The `golangci` job will pass without running if no *.{go, mod, sum} files have been modified.
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
golangci-lint:
|
||||
golangci:
|
||||
name: golangci-lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 8
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: golangci-lint
|
||||
uses: reviewdog/action-golangci-lint@v1
|
||||
- uses: actions/checkout@v2
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
reporter: github-pr-review
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: golangci/golangci-lint-action@v2.3.0
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: v1.48
|
||||
args: --timeout 10m
|
||||
github-token: ${{ secrets.github_token }}
|
||||
if: env.GIT_DIFF
|
||||
|
||||
31
.github/workflows/linter.yml
vendored
Normal file
31
.github/workflows/linter.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Lint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "**.md"
|
||||
- "**.yml"
|
||||
- "**.yaml"
|
||||
pull_request:
|
||||
branches: [master]
|
||||
paths:
|
||||
- "**.md"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Super linter
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Lint Code Base
|
||||
uses: docker://github/super-linter:v3
|
||||
env:
|
||||
LINTER_RULES_PATH: .
|
||||
VALIDATE_ALL_CODEBASE: true
|
||||
DEFAULT_BRANCH: master
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VALIDATE_MD: true
|
||||
VALIDATE_OPAENAPI: true
|
||||
VALIDATE_YAML: true
|
||||
18
.github/workflows/proto.yml
vendored
18
.github/workflows/proto.yml
vendored
@@ -1,12 +1,22 @@
|
||||
name: Proto check
|
||||
on: [pull_request]
|
||||
name: Protobuf
|
||||
# Protobuf runs buf (https://buf.build/) lint and check-breakage
|
||||
# This workflow is only run when a .proto file has been modified
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.proto"
|
||||
jobs:
|
||||
proto-checks:
|
||||
proto-lint:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 4
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: docker-practice/actions-setup-docker@master
|
||||
- name: lint
|
||||
run: make proto-lint
|
||||
proto-breakage:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 4
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: check-breakage
|
||||
run: make proto-check-breaking-ci
|
||||
|
||||
29
.github/workflows/release.yml
vendored
Normal file
29
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: "Release"
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v[0-9]+.[0-9]+.[0-9]+" # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.15'
|
||||
|
||||
- run: echo https://github.com/tendermint/tendermint/blob/${GITHUB_REF#refs/tags/}/CHANGELOG.md#${GITHUB_REF#refs/tags/} > ../release_notes.md
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist --release-notes=../release_notes.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
18
.github/workflows/stale.yml
vendored
Normal file
18
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: "Close stale pull requests"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
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 if no further activity occurs. Thank you
|
||||
for your contributions."
|
||||
days-before-stale: 10
|
||||
days-before-close: 4
|
||||
exempt-pr-labels: "S:wip"
|
||||
120
.github/workflows/tests.yml
vendored
120
.github/workflows/tests.yml
vendored
@@ -1,4 +1,7 @@
|
||||
name: Tests
|
||||
# Tests runs different tests (test_abci_apps, test_abci_cli, test_apps)
|
||||
# This workflow runs on every push to master or release branch and every pull requests
|
||||
# All jobs will pass without running if no *{.go, .mod, .sum} files have been modified
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
@@ -14,69 +17,130 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'"
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/setup-go@v2-beta
|
||||
- name: Set GOBIN
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.15"
|
||||
- uses: actions/checkout@v2
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: install
|
||||
run: make install install_abci
|
||||
# Cache bin
|
||||
- uses: actions/cache@v1
|
||||
if: "env.GIT_DIFF != ''"
|
||||
- uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
if: env.GIT_DIFF
|
||||
# Cache binaries for use by other jobs
|
||||
- uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-tm-binary
|
||||
key: ${{ runner.os }}-${{ github.sha }}-tm-binary
|
||||
if: env.GIT_DIFF
|
||||
|
||||
test_abci_apps:
|
||||
runs-on: ubuntu-latest
|
||||
needs: Build
|
||||
needs: build
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/setup-go@v2-beta
|
||||
- name: Set GOBIN
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.15"
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v1
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
if: env.GIT_DIFF
|
||||
- uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-tm-binary
|
||||
key: ${{ runner.os }}-${{ github.sha }}-tm-binary
|
||||
if: env.GIT_DIFF
|
||||
- name: test_abci_apps
|
||||
run: abci/tests/test_app/test.sh
|
||||
shell: bash
|
||||
if: env.GIT_DIFF
|
||||
|
||||
test_abci_cli:
|
||||
runs-on: ubuntu-latest
|
||||
needs: Build
|
||||
needs: build
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/setup-go@v2-beta
|
||||
- name: Set GOBIN
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.15"
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v1
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
if: env.GIT_DIFF
|
||||
- uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-tm-binary
|
||||
key: ${{ runner.os }}-${{ github.sha }}-tm-binary
|
||||
if: env.GIT_DIFF
|
||||
- run: abci/tests/test_cli/test.sh
|
||||
shell: bash
|
||||
if: env.GIT_DIFF
|
||||
|
||||
test_apps:
|
||||
runs-on: ubuntu-latest
|
||||
needs: Build
|
||||
needs: build
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/setup-go@v2-beta
|
||||
- name: Set GOBIN
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.15"
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v1
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
if: env.GIT_DIFF
|
||||
- uses: actions/cache@v2.1.3
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-tm-binary
|
||||
key: ${{ runner.os }}-${{ github.sha }}-tm-binary
|
||||
if: env.GIT_DIFF
|
||||
- name: test_apps
|
||||
run: test/app/test.sh
|
||||
shell: bash
|
||||
if: env.GIT_DIFF
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -12,6 +12,8 @@ vendor
|
||||
.vagrant
|
||||
test/p2p/data/
|
||||
test/logs
|
||||
test/fuzz
|
||||
test/e2e
|
||||
coverage.txt
|
||||
docs/_build
|
||||
docs/dist
|
||||
|
||||
@@ -10,13 +10,13 @@ linters:
|
||||
# - gochecknoglobals
|
||||
# - gochecknoinits
|
||||
- goconst
|
||||
- gocritic
|
||||
# - gocritic
|
||||
# - gocyclo
|
||||
# - godox
|
||||
- gofmt
|
||||
- goimports
|
||||
- golint
|
||||
- gosec
|
||||
# - gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
@@ -43,6 +43,9 @@ linters:
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gosec
|
||||
- linters:
|
||||
- lll
|
||||
source: "https://"
|
||||
@@ -68,3 +71,5 @@ linters-settings:
|
||||
# disabled-checks:
|
||||
# - wrapperFunc
|
||||
# - commentFormatting # https://github.com/go-critic/go-critic/issues/755
|
||||
service:
|
||||
golangci-lint-version: 1.31.x
|
||||
|
||||
28
.goreleaser.yml
Normal file
28
.goreleaser.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
project_name: Tendermint
|
||||
|
||||
env:
|
||||
# Require use of Go modules.
|
||||
- GO111MODULE=on
|
||||
|
||||
builds:
|
||||
- id: "Tendermint"
|
||||
main: ./cmd/tendermint/main.go
|
||||
ldflags:
|
||||
- -s -w -X github.com/tendermint/tendermint/version.TMCoreSemVer={{ .Version }}
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
|
||||
checksum:
|
||||
name_template: SHA256SUMS-{{.Version}}.txt
|
||||
algorithm: sha256
|
||||
|
||||
release:
|
||||
name_template: "{{.Version}} (WARNING: BETA SOFTWARE)"
|
||||
176
CHANGELOG.md
176
CHANGELOG.md
@@ -1,5 +1,162 @@
|
||||
# Changelog
|
||||
|
||||
## v0.33.9
|
||||
|
||||
*November 10, 2020*
|
||||
|
||||
This release reduces the pings frequency for remote private validators and the
|
||||
number of `GetPubKey` requests. Fixes
|
||||
[\#5550](https://github.com/tendermint/tendermint/issues/5550).
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@JoeKash, @joe-bowman
|
||||
|
||||
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [consensus] [\#5143](https://github.com/tendermint/tendermint/pull/5143) Only call `privValidator.GetPubKey` once per block (@melekes)
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [consensus] [\#4895](https://github.com/tendermint/tendermint/pull/4895) Cache the address of the validator to reduce querying a remote KMS (@joe-bowman)
|
||||
- [privval] [\#5638](https://github.com/tendermint/tendermint/pull/5638) Increase read/write timeout to 5s and calculate ping interval based on it (@JoeKash)
|
||||
|
||||
## v0.33.8
|
||||
|
||||
*August 11, 2020*
|
||||
|
||||
## Go security update
|
||||
|
||||
Go reported a security vulnerability that affected the `encoding/binary` package. The most recent binary for tendermint is using 1.14.6, for this
|
||||
reason the Tendermint engineering team has opted to conduct a release to aid users in using the correct version of Go. Read more about the security issue [here](https://github.com/golang/go/issues/40618).
|
||||
|
||||
## v0.33.7
|
||||
|
||||
*August 4, 2020*
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [go] Build release binary using Go 1.14.4, to avoid halt caused by Go 1.14.1 (https://github.com/golang/go/issues/38223)
|
||||
- [privval] [\#5140](https://github.com/tendermint/tendermint/pull/5140) `RemoteSignerError` from remote signers are no longer retried (@melekes)
|
||||
|
||||
## v0.33.6
|
||||
|
||||
*July 2, 2020*
|
||||
|
||||
This security release fixes:
|
||||
|
||||
### Denial of service
|
||||
|
||||
Tendermint 0.33.0 and above allow block proposers to include signatures for the
|
||||
wrong block. This may happen naturally if you start a network, have it run for
|
||||
some time and restart it **without changing the chainID**. (It is a
|
||||
[misconfiguration](https://docs.tendermint.com/v0.33/tendermint-core/using-tendermint.html)
|
||||
to reuse chainIDs.) Correct block proposers will accidentally include signatures
|
||||
for the wrong block if they see these signatures, and then commits won't validate,
|
||||
making all proposed blocks invalid. A malicious validator (even with a minimal
|
||||
amount of stake) can use this vulnerability to completely halt the network.
|
||||
|
||||
Tendermint 0.33.6 checks all the signatures are for the block with +2/3
|
||||
majority before creating a commit.
|
||||
|
||||
### False Witness
|
||||
|
||||
Tendermint 0.33.1 and above are no longer fully verifying commit signatures
|
||||
during block execution - they stop after +2/3. This means proposers can propose
|
||||
blocks that contain valid +2/3 signatures and then the rest of the signatures
|
||||
can be whatever they want. They can claim that all the other validators signed
|
||||
just by including a CommitSig with arbitrary signature data. While this doesn't
|
||||
seem to impact safety of Tendermint per se, it means that Commits may contain a
|
||||
lot of invalid data.
|
||||
|
||||
_This was already true of blocks, since they could include invalid txs filled
|
||||
with garbage, but in that case the application knew that they are invalid and
|
||||
could punish the proposer. But since applications didn't--and don't--
|
||||
verify commit signatures directly (they trust Tendermint to do that),
|
||||
they won't be able to detect it._
|
||||
|
||||
This can impact incentivization logic in the application that depends on the
|
||||
LastCommitInfo sent in BeginBlock, which includes which validators signed. For
|
||||
instance, Gaia incentivizes proposers with a bonus for including more than +2/3
|
||||
of the signatures. But a proposer can now claim that bonus just by including
|
||||
arbitrary data for the final -1/3 of validators without actually waiting for
|
||||
their signatures. There may be other tricks that can be played because of this.
|
||||
|
||||
Tendermint 0.33.6 verifies all the signatures during block execution.
|
||||
|
||||
_Please note that the light client does not check nil votes and exits as soon
|
||||
as 2/3+ of the signatures are checked._
|
||||
|
||||
**All clients are recommended to upgrade.**
|
||||
|
||||
Special thanks to @njmurarka at Bluzelle Networks for reporting this.
|
||||
|
||||
Friendly reminder, we have a [bug bounty
|
||||
program](https://hackerone.com/tendermint).
|
||||
|
||||
### SECURITY:
|
||||
|
||||
- [consensus] Do not allow signatures for a wrong block in commits (@ebuchman)
|
||||
- [consensus] Verify all the signatures during block execution (@melekes)
|
||||
|
||||
## v.0.33.5
|
||||
|
||||
Special thanks to our external contributor on this release: @tau3
|
||||
|
||||
Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
- Go API
|
||||
|
||||
- [privval] [\#4744](https://github.com/tendermint/tendermint/pull/4744) Remove deprecated `OldFilePV` (@melekes)
|
||||
- [mempool] [\#4759](https://github.com/tendermint/tendermint/pull/4759) Modify `Mempool#InitWAL` to return an error (@melekes)
|
||||
- [node] [\#4832](https://github.com/tendermint/tendermint/pull/4832) `ConfigureRPC` returns an error (@melekes)
|
||||
- [rpc] [\#4836](https://github.com/tendermint/tendermint/pull/4836) Overhaul `lib` folder (@melekes)
|
||||
Move lib/ folder to jsonrpc/.
|
||||
Rename:
|
||||
rpc package -> jsonrpc package
|
||||
rpcclient package -> client package
|
||||
rpcserver package -> server package
|
||||
JSONRPCClient to Client
|
||||
JSONRPCRequestBatch to RequestBatch
|
||||
JSONRPCCaller to Caller
|
||||
StartHTTPServer to Serve
|
||||
StartHTTPAndTLSServer to ServeTLS
|
||||
NewURIClient to NewURI
|
||||
NewJSONRPCClient to New
|
||||
NewJSONRPCClientWithHTTPClient to NewWithHTTPClient
|
||||
NewWSClient to NewWS
|
||||
Unexpose ResponseWriterWrapper
|
||||
Remove unused http_params.go
|
||||
|
||||
|
||||
### FEATURES:
|
||||
|
||||
- [pex] [\#4439](https://github.com/tendermint/tendermint/pull/4439) Use highwayhash for pex buckets (@tau3)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [abci/server] [\#4719](https://github.com/tendermint/tendermint/pull/4719) Print panic & stack trace to STDERR if logger is not set (@melekes)
|
||||
- [types] [\#4638](https://github.com/tendermint/tendermint/pull/4638) Implement `Header#ValidateBasic` (@alexanderbez)
|
||||
- [buildsystem] [\#4378](https://github.com/tendermint/tendermint/pull/4738) Replace build_c and install_c with TENDERMINT_BUILD_OPTIONS parsing. The following options are available:
|
||||
- nostrip: don't strip debugging symbols nor DWARF tables.
|
||||
- cleveldb: use cleveldb as db backend instead of goleveldb.
|
||||
- race: pass -race to go build and enable data race detection.
|
||||
- [mempool] [\#4759](https://github.com/tendermint/tendermint/pull/4759) Allow ReapX and CheckTx functions to run in parallel (@melekes)
|
||||
- [rpc/core] [\#4844](https://github.com/tendermint/tendermint/pull/4844) Do not lock consensus state in `/validators`, `/consensus_params` and `/status` (@melekes)
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [blockchain/v2] [\#4761](https://github.com/tendermint/tendermint/pull/4761) Fix excessive CPU usage caused by spinning on closed channels (@erikgrinaker)
|
||||
- [blockchain/v2] Respect `fast_sync` option (@erikgrinaker)
|
||||
- [light] [\#4741](https://github.com/tendermint/tendermint/pull/4741) Correctly return `ErrSignedHeaderNotFound` and `ErrValidatorSetNotFound` on corresponding RPC errors (@erikgrinaker)
|
||||
- [rpc] [\#4805](https://github.com/tendermint/tendermint/issues/4805) Attempt to handle panics during panic recovery (@erikgrinaker)
|
||||
- [types] [\#4764](https://github.com/tendermint/tendermint/pull/4764) Return an error if voting power overflows in `VerifyCommitTrusting` (@melekes)
|
||||
- [privval] [\#4812](https://github.com/tendermint/tendermint/pull/4812) Retry `GetPubKey/SignVote/SignProposal` a few times before returning an error (@melekes)
|
||||
- [p2p] [\#4847](https://github.com/tendermint/tendermint/pull/4847) Return masked IP (not the actual IP) in addrbook#groupKey (@melekes)
|
||||
|
||||
## v0.33.4
|
||||
|
||||
- Nodes are no longer guaranteed to contain all blocks up to the latest height. The ABCI app can now control which blocks to retain through the ABCI field `ResponseCommit.retain_height`, all blocks and associated data below this height will be removed.
|
||||
@@ -42,7 +199,6 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
|
||||
### BUG FIXES:
|
||||
|
||||
- [rpc] [\#4568](https://github.com/tendermint/tendermint/issues/4568) Fix panic when `Subscribe` is called, but HTTP client is not running. `Subscribe`, `Unsubscribe(All)` methods return an error now (@melekes).
|
||||
|
||||
|
||||
## v0.33.3
|
||||
|
||||
@@ -206,7 +362,7 @@ and a validator address plus a timestamp. Note we may remove the validator
|
||||
address & timestamp fields in the future (see ADR-25).
|
||||
|
||||
`lite2` package has been added to solve `lite` issues and introduce weak
|
||||
subjectivity interface. Refer to the [spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md) for complete details.
|
||||
subjectivity interface. Refer to the [spec](https://github.com/tendermint/tendermint/blob/main/spec/consensus/light-client.md) for complete details.
|
||||
`lite` package is now deprecated and will be removed in v0.34 release.
|
||||
|
||||
### BREAKING CHANGES:
|
||||
@@ -339,6 +495,12 @@ subjectivity interface. Refer to the [spec](https://github.com/tendermint/spec/b
|
||||
- [consensus/types] [\#4243](https://github.com/tendermint/tendermint/issues/4243) fix BenchmarkRoundStateDeepCopy panics (@cuonglm)
|
||||
- [rpc] [\#4256](https://github.com/tendermint/tendermint/issues/4256) Pass `outCapacity` to `eventBus#Subscribe` when subscribing using a local client
|
||||
|
||||
## v0.32.11
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [privval] [\#4275](https://github.com/tendermint/tendermint/issues/4275) Fix consensus failure when remote signer drops (@melekes)
|
||||
|
||||
## v0.32.10
|
||||
|
||||
*April 6, 2020*
|
||||
@@ -421,7 +583,7 @@ program](https://hackerone.com/tendermint).
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [rpc/lib] [\#4051](https://github.com/tendermint/tendermint/pull/4131) Fix RPC client, which was previously resolving https protocol to http (@yenkhoon)
|
||||
- [rpc/lib] [\#4131](https://github.com/tendermint/tendermint/pull/4131) Fix RPC client, which was previously resolving https protocol to http (@yenkhoon)
|
||||
- [cs] [\#4069](https://github.com/tendermint/tendermint/issues/4069) Don't panic when block meta is not found in store (@gregzaitsev)
|
||||
|
||||
## v0.32.8
|
||||
@@ -564,7 +726,7 @@ program](https://hackerone.com/tendermint).
|
||||
*August 28, 2019*
|
||||
|
||||
@climber73 wrote the [Writing a Tendermint Core application in Java
|
||||
(gRPC)](https://github.com/tendermint/tendermint/blob/master/docs/guides/java.md)
|
||||
(gRPC)](https://github.com/tendermint/tendermint/blob/main/docs/guides/java.md)
|
||||
guide.
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@@ -603,7 +765,7 @@ program](https://hackerone.com/tendermint).
|
||||
|
||||
### FEATURES:
|
||||
|
||||
- [blockchain] [\#3561](https://github.com/tendermint/tendermint/issues/3561) Add early version of the new blockchain reactor, which is supposed to be more modular and testable compared to the old version. To try it, you'll have to change `version` in the config file, [here](https://github.com/tendermint/tendermint/blob/master/config/toml.go#L303) NOTE: It's not ready for a production yet. For further information, see [ADR-40](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-040-blockchain-reactor-refactor.md) & [ADR-43](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-043-blockchain-riri-org.md)
|
||||
- [blockchain] [\#3561](https://github.com/tendermint/tendermint/issues/3561) Add early version of the new blockchain reactor, which is supposed to be more modular and testable compared to the old version. To try it, you'll have to change `version` in the config file, [here](https://github.com/tendermint/tendermint/blob/main/config/toml.go#L303) NOTE: It's not ready for a production yet. For further information, see [ADR-40](https://github.com/tendermint/tendermint/blob/main/docs/architecture/adr-040-blockchain-reactor-refactor.md) & [ADR-43](https://github.com/tendermint/tendermint/blob/main/docs/architecture/adr-043-blockchain-riri-org.md)
|
||||
- [mempool] [\#3826](https://github.com/tendermint/tendermint/issues/3826) Make `max_msg_bytes` configurable(@bluele)
|
||||
- [node] [\#3846](https://github.com/tendermint/tendermint/pull/3846) Allow replacing existing p2p.Reactor(s) using [`CustomReactors`
|
||||
option](https://godoc.org/github.com/tendermint/tendermint/node#CustomReactors).
|
||||
@@ -938,7 +1100,7 @@ Special thanks to external contributors on this release:
|
||||
- [libs/db] [\#3611](https://github.com/tendermint/tendermint/issues/3611) Conditional compilation
|
||||
* Use `cleveldb` tag instead of `gcc` to compile Tendermint with CLevelDB or
|
||||
use `make build_c` / `make install_c` (full instructions can be found at
|
||||
https://docs.tendermint.com/master/introduction/install.html#compile-with-cleveldb-support)
|
||||
<https://docs.tendermint.com>)
|
||||
* Use `boltdb` tag to compile Tendermint with bolt db
|
||||
- [node] [\#3362](https://github.com/tendermint/tendermint/issues/3362) Return an error if `persistent_peers` list is invalid (except
|
||||
when IP lookup fails)
|
||||
@@ -2854,7 +3016,7 @@ Also includes the Grand Repo-Merge of 2017.
|
||||
BREAKING CHANGES:
|
||||
|
||||
- Config and Flags:
|
||||
- The `config` map is replaced with a [`Config` struct](https://github.com/tendermint/tendermint/blob/master/config/config.go#L11),
|
||||
- The `config` map is replaced with a [`Config` struct](https://github.com/tendermint/tendermint/blob/main/config/config.go#L11),
|
||||
containing substructs: `BaseConfig`, `P2PConfig`, `MempoolConfig`, `ConsensusConfig`, `RPCConfig`
|
||||
- This affects the following flags:
|
||||
- `--seeds` is now `--p2p.seeds`
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
## v0.33.5
|
||||
## v0.33.10
|
||||
|
||||
|
||||
\*\*
|
||||
|
||||
@@ -16,10 +17,15 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
|
||||
|
||||
- Go API
|
||||
|
||||
### FEATURES:
|
||||
- Blockchain Protocol
|
||||
|
||||
### FEATURES:
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [blockchain/v1] [\#5701](https://github.com/tendermint/tendermint/pull/5701) Handle peers without blocks (@melekes)
|
||||
- [blockchain/v1] \#5711 Fix deadlock (@melekes)
|
||||
- [proxy] \#5078 Fix a bug, where TM does not exit when ABCI app crashes (@melekes)
|
||||
- [evidence] \#6068 Terminate broadcastEvidenceRoutine when peer is stopped (@melekes)
|
||||
|
||||
@@ -26,7 +26,7 @@ will indicate their support with a heartfelt emoji.
|
||||
|
||||
If the issue would benefit from thorough discussion, maintainers may
|
||||
request that you create a [Request For
|
||||
Comment](https://github.com/tendermint/spec/tree/master/rfc). Discussion
|
||||
Comment](https://github.com/tendermint/tendermint/tree/main/docs/rfc). Discussion
|
||||
at the RFC stage will build collective understanding of the dimensions
|
||||
of the problems and help structure conversations around trade-offs.
|
||||
|
||||
|
||||
@@ -24,5 +24,5 @@ ENV GOPATH=/go/src
|
||||
RUN mkdir -p /tendermint
|
||||
WORKDIR /tendermint
|
||||
|
||||
CMD ["/usr/bin/make", "build_c"]
|
||||
CMD ["/usr/bin/make", "build", "TENDERMINT_BUILD_OPTIONS=cleveldb"]
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ DockerHub tags for official releases are [here](https://hub.docker.com/r/tenderm
|
||||
|
||||
Official releases can be found [here](https://github.com/tendermint/tendermint/releases).
|
||||
|
||||
The Dockerfile for tendermint is not expected to change in the near future. The master file used for all builds can be found [here](https://raw.githubusercontent.com/tendermint/tendermint/master/DOCKER/Dockerfile).
|
||||
The Dockerfile for tendermint is not expected to change in the near future. The main file used for all builds can be found [here](https://raw.githubusercontent.com/tendermint/tendermint/main/DOCKER/Dockerfile).
|
||||
|
||||
Respective versioned files can be found https://raw.githubusercontent.com/tendermint/tendermint/vX.XX.XX/DOCKER/Dockerfile (replace the Xs with the version number).
|
||||
|
||||
@@ -20,9 +20,9 @@ Respective versioned files can be found https://raw.githubusercontent.com/tender
|
||||
|
||||
Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine, written in any programming language, and securely replicates it on many machines.
|
||||
|
||||
For more background, see the [the docs](https://docs.tendermint.com/master/introduction/#quick-start).
|
||||
For more background, see the [the docs](https://docs.tendermint.com/v0.33/introduction/#quick-start).
|
||||
|
||||
To get started developing applications, see the [application developers guide](https://docs.tendermint.com/master/introduction/quick-start.html).
|
||||
To get started developing applications, see the [application developers guide](https://docs.tendermint.com/v0.33/introduction/quick-start.html).
|
||||
|
||||
## How to use this image
|
||||
|
||||
@@ -37,7 +37,7 @@ docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app
|
||||
|
||||
## Local cluster
|
||||
|
||||
To run a 4-node network, see the `Makefile` in the root of [the repo](https://github.com/tendermint/tendermint/blob/master/Makefile) and run:
|
||||
To run a 4-node network, see the `Makefile` in the root of [the repo](https://github.com/tendermint/tendermint/blob/main/Makefile) and run:
|
||||
|
||||
```
|
||||
make build-linux
|
||||
@@ -49,8 +49,8 @@ Note that this will build and use a different image than the ones provided here.
|
||||
|
||||
## License
|
||||
|
||||
- Tendermint's license is [Apache 2.0](https://github.com/tendermint/tendermint/blob/master/LICENSE).
|
||||
- Tendermint's license is [Apache 2.0](https://github.com/tendermint/tendermint/blob/main/LICENSE).
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are most welcome! See the [contributing file](https://github.com/tendermint/tendermint/blob/master/CONTRIBUTING.md) for more information.
|
||||
Contributions are most welcome! See the [contributing file](https://github.com/tendermint/tendermint/blob/main/CONTRIBUTING.md) for more information.
|
||||
|
||||
68
Makefile
68
Makefile
@@ -1,11 +1,33 @@
|
||||
PACKAGES=$(shell go list ./...)
|
||||
OUTPUT?=build/tendermint
|
||||
|
||||
BUILD_TAGS?='tendermint'
|
||||
LD_FLAGS = -X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD` -s -w
|
||||
BUILD_TAGS?=tendermint
|
||||
LD_FLAGS = -X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`
|
||||
BUILD_FLAGS = -mod=readonly -ldflags "$(LD_FLAGS)"
|
||||
HTTPS_GIT := https://github.com/tendermint/tendermint.git
|
||||
DOCKER_BUF := docker run -v $(shell pwd):/workspace --workdir /workspace bufbuild/buf
|
||||
CGO_ENABLED ?= 0
|
||||
|
||||
# handle nostrip
|
||||
ifeq (,$(findstring nostrip,$(TENDERMINT_BUILD_OPTIONS)))
|
||||
BUILD_FLAGS += -trimpath
|
||||
LD_FLAGS += -s -w
|
||||
endif
|
||||
|
||||
# handle race
|
||||
ifeq (race,$(findstring race,$(TENDERMINT_BUILD_OPTIONS)))
|
||||
CGO_ENABLED=1
|
||||
BUILD_FLAGS += -race
|
||||
endif
|
||||
|
||||
# handle cleveldb
|
||||
ifeq (cleveldb,$(findstring cleveldb,$(TENDERMINT_BUILD_OPTIONS)))
|
||||
CGO_ENABLED=1
|
||||
BUILD_TAGS += cleveldb
|
||||
endif
|
||||
|
||||
# allow users to pass additional flags via the conventional LDFLAGS variable
|
||||
LD_FLAGS += $(LDFLAGS)
|
||||
|
||||
all: check build test install
|
||||
.PHONY: all
|
||||
@@ -19,25 +41,13 @@ include tests.mk
|
||||
###############################################################################
|
||||
|
||||
build:
|
||||
CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint/
|
||||
CGO_ENABLED=$(CGO_ENABLED) go build $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o $(OUTPUT) ./cmd/tendermint/
|
||||
.PHONY: build
|
||||
|
||||
build_c:
|
||||
CGO_ENABLED=1 go build $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" -o $(OUTPUT) ./cmd/tendermint/
|
||||
.PHONY: build_c
|
||||
|
||||
build_race:
|
||||
CGO_ENABLED=1 go build -race $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint
|
||||
.PHONY: build_race
|
||||
|
||||
install:
|
||||
CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint
|
||||
CGO_ENABLED=$(CGO_ENABLED) go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint
|
||||
.PHONY: install
|
||||
|
||||
install_c:
|
||||
CGO_ENABLED=1 go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" ./cmd/tendermint
|
||||
.PHONY: install_c
|
||||
|
||||
###############################################################################
|
||||
### Protobuf ###
|
||||
###############################################################################
|
||||
@@ -125,15 +135,15 @@ gen_certs: clean_certs
|
||||
certstrap init --common-name "tendermint.com" --passphrase ""
|
||||
certstrap request-cert --common-name "server" -ip "127.0.0.1" --passphrase ""
|
||||
certstrap sign "server" --CA "tendermint.com" --passphrase ""
|
||||
mv out/server.crt rpc/lib/server/test.crt
|
||||
mv out/server.key rpc/lib/server/test.key
|
||||
mv out/server.crt rpc/jsonrpc/server/test.crt
|
||||
mv out/server.key rpc/jsonrpc/server/test.key
|
||||
rm -rf out
|
||||
.PHONY: gen_certs
|
||||
|
||||
# deletes generated certificates
|
||||
clean_certs:
|
||||
rm -f rpc/lib/server/test.crt
|
||||
rm -f rpc/lib/server/test.key
|
||||
rm -f rpc/jsonrpc/server/test.crt
|
||||
rm -f rpc/jsonrpc/server/test.key
|
||||
.PHONY: clean_certs
|
||||
|
||||
###############################################################################
|
||||
@@ -157,12 +167,12 @@ DESTINATION = ./index.html.md
|
||||
###############################################################################
|
||||
|
||||
build-docs:
|
||||
cd docs && \
|
||||
while read p; do \
|
||||
(git checkout $${p} && npm install && VUEPRESS_BASE="/$${p}/" npm run build) ; \
|
||||
mkdir -p ~/output/$${p} ; \
|
||||
cp -r .vuepress/dist/* ~/output/$${p}/ ; \
|
||||
cp ~/output/$${p}/index.html ~/output ; \
|
||||
@cd docs && \
|
||||
while read -r branch path_prefix; do \
|
||||
(git checkout $${branch} && npm ci && VUEPRESS_BASE="/$${path_prefix}/" npm run build) ; \
|
||||
mkdir -p ~/output/$${path_prefix} ; \
|
||||
cp -r .vuepress/dist/* ~/output/$${path_prefix}/ ; \
|
||||
cp ~/output/$${path_prefix}/index.html ~/output ; \
|
||||
done < versions ;
|
||||
.PHONY: build-docs
|
||||
|
||||
@@ -197,9 +207,9 @@ build-docker-localnode:
|
||||
@cd networks/local && make
|
||||
.PHONY: build-docker-localnode
|
||||
|
||||
# Runs `make build_c` from within an Amazon Linux (v2)-based Docker build
|
||||
# container in order to build an Amazon Linux-compatible binary. Produces a
|
||||
# compatible binary at ./build/tendermint
|
||||
# Runs `make build TENDERMINT_BUILD_OPTIONS=cleveldb` from within an Amazon
|
||||
# Linux (v2)-based Docker build container in order to build an Amazon
|
||||
# Linux-compatible binary. Produces a compatible binary at ./build/tendermint
|
||||
build_c-amazonlinux:
|
||||
$(MAKE) -C ./DOCKER build_amazonlinux_buildimage
|
||||
docker run --rm -it -v `pwd`:/tendermint tendermint/tendermint:build_c-amazonlinux
|
||||
|
||||
15
README.md
15
README.md
@@ -10,7 +10,7 @@ Or [Blockchain](<https://en.wikipedia.org/wiki/Blockchain_(database)>), for shor
|
||||
[](https://godoc.org/github.com/tendermint/tendermint)
|
||||
[](https://github.com/moovweb/gvm)
|
||||
[](https://discord.gg/AzefAFd)
|
||||
[](https://github.com/tendermint/tendermint/blob/master/LICENSE)
|
||||
[](https://github.com/tendermint/tendermint/blob/main/LICENSE)
|
||||
[](https://github.com/tendermint/tendermint)
|
||||
|
||||
| Branch | Tests | Coverage |
|
||||
@@ -20,7 +20,8 @@ Or [Blockchain](<https://en.wikipedia.org/wiki/Blockchain_(database)>), for shor
|
||||
Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine - written in any programming language -
|
||||
and securely replicates it on many machines.
|
||||
|
||||
For protocol details, see [the specification](https://github.com/tendermint/spec).
|
||||
For protocol details, see [the
|
||||
specification](https://github.com/tendermint/tendermint/tree/main/spec).
|
||||
|
||||
For detailed analysis of the consensus protocol, including safety and liveness proofs,
|
||||
see our recent paper, "[The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938)".
|
||||
@@ -53,7 +54,7 @@ For examples of the kinds of bugs we're looking for, see [SECURITY.md](SECURITY.
|
||||
|
||||
## Documentation
|
||||
|
||||
Complete documentation can be found on the [website](https://docs.tendermint.com/master/).
|
||||
Complete documentation can be found on the [website](https://docs.tendermint.com/v0.33/).
|
||||
|
||||
### Install
|
||||
|
||||
@@ -76,8 +77,8 @@ and the [style guide](STYLE_GUIDE.md).
|
||||
To get more active, Join the wider community at [Discord](https://discord.gg/AzefAFd) or jump onto the [Forum](https://forum.cosmos.network/).
|
||||
|
||||
Learn more by reading the code and the
|
||||
[specifications](https://github.com/tendermint/spec) or watch the [Developer Sessions](/docs/DEV_SESSIONS.md) and read up on the
|
||||
[Architectural Decision Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture).
|
||||
[specifications](https://github.com/tendermint/tendermint/tree/main/spec/) or watch the [Developer Sessions](/docs/DEV_SESSIONS.md) and read up on the
|
||||
[Architectural Decision Records](https://github.com/tendermint/tendermint/tree/main/docs/architecture).
|
||||
|
||||
## Versioning
|
||||
|
||||
@@ -135,10 +136,10 @@ in [UPGRADING.md](./UPGRADING.md).
|
||||
### Tendermint Core
|
||||
|
||||
For details about the blockchain data structures and the p2p protocols, see the
|
||||
[Tendermint specification](https://docs.tendermint.com/master/spec/).
|
||||
[Tendermint specification](./spec/).
|
||||
|
||||
For details on using the software, see the [documentation](/docs/) which is also
|
||||
hosted at: https://docs.tendermint.com/master/
|
||||
hosted at: https://docs.tendermint.com/v0.33/
|
||||
|
||||
### Tools
|
||||
|
||||
|
||||
18
UPGRADING.md
18
UPGRADING.md
@@ -7,6 +7,7 @@ a newer version of Tendermint Core.
|
||||
|
||||
<Overview>
|
||||
|
||||
|
||||
## v0.33.4
|
||||
|
||||
### Go API
|
||||
@@ -17,6 +18,15 @@ a newer version of Tendermint Core.
|
||||
|
||||
When upgrading to version 0.33.4 you will have to fetch the `third_party` directory along with the updated proto files.
|
||||
|
||||
## v0.33.1
|
||||
|
||||
This release is compatible with the previous version. The only change that is required is if you are fetching the protobuf files for application use.
|
||||
|
||||
### Protobuf Changes
|
||||
|
||||
When upgrading to version 0.33.1 you will have to fetch the `third_party` directory along with the updated proto files.
|
||||
|
||||
|
||||
## v0.33.0
|
||||
|
||||
This release is not compatible with previous blockchains due to commit becoming signatures only and fields in the header have been removed.
|
||||
@@ -44,7 +54,7 @@ You will need to generate a new config if you have used a prior version of tende
|
||||
### RPC Changes
|
||||
|
||||
- `/validators` is now paginated (default: 30 vals per page)
|
||||
- `/block_results` response format updated [see RPC docs for details](https://docs.tendermint.com/master/rpc/#/Info/block_results)
|
||||
- `/block_results` response format updated [see RPC docs for details](https://docs.tendermint.com/v0.33/rpc/#/Info/block_results)
|
||||
- Event suffix has been removed from the ID in event responses
|
||||
- IDs are now integers not `json-client-XYZ`
|
||||
|
||||
@@ -238,14 +248,14 @@ due to changes in how various data structures are hashed.
|
||||
Any implementations of Tendermint blockchain verification, including lite clients,
|
||||
will need to be updated. For specific details:
|
||||
|
||||
- [Merkle tree](https://github.com/tendermint/spec/blob/master/spec/blockchain/encoding.md#merkle-trees)
|
||||
- [ConsensusParams](https://github.com/tendermint/spec/blob/master/spec/blockchain/state.md#consensusparams)
|
||||
- [Merkle tree](https://github.com/tendermint/tendermint/blob/main/spec/blockchain/encoding.md#merkle-trees)
|
||||
- [ConsensusParams](https://github.com/tendermint/tendermint/blob/main/spec/blockchain/state.md#consensusparams)
|
||||
|
||||
There was also a small change to field ordering in the vote struct. Any
|
||||
implementations of an out-of-process validator (like a Key-Management Server)
|
||||
will need to be updated. For specific details:
|
||||
|
||||
- [Vote](https://github.com/tendermint/spec/blob/master/spec/consensus/signing.md#votes)
|
||||
- [Vote](https://github.com/tendermint/tendermint/blob/main/spec/consensus/signing.md#votes)
|
||||
|
||||
Finally, the proposer selection algorithm continues to evolve. See the
|
||||
[work-in-progress
|
||||
|
||||
@@ -19,7 +19,7 @@ To get up and running quickly, see the [getting started guide](../docs/app-dev/g
|
||||
|
||||
A detailed description of the ABCI methods and message types is contained in:
|
||||
|
||||
- [The main spec](https://github.com/tendermint/spec/blob/master/spec/abci/abci.md)
|
||||
- [The main spec](https://github.com/tendermint/tendermint/blob/main/spec/abci/abci.md)
|
||||
- [A protobuf file](./types/types.proto)
|
||||
- [A Go interface](./types/application.go)
|
||||
|
||||
|
||||
580
abci/client/mocks/client.go
Normal file
580
abci/client/mocks/client.go
Normal file
@@ -0,0 +1,580 @@
|
||||
// Code generated by mockery v2.3.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
abcicli "github.com/tendermint/tendermint/abci/client"
|
||||
log "github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// Client is an autogenerated mock type for the Client type
|
||||
type Client struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// BeginBlockAsync provides a mock function with given fields: _a0
|
||||
func (_m *Client) BeginBlockAsync(_a0 types.RequestBeginBlock) *abcicli.ReqRes {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func(types.RequestBeginBlock) *abcicli.ReqRes); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// BeginBlockSync provides a mock function with given fields: _a0
|
||||
func (_m *Client) BeginBlockSync(_a0 types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *types.ResponseBeginBlock
|
||||
if rf, ok := ret.Get(0).(func(types.RequestBeginBlock) *types.ResponseBeginBlock); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ResponseBeginBlock)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(types.RequestBeginBlock) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CheckTxAsync provides a mock function with given fields: _a0
|
||||
func (_m *Client) CheckTxAsync(_a0 types.RequestCheckTx) *abcicli.ReqRes {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func(types.RequestCheckTx) *abcicli.ReqRes); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// CheckTxSync provides a mock function with given fields: _a0
|
||||
func (_m *Client) CheckTxSync(_a0 types.RequestCheckTx) (*types.ResponseCheckTx, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *types.ResponseCheckTx
|
||||
if rf, ok := ret.Get(0).(func(types.RequestCheckTx) *types.ResponseCheckTx); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ResponseCheckTx)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(types.RequestCheckTx) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CommitAsync provides a mock function with given fields:
|
||||
func (_m *Client) CommitAsync() *abcicli.ReqRes {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func() *abcicli.ReqRes); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// CommitSync provides a mock function with given fields:
|
||||
func (_m *Client) CommitSync() (*types.ResponseCommit, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 *types.ResponseCommit
|
||||
if rf, ok := ret.Get(0).(func() *types.ResponseCommit); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ResponseCommit)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DeliverTxAsync provides a mock function with given fields: _a0
|
||||
func (_m *Client) DeliverTxAsync(_a0 types.RequestDeliverTx) *abcicli.ReqRes {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func(types.RequestDeliverTx) *abcicli.ReqRes); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeliverTxSync provides a mock function with given fields: _a0
|
||||
func (_m *Client) DeliverTxSync(_a0 types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *types.ResponseDeliverTx
|
||||
if rf, ok := ret.Get(0).(func(types.RequestDeliverTx) *types.ResponseDeliverTx); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ResponseDeliverTx)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(types.RequestDeliverTx) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// EchoAsync provides a mock function with given fields: msg
|
||||
func (_m *Client) EchoAsync(msg string) *abcicli.ReqRes {
|
||||
ret := _m.Called(msg)
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func(string) *abcicli.ReqRes); ok {
|
||||
r0 = rf(msg)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// EchoSync provides a mock function with given fields: msg
|
||||
func (_m *Client) EchoSync(msg string) (*types.ResponseEcho, error) {
|
||||
ret := _m.Called(msg)
|
||||
|
||||
var r0 *types.ResponseEcho
|
||||
if rf, ok := ret.Get(0).(func(string) *types.ResponseEcho); ok {
|
||||
r0 = rf(msg)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ResponseEcho)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(msg)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// EndBlockAsync provides a mock function with given fields: _a0
|
||||
func (_m *Client) EndBlockAsync(_a0 types.RequestEndBlock) *abcicli.ReqRes {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func(types.RequestEndBlock) *abcicli.ReqRes); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// EndBlockSync provides a mock function with given fields: _a0
|
||||
func (_m *Client) EndBlockSync(_a0 types.RequestEndBlock) (*types.ResponseEndBlock, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *types.ResponseEndBlock
|
||||
if rf, ok := ret.Get(0).(func(types.RequestEndBlock) *types.ResponseEndBlock); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ResponseEndBlock)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(types.RequestEndBlock) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Error provides a mock function with given fields:
|
||||
func (_m *Client) Error() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// FlushAsync provides a mock function with given fields:
|
||||
func (_m *Client) FlushAsync() *abcicli.ReqRes {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func() *abcicli.ReqRes); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// FlushSync provides a mock function with given fields:
|
||||
func (_m *Client) FlushSync() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// InfoAsync provides a mock function with given fields: _a0
|
||||
func (_m *Client) InfoAsync(_a0 types.RequestInfo) *abcicli.ReqRes {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func(types.RequestInfo) *abcicli.ReqRes); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// InfoSync provides a mock function with given fields: _a0
|
||||
func (_m *Client) InfoSync(_a0 types.RequestInfo) (*types.ResponseInfo, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *types.ResponseInfo
|
||||
if rf, ok := ret.Get(0).(func(types.RequestInfo) *types.ResponseInfo); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ResponseInfo)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(types.RequestInfo) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// InitChainAsync provides a mock function with given fields: _a0
|
||||
func (_m *Client) InitChainAsync(_a0 types.RequestInitChain) *abcicli.ReqRes {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func(types.RequestInitChain) *abcicli.ReqRes); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// InitChainSync provides a mock function with given fields: _a0
|
||||
func (_m *Client) InitChainSync(_a0 types.RequestInitChain) (*types.ResponseInitChain, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *types.ResponseInitChain
|
||||
if rf, ok := ret.Get(0).(func(types.RequestInitChain) *types.ResponseInitChain); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ResponseInitChain)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(types.RequestInitChain) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// IsRunning provides a mock function with given fields:
|
||||
func (_m *Client) IsRunning() bool {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// OnReset provides a mock function with given fields:
|
||||
func (_m *Client) OnReset() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// OnStart provides a mock function with given fields:
|
||||
func (_m *Client) OnStart() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// OnStop provides a mock function with given fields:
|
||||
func (_m *Client) OnStop() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// QueryAsync provides a mock function with given fields: _a0
|
||||
func (_m *Client) QueryAsync(_a0 types.RequestQuery) *abcicli.ReqRes {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func(types.RequestQuery) *abcicli.ReqRes); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// QuerySync provides a mock function with given fields: _a0
|
||||
func (_m *Client) QuerySync(_a0 types.RequestQuery) (*types.ResponseQuery, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *types.ResponseQuery
|
||||
if rf, ok := ret.Get(0).(func(types.RequestQuery) *types.ResponseQuery); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ResponseQuery)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(types.RequestQuery) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Quit provides a mock function with given fields:
|
||||
func (_m *Client) Quit() <-chan struct{} {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 <-chan struct{}
|
||||
if rf, ok := ret.Get(0).(func() <-chan struct{}); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(<-chan struct{})
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Reset provides a mock function with given fields:
|
||||
func (_m *Client) Reset() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetLogger provides a mock function with given fields: _a0
|
||||
func (_m *Client) SetLogger(_a0 log.Logger) {
|
||||
_m.Called(_a0)
|
||||
}
|
||||
|
||||
// SetOptionAsync provides a mock function with given fields: _a0
|
||||
func (_m *Client) SetOptionAsync(_a0 types.RequestSetOption) *abcicli.ReqRes {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *abcicli.ReqRes
|
||||
if rf, ok := ret.Get(0).(func(types.RequestSetOption) *abcicli.ReqRes); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*abcicli.ReqRes)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetOptionSync provides a mock function with given fields: _a0
|
||||
func (_m *Client) SetOptionSync(_a0 types.RequestSetOption) (*types.ResponseSetOption, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 *types.ResponseSetOption
|
||||
if rf, ok := ret.Get(0).(func(types.RequestSetOption) *types.ResponseSetOption); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*types.ResponseSetOption)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(types.RequestSetOption) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// SetResponseCallback provides a mock function with given fields: _a0
|
||||
func (_m *Client) SetResponseCallback(_a0 abcicli.Callback) {
|
||||
_m.Called(_a0)
|
||||
}
|
||||
|
||||
// Start provides a mock function with given fields:
|
||||
func (_m *Client) Start() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Stop provides a mock function with given fields:
|
||||
func (_m *Client) Stop() error {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// String provides a mock function with given fields:
|
||||
func (_m *Client) String() string {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
@@ -179,7 +179,7 @@ func isValidatorTx(tx []byte) bool {
|
||||
func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx {
|
||||
tx = tx[len(ValidatorSetChangePrefix):]
|
||||
|
||||
//get the pubkey and power
|
||||
// get the pubkey and power
|
||||
pubKeyAndPower := strings.Split(string(tx), "!")
|
||||
if len(pubKeyAndPower) != 2 {
|
||||
return types.ResponseDeliverTx{
|
||||
|
||||
@@ -34,25 +34,24 @@ func NewGRPCServer(protoAddr string, app types.ABCIApplicationServer) service.Se
|
||||
return s
|
||||
}
|
||||
|
||||
// OnStart starts the gRPC service
|
||||
// OnStart starts the gRPC service.
|
||||
func (s *GRPCServer) OnStart() error {
|
||||
if err := s.BaseService.OnStart(); err != nil {
|
||||
return err
|
||||
}
|
||||
ln, err := net.Listen(s.proto, s.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Logger.Info("Listening", "proto", s.proto, "addr", s.addr)
|
||||
|
||||
s.listener = ln
|
||||
s.server = grpc.NewServer()
|
||||
types.RegisterABCIApplicationServer(s.server, s.app)
|
||||
|
||||
s.Logger.Info("Listening", "proto", s.proto, "addr", s.addr)
|
||||
go s.server.Serve(s.listener)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnStop stops the gRPC server
|
||||
// OnStop stops the gRPC server.
|
||||
func (s *GRPCServer) OnStop() {
|
||||
s.BaseService.OnStop()
|
||||
s.server.Stop()
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
Package server is used to start a new ABCI server.
|
||||
|
||||
It contains two server implementation:
|
||||
* gRPC server
|
||||
* socket server
|
||||
|
||||
- gRPC server
|
||||
- socket server
|
||||
*/
|
||||
package server
|
||||
|
||||
|
||||
@@ -5,9 +5,12 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/types"
|
||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||
tmnet "github.com/tendermint/tendermint/libs/net"
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
)
|
||||
@@ -16,6 +19,7 @@ import (
|
||||
|
||||
type SocketServer struct {
|
||||
service.BaseService
|
||||
isLoggerSet bool
|
||||
|
||||
proto string
|
||||
addr string
|
||||
@@ -42,21 +46,24 @@ func NewSocketServer(protoAddr string, app types.Application) service.Service {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *SocketServer) SetLogger(l tmlog.Logger) {
|
||||
s.BaseService.SetLogger(l)
|
||||
s.isLoggerSet = true
|
||||
}
|
||||
|
||||
func (s *SocketServer) OnStart() error {
|
||||
if err := s.BaseService.OnStart(); err != nil {
|
||||
return err
|
||||
}
|
||||
ln, err := net.Listen(s.proto, s.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.listener = ln
|
||||
go s.acceptConnectionsRoutine()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SocketServer) OnStop() {
|
||||
s.BaseService.OnStop()
|
||||
if err := s.listener.Close(); err != nil {
|
||||
s.Logger.Error("Error closing listener", "err", err)
|
||||
}
|
||||
@@ -105,7 +112,7 @@ func (s *SocketServer) acceptConnectionsRoutine() {
|
||||
if !s.IsRunning() {
|
||||
return // Ignore error from listener closing.
|
||||
}
|
||||
s.Logger.Error("Failed to accept connection: " + err.Error())
|
||||
s.Logger.Error("Failed to accept connection", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -132,15 +139,15 @@ func (s *SocketServer) waitForClose(closeConn chan error, connID int) {
|
||||
case err == io.EOF:
|
||||
s.Logger.Error("Connection was closed by client")
|
||||
case err != nil:
|
||||
s.Logger.Error("Connection error", "error", err)
|
||||
s.Logger.Error("Connection error", "err", err)
|
||||
default:
|
||||
// never happens
|
||||
s.Logger.Error("Connection was closed.")
|
||||
s.Logger.Error("Connection was closed")
|
||||
}
|
||||
|
||||
// Close the connection
|
||||
if err := s.rmConn(connID); err != nil {
|
||||
s.Logger.Error("Error in closing connection", "error", err)
|
||||
s.Logger.Error("Error closing connection", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +160,14 @@ func (s *SocketServer) handleRequests(closeConn chan error, conn io.Reader, resp
|
||||
// make sure to recover from any app-related panics to allow proper socket cleanup
|
||||
r := recover()
|
||||
if r != nil {
|
||||
closeConn <- fmt.Errorf("recovered from panic: %v", r)
|
||||
const size = 64 << 10
|
||||
buf := make([]byte, size)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
err := fmt.Errorf("recovered from panic: %v\n%s", r, buf)
|
||||
if !s.isLoggerSet {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
closeConn <- err
|
||||
s.appMtx.Unlock()
|
||||
}
|
||||
}()
|
||||
@@ -166,7 +180,7 @@ func (s *SocketServer) handleRequests(closeConn chan error, conn io.Reader, resp
|
||||
if err == io.EOF {
|
||||
closeConn <- err
|
||||
} else {
|
||||
closeConn <- fmt.Errorf("error reading message: %v", err)
|
||||
closeConn <- fmt.Errorf("error reading message: %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -223,13 +237,13 @@ func (s *SocketServer) handleResponses(closeConn chan error, conn io.Writer, res
|
||||
var res = <-responses
|
||||
err := types.WriteMessage(res, bufWriter)
|
||||
if err != nil {
|
||||
closeConn <- fmt.Errorf("error writing message: %v", err.Error())
|
||||
closeConn <- fmt.Errorf("error writing message: %w", err)
|
||||
return
|
||||
}
|
||||
if _, ok := res.Value.(*types.Response_Flush); ok {
|
||||
err = bufWriter.Flush()
|
||||
if err != nil {
|
||||
closeConn <- fmt.Errorf("error flushing write buffer: %v", err.Error())
|
||||
closeConn <- fmt.Errorf("error flushing write buffer: %w", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ func testCounter() {
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
if err := ensureABCIIsUp(abciType, maxABCIConnectTries); err != nil {
|
||||
log.Fatalf("echo failed: %v", err)
|
||||
log.Fatalf("echo failed: %v", err) //nolint:gocritic
|
||||
}
|
||||
|
||||
client := startClient(abciType)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build ignore
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
@@ -28,7 +28,5 @@ func (v ValidatorUpdates) Less(i, j int) bool {
|
||||
}
|
||||
|
||||
func (v ValidatorUpdates) Swap(i, j int) {
|
||||
v1 := v[i]
|
||||
v[i] = v[j]
|
||||
v[j] = v1
|
||||
v[i], v[j] = v[j], v[i]
|
||||
}
|
||||
|
||||
@@ -409,6 +409,7 @@ func (pool *BlockPool) sendError(err error, peerID p2p.ID) {
|
||||
}
|
||||
|
||||
// for debugging purposes
|
||||
//
|
||||
//nolint:unused
|
||||
func (pool *BlockPool) debug() string {
|
||||
pool.mtx.Lock()
|
||||
@@ -508,7 +509,7 @@ type bpRequester struct {
|
||||
pool *BlockPool
|
||||
height int64
|
||||
gotBlockCh chan struct{}
|
||||
redoCh chan p2p.ID //redo may send multitime, add peerId to identify repeat
|
||||
redoCh chan p2p.ID // redo may send multitime, add peerId to identify repeat
|
||||
|
||||
mtx sync.Mutex
|
||||
peerID p2p.ID
|
||||
@@ -600,7 +601,7 @@ OUTER_LOOP:
|
||||
}
|
||||
peer = bpr.pool.pickIncrAvailablePeer(bpr.height)
|
||||
if peer == nil {
|
||||
//log.Info("No peers available", "height", height)
|
||||
// log.Info("No peers available", "height", height)
|
||||
time.Sleep(requestIntervalMS * time.Millisecond)
|
||||
continue PICK_PEER_LOOP
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ type testPeer struct {
|
||||
id p2p.ID
|
||||
base int64
|
||||
height int64
|
||||
inputChan chan inputData //make sure each peer's data is sequential
|
||||
inputChan chan inputData // make sure each peer's data is sequential
|
||||
}
|
||||
|
||||
type inputData struct {
|
||||
|
||||
@@ -177,7 +177,7 @@ func (bcR *BlockchainReactor) respondToPeer(msg *bcBlockRequestMessage,
|
||||
func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
|
||||
msg, err := decodeMsg(msgBytes)
|
||||
if err != nil {
|
||||
bcR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
|
||||
bcR.Logger.Error("Error decoding message", "src", src, "chId", chID, "err", err)
|
||||
bcR.Switch.StopPeerForError(src, err)
|
||||
return
|
||||
}
|
||||
@@ -299,7 +299,7 @@ FOR_LOOP:
|
||||
|
||||
// See if there are any blocks to sync.
|
||||
first, second := bcR.pool.PeekTwoBlocks()
|
||||
//bcR.Logger.Info("TrySync peeked", "first", first, "second", second)
|
||||
// bcR.Logger.Info("TrySync peeked", "first", first, "second", second)
|
||||
if first == nil || second == nil {
|
||||
// We need both to sync the first block.
|
||||
continue FOR_LOOP
|
||||
|
||||
@@ -227,10 +227,10 @@ func TestBadBlockStopsPeer(t *testing.T) {
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
//at this time, reactors[0-3] is the newest
|
||||
// at this time, reactors[0-3] is the newest
|
||||
assert.Equal(t, 3, reactorPairs[1].reactor.Switch.Peers().Size())
|
||||
|
||||
//mark reactorPairs[3] is an invalid peer
|
||||
// mark reactorPairs[3] is an invalid peer
|
||||
reactorPairs[3].reactor.store = otherChain.reactor.store
|
||||
|
||||
lastReactorPair := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
|
||||
@@ -31,6 +31,7 @@ type BpPeer struct {
|
||||
Height int64 // the peer reported height
|
||||
NumPendingBlockRequests int // number of requests still waiting for block responses
|
||||
blocks map[int64]*types.Block // blocks received or expected to be received from this peer
|
||||
noBlocks map[int64]struct{} // heights for which the peer does not have blocks
|
||||
blockResponseTimer *time.Timer
|
||||
recvMonitor *flow.Monitor
|
||||
params *BpPeerParams // parameters for timer and monitor
|
||||
@@ -46,13 +47,14 @@ func NewBpPeer(peerID p2p.ID, base int64, height int64,
|
||||
params = BpPeerDefaultParams()
|
||||
}
|
||||
return &BpPeer{
|
||||
ID: peerID,
|
||||
Base: base,
|
||||
Height: height,
|
||||
blocks: make(map[int64]*types.Block, maxRequestsPerPeer),
|
||||
logger: log.NewNopLogger(),
|
||||
onErr: onErr,
|
||||
params: params,
|
||||
ID: peerID,
|
||||
Base: base,
|
||||
Height: height,
|
||||
blocks: make(map[int64]*types.Block, maxRequestsPerPeer),
|
||||
noBlocks: make(map[int64]struct{}),
|
||||
logger: log.NewNopLogger(),
|
||||
onErr: onErr,
|
||||
params: params,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +133,19 @@ func (peer *BpPeer) RemoveBlock(height int64) {
|
||||
delete(peer.blocks, height)
|
||||
}
|
||||
|
||||
// SetNoBlock records that the peer does not have a block for height.
|
||||
func (peer *BpPeer) SetNoBlock(height int64) {
|
||||
peer.noBlocks[height] = struct{}{}
|
||||
}
|
||||
|
||||
// NoBlock returns true if the peer does not have a block for height.
|
||||
func (peer *BpPeer) NoBlock(height int64) bool {
|
||||
if _, ok := peer.noBlocks[height]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RequestSent records that a request was sent, and starts the peer timer and monitor if needed.
|
||||
func (peer *BpPeer) RequestSent(height int64) {
|
||||
peer.blocks[height] = nil
|
||||
|
||||
@@ -100,6 +100,18 @@ func (pool *BlockPool) UpdatePeer(peerID p2p.ID, base int64, height int64) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetNoBlock records that the peer does not have a block for height and
|
||||
// schedules a new request for that height from another peer.
|
||||
func (pool *BlockPool) SetNoBlock(peerID p2p.ID, height int64) {
|
||||
peer := pool.peers[peerID]
|
||||
if peer == nil {
|
||||
return
|
||||
}
|
||||
peer.SetNoBlock(height)
|
||||
|
||||
pool.rescheduleRequest(peerID, height)
|
||||
}
|
||||
|
||||
// Cleans and deletes the peer. Recomputes the max peer height.
|
||||
func (pool *BlockPool) deletePeer(peer *BpPeer) {
|
||||
if peer == nil {
|
||||
@@ -214,7 +226,7 @@ func (pool *BlockPool) sendRequest(height int64) bool {
|
||||
if peer.NumPendingBlockRequests >= maxRequestsPerPeer {
|
||||
continue
|
||||
}
|
||||
if peer.Base > height || peer.Height < height {
|
||||
if peer.Base > height || peer.Height < height || peer.NoBlock(height) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
@@ -74,6 +75,9 @@ type BlockchainReactor struct {
|
||||
eventsFromFSMCh chan bcFsmMessage
|
||||
|
||||
swReporter *behaviour.SwitchReporter
|
||||
|
||||
// Atomic integer (0 - sync in progress, 1 - finished syncing)
|
||||
syncEnded int32
|
||||
}
|
||||
|
||||
// NewBlockchainReactor returns new reactor instance.
|
||||
@@ -104,7 +108,7 @@ func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *st
|
||||
fsm := NewFSM(startHeight, bcR)
|
||||
bcR.fsm = fsm
|
||||
bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR)
|
||||
//bcR.swReporter = behaviour.NewSwitchReporter(bcR.BaseReactor.Switch)
|
||||
// bcR.swReporter = behaviour.NewSwitchReporter(bcR.BaseReactor.Switch)
|
||||
|
||||
return bcR
|
||||
}
|
||||
@@ -145,13 +149,22 @@ func (bcR *BlockchainReactor) OnStart() error {
|
||||
bcR.swReporter = behaviour.NewSwitchReporter(bcR.BaseReactor.Switch)
|
||||
if bcR.fastSync {
|
||||
go bcR.poolRoutine()
|
||||
} else { // if we're not fast syncing, mark it as finished
|
||||
bcR.setSyncEnded()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnStop implements service.Service.
|
||||
func (bcR *BlockchainReactor) OnStop() {
|
||||
_ = bcR.Stop()
|
||||
}
|
||||
|
||||
func (bcR *BlockchainReactor) isSyncEnded() bool {
|
||||
return atomic.LoadInt32(&(bcR.syncEnded)) != 0
|
||||
}
|
||||
|
||||
func (bcR *BlockchainReactor) setSyncEnded() {
|
||||
atomic.StoreInt32(&(bcR.syncEnded), 1)
|
||||
}
|
||||
|
||||
// GetChannels implements Reactor
|
||||
@@ -208,6 +221,10 @@ func (bcR *BlockchainReactor) sendStatusResponseToPeer(msg *bcStatusRequestMessa
|
||||
|
||||
// RemovePeer implements Reactor by removing peer from the pool.
|
||||
func (bcR *BlockchainReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
|
||||
if bcR.isSyncEnded() {
|
||||
return
|
||||
}
|
||||
|
||||
msgData := bcReactorMessage{
|
||||
event: peerRemoveEv,
|
||||
data: bReactorEventData{
|
||||
@@ -251,6 +268,10 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
}
|
||||
|
||||
case *bcBlockResponseMessage:
|
||||
if bcR.isSyncEnded() {
|
||||
return
|
||||
}
|
||||
|
||||
msgForFSM := bcReactorMessage{
|
||||
event: blockResponseEv,
|
||||
data: bReactorEventData{
|
||||
@@ -263,7 +284,25 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
bcR.Logger.Info("Received", "src", src, "height", msg.Block.Height)
|
||||
bcR.messagesForFSMCh <- msgForFSM
|
||||
|
||||
case *bcNoBlockResponseMessage:
|
||||
if bcR.isSyncEnded() {
|
||||
return
|
||||
}
|
||||
|
||||
msgForFSM := bcReactorMessage{
|
||||
event: noBlockResponseEv,
|
||||
data: bReactorEventData{
|
||||
peerID: src.ID(),
|
||||
height: msg.Height,
|
||||
},
|
||||
}
|
||||
bcR.Logger.Debug("Peer does not have requested block", "peer", src, "height", msg.Height)
|
||||
bcR.messagesForFSMCh <- msgForFSM
|
||||
|
||||
case *bcStatusResponseMessage:
|
||||
if bcR.isSyncEnded() {
|
||||
return
|
||||
}
|
||||
// Got a peer status. Unverified.
|
||||
msgForFSM := bcReactorMessage{
|
||||
event: statusResponseEv,
|
||||
@@ -274,7 +313,6 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
},
|
||||
}
|
||||
bcR.messagesForFSMCh <- msgForFSM
|
||||
|
||||
default:
|
||||
bcR.Logger.Error(fmt.Sprintf("unknown message type %v", reflect.TypeOf(msg)))
|
||||
}
|
||||
@@ -282,16 +320,20 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
|
||||
// processBlocksRoutine processes blocks until signlaed to stop over the stopProcessing channel
|
||||
func (bcR *BlockchainReactor) processBlocksRoutine(stopProcessing chan struct{}) {
|
||||
|
||||
processReceivedBlockTicker := time.NewTicker(trySyncIntervalMS * time.Millisecond)
|
||||
doProcessBlockCh := make(chan struct{}, 1)
|
||||
defer processReceivedBlockTicker.Stop()
|
||||
|
||||
lastHundred := time.Now()
|
||||
lastRate := 0.0
|
||||
var (
|
||||
doProcessBlockCh = make(chan struct{}, 1)
|
||||
lastHundred = time.Now()
|
||||
lastRate = 0.0
|
||||
)
|
||||
|
||||
ForLoop:
|
||||
for {
|
||||
select {
|
||||
case <-bcR.Quit():
|
||||
break ForLoop
|
||||
case <-stopProcessing:
|
||||
bcR.Logger.Info("finishing block execution")
|
||||
break ForLoop
|
||||
@@ -334,12 +376,14 @@ ForLoop:
|
||||
|
||||
// poolRoutine receives and handles messages from the Receive() routine and from the FSM.
|
||||
func (bcR *BlockchainReactor) poolRoutine() {
|
||||
|
||||
bcR.fsm.Start()
|
||||
|
||||
sendBlockRequestTicker := time.NewTicker(trySendIntervalMS * time.Millisecond)
|
||||
statusUpdateTicker := time.NewTicker(statusUpdateIntervalSeconds * time.Second)
|
||||
|
||||
defer sendBlockRequestTicker.Stop()
|
||||
// NOTE: statusUpdateTicker can continue to run
|
||||
|
||||
stopProcessing := make(chan struct{}, 1)
|
||||
go bcR.processBlocksRoutine(stopProcessing)
|
||||
|
||||
@@ -372,12 +416,10 @@ ForLoop:
|
||||
|
||||
case msg := <-bcR.eventsFromFSMCh:
|
||||
switch msg.event {
|
||||
case syncFinishedEv:
|
||||
case syncFinishedEv: // Sent from the FSM when it enters finished state.
|
||||
stopProcessing <- struct{}{}
|
||||
// Sent from the FSM when it enters finished state.
|
||||
break ForLoop
|
||||
case peerErrorEv:
|
||||
// Sent from the FSM when it detects peer error
|
||||
bcR.setSyncEnded()
|
||||
case peerErrorEv: // Sent from the FSM when it detects peer error
|
||||
bcR.reportPeerErrorToSwitch(msg.data.err, msg.data.peerID)
|
||||
if msg.data.err == errNoPeerResponse {
|
||||
// Sent from the peer timeout handler routine
|
||||
@@ -444,7 +486,6 @@ func (bcR *BlockchainReactor) processBlock() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements bcRNotifier
|
||||
// sendStatusRequest broadcasts `BlockStore` height.
|
||||
func (bcR *BlockchainReactor) sendStatusRequest() {
|
||||
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{
|
||||
@@ -454,7 +495,6 @@ func (bcR *BlockchainReactor) sendStatusRequest() {
|
||||
bcR.Switch.Broadcast(BlockchainChannel, msgBytes)
|
||||
}
|
||||
|
||||
// Implements bcRNotifier
|
||||
// BlockRequest sends `BlockRequest` height.
|
||||
func (bcR *BlockchainReactor) sendBlockRequest(peerID p2p.ID, height int64) error {
|
||||
peer := bcR.Switch.Peers().Get(peerID)
|
||||
@@ -470,24 +510,19 @@ func (bcR *BlockchainReactor) sendBlockRequest(peerID p2p.ID, height int64) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements bcRNotifier
|
||||
func (bcR *BlockchainReactor) switchToConsensus() {
|
||||
conR, ok := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
|
||||
if ok {
|
||||
conR.SwitchToConsensus(bcR.state, bcR.blocksSynced)
|
||||
bcR.eventsFromFSMCh <- bcFsmMessage{event: syncFinishedEv}
|
||||
}
|
||||
// else {
|
||||
// Should only happen during testing.
|
||||
// }
|
||||
}
|
||||
|
||||
// Implements bcRNotifier
|
||||
// Called by FSM and pool:
|
||||
// - pool calls when it detects slow peer or when peer times out
|
||||
// - FSM calls when:
|
||||
// - adding a block (addBlock) fails
|
||||
// - reactor processing of a block reports failure and FSM sends back the peers of first and second blocks
|
||||
// - adding a block (addBlock) fails
|
||||
// - reactor processing of a block reports failure and FSM sends back the peers of first and second blocks
|
||||
func (bcR *BlockchainReactor) sendPeerError(err error, peerID p2p.ID) {
|
||||
bcR.Logger.Info("sendPeerError:", "peer", peerID, "error", err)
|
||||
msgData := bcFsmMessage{
|
||||
@@ -500,7 +535,6 @@ func (bcR *BlockchainReactor) sendPeerError(err error, peerID p2p.ID) {
|
||||
bcR.eventsFromFSMCh <- msgData
|
||||
}
|
||||
|
||||
// Implements bcRNotifier
|
||||
func (bcR *BlockchainReactor) resetStateTimer(name string, timer **time.Timer, timeout time.Duration) {
|
||||
if timer == nil {
|
||||
panic("nil timer pointer parameter")
|
||||
|
||||
@@ -74,6 +74,7 @@ const (
|
||||
startFSMEv = iota + 1
|
||||
statusResponseEv
|
||||
blockResponseEv
|
||||
noBlockResponseEv
|
||||
processedBlockEv
|
||||
makeRequestsEv
|
||||
stopFSMEv
|
||||
@@ -94,6 +95,9 @@ func (msg *bcReactorMessage) String() string {
|
||||
case blockResponseEv:
|
||||
dataStr = fmt.Sprintf("peer=%v block.height=%v length=%v",
|
||||
msg.data.peerID, msg.data.block.Height, msg.data.length)
|
||||
case noBlockResponseEv:
|
||||
dataStr = fmt.Sprintf("peer=%v requested height=%v",
|
||||
msg.data.peerID, msg.data.height)
|
||||
case processedBlockEv:
|
||||
dataStr = fmt.Sprintf("error=%v", msg.data.err)
|
||||
case makeRequestsEv:
|
||||
@@ -105,7 +109,7 @@ func (msg *bcReactorMessage) String() string {
|
||||
case stateTimeoutEv:
|
||||
dataStr = fmt.Sprintf("state=%v", msg.data.stateName)
|
||||
default:
|
||||
dataStr = fmt.Sprintf("cannot interpret message data")
|
||||
dataStr = "cannot interpret message data"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v: %v", msg.event, dataStr)
|
||||
@@ -119,6 +123,8 @@ func (ev bReactorEvent) String() string {
|
||||
return "statusResponseEv"
|
||||
case blockResponseEv:
|
||||
return "blockResponseEv"
|
||||
case noBlockResponseEv:
|
||||
return "noBlockResponseEv"
|
||||
case processedBlockEv:
|
||||
return "processedBlockEv"
|
||||
case makeRequestsEv:
|
||||
@@ -269,7 +275,11 @@ func init() {
|
||||
return waitForPeer, err
|
||||
}
|
||||
return waitForBlock, err
|
||||
case noBlockResponseEv:
|
||||
fsm.logger.Error("peer does not have requested block", "peer", data.peerID)
|
||||
fsm.pool.SetNoBlock(data.peerID, data.height)
|
||||
|
||||
return waitForBlock, nil
|
||||
case processedBlockEv:
|
||||
if data.err != nil {
|
||||
first, second, _ := fsm.pool.FirstTwoBlocksAndPeers()
|
||||
|
||||
@@ -102,6 +102,19 @@ func sProcessedBlockEv(current, expected string, reactorError error) fsmStepTest
|
||||
}
|
||||
}
|
||||
|
||||
func sNoBlockResponseEv(current, expected string, peerID p2p.ID, height int64, err error) fsmStepTestValues {
|
||||
return fsmStepTestValues{
|
||||
currentState: current,
|
||||
event: noBlockResponseEv,
|
||||
data: bReactorEventData{
|
||||
peerID: peerID,
|
||||
height: height,
|
||||
},
|
||||
wantState: expected,
|
||||
wantErr: err,
|
||||
}
|
||||
}
|
||||
|
||||
func sStatusEv(current, expected string, peerID p2p.ID, height int64, err error) fsmStepTestValues {
|
||||
return fsmStepTestValues{
|
||||
currentState: current,
|
||||
@@ -354,6 +367,46 @@ func TestFSMBlockVerificationFailure(t *testing.T) {
|
||||
executeFSMTests(t, tests, false)
|
||||
}
|
||||
|
||||
func TestFSMNoBlockResponse(t *testing.T) {
|
||||
tests := []testFields{
|
||||
{
|
||||
name: "no block response",
|
||||
startingHeight: 1,
|
||||
maxRequestsPerPeer: 3,
|
||||
steps: []fsmStepTestValues{
|
||||
sStartFSMEv(),
|
||||
|
||||
// add P1 and get blocks 1-3 from it
|
||||
sStatusEv("waitForPeer", "waitForBlock", "P1", 3, nil),
|
||||
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
|
||||
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 1, []int64{}),
|
||||
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 2, []int64{1}),
|
||||
sBlockRespEv("waitForBlock", "waitForBlock", "P1", 3, []int64{1, 2}),
|
||||
|
||||
// add P2
|
||||
sStatusEv("waitForBlock", "waitForBlock", "P2", 3, nil),
|
||||
|
||||
// process block failure, should remove P1 and all blocks
|
||||
sNoBlockResponseEv("waitForBlock", "waitForBlock", "P1", 1, nil),
|
||||
sNoBlockResponseEv("waitForBlock", "waitForBlock", "P1", 2, nil),
|
||||
sNoBlockResponseEv("waitForBlock", "waitForBlock", "P1", 3, nil),
|
||||
|
||||
// get blocks 1-3 from P2
|
||||
sMakeRequestsEv("waitForBlock", "waitForBlock", maxNumRequests),
|
||||
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 1, []int64{}),
|
||||
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 2, []int64{1}),
|
||||
sBlockRespEv("waitForBlock", "waitForBlock", "P2", 3, []int64{1, 2}),
|
||||
|
||||
// finish after processing blocks 1 and 2
|
||||
sProcessedBlockEv("waitForBlock", "waitForBlock", nil),
|
||||
sProcessedBlockEv("waitForBlock", "finished", nil),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
executeFSMTests(t, tests, false)
|
||||
}
|
||||
|
||||
func TestFSMBadBlockFromPeer(t *testing.T) {
|
||||
tests := []testFields{
|
||||
{
|
||||
@@ -822,7 +875,7 @@ const (
|
||||
maxRequestsPerPeerTest = 20
|
||||
maxTotalPendingRequestsTest = 600
|
||||
maxNumPeersTest = 1000
|
||||
maxNumBlocksInChainTest = 10000 //should be smaller than 9999999
|
||||
maxNumBlocksInChainTest = 10000 // should be smaller than 9999999
|
||||
)
|
||||
|
||||
func makeCorrectTransitionSequenceWithRandomParameters() testFields {
|
||||
|
||||
@@ -235,11 +235,9 @@ func TestFastSyncNoBlockResponse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This is too hard to test without
|
||||
// an easy way to add test peer to switch
|
||||
// or without significant refactoring of the module.
|
||||
// Alternatively we could actually dial a TCP conn but
|
||||
// that seems extreme.
|
||||
// NOTE: This is too hard to test without an easy way to add test peer to
|
||||
// switch or without significant refactoring of the module. Alternatively we
|
||||
// could actually dial a TCP conn but that seems extreme.
|
||||
func TestFastSyncBadBlockStopsPeer(t *testing.T) {
|
||||
numNodes := 4
|
||||
maxBlockHeight := int64(148)
|
||||
@@ -298,10 +296,10 @@ outerFor:
|
||||
break
|
||||
}
|
||||
|
||||
//at this time, reactors[0-3] is the newest
|
||||
// at this time, reactors[0-3] is the newest
|
||||
assert.Equal(t, numNodes-1, reactorPairs[1].bcR.Switch.Peers().Size())
|
||||
|
||||
//mark last reactorPair as an invalid peer
|
||||
// mark last reactorPair as an invalid peer
|
||||
reactorPairs[numNodes-1].bcR.store = otherChain.bcR.store
|
||||
|
||||
lastLogger := log.TestingLogger()
|
||||
|
||||
@@ -129,8 +129,8 @@ type blockStore interface {
|
||||
type BlockchainReactor struct {
|
||||
p2p.BaseReactor
|
||||
|
||||
fastSync bool // if true, enable fast sync on start
|
||||
events chan Event // XXX: Rename eventsFromPeers
|
||||
stopDemux chan struct{}
|
||||
scheduler *Routine
|
||||
processor *Routine
|
||||
logger log.Logger
|
||||
@@ -157,7 +157,7 @@ type blockApplier interface {
|
||||
// XXX: unify naming in this package around tmState
|
||||
// XXX: V1 stores a copy of state as initialState, which is never mutated. Is that nessesary?
|
||||
func newReactor(state state.State, store blockStore, reporter behaviour.Reporter,
|
||||
blockApplier blockApplier, bufferSize int) *BlockchainReactor {
|
||||
blockApplier blockApplier, bufferSize int, fastSync bool) *BlockchainReactor {
|
||||
scheduler := newScheduler(state.LastBlockHeight, time.Now())
|
||||
pContext := newProcessorContext(store, blockApplier, state)
|
||||
// TODO: Fix naming to just newProcesssor
|
||||
@@ -166,12 +166,12 @@ func newReactor(state state.State, store blockStore, reporter behaviour.Reporter
|
||||
|
||||
return &BlockchainReactor{
|
||||
events: make(chan Event, bufferSize),
|
||||
stopDemux: make(chan struct{}),
|
||||
scheduler: newRoutine("scheduler", scheduler.handle, bufferSize),
|
||||
processor: newRoutine("processor", processor.handle, bufferSize),
|
||||
store: store,
|
||||
reporter: reporter,
|
||||
logger: log.NewNopLogger(),
|
||||
fastSync: fastSync,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func NewBlockchainReactor(
|
||||
store blockStore,
|
||||
fastSync bool) *BlockchainReactor {
|
||||
reporter := behaviour.NewMockReporter()
|
||||
return newReactor(state, store, reporter, blockApplier, 1000)
|
||||
return newReactor(state, store, reporter, blockApplier, 1000, fastSync)
|
||||
}
|
||||
|
||||
// SetSwitch implements Reactor interface.
|
||||
@@ -226,9 +226,11 @@ func (r *BlockchainReactor) SetLogger(logger log.Logger) {
|
||||
// Start implements cmn.Service interface
|
||||
func (r *BlockchainReactor) Start() error {
|
||||
r.reporter = behaviour.NewSwitchReporter(r.BaseReactor.Switch)
|
||||
go r.scheduler.start()
|
||||
go r.processor.start()
|
||||
go r.demux()
|
||||
if r.fastSync {
|
||||
go r.scheduler.start()
|
||||
go r.processor.start()
|
||||
go r.demux()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -306,19 +308,29 @@ func (r *BlockchainReactor) demux() {
|
||||
processBlockFreq = 20 * time.Millisecond
|
||||
doProcessBlockCh = make(chan struct{}, 1)
|
||||
doProcessBlockTk = time.NewTicker(processBlockFreq)
|
||||
)
|
||||
defer doProcessBlockTk.Stop()
|
||||
|
||||
var (
|
||||
prunePeerFreq = 1 * time.Second
|
||||
doPrunePeerCh = make(chan struct{}, 1)
|
||||
doPrunePeerTk = time.NewTicker(prunePeerFreq)
|
||||
)
|
||||
defer doPrunePeerTk.Stop()
|
||||
|
||||
var (
|
||||
scheduleFreq = 20 * time.Millisecond
|
||||
doScheduleCh = make(chan struct{}, 1)
|
||||
doScheduleTk = time.NewTicker(scheduleFreq)
|
||||
)
|
||||
defer doScheduleTk.Stop()
|
||||
|
||||
var (
|
||||
statusFreq = 10 * time.Second
|
||||
doStatusCh = make(chan struct{}, 1)
|
||||
doStatusTk = time.NewTicker(statusFreq)
|
||||
)
|
||||
defer doStatusTk.Stop()
|
||||
|
||||
// XXX: Extract timers to make testing atemporal
|
||||
for {
|
||||
@@ -355,14 +367,20 @@ func (r *BlockchainReactor) demux() {
|
||||
case <-doStatusCh:
|
||||
r.io.broadcastStatusRequest(r.store.Base(), r.SyncHeight())
|
||||
|
||||
// Events from peers
|
||||
case event := <-r.events:
|
||||
// Events from peers. Closing the channel signals event loop termination.
|
||||
case event, ok := <-r.events:
|
||||
if !ok {
|
||||
r.logger.Info("Stopping event processing")
|
||||
return
|
||||
}
|
||||
switch event := event.(type) {
|
||||
case bcStatusResponse:
|
||||
r.setMaxPeerHeight(event.height)
|
||||
r.scheduler.send(event)
|
||||
case bcAddNewPeer, bcRemovePeer, bcBlockResponse, bcNoBlockResponse:
|
||||
r.scheduler.send(event)
|
||||
default:
|
||||
r.logger.Error("Received unknown event", "event", fmt.Sprintf("%T", event))
|
||||
}
|
||||
|
||||
// Incremental events form scheduler
|
||||
@@ -378,6 +396,9 @@ func (r *BlockchainReactor) demux() {
|
||||
case scFinishedEv:
|
||||
r.processor.send(event)
|
||||
r.scheduler.stop()
|
||||
case noOpEvent:
|
||||
default:
|
||||
r.logger.Error("Received unknown scheduler event", "event", fmt.Sprintf("%T", event))
|
||||
}
|
||||
|
||||
// Incremental events from processor
|
||||
@@ -397,20 +418,28 @@ func (r *BlockchainReactor) demux() {
|
||||
case pcFinished:
|
||||
r.io.trySwitchToConsensus(event.tmState, event.blocksSynced)
|
||||
r.processor.stop()
|
||||
case noOpEvent:
|
||||
default:
|
||||
r.logger.Error("Received unknown processor event", "event", fmt.Sprintf("%T", event))
|
||||
}
|
||||
|
||||
// Terminal events from scheduler
|
||||
// Terminal event from scheduler
|
||||
case err := <-r.scheduler.final():
|
||||
r.logger.Info(fmt.Sprintf("scheduler final %s", err))
|
||||
// send the processor stop?
|
||||
switch err {
|
||||
case nil:
|
||||
r.logger.Info("Scheduler stopped")
|
||||
default:
|
||||
r.logger.Error("Scheduler aborted with error", "err", err)
|
||||
}
|
||||
|
||||
// Terminal event from processor
|
||||
case event := <-r.processor.final():
|
||||
r.logger.Info(fmt.Sprintf("processor final %s", event))
|
||||
|
||||
case <-r.stopDemux:
|
||||
r.logger.Info("demuxing stopped")
|
||||
return
|
||||
case err := <-r.processor.final():
|
||||
switch err {
|
||||
case nil:
|
||||
r.logger.Info("Processor stopped")
|
||||
default:
|
||||
r.logger.Error("Processor aborted with error", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -421,7 +450,6 @@ func (r *BlockchainReactor) Stop() error {
|
||||
|
||||
r.scheduler.stop()
|
||||
r.processor.stop()
|
||||
close(r.stopDemux)
|
||||
close(r.events)
|
||||
|
||||
r.logger.Info("reactor stopped")
|
||||
@@ -465,7 +493,7 @@ func (r *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
|
||||
msg, err := decodeMsg(msgBytes)
|
||||
if err != nil {
|
||||
r.logger.Error("error decoding message",
|
||||
"src", src.ID(), "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
|
||||
"src", src.ID(), "chId", chID, "msg", msg, "err", err)
|
||||
_ = r.reporter.Report(behaviour.BadMessage(src.ID(), err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -61,14 +61,17 @@ type mockBlockStore struct {
|
||||
blocks map[int64]*types.Block
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func (ml *mockBlockStore) Height() int64 {
|
||||
return int64(len(ml.blocks))
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func (ml *mockBlockStore) LoadBlock(height int64) *types.Block {
|
||||
return ml.blocks[height]
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func (ml *mockBlockStore) SaveBlock(block *types.Block, part *types.PartSet, commit *types.Commit) {
|
||||
ml.blocks[block.Height] = block
|
||||
}
|
||||
@@ -156,7 +159,7 @@ func newTestReactor(p testReactorParams) *BlockchainReactor {
|
||||
sm.SaveState(db, state)
|
||||
}
|
||||
|
||||
r := newReactor(state, store, reporter, appl, p.bufferSize)
|
||||
r := newReactor(state, store, reporter, appl, p.bufferSize, true)
|
||||
logger := log.TestingLogger()
|
||||
r.SetLogger(logger.With("module", "blockchain"))
|
||||
|
||||
|
||||
@@ -69,9 +69,11 @@ func (rt *Routine) start() {
|
||||
|
||||
for {
|
||||
events, err := rt.queue.Get(1)
|
||||
if err != nil {
|
||||
rt.logger.Info(fmt.Sprintf("%s: stopping\n", rt.name))
|
||||
rt.terminate(fmt.Errorf("stopped"))
|
||||
if err == queue.ErrDisposed {
|
||||
rt.terminate(nil)
|
||||
return
|
||||
} else if err != nil {
|
||||
rt.terminate(err)
|
||||
return
|
||||
}
|
||||
oEvent, err := rt.handle(events[0].(Event))
|
||||
@@ -131,6 +133,7 @@ func (rt *Routine) final() chan error {
|
||||
|
||||
// XXX: Maybe get rid of this
|
||||
func (rt *Routine) terminate(reason error) {
|
||||
close(rt.out)
|
||||
// We don't close the rt.out channel here, to avoid spinning on the closed channel
|
||||
// in the event loop.
|
||||
rt.fin <- reason
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ func newScheduler(initHeight int64, startTime time.Time) *scheduler {
|
||||
receivedBlocks: make(map[int64]p2p.ID),
|
||||
targetPending: 10, // TODO - pass as param
|
||||
peerTimeout: 15 * time.Second, // TODO - pass as param
|
||||
minRecvRate: 0, //int64(7680), TODO - pass as param
|
||||
minRecvRate: 0, // int64(7680), TODO - pass as param
|
||||
}
|
||||
|
||||
return &sc
|
||||
@@ -511,9 +511,7 @@ func (peers PeerByID) Less(i, j int) bool {
|
||||
}
|
||||
|
||||
func (peers PeerByID) Swap(i, j int) {
|
||||
it := peers[i]
|
||||
peers[i] = peers[j]
|
||||
peers[j] = it
|
||||
peers[i], peers[j] = peers[j], peers[i]
|
||||
}
|
||||
|
||||
// Handlers
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
lrpc "github.com/tendermint/tendermint/lite2/rpc"
|
||||
dbs "github.com/tendermint/tendermint/lite2/store/db"
|
||||
rpchttp "github.com/tendermint/tendermint/rpc/client/http"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/lib/server"
|
||||
rpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server"
|
||||
)
|
||||
|
||||
// LiteCmd represents the base command when called without any subcommands
|
||||
|
||||
@@ -51,11 +51,6 @@ var (
|
||||
defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName)
|
||||
)
|
||||
|
||||
var (
|
||||
oldPrivVal = "priv_validator.json"
|
||||
oldPrivValPath = filepath.Join(defaultConfigDir, oldPrivVal)
|
||||
)
|
||||
|
||||
// Config defines the top level configuration for a Tendermint node
|
||||
type Config struct {
|
||||
// Top level options use an anonymous struct
|
||||
@@ -263,12 +258,6 @@ func (cfg BaseConfig) PrivValidatorStateFile() string {
|
||||
return rootify(cfg.PrivValidatorState, cfg.RootDir)
|
||||
}
|
||||
|
||||
// OldPrivValidatorFile returns the full path of the priv_validator.json from pre v0.28.0.
|
||||
// TODO: eventually remove.
|
||||
func (cfg BaseConfig) OldPrivValidatorFile() string {
|
||||
return rootify(oldPrivValPath, cfg.RootDir)
|
||||
}
|
||||
|
||||
// NodeKeyFile returns the full path to the node_key.json file
|
||||
func (cfg BaseConfig) NodeKeyFile() string {
|
||||
return rootify(cfg.NodeKey, cfg.RootDir)
|
||||
@@ -887,12 +876,14 @@ func (cfg *ConsensusConfig) ValidateBasic() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
// TxIndexConfig
|
||||
// Remember that Event has the following structure:
|
||||
// type: [
|
||||
// key: value,
|
||||
// ...
|
||||
//
|
||||
// key: value,
|
||||
// ...
|
||||
//
|
||||
// ]
|
||||
//
|
||||
// CompositeKeys are constructed by `type.key`
|
||||
|
||||
@@ -1 +1 @@
|
||||
See the [consensus spec](https://github.com/tendermint/spec/tree/master/spec/consensus) and the [reactor consensus spec](https://github.com/tendermint/spec/tree/master/spec/reactors/consensus) for more information.
|
||||
See the [consensus spec](https://github.com/tendermint/tendermint/tree/main/spec/consensus) and the [reactor consensus spec](https://github.com/tendermint/tendermint/tree/main/spec/reactors/consensus) for more information.
|
||||
|
||||
@@ -2,7 +2,6 @@ package consensus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -162,8 +161,8 @@ func TestByzantine(t *testing.T) {
|
||||
case <-done:
|
||||
case <-tick.C:
|
||||
for i, reactor := range reactors {
|
||||
t.Log(fmt.Sprintf("Consensus Reactor %v", i))
|
||||
t.Log(fmt.Sprintf("%v", reactor))
|
||||
t.Logf("Consensus Reactor %v", i)
|
||||
t.Logf("%v", reactor)
|
||||
}
|
||||
t.Fatalf("Timed out waiting for all validators to commit first block")
|
||||
}
|
||||
|
||||
94
consensus/invalid_test.go
Normal file
94
consensus/invalid_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
//----------------------------------------------
|
||||
// byzantine failures
|
||||
|
||||
// one byz val sends a precommit for a random block at each height
|
||||
// Ensure a testnet makes blocks
|
||||
func TestReactorInvalidPrecommit(t *testing.T) {
|
||||
N := 4
|
||||
css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter)
|
||||
defer cleanup()
|
||||
|
||||
for i := 0; i < 4; i++ {
|
||||
ticker := NewTimeoutTicker()
|
||||
ticker.SetLogger(css[i].Logger)
|
||||
css[i].SetTimeoutTicker(ticker)
|
||||
|
||||
}
|
||||
|
||||
reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N)
|
||||
|
||||
// this val sends a random precommit at each height
|
||||
byzValIdx := 0
|
||||
byzVal := css[byzValIdx]
|
||||
byzR := reactors[byzValIdx]
|
||||
|
||||
// update the doPrevote function to just send a valid precommit for a random block
|
||||
// and otherwise disable the priv validator
|
||||
byzVal.mtx.Lock()
|
||||
pv := byzVal.privValidator
|
||||
byzVal.doPrevote = func(height int64, round int) {
|
||||
invalidDoPrevoteFunc(t, height, round, byzVal, byzR.Switch, pv)
|
||||
}
|
||||
byzVal.mtx.Unlock()
|
||||
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
|
||||
|
||||
// wait for a bunch of blocks
|
||||
// TODO: make this tighter by ensuring the halt happens by block 2
|
||||
for i := 0; i < 10; i++ {
|
||||
timeoutWaitGroup(t, N, func(j int) {
|
||||
<-blocksSubs[j].Out()
|
||||
}, css)
|
||||
}
|
||||
}
|
||||
|
||||
func invalidDoPrevoteFunc(t *testing.T, height int64, round int, cs *State, sw *p2p.Switch, pv types.PrivValidator) {
|
||||
// routine to:
|
||||
// - precommit for a random block
|
||||
// - send precommit to all peers
|
||||
// - disable privValidator (so we don't do normal precommits)
|
||||
go func() {
|
||||
cs.mtx.Lock()
|
||||
cs.privValidator = pv
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
addr := pubKey.Address()
|
||||
valIndex, _ := cs.Validators.GetByAddress(addr)
|
||||
|
||||
// precommit a random block
|
||||
blockHash := bytes.HexBytes(tmrand.Bytes(32))
|
||||
precommit := &types.Vote{
|
||||
ValidatorAddress: addr,
|
||||
ValidatorIndex: valIndex,
|
||||
Height: cs.Height,
|
||||
Round: cs.Round,
|
||||
Timestamp: cs.voteTime(),
|
||||
Type: types.PrecommitType,
|
||||
BlockID: types.BlockID{
|
||||
Hash: blockHash,
|
||||
PartsHeader: types.PartSetHeader{Total: 1, Hash: tmrand.Bytes(32)}},
|
||||
}
|
||||
cs.privValidator.SignVote(cs.state.ChainID, precommit)
|
||||
cs.privValidator = nil // disable priv val so we don't do normal votes
|
||||
cs.mtx.Unlock()
|
||||
|
||||
peers := sw.Peers().List()
|
||||
for _, peer := range peers {
|
||||
cs.Logger.Info("Sending bad vote", "block", blockHash, "peer", peer)
|
||||
peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{precommit}))
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -219,7 +219,7 @@ func (conR *Reactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
|
||||
|
||||
msg, err := decodeMsg(msgBytes)
|
||||
if err != nil {
|
||||
conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
|
||||
conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "err", err)
|
||||
conR.Switch.StopPeerForError(src, err)
|
||||
return
|
||||
}
|
||||
@@ -626,7 +626,7 @@ OUTER_LOOP:
|
||||
sleeping = 0
|
||||
}
|
||||
|
||||
//logger.Debug("gossipVotesRoutine", "rsHeight", rs.Height, "rsRound", rs.Round,
|
||||
// logger.Debug("gossipVotesRoutine", "rsHeight", rs.Height, "rsRound", rs.Round,
|
||||
// "prsHeight", prs.Height, "prsRound", prs.Round, "prsStep", prs.Step)
|
||||
|
||||
// If height matches, then send LastCommit, Prevotes, Precommits.
|
||||
@@ -1453,7 +1453,7 @@ func (m *NewRoundStepMessage) String() string {
|
||||
//-------------------------------------
|
||||
|
||||
// NewValidBlockMessage is sent when a validator observes a valid block B in some round r,
|
||||
//i.e., there is a Proposal for block B and 2/3+ prevotes for the block B in the round r.
|
||||
// i.e., there is a Proposal for block B and 2/3+ prevotes for the block B in the round r.
|
||||
// In case the block is also committed, then IsCommit flag is set to true.
|
||||
type NewValidBlockMessage struct {
|
||||
Height int64
|
||||
|
||||
@@ -281,7 +281,7 @@ func (w *crashingWAL) Start() error { return w.next.Start() }
|
||||
func (w *crashingWAL) Stop() error { return w.next.Stop() }
|
||||
func (w *crashingWAL) Wait() { w.next.Wait() }
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------------------------
|
||||
type testSim struct {
|
||||
GenesisState sm.State
|
||||
Config *cfg.Config
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/libs/fail"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
"github.com/tendermint/tendermint/libs/service"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
@@ -32,6 +32,8 @@ var (
|
||||
ErrInvalidProposalPOLRound = errors.New("error invalid proposal POL round")
|
||||
ErrAddingVote = errors.New("error adding vote")
|
||||
ErrVoteHeightMismatch = errors.New("error vote height mismatch")
|
||||
|
||||
errPubKeyIsNotSet = errors.New("pubkey is not set. Look for \"Can't get private validator pubkey\" errors")
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -96,6 +98,9 @@ type State struct {
|
||||
mtx sync.RWMutex
|
||||
cstypes.RoundState
|
||||
state sm.State // State until height-1.
|
||||
// privValidator pubkey, memoized for the duration of one block
|
||||
// to avoid extra requests to HSM
|
||||
privValidatorPubKey crypto.PubKey
|
||||
|
||||
// state changes may be triggered by: msgs from peers,
|
||||
// msgs from ourself, or by timeouts
|
||||
@@ -205,7 +210,7 @@ func StateMetrics(metrics *Metrics) StateOption {
|
||||
// String returns a string.
|
||||
func (cs *State) String() string {
|
||||
// better not to access shared variables
|
||||
return fmt.Sprintf("ConsensusState") //(H:%v R:%v S:%v", cs.Height, cs.Round, cs.Step)
|
||||
return "ConsensusState" //(H:%v R:%v S:%v", cs.Height, cs.Round, cs.Step)
|
||||
}
|
||||
|
||||
// GetState returns a copy of the chain state.
|
||||
@@ -252,11 +257,17 @@ func (cs *State) GetValidators() (int64, []*types.Validator) {
|
||||
return cs.state.LastBlockHeight, cs.state.Validators.Copy().Validators
|
||||
}
|
||||
|
||||
// SetPrivValidator sets the private validator account for signing votes.
|
||||
// SetPrivValidator sets the private validator account for signing votes. It
|
||||
// immediately requests pubkey and caches it.
|
||||
func (cs *State) SetPrivValidator(priv types.PrivValidator) {
|
||||
cs.mtx.Lock()
|
||||
defer cs.mtx.Unlock()
|
||||
|
||||
cs.privValidator = priv
|
||||
cs.mtx.Unlock()
|
||||
|
||||
if err := cs.updatePrivValidatorPubKey(); err != nil {
|
||||
cs.Logger.Error("Can't get private validator pubkey", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SetTimeoutTicker sets the local timer. It may be useful to overwrite for testing.
|
||||
@@ -807,7 +818,9 @@ func (cs *State) handleTxsAvailable() {
|
||||
// Used internally by handleTimeout and handleMsg to make state transitions
|
||||
|
||||
// Enter: `timeoutNewHeight` by startTime (commitTime+timeoutCommit),
|
||||
// or, if SkipTimeoutCommit==true, after receiving all precommits from (height,round-1)
|
||||
//
|
||||
// or, if SkipTimeoutCommit==true, after receiving all precommits from (height,round-1)
|
||||
//
|
||||
// Enter: `timeoutPrecommits` after any +2/3 precommits from (height,round-1)
|
||||
// Enter: +2/3 precommits for nil at (height,round-1)
|
||||
// Enter: +2/3 prevotes any or +2/3 precommits for block or any from (height, round)
|
||||
@@ -890,7 +903,9 @@ func (cs *State) needProofBlock(height int64) bool {
|
||||
|
||||
// Enter (CreateEmptyBlocks): from enterNewRound(height,round)
|
||||
// Enter (CreateEmptyBlocks, CreateEmptyBlocksInterval > 0 ):
|
||||
// after enterNewRound(height,round), after timeout of CreateEmptyBlocksInterval
|
||||
//
|
||||
// after enterNewRound(height,round), after timeout of CreateEmptyBlocksInterval
|
||||
//
|
||||
// Enter (!CreateEmptyBlocks) : after enterNewRound(height,round), once txs are in the mempool
|
||||
func (cs *State) enterPropose(height int64, round int) {
|
||||
logger := cs.Logger.With("height", height, "round", round)
|
||||
@@ -930,14 +945,13 @@ func (cs *State) enterPropose(height int64, round int) {
|
||||
}
|
||||
logger.Debug("This node is a validator")
|
||||
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
if cs.privValidatorPubKey == nil {
|
||||
// If this node is a validator & proposer in the current round, it will
|
||||
// miss the opportunity to create a block.
|
||||
logger.Error("Error on retrival of pubkey", "err", err)
|
||||
logger.Error(fmt.Sprintf("enterPropose: %v", errPubKeyIsNotSet))
|
||||
return
|
||||
}
|
||||
address := pubKey.Address()
|
||||
address := cs.privValidatorPubKey.Address()
|
||||
|
||||
// if not a validator, we're done
|
||||
if !cs.Validators.HasAddress(address) {
|
||||
@@ -1027,6 +1041,10 @@ func (cs *State) isProposalComplete() bool {
|
||||
// NOTE: keep it side-effect free for clarity.
|
||||
// CONTRACT: cs.privValidator is not nil.
|
||||
func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.PartSet) {
|
||||
if cs.privValidator == nil {
|
||||
panic("entered createProposalBlock with privValidator being nil")
|
||||
}
|
||||
|
||||
var commit *types.Commit
|
||||
switch {
|
||||
case cs.Height == 1:
|
||||
@@ -1041,17 +1059,13 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa
|
||||
return
|
||||
}
|
||||
|
||||
if cs.privValidator == nil {
|
||||
panic("entered createProposalBlock with privValidator being nil")
|
||||
}
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
if cs.privValidatorPubKey == nil {
|
||||
// If this node is a validator & proposer in the current round, it will
|
||||
// miss the opportunity to create a block.
|
||||
cs.Logger.Error("Error on retrival of pubkey", "err", err)
|
||||
cs.Logger.Error(fmt.Sprintf("enterPropose: %v", errPubKeyIsNotSet))
|
||||
return
|
||||
}
|
||||
proposerAddr := pubKey.Address()
|
||||
proposerAddr := cs.privValidatorPubKey.Address()
|
||||
|
||||
return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr)
|
||||
}
|
||||
@@ -1393,13 +1407,13 @@ func (cs *State) finalizeCommit(height int64) {
|
||||
block, blockParts := cs.ProposalBlock, cs.ProposalBlockParts
|
||||
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Cannot finalizeCommit, commit does not have two thirds majority"))
|
||||
panic("Cannot finalizeCommit, commit does not have two thirds majority")
|
||||
}
|
||||
if !blockParts.HasHeader(blockID.PartsHeader) {
|
||||
panic(fmt.Sprintf("Expected ProposalBlockParts header to be commit header"))
|
||||
panic("Expected ProposalBlockParts header to be commit header")
|
||||
}
|
||||
if !block.HashesTo(blockID.Hash) {
|
||||
panic(fmt.Sprintf("Cannot finalizeCommit, ProposalBlock does not hash to commit hash"))
|
||||
panic("Cannot finalizeCommit, ProposalBlock does not hash to commit hash")
|
||||
}
|
||||
if err := cs.blockExec.ValidateBlock(cs.state, block); err != nil {
|
||||
panic(fmt.Sprintf("+2/3 committed an invalid block: %v", err))
|
||||
@@ -1461,11 +1475,7 @@ func (cs *State) finalizeCommit(height int64) {
|
||||
types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()},
|
||||
block)
|
||||
if err != nil {
|
||||
cs.Logger.Error("Error on ApplyBlock. Did the application crash? Please restart tendermint", "err", err)
|
||||
err := tmos.Kill()
|
||||
if err != nil {
|
||||
cs.Logger.Error("Failed to kill this process - please do so manually", "err", err)
|
||||
}
|
||||
cs.Logger.Error("Error on ApplyBlock", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1489,6 +1499,11 @@ func (cs *State) finalizeCommit(height int64) {
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
// Private validator might have changed it's key pair => refetch pubkey.
|
||||
if err := cs.updatePrivValidatorPubKey(); err != nil {
|
||||
cs.Logger.Error("Can't get private validator pubkey", "err", err)
|
||||
}
|
||||
|
||||
// cs.StartTime is already set.
|
||||
// Schedule Round0 to start soon.
|
||||
cs.scheduleRound0(&cs.RoundState)
|
||||
@@ -1532,12 +1547,22 @@ func (cs *State) recordMetrics(height int64, block *types.Block) {
|
||||
var (
|
||||
commitSize = block.LastCommit.Size()
|
||||
valSetLen = len(cs.LastValidators.Validators)
|
||||
address types.Address
|
||||
)
|
||||
if commitSize != valSetLen {
|
||||
panic(fmt.Sprintf("commit size (%d) doesn't match valset length (%d) at height %d\n\n%v\n\n%v",
|
||||
commitSize, valSetLen, block.Height, block.LastCommit.Signatures, cs.LastValidators.Validators))
|
||||
}
|
||||
|
||||
if cs.privValidator != nil {
|
||||
if cs.privValidatorPubKey == nil {
|
||||
// Metrics won't be updated, but it's not critical.
|
||||
cs.Logger.Error(fmt.Sprintf("recordMetrics: %v", errPubKeyIsNotSet))
|
||||
} else {
|
||||
address = cs.privValidatorPubKey.Address()
|
||||
}
|
||||
}
|
||||
|
||||
for i, val := range cs.LastValidators.Validators {
|
||||
commitSig := block.LastCommit.Signatures[i]
|
||||
if commitSig.Absent() {
|
||||
@@ -1545,26 +1570,18 @@ func (cs *State) recordMetrics(height int64, block *types.Block) {
|
||||
missingValidatorsPower += val.VotingPower
|
||||
}
|
||||
|
||||
if cs.privValidator != nil {
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
// Metrics won't be updated, but it's not critical.
|
||||
cs.Logger.Error("Error on retrival of pubkey", "err", err)
|
||||
continue
|
||||
if bytes.Equal(val.Address, address) {
|
||||
label := []string{
|
||||
"validator_address", val.Address.String(),
|
||||
}
|
||||
|
||||
if bytes.Equal(val.Address, pubKey.Address()) {
|
||||
label := []string{
|
||||
"validator_address", val.Address.String(),
|
||||
}
|
||||
cs.metrics.ValidatorPower.With(label...).Set(float64(val.VotingPower))
|
||||
if commitSig.ForBlock() {
|
||||
cs.metrics.ValidatorLastSignedHeight.With(label...).Set(float64(height))
|
||||
} else {
|
||||
cs.metrics.ValidatorMissedBlocks.With(label...).Add(float64(1))
|
||||
}
|
||||
cs.metrics.ValidatorPower.With(label...).Set(float64(val.VotingPower))
|
||||
if commitSig.ForBlock() {
|
||||
cs.metrics.ValidatorLastSignedHeight.With(label...).Set(float64(height))
|
||||
} else {
|
||||
cs.metrics.ValidatorMissedBlocks.With(label...).Add(float64(1))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
cs.metrics.MissingValidators.Set(float64(missingValidators))
|
||||
@@ -1713,12 +1730,11 @@ func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) {
|
||||
if err == ErrVoteHeightMismatch {
|
||||
return added, err
|
||||
} else if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok {
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "can't get pubkey")
|
||||
if cs.privValidatorPubKey == nil {
|
||||
return false, errPubKeyIsNotSet
|
||||
}
|
||||
|
||||
if bytes.Equal(vote.ValidatorAddress, pubKey.Address()) {
|
||||
if bytes.Equal(vote.ValidatorAddress, cs.privValidatorPubKey.Address()) {
|
||||
cs.Logger.Error(
|
||||
"Found conflicting vote from ourselves. Did you unsafe_reset a validator?",
|
||||
"height",
|
||||
@@ -1917,11 +1933,10 @@ func (cs *State) signVote(
|
||||
// and the privValidator will refuse to sign anything.
|
||||
cs.wal.FlushAndSync()
|
||||
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't get pubkey")
|
||||
if cs.privValidatorPubKey == nil {
|
||||
return nil, errPubKeyIsNotSet
|
||||
}
|
||||
addr := pubKey.Address()
|
||||
addr := cs.privValidatorPubKey.Address()
|
||||
valIdx, _ := cs.Validators.GetByAddress(addr)
|
||||
|
||||
vote := &types.Vote{
|
||||
@@ -1934,7 +1949,7 @@ func (cs *State) signVote(
|
||||
BlockID: types.BlockID{Hash: hash, PartsHeader: header},
|
||||
}
|
||||
|
||||
err = cs.privValidator.SignVote(cs.state.ChainID, vote)
|
||||
err := cs.privValidator.SignVote(cs.state.ChainID, vote)
|
||||
return vote, err
|
||||
}
|
||||
|
||||
@@ -1942,13 +1957,14 @@ func (cs *State) voteTime() time.Time {
|
||||
now := tmtime.Now()
|
||||
minVoteTime := now
|
||||
// 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://docs.tendermint.com/master/spec/.
|
||||
timeIotaMs := time.Duration(cs.state.ConsensusParams.Block.TimeIotaMs) * time.Millisecond
|
||||
// even if cs.LockedBlock != nil. See https://github.com/tendermint/tendermint/tree/v0.33.x/spec/.
|
||||
timeIota := time.Duration(cs.state.ConsensusParams.Block.TimeIotaMs) * time.Millisecond
|
||||
if cs.LockedBlock != nil {
|
||||
// See the BFT time spec https://docs.tendermint.com/master/spec/consensus/bft-time.html
|
||||
minVoteTime = cs.LockedBlock.Time.Add(timeIotaMs)
|
||||
// See the BFT time spec
|
||||
// https://github.com/tendermint/tendermint/blob/v0.33.x/spec/consensus/bft-time.md
|
||||
minVoteTime = cs.LockedBlock.Time.Add(timeIota)
|
||||
} else if cs.ProposalBlock != nil {
|
||||
minVoteTime = cs.ProposalBlock.Time.Add(timeIotaMs)
|
||||
minVoteTime = cs.ProposalBlock.Time.Add(timeIota)
|
||||
}
|
||||
|
||||
if now.After(minVoteTime) {
|
||||
@@ -1963,15 +1979,14 @@ func (cs *State) signAddVote(msgType types.SignedMsgType, hash []byte, header ty
|
||||
return nil
|
||||
}
|
||||
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
if cs.privValidatorPubKey == nil {
|
||||
// Vote won't be signed, but it's not critical.
|
||||
cs.Logger.Error("Error on retrival of pubkey", "err", err)
|
||||
cs.Logger.Error(fmt.Sprintf("signAddVote: %v", errPubKeyIsNotSet))
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the node not in the validator set, do nothing.
|
||||
if !cs.Validators.HasAddress(pubKey.Address()) {
|
||||
if !cs.Validators.HasAddress(cs.privValidatorPubKey.Address()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1988,6 +2003,22 @@ func (cs *State) signAddVote(msgType types.SignedMsgType, hash []byte, header ty
|
||||
return nil
|
||||
}
|
||||
|
||||
// updatePrivValidatorPubKey get's the private validator public key and
|
||||
// memoizes it. This func returns an error if the private validator is not
|
||||
// responding or responds with an error.
|
||||
func (cs *State) updatePrivValidatorPubKey() error {
|
||||
if cs.privValidator == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pubKey, err := cs.privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs.privValidatorPubKey = pubKey
|
||||
return nil
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
||||
func CompareHRS(h1 int64, r1 int, s1 cstypes.RoundStepType, h2 int64, r2 int, s2 cstypes.RoundStepType) int {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build gofuzz
|
||||
// +build gofuzz
|
||||
|
||||
package consensus
|
||||
|
||||
@@ -105,7 +105,7 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
//WALWithNBlocks returns a WAL content with numBlocks.
|
||||
// WALWithNBlocks returns a WAL content with numBlocks.
|
||||
func WALWithNBlocks(t *testing.T, numBlocks int) (data []byte, err error) {
|
||||
var b bytes.Buffer
|
||||
wr := bufio.NewWriter(&b)
|
||||
|
||||
@@ -14,7 +14,7 @@ If you want to decode bytes into one of the types, but don't care about the spec
|
||||
|
||||
## Binary encoding
|
||||
|
||||
For Binary encoding, please refer to the [Tendermint encoding specification](https://docs.tendermint.com/master/spec/blockchain/encoding.html).
|
||||
For Binary encoding, please refer to the [Tendermint encoding specification](https://github.com/tendermint/tendermint/blob/v0.33.x/spec/core/encoding.md).
|
||||
|
||||
## JSON Encoding
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ func checkAminoJSON(t *testing.T, src interface{}, dst interface{}, isNil bool)
|
||||
}
|
||||
|
||||
// ExamplePrintRegisteredTypes refers to unknown identifier: PrintRegisteredTypes
|
||||
//nolint:govet
|
||||
// nolint:govet, gocritic
|
||||
func ExamplePrintRegisteredTypes() {
|
||||
cdc.PrintTypes(os.Stdout)
|
||||
// Output: | Type | Name | Prefix | Length | Notes |
|
||||
|
||||
81
crypto/encoding/codec.go
Normal file
81
crypto/encoding/codec.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package encoding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
pc "github.com/tendermint/tendermint/proto/crypto/keys"
|
||||
)
|
||||
|
||||
// PubKeyToProto takes crypto.PubKey and transforms it to a protobuf Pubkey
|
||||
func PubKeyToProto(k crypto.PubKey) (pc.PublicKey, error) {
|
||||
if k == nil {
|
||||
return pc.PublicKey{}, errors.New("nil PublicKey")
|
||||
}
|
||||
var kp pc.PublicKey
|
||||
switch k := k.(type) {
|
||||
case ed25519.PubKeyEd25519:
|
||||
kp = pc.PublicKey{
|
||||
Sum: &pc.PublicKey_Ed25519{
|
||||
Ed25519: k[:],
|
||||
},
|
||||
}
|
||||
default:
|
||||
return kp, fmt.Errorf("toproto: key type %v is not supported", k)
|
||||
}
|
||||
return kp, nil
|
||||
}
|
||||
|
||||
// PubKeyFromProto takes a protobuf Pubkey and transforms it to a crypto.Pubkey
|
||||
func PubKeyFromProto(k *pc.PublicKey) (crypto.PubKey, error) {
|
||||
if k == nil {
|
||||
return nil, errors.New("nil PublicKey")
|
||||
}
|
||||
switch k := k.Sum.(type) {
|
||||
case *pc.PublicKey_Ed25519:
|
||||
if len(k.Ed25519) != ed25519.PubKeyEd25519Size {
|
||||
return nil, fmt.Errorf("invalid size for PubKeyEd25519. Got %d, expected %d",
|
||||
len(k.Ed25519), ed25519.PubKeyEd25519Size)
|
||||
}
|
||||
var pk ed25519.PubKeyEd25519
|
||||
copy(pk[:], k.Ed25519)
|
||||
return pk, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("fromproto: key type %v is not supported", k)
|
||||
}
|
||||
}
|
||||
|
||||
// PrivKeyToProto takes crypto.PrivKey and transforms it to a protobuf PrivKey
|
||||
func PrivKeyToProto(k crypto.PrivKey) (pc.PrivateKey, error) {
|
||||
var kp pc.PrivateKey
|
||||
switch k := k.(type) {
|
||||
case ed25519.PrivKeyEd25519:
|
||||
kp = pc.PrivateKey{
|
||||
Sum: &pc.PrivateKey_Ed25519{
|
||||
Ed25519: k[:],
|
||||
},
|
||||
}
|
||||
default:
|
||||
return kp, errors.New("toproto: key type is not supported")
|
||||
}
|
||||
return kp, nil
|
||||
}
|
||||
|
||||
// PrivKeyFromProto takes a protobuf PrivateKey and transforms it to a crypto.PrivKey
|
||||
func PrivKeyFromProto(k pc.PrivateKey) (crypto.PrivKey, error) {
|
||||
switch k := k.Sum.(type) {
|
||||
case *pc.PrivateKey_Ed25519:
|
||||
|
||||
if len(k.Ed25519) != ed25519.PubKeyEd25519Size {
|
||||
return nil, fmt.Errorf("invalid size for PubKeyEd25519. Got %d, expected %d",
|
||||
len(k.Ed25519), ed25519.PubKeyEd25519Size)
|
||||
}
|
||||
var pk ed25519.PrivKeyEd25519
|
||||
copy(pk[:], k.Ed25519)
|
||||
return pk, nil
|
||||
default:
|
||||
return nil, errors.New("fromproto: key type not supported")
|
||||
}
|
||||
}
|
||||
@@ -12,20 +12,19 @@ second pre-image attacks. Hence, use this library with caution.
|
||||
Otherwise you might run into similar issues as, e.g., in early Bitcoin:
|
||||
https://bitcointalk.org/?topic=102395
|
||||
|
||||
*
|
||||
/ \
|
||||
/ \
|
||||
/ \
|
||||
/ \
|
||||
* *
|
||||
/ \ / \
|
||||
/ \ / \
|
||||
/ \ / \
|
||||
* * * h6
|
||||
/ \ / \ / \
|
||||
h0 h1 h2 h3 h4 h5
|
||||
*
|
||||
/ \
|
||||
/ \
|
||||
/ \
|
||||
/ \
|
||||
* *
|
||||
/ \ / \
|
||||
/ \ / \
|
||||
/ \ / \
|
||||
* * * h6
|
||||
/ \ / \ / \
|
||||
h0 h1 h2 h3 h4 h5
|
||||
|
||||
TODO(ismail): add 2nd pre-image protection or clarify further on how we use this and why this secure.
|
||||
|
||||
*/
|
||||
package merkle
|
||||
|
||||
@@ -47,10 +47,10 @@ func SimpleHashFromByteSlices(items [][]byte) []byte {
|
||||
//
|
||||
// These preliminary results suggest:
|
||||
//
|
||||
// 1. The performance of the SimpleHashFromByteSlice is pretty good
|
||||
// 2. Go has low overhead for recursive functions
|
||||
// 3. The performance of the SimpleHashFromByteSlice routine is dominated
|
||||
// by the actual hashing of data
|
||||
// 1. The performance of the SimpleHashFromByteSlice is pretty good
|
||||
// 2. Go has low overhead for recursive functions
|
||||
// 3. The performance of the SimpleHashFromByteSlice routine is dominated
|
||||
// by the actual hashing of data
|
||||
//
|
||||
// Although this work is in no way exhaustive, point #3 suggests that
|
||||
// optimization of this routine would need to take an alternative
|
||||
|
||||
@@ -115,7 +115,7 @@ func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
|
||||
return x3.Cmp(y2) == 0
|
||||
}
|
||||
|
||||
//TODO: double check if the function is okay
|
||||
// TODO: double check if the function is okay
|
||||
// affineFromJacobian reverses the Jacobian transform. See the comment at the
|
||||
// top of the file.
|
||||
func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
// -------------------------------------
|
||||
const (
|
||||
PrivKeyAminoName = "tendermint/PrivKeySecp256k1"
|
||||
PubKeyAminoName = "tendermint/PubKeySecp256k1"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build libsecp256k1
|
||||
// +build libsecp256k1
|
||||
|
||||
package secp256k1
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build libsecp256k1
|
||||
// +build libsecp256k1
|
||||
|
||||
package secp256k1
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build !libsecp256k1
|
||||
// +build !libsecp256k1
|
||||
|
||||
package secp256k1
|
||||
@@ -12,8 +13,8 @@ import (
|
||||
|
||||
// used to reject malleable signatures
|
||||
// see:
|
||||
// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93
|
||||
// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/crypto.go#L39
|
||||
// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93
|
||||
// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/crypto.go#L39
|
||||
var secp256k1halfN = new(big.Int).Rsh(secp256k1.S256().N, 1)
|
||||
|
||||
// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build !libsecp256k1
|
||||
// +build !libsecp256k1
|
||||
|
||||
package secp256k1
|
||||
|
||||
@@ -1,140 +1,157 @@
|
||||
module.exports = {
|
||||
theme: "cosmos",
|
||||
title: "Tendermint",
|
||||
// locales: {
|
||||
// "/": {
|
||||
// lang: "en-US"
|
||||
// },
|
||||
// "/ru/": {
|
||||
// lang: "ru"
|
||||
// }
|
||||
// },
|
||||
theme: 'cosmos',
|
||||
title: 'Tendermint Core',
|
||||
base: process.env.VUEPRESS_BASE,
|
||||
themeConfig: {
|
||||
docsRepo: "tendermint/tendermint",
|
||||
repo: 'tendermint/tendermint',
|
||||
docsRepo: 'tendermint/tendermint',
|
||||
docsDir: 'docs',
|
||||
editLinks: true,
|
||||
docsDir: "docs",
|
||||
logo: "/logo.svg",
|
||||
label: "core",
|
||||
gutter: {
|
||||
title: "Help & Support",
|
||||
editLink: true,
|
||||
forum: {
|
||||
title: "Tendermint Forum",
|
||||
text: "Join the Tendermint forum to learn more",
|
||||
url: "https://forum.cosmos.network/c/tendermint",
|
||||
bg: "#0B7E0B",
|
||||
logo: "tendermint"
|
||||
},
|
||||
github: {
|
||||
title: "Found an Issue?",
|
||||
text: "Help us improve this page by suggesting edits on GitHub."
|
||||
}
|
||||
label: 'core',
|
||||
algolia: {
|
||||
id: "BH4D9OD16A",
|
||||
key: "59f0e2deb984aa9cdf2b3a5fd24ac501",
|
||||
index: "tendermint"
|
||||
},
|
||||
footer: {
|
||||
questionsText: "Chat with Cosmos developers in [Discord](https://discordapp.com/channels/669268347736686612) or reach out on the [SDK Developer Forum](https://forum.cosmos.network/c/tendermint) to learn more.",
|
||||
logo: "/logo-bw.svg",
|
||||
textLink: {
|
||||
text: "tendermint.com",
|
||||
url: "https://tendermint.com"
|
||||
versions: [
|
||||
{
|
||||
"label": "v0.34 (latest)",
|
||||
"key": "v0.34"
|
||||
},
|
||||
services: [
|
||||
{
|
||||
"label": "v0.33",
|
||||
"key": "v0.33"
|
||||
}
|
||||
],
|
||||
topbar: {
|
||||
banner: false,
|
||||
},
|
||||
sidebar: {
|
||||
auto: true,
|
||||
nav: [
|
||||
{
|
||||
service: "medium",
|
||||
url: "https://medium.com/@tendermint"
|
||||
},
|
||||
{
|
||||
service: "twitter",
|
||||
url: "https://twitter.com/tendermint_team"
|
||||
},
|
||||
{
|
||||
service: "linkedin",
|
||||
url: "https://www.linkedin.com/company/tendermint/"
|
||||
},
|
||||
{
|
||||
service: "reddit",
|
||||
url: "https://reddit.com/r/cosmosnetwork"
|
||||
},
|
||||
{
|
||||
service: "telegram",
|
||||
url: "https://t.me/cosmosproject"
|
||||
},
|
||||
{
|
||||
service: "youtube",
|
||||
url: "https://www.youtube.com/c/CosmosProject"
|
||||
}
|
||||
],
|
||||
smallprint:
|
||||
"The development of the Tendermint project is led primarily by Tendermint Inc., the for-profit entity which also maintains this website. Funding for this development comes primarily from the Interchain Foundation, a Swiss non-profit.",
|
||||
links: [
|
||||
{
|
||||
title: "Documentation",
|
||||
title: 'Resources',
|
||||
children: [
|
||||
{
|
||||
title: "Cosmos SDK",
|
||||
url: "https://cosmos.network/docs"
|
||||
title: 'RPC',
|
||||
path: (process.env.VUEPRESS_BASE ? process.env.VUEPRESS_BASE : '/')+'rpc/',
|
||||
static: true
|
||||
},
|
||||
{
|
||||
title: "Cosmos Hub",
|
||||
url: "https://hub.cosmos.network/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Community",
|
||||
children: [
|
||||
{
|
||||
title: "Tendermint blog",
|
||||
url: "https://medium.com/@tendermint"
|
||||
},
|
||||
{
|
||||
title: "Forum",
|
||||
url: "https://forum.cosmos.network/c/tendermint"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Contributing",
|
||||
children: [
|
||||
{
|
||||
title: "Contributing to the docs",
|
||||
url: "https://github.com/tendermint/tendermint"
|
||||
},
|
||||
{
|
||||
title: "Source code on GitHub",
|
||||
url: "https://github.com/tendermint/tendermint"
|
||||
},
|
||||
{
|
||||
title: "Careers at Tendermint",
|
||||
url: "https://tendermint.com/careers"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
sidebar: [
|
||||
{
|
||||
title: "Resources",
|
||||
children: [
|
||||
{
|
||||
title: "Developer Sessions",
|
||||
path: "/DEV_SESSIONS.html"
|
||||
},
|
||||
{
|
||||
title: "RPC",
|
||||
path: "/master/rpc/",
|
||||
static: true
|
||||
}
|
||||
]
|
||||
gutter: {
|
||||
title: 'Help & Support',
|
||||
editLink: true,
|
||||
forum: {
|
||||
title: 'Tendermint Discussions',
|
||||
text: 'Join the Tendermint discussions to learn more',
|
||||
url: 'https://github.com/tendermint/tendermint/discussions',
|
||||
bg: '#0B7E0B',
|
||||
logo: 'tendermint'
|
||||
},
|
||||
github: {
|
||||
title: 'Found an Issue?',
|
||||
text: 'Help us improve this page by suggesting edits on GitHub.'
|
||||
}
|
||||
]
|
||||
},
|
||||
footer: {
|
||||
question: {
|
||||
text: 'Chat with Tendermint developers in <a href=\'https://discord.gg/vcExX9T\' target=\'_blank\'>Discord</a> or reach out on <a href=\'https://github.com/tendermint/tendermint/discussions\' target=\'_blank\'>GitHub</a> to learn more.'
|
||||
},
|
||||
logo: '/logo-bw.svg',
|
||||
textLink: {
|
||||
text: 'tendermint.com',
|
||||
url: 'https://tendermint.com'
|
||||
},
|
||||
services: [
|
||||
{
|
||||
service: 'medium',
|
||||
url: 'https://medium.com/@tendermint'
|
||||
},
|
||||
{
|
||||
service: 'twitter',
|
||||
url: 'https://twitter.com/tendermint_team'
|
||||
},
|
||||
{
|
||||
service: 'linkedin',
|
||||
url: 'https://www.linkedin.com/company/tendermint/'
|
||||
},
|
||||
{
|
||||
service: 'reddit',
|
||||
url: 'https://reddit.com/r/cosmosnetwork'
|
||||
},
|
||||
{
|
||||
service: 'telegram',
|
||||
url: 'https://t.me/cosmosproject'
|
||||
},
|
||||
{
|
||||
service: 'youtube',
|
||||
url: 'https://www.youtube.com/c/CosmosProject'
|
||||
}
|
||||
],
|
||||
smallprint:
|
||||
'The development of Tendermint Core is led primarily by [Interchain GmbH](https://interchain.berlin/). Funding for this development comes primarily from the Interchain Foundation, a Swiss non-profit. The Tendermint trademark is owned by Tendermint Inc, the for-profit entity that also maintains this website.',
|
||||
links: [
|
||||
{
|
||||
title: 'Documentation',
|
||||
children: [
|
||||
{
|
||||
title: 'Cosmos SDK',
|
||||
url: 'https://docs.cosmos.network'
|
||||
},
|
||||
{
|
||||
title: 'Cosmos Hub',
|
||||
url: 'https://hub.cosmos.network'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Community',
|
||||
children: [
|
||||
{
|
||||
title: 'Tendermint blog',
|
||||
url: 'https://medium.com/@tendermint'
|
||||
},
|
||||
{
|
||||
title: 'GitHub Discussions',
|
||||
url: 'https://github.com/tendermint/tendermint/discussions'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Contributing',
|
||||
children: [
|
||||
{
|
||||
title: 'Contributing to the docs',
|
||||
url: 'https://github.com/tendermint/tendermint'
|
||||
},
|
||||
{
|
||||
title: 'Source code on GitHub',
|
||||
url: 'https://github.com/tendermint/tendermint'
|
||||
},
|
||||
{
|
||||
title: 'Careers at Tendermint',
|
||||
url: 'https://tendermint.com/careers'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
[
|
||||
"@vuepress/google-analytics",
|
||||
'@vuepress/google-analytics',
|
||||
{
|
||||
ga: "UA-51029217-11"
|
||||
ga: 'UA-51029217-11'
|
||||
}
|
||||
],
|
||||
[
|
||||
'@vuepress/plugin-html-redirect',
|
||||
{
|
||||
countdown: 0
|
||||
}
|
||||
]
|
||||
],
|
||||
]
|
||||
};
|
||||
|
||||
1
docs/.vuepress/redirects
Normal file
1
docs/.vuepress/redirects
Normal file
@@ -0,0 +1 @@
|
||||
/master/ /main/
|
||||
@@ -1,3 +1,3 @@
|
||||
:root
|
||||
--accent-color #018A01
|
||||
--background #222222
|
||||
--color-link #018A01
|
||||
--color-primary #00BB00
|
||||
@@ -2,39 +2,39 @@
|
||||
|
||||
The documentation for Tendermint Core is hosted at:
|
||||
|
||||
- https://docs.tendermint.com/master/
|
||||
- <https://docs.tendermint.com/v0.33/>
|
||||
|
||||
built from the files in this (`/docs`) directory for
|
||||
[master](https://github.com/tendermint/tendermint/tree/master/docs) respectively.
|
||||
[main](https://github.com/tendermint/tendermint/tree/main/docs) respectively.
|
||||
|
||||
## How It Works
|
||||
|
||||
There is a CircleCI job listening for changes in the `/docs` directory, on both
|
||||
the `master` branch. Any updates to files in this directory
|
||||
on those branches will automatically trigger a website deployment. Under the hood,
|
||||
the private website repository has a `make build-docs` target consumed by a CircleCI job in that repo.
|
||||
There is a [GitHub Action](../.github/workflows/docs-deployment.yml) that is
|
||||
triggered by changes in the `/docs` directory on `main` as well as the branch of
|
||||
each major supported version (e.g. `v0.34.x`). Any updates to files in this
|
||||
directory on those branches will automatically trigger a website deployment.
|
||||
|
||||
## README
|
||||
|
||||
The [README.md](./README.md) is also the landing page for the documentation
|
||||
on the website. During the Jenkins build, the current commit is added to the bottom
|
||||
of the README.
|
||||
The [README.md](./README.md) is also the landing page for the documentation on
|
||||
the website.
|
||||
|
||||
## Config.js
|
||||
|
||||
The [config.js](./.vuepress/config.js) generates the sidebar and Table of Contents
|
||||
on the website docs. Note the use of relative links and the omission of
|
||||
file extensions. Additional features are available to improve the look
|
||||
of the sidebar.
|
||||
The [config.js](./.vuepress/config.js) generates the sidebar and Table of
|
||||
Contents on the website docs. Note the use of relative links and the omission of
|
||||
file extensions. Additional features are available to improve the look of the
|
||||
sidebar.
|
||||
|
||||
## Links
|
||||
|
||||
**NOTE:** Strongly consider the existing links - both within this directory
|
||||
and to the website docs - when moving or deleting files.
|
||||
**NOTE:** Strongly consider the existing links - both within this directory and
|
||||
to the website docs - when moving or deleting files.
|
||||
|
||||
Links to directories _MUST_ end in a `/`.
|
||||
|
||||
Relative links should be used nearly everywhere, having discovered and weighed the following:
|
||||
Relative links should be used nearly everywhere, having discovered and weighed
|
||||
the following:
|
||||
|
||||
### Relative
|
||||
|
||||
@@ -61,35 +61,43 @@ to send users to the GitHub.
|
||||
|
||||
Make sure you are in the `docs` directory and run the following commands:
|
||||
|
||||
```sh
|
||||
```bash
|
||||
rm -rf node_modules
|
||||
```
|
||||
|
||||
This command will remove old version of the visual theme and required packages. This step is optional.
|
||||
This command will remove old version of the visual theme and required packages.
|
||||
This step is optional.
|
||||
|
||||
```sh
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
Install the theme and all dependencies.
|
||||
|
||||
```sh
|
||||
```bash
|
||||
npm run serve
|
||||
```
|
||||
|
||||
<!-- markdown-link-check-disable -->
|
||||
|
||||
Run `pre` and `post` hooks and start a hot-reloading web-server. See output of this command for the URL (it is often https://localhost:8080).
|
||||
Run `pre` and `post` hooks and start a hot-reloading web-server. See output of
|
||||
this command for the URL (it is often <https://localhost:8080>).
|
||||
|
||||
<!-- markdown-link-check-enable -->
|
||||
|
||||
To build documentation as a static website run `npm run build`. You will find the website in `.vuepress/dist` directory.
|
||||
To build documentation as a static website run `npm run build`. You will find
|
||||
the website in `.vuepress/dist` directory.
|
||||
|
||||
## Search
|
||||
|
||||
We are using [Algolia](https://www.algolia.com) to power full-text search. This uses a public API search-only key in the `config.js` as well as a [tendermint.json](https://github.com/algolia/docsearch-configs/blob/master/configs/tendermint.json) configuration file that we can update with PRs.
|
||||
We are using [Algolia](https://www.algolia.com) to power full-text search. This
|
||||
uses a public API search-only key in the `config.js` as well as a
|
||||
[tendermint.json](https://github.com/algolia/docsearch-configs/blob/master/configs/tendermint.json)
|
||||
configuration file that we can update with PRs.
|
||||
|
||||
## Consistency
|
||||
|
||||
Because the build processes are identical (as is the information contained herein), this file should be kept in sync as
|
||||
much as possible with its [counterpart in the Cosmos SDK repo](https://github.com/cosmos/cosmos-sdk/blob/master/docs/DOCS_README.md).
|
||||
Because the build processes are identical (as is the information contained
|
||||
herein), this file should be kept in sync as much as possible with its
|
||||
[counterpart in the Cosmos SDK
|
||||
repo](https://github.com/cosmos/cosmos-sdk/blob/master/docs/DOCS_README.md).
|
||||
|
||||
@@ -14,7 +14,7 @@ Tendermint?](introduction/what-is-tendermint.md).
|
||||
|
||||
To get started quickly with an example application, see the [quick start guide](introduction/quick-start.md).
|
||||
|
||||
To learn about application development on Tendermint, see the [Application Blockchain Interface](https://github.com/tendermint/spec/tree/master/spec/abci).
|
||||
To learn about application development on Tendermint, see the [Application Blockchain Interface](https://github.com/tendermint/tendermint/tree/main/spec/abci).
|
||||
|
||||
For more details on using Tendermint, see the respective documentation for
|
||||
[Tendermint Core](tendermint-core/), [benchmarking and monitoring](tools/), and [network deployments](networks/).
|
||||
@@ -23,5 +23,5 @@ To find out about the Tendermint ecosystem you can go [here](https://github.com/
|
||||
|
||||
## Contribute
|
||||
|
||||
To contribute to the documentation, see [this file](https://github.com/tendermint/tendermint/blob/master/docs/DOCS_README.md) for details of the build process and
|
||||
To contribute to the documentation, see [this file](https://github.com/tendermint/tendermint/blob/main/docs/DOCS_README.md) for details of the build process and
|
||||
considerations when making changes.
|
||||
|
||||
@@ -66,7 +66,7 @@ as `abci-cli` above. The kvstore just stores transactions in a merkle
|
||||
tree.
|
||||
|
||||
Its code can be found
|
||||
[here](https://github.com/tendermint/tendermint/blob/master/abci/cmd/abci-cli/abci-cli.go)
|
||||
[here](https://github.com/tendermint/tendermint/blob/v0.33.x/abci/cmd/abci-cli/abci-cli.go)
|
||||
and looks like:
|
||||
|
||||
```
|
||||
@@ -141,7 +141,7 @@ response.
|
||||
|
||||
The server may be generic for a particular language, and we provide a
|
||||
[reference implementation in
|
||||
Golang](https://github.com/tendermint/tendermint/tree/master/abci/server). See the
|
||||
Golang](https://github.com/tendermint/tendermint/tree/main/abci/server). See the
|
||||
[list of other ABCI implementations](https://github.com/tendermint/awesome#ecosystem) for servers in
|
||||
other languages.
|
||||
|
||||
@@ -223,7 +223,7 @@ Now that we've got the hang of it, let's try another application, the
|
||||
"counter" app.
|
||||
|
||||
Like the kvstore app, its code can be found
|
||||
[here](https://github.com/tendermint/tendermint/blob/master/abci/cmd/abci-cli/abci-cli.go)
|
||||
[here](https://github.com/tendermint/tendermint/blob/v0.33.x/abci/cmd/abci-cli/abci-cli.go)
|
||||
and looks like:
|
||||
|
||||
```
|
||||
@@ -328,7 +328,7 @@ But the ultimate flexibility comes from being able to write the
|
||||
application easily in any language.
|
||||
|
||||
We have implemented the counter in a number of languages [see the
|
||||
example directory](https://github.com/tendermint/tendermint/tree/master/abci/example).
|
||||
example directory](https://github.com/tendermint/tendermint/tree/main/abci/example).
|
||||
|
||||
To run the Node.js version, fist download & install [the Javascript ABCI server](https://github.com/tendermint/js-abci):
|
||||
|
||||
|
||||
@@ -56,6 +56,6 @@ Tendermint.
|
||||
See the following for more extensive documentation:
|
||||
|
||||
- [Interchain Standard for the Light-Client REST API](https://github.com/cosmos/cosmos-sdk/pull/1028)
|
||||
- [Tendermint RPC Docs](https://docs.tendermint.com/master/rpc/)
|
||||
- [Tendermint RPC Docs](https://docs.tendermint.com/v0.33/rpc/)
|
||||
- [Tendermint in Production](../tendermint-core/running-in-production.md)
|
||||
- [ABCI spec](https://github.com/tendermint/spec/tree/95cf253b6df623066ff7cd4074a94e7a3f147c7a/spec/abci)
|
||||
|
||||
@@ -7,7 +7,7 @@ order: 4
|
||||
## XXX
|
||||
|
||||
This page is undergoing deprecation. All content is being moved to the new [home
|
||||
of the ABCI specification](https://github.com/tendermint/spec/tree/master/spec/abci).
|
||||
of the ABCI specification](https://github.com/tendermint/tendermint/tree/main/spec/abci).
|
||||
|
||||
## ABCI Design
|
||||
|
||||
@@ -52,9 +52,9 @@ open ABCI connection with the application, which hosts an ABCI server.
|
||||
Shown are the request and response types sent on each connection.
|
||||
|
||||
Most of the examples below are from [kvstore
|
||||
application](https://github.com/tendermint/tendermint/blob/master/abci/example/kvstore/kvstore.go),
|
||||
application](https://github.com/tendermint/tendermint/blob/main/abci/example/kvstore/kvstore.go),
|
||||
which is a part of the abci repo. [persistent_kvstore
|
||||
application](https://github.com/tendermint/tendermint/blob/master/abci/example/kvstore/persistent_kvstore.go)
|
||||
application](https://github.com/tendermint/tendermint/blob/main/abci/example/kvstore/persistent_kvstore.go)
|
||||
is used to show `BeginBlock`, `EndBlock` and `InitChain` example
|
||||
implementations.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ type, only the key-value pairs defined in `EndBlock` are used.
|
||||
|
||||
Each event contains a type and a list of attributes, which are key-value pairs
|
||||
denoting something about what happened during the method's execution. For more
|
||||
details on `Events`, see the [ABCI]https://github.com/tendermint/spec/blob/master/spec/abci/abci.md#events) documentation.
|
||||
details on `Events`, see the [ABCI]https://github.com/tendermint/tendermint/blob/main/spec/abci/abci.md#events) documentation.
|
||||
|
||||
An Event has a composite key associated with it. A `compositeKey` is constructed by its type and key separated by a dot.
|
||||
For example:
|
||||
@@ -106,7 +106,7 @@ You can query the transaction results by calling `/tx_search` RPC endpoint:
|
||||
curl "localhost:26657/tx_search?query=\"account.name='igor'\"&prove=true"
|
||||
```
|
||||
|
||||
Check out [API docs](https://docs.tendermint.com/master/rpc/#/Info/tx_search) for more information
|
||||
Check out [API docs](https://docs.tendermint.com/v0.33/rpc/#/Info/tx_search) for more information
|
||||
on query syntax and other options.
|
||||
|
||||
## Subscribing to Transactions
|
||||
@@ -125,5 +125,5 @@ a query to `/subscribe` RPC endpoint.
|
||||
}
|
||||
```
|
||||
|
||||
Check out [API docs](https://docs.tendermint.com/master/rpc/#subscribe) for more information
|
||||
Check out [API docs](https://docs.tendermint.com/v0.33/rpc/#subscribe) for more information
|
||||
on query syntax and other options.
|
||||
|
||||
@@ -24,7 +24,7 @@ method via Websocket.
|
||||
}
|
||||
```
|
||||
|
||||
Check out [API docs](https://docs.tendermint.com/master/rpc/) for
|
||||
Check out [API docs](https://docs.tendermint.com/v0.33/rpc/) for
|
||||
more information on query syntax and other options.
|
||||
|
||||
You can also use tags, given you had included them into DeliverTx
|
||||
@@ -36,7 +36,7 @@ transactions](./indexing-transactions.md) for details.
|
||||
When validator set changes, ValidatorSetUpdates event is published. The
|
||||
event carries a list of pubkey/power pairs. The list is the same
|
||||
Tendermint receives from ABCI application (see [EndBlock
|
||||
section](https://github.com/tendermint/spec/blob/master/spec/abci/abci.md#endblock) in
|
||||
section](https://github.com/tendermint/tendermint/blob/main/spec/abci/abci.md#endblock) in
|
||||
the ABCI spec).
|
||||
|
||||
Response:
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
---
|
||||
order: 1
|
||||
parent:
|
||||
order: false
|
||||
---
|
||||
|
||||
# Architecture Decision Records (ADR)
|
||||
|
||||
This is a location to record all high-level architecture decisions in the tendermint project.
|
||||
|
||||
You can read more about the ADR concept in this [blog post](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0#.78xhdix6t).
|
||||
|
||||
An ADR should provide:
|
||||
|
||||
- Context on the relevant goals and the current state
|
||||
- Proposed changes to achieve the goals
|
||||
- Summary of pros and cons
|
||||
- References
|
||||
- Changelog
|
||||
|
||||
Note the distinction between an ADR and a spec. The ADR provides the context, intuition, reasoning, and
|
||||
justification for a change in architecture, or for the architecture of something
|
||||
new. The spec is much more compressed and streamlined summary of everything as
|
||||
it stands today.
|
||||
|
||||
If recorded decisions turned out to be lacking, convene a discussion, record the new decisions here, and then modify the code to match.
|
||||
|
||||
Note the context/background should be written in the present tense.
|
||||
|
||||
### Table of Contents:
|
||||
|
||||
- [ADR-001-Logging](./adr-001-logging.md)
|
||||
- [ADR-002-Event-Subscription](./adr-002-event-subscription.md)
|
||||
- [ADR-003-ABCI-APP-RPC](./adr-003-abci-app-rpc.md)
|
||||
- [ADR-004-Historical-Validators](./adr-004-historical-validators.md)
|
||||
- [ADR-005-Consensus-Params](./adr-005-consensus-params.md)
|
||||
- [ADR-006-Trust-Metric](./adr-006-trust-metric.md)
|
||||
- [ADR-007-Trust-Metric-Usage](./adr-007-trust-metric-usage.md)
|
||||
- [ADR-008-Priv-Validator](./adr-008-priv-validator.md)
|
||||
- [ADR-009-ABCI-Design](./adr-009-ABCI-design.md)
|
||||
- [ADR-010-Crypto-Changes](./adr-010-crypto-changes.md)
|
||||
- [ADR-011-Monitoring](./adr-011-monitoring.md)
|
||||
- [ADR-012-Peer-Transport](./adr-012-peer-transport.md)
|
||||
- [ADR-013-Symmetric-Crypto](./adr-013-symmetric-crypto.md)
|
||||
- [ADR-014-Secp-Malleability](./adr-014-secp-malleability.md)
|
||||
- [ADR-015-Crypto-Encoding](./adr-015-crypto-encoding.md)
|
||||
- [ADR-016-Protocol-Versions](./adr-016-protocol-versions.md)
|
||||
- [ADR-017-Chain-Versions](./adr-017-chain-versions.md)
|
||||
- [ADR-018-ABCI-Validators](./adr-018-ABCI-Validators.md)
|
||||
- [ADR-019-Multisigs](./adr-019-multisigs.md)
|
||||
- [ADR-020-Block-Size](./adr-020-block-size.md)
|
||||
- [ADR-021-ABCI-Events](./adr-021-abci-events.md)
|
||||
- [ADR-022-ABCI-Errors](./adr-022-abci-errors.md)
|
||||
- [ADR-023-ABCI-Propose-tx](./adr-023-ABCI-propose-tx.md)
|
||||
- [ADR-024-Sign-Bytes](./adr-024-sign-bytes.md)
|
||||
- [ADR-025-Commit](./adr-025-commit.md)
|
||||
- [ADR-026-General-Merkle-Proof](./adr-026-general-merkle-proof.md)
|
||||
- [ADR-029-Check-Tx-Consensus](./adr-029-check-tx-consensus.md)
|
||||
- [ADR-030-Consensus-Refactor](./adr-030-consensus-refactor.md)
|
||||
- [ADR-033-Pubsub](./adr-033-pubsub.md)
|
||||
- [ADR-034-Priv-Validator-File-Structure](./adr-034-priv-validator-file-structure.md)
|
||||
- [ADR-035-Documentation](./adr-035-documentation.md)
|
||||
- [ADR-037-Deliver-Block](./adr-037-deliver-block.md)
|
||||
- [ADR-039-Peer-Behaviour](./adr-039-peer-behaviour.md)
|
||||
- [ADR-041-Proposer-Selection-via-ABCI](./adr-041-proposer-selection-via-abci.md)
|
||||
- [ADR-043-Blockchain-RiRi-Org](./adr-043-blockchain-riri-org.md)
|
||||
- [ADR-044-Lite-Client-With-Weak-Subjectivity](./adr-044-lite-client-with-weak-subjectivity.md)
|
||||
- [ADR-045-ABCI-Evidence](./adr-045-abci-evidence.md)
|
||||
- [ADR-046-Light-Client-Implementation](./adr-046-light-client-implementation.md)
|
||||
- [ADR-047-Handling-Evidence-From-Light-Client](./adr-047-handling-evidence-from-light-client.md)
|
||||
- [ADR-051-Double-Signing-Risk-Reduction](./adr-051-double-signing-risk-reduction.md)
|
||||
- [ADR-052-Tendermint-Mode](./adr-052-tendermint-mode.md)
|
||||
- [ADR-053-State-Sync-Prototype](./adr-053-state-sync-prototype.md)
|
||||
- [ADR-054-crypto-encoding-2](./adr-054-crypto-encoding-2.md)
|
||||
- [ADR-055-protobuf-design](./adr-055-protobuf-design.md)
|
||||
@@ -1,216 +0,0 @@
|
||||
# ADR 1: Logging
|
||||
|
||||
## Context
|
||||
|
||||
Current logging system in Tendermint is very static and not flexible enough.
|
||||
|
||||
Issues: [358](https://github.com/tendermint/tendermint/issues/358), [375](https://github.com/tendermint/tendermint/issues/375).
|
||||
|
||||
What we want from the new system:
|
||||
|
||||
- per package dynamic log levels
|
||||
- dynamic logger setting (logger tied to the processing struct)
|
||||
- conventions
|
||||
- be more visually appealing
|
||||
|
||||
"dynamic" here means the ability to set smth in runtime.
|
||||
|
||||
## Decision
|
||||
|
||||
### 1) An interface
|
||||
|
||||
First, we will need an interface for all of our libraries (`tmlibs`, Tendermint, etc.). My personal preference is go-kit `Logger` interface (see Appendix A.), but that is too much a bigger change. Plus we will still need levels.
|
||||
|
||||
```go
|
||||
# log.go
|
||||
type Logger interface {
|
||||
Debug(msg string, keyvals ...interface{}) error
|
||||
Info(msg string, keyvals ...interface{}) error
|
||||
Error(msg string, keyvals ...interface{}) error
|
||||
|
||||
With(keyvals ...interface{}) Logger
|
||||
}
|
||||
```
|
||||
|
||||
On a side note: difference between `Info` and `Notice` is subtle. We probably
|
||||
could do without `Notice`. Don't think we need `Panic` or `Fatal` as a part of
|
||||
the interface. These funcs could be implemented as helpers. In fact, we already
|
||||
have some in `tmlibs/common`.
|
||||
|
||||
- `Debug` - extended output for devs
|
||||
- `Info` - all that is useful for a user
|
||||
- `Error` - errors
|
||||
|
||||
`Notice` should become `Info`, `Warn` either `Error` or `Debug` depending on the message, `Crit` -> `Error`.
|
||||
|
||||
This interface should go into `tmlibs/log`. All libraries which are part of the core (tendermint/tendermint) should obey it.
|
||||
|
||||
### 2) Logger with our current formatting
|
||||
|
||||
On top of this interface, we will need to implement a stdout logger, which will be used when Tendermint is configured to output logs to STDOUT.
|
||||
|
||||
Many people say that they like the current output, so let's stick with it.
|
||||
|
||||
```
|
||||
NOTE[2017-04-25|14:45:08] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0
|
||||
```
|
||||
|
||||
Couple of minor changes:
|
||||
|
||||
```
|
||||
I[2017-04-25|14:45:08.322] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0
|
||||
```
|
||||
|
||||
Notice the level is encoded using only one char plus milliseconds.
|
||||
|
||||
Note: there are many other formats out there like [logfmt](https://brandur.org/logfmt).
|
||||
|
||||
This logger could be implemented using any logger - [logrus](https://github.com/sirupsen/logrus), [go-kit/log](https://github.com/go-kit/kit/tree/master/log), [zap](https://github.com/uber-go/zap), log15 so far as it
|
||||
|
||||
a) supports coloring output<br>
|
||||
b) is moderately fast (buffering) <br>
|
||||
c) conforms to the new interface or adapter could be written for it <br>
|
||||
d) is somewhat configurable<br>
|
||||
|
||||
go-kit is my favorite so far. Check out how easy it is to color errors in red https://github.com/go-kit/kit/blob/master/log/term/example_test.go#L12. Although, coloring could only be applied to the whole string :(
|
||||
|
||||
```
|
||||
go-kit +: flexible, modular
|
||||
go-kit “-”: logfmt format https://brandur.org/logfmt
|
||||
|
||||
logrus +: popular, feature rich (hooks), API and output is more like what we want
|
||||
logrus -: not so flexible
|
||||
```
|
||||
|
||||
```go
|
||||
# tm_logger.go
|
||||
// NewTmLogger returns a logger that encodes keyvals to the Writer in
|
||||
// tm format.
|
||||
func NewTmLogger(w io.Writer) Logger {
|
||||
return &tmLogger{kitlog.NewLogfmtLogger(w)}
|
||||
}
|
||||
|
||||
func (l tmLogger) SetLevel(level string() {
|
||||
switch (level) {
|
||||
case "debug":
|
||||
l.sourceLogger = level.NewFilter(l.sourceLogger, level.AllowDebug())
|
||||
}
|
||||
}
|
||||
|
||||
func (l tmLogger) Info(msg string, keyvals ...interface{}) error {
|
||||
l.sourceLogger.Log("msg", msg, keyvals...)
|
||||
}
|
||||
|
||||
# log.go
|
||||
func With(logger Logger, keyvals ...interface{}) Logger {
|
||||
kitlog.With(logger.sourceLogger, keyvals...)
|
||||
}
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```go
|
||||
logger := log.NewTmLogger(os.Stdout)
|
||||
logger.SetLevel(config.GetString("log_level"))
|
||||
node.SetLogger(log.With(logger, "node", Name))
|
||||
```
|
||||
|
||||
**Other log formatters**
|
||||
|
||||
In the future, we may want other formatters like JSONFormatter.
|
||||
|
||||
```
|
||||
{ "level": "notice", "time": "2017-04-25 14:45:08.562471297 -0400 EDT", "module": "consensus", "msg": "ABCI Replay Blocks", "appHeight": 0, "storeHeight": 0, "stateHeight": 0 }
|
||||
```
|
||||
|
||||
### 3) Dynamic logger setting
|
||||
|
||||
https://dave.cheney.net/2017/01/23/the-package-level-logger-anti-pattern
|
||||
|
||||
This is the hardest part and where the most work will be done. logger should be tied to the processing struct, or the context if it adds some fields to the logger.
|
||||
|
||||
```go
|
||||
type BaseService struct {
|
||||
log log15.Logger
|
||||
name string
|
||||
started uint32 // atomic
|
||||
stopped uint32 // atomic
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
BaseService already contains `log` field, so most of the structs embedding it should be fine. We should rename it to `logger`.
|
||||
|
||||
The only thing missing is the ability to set logger:
|
||||
|
||||
```
|
||||
func (bs *BaseService) SetLogger(l log.Logger) {
|
||||
bs.logger = l
|
||||
}
|
||||
```
|
||||
|
||||
### 4) Conventions
|
||||
|
||||
Important keyvals should go first. Example:
|
||||
|
||||
```
|
||||
correct
|
||||
I[2017-04-25|14:45:08.322] ABCI Replay Blocks module=consensus instance=1 appHeight=0 storeHeight=0 stateHeight=0
|
||||
```
|
||||
|
||||
not
|
||||
|
||||
```
|
||||
wrong
|
||||
I[2017-04-25|14:45:08.322] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0 instance=1
|
||||
```
|
||||
|
||||
for that in most cases you'll need to add `instance` field to a logger upon creating, not when u log a particular message:
|
||||
|
||||
```go
|
||||
colorFn := func(keyvals ...interface{}) term.FgBgColor {
|
||||
for i := 1; i < len(keyvals); i += 2 {
|
||||
if keyvals[i] == "instance" && keyvals[i+1] == "1" {
|
||||
return term.FgBgColor{Fg: term.Blue}
|
||||
} else if keyvals[i] == "instance" && keyvals[i+1] == "1" {
|
||||
return term.FgBgColor{Fg: term.Red}
|
||||
}
|
||||
}
|
||||
return term.FgBgColor{}
|
||||
}
|
||||
logger := term.NewLogger(os.Stdout, log.NewTmLogger, colorFn)
|
||||
|
||||
c1 := NewConsensusReactor(...)
|
||||
c1.SetLogger(log.With(logger, "instance", 1))
|
||||
|
||||
c2 := NewConsensusReactor(...)
|
||||
c2.SetLogger(log.With(logger, "instance", 2))
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
proposed
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
Dynamic logger, which could be turned off for some modules at runtime. Public interface for other projects using Tendermint libraries.
|
||||
|
||||
### Negative
|
||||
|
||||
We may loose the ability to color keys in keyvalue pairs. go-kit allow you to easily change foreground / background colors of the whole string, but not its parts.
|
||||
|
||||
### Neutral
|
||||
|
||||
## Appendix A.
|
||||
|
||||
I really like a minimalistic approach go-kit took with his logger https://github.com/go-kit/kit/tree/master/log:
|
||||
|
||||
```
|
||||
type Logger interface {
|
||||
Log(keyvals ...interface{}) error
|
||||
}
|
||||
```
|
||||
|
||||
See [The Hunt for a Logger Interface](https://go-talks.appspot.com/github.com/ChrisHines/talks/structured-logging/structured-logging.slide). The advantage is greater composability (check out how go-kit defines colored logging or log-leveled logging on top of this interface https://github.com/go-kit/kit/tree/master/log).
|
||||
@@ -1,88 +0,0 @@
|
||||
# ADR 2: Event Subscription
|
||||
|
||||
## Context
|
||||
|
||||
In the light client (or any other client), the user may want to **subscribe to
|
||||
a subset of transactions** (rather than all of them) using `/subscribe?event=X`. For
|
||||
example, I want to subscribe for all transactions associated with a particular
|
||||
account. Same for fetching. The user may want to **fetch transactions based on
|
||||
some filter** (rather than fetching all the blocks). For example, I want to get
|
||||
all transactions for a particular account in the last two weeks (`tx's block time >= '2017-06-05'`).
|
||||
|
||||
Now you can't even subscribe to "all txs" in Tendermint.
|
||||
|
||||
The goal is a simple and easy to use API for doing that.
|
||||
|
||||

|
||||
|
||||
## Decision
|
||||
|
||||
ABCI app return tags with a `DeliverTx` response inside the `data` field (_for
|
||||
now, later we may create a separate field_). Tags is a list of key-value pairs,
|
||||
protobuf encoded.
|
||||
|
||||
Example data:
|
||||
|
||||
```json
|
||||
{
|
||||
"abci.account.name": "Igor",
|
||||
"abci.account.address": "0xdeadbeef",
|
||||
"tx.gas": 7
|
||||
}
|
||||
```
|
||||
|
||||
### Subscribing for transactions events
|
||||
|
||||
If the user wants to receive only a subset of transactions, ABCI-app must
|
||||
return a list of tags with a `DeliverTx` response. These tags will be parsed and
|
||||
matched with the current queries (subscribers). If the query matches the tags,
|
||||
subscriber will get the transaction event.
|
||||
|
||||
```
|
||||
/subscribe?query="tm.event = Tx AND tx.hash = AB0023433CF0334223212243BDD AND abci.account.invoice.number = 22"
|
||||
```
|
||||
|
||||
A new package must be developed to replace the current `events` package. It
|
||||
will allow clients to subscribe to a different types of events in the future:
|
||||
|
||||
```
|
||||
/subscribe?query="abci.account.invoice.number = 22"
|
||||
/subscribe?query="abci.account.invoice.owner CONTAINS Igor"
|
||||
```
|
||||
|
||||
### Fetching transactions
|
||||
|
||||
This is a bit tricky because a) we want to support a number of indexers, all of
|
||||
which have a different API b) we don't know whenever tags will be sufficient
|
||||
for the most apps (I guess we'll see).
|
||||
|
||||
```
|
||||
/txs/search?query="tx.hash = AB0023433CF0334223212243BDD AND abci.account.owner CONTAINS Igor"
|
||||
/txs/search?query="abci.account.owner = Igor"
|
||||
```
|
||||
|
||||
For historic queries we will need a indexing storage (Postgres, SQLite, ...).
|
||||
|
||||
### Issues
|
||||
|
||||
- https://github.com/tendermint/tendermint/issues/376
|
||||
- https://github.com/tendermint/tendermint/issues/287
|
||||
- https://github.com/tendermint/tendermint/issues/525 (related)
|
||||
|
||||
## Status
|
||||
|
||||
proposed
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- same format for event notifications and search APIs
|
||||
- powerful enough query
|
||||
|
||||
### Negative
|
||||
|
||||
- performance of the `match` function (where we have too many queries / subscribers)
|
||||
- there is an issue where there are too many txs in the DB
|
||||
|
||||
### Neutral
|
||||
@@ -1,34 +0,0 @@
|
||||
# ADR 3: Must an ABCI-app have an RPC server?
|
||||
|
||||
## Context
|
||||
|
||||
ABCI-server could expose its own RPC-server and act as a proxy to Tendermint.
|
||||
|
||||
The idea was for the Tendermint RPC to just be a transparent proxy to the app.
|
||||
Clients need to talk to Tendermint for proofs, unless we burden all app devs
|
||||
with exposing Tendermint proof stuff. Also seems less complex to lock down one
|
||||
server than two, but granted it makes querying a bit more kludgy since it needs
|
||||
to be passed as a `Query`. Also, **having a very standard rpc interface means
|
||||
the light-client can work with all apps and handle proofs**. The only
|
||||
app-specific logic is decoding the binary data to a more readable form (eg.
|
||||
json). This is a huge advantage for code-reuse and standardization.
|
||||
|
||||
## Decision
|
||||
|
||||
We dont expose an RPC server on any of our ABCI-apps.
|
||||
|
||||
## Status
|
||||
|
||||
accepted
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Unified interface for all apps
|
||||
|
||||
### Negative
|
||||
|
||||
- `Query` interface
|
||||
|
||||
### Neutral
|
||||
@@ -1,38 +0,0 @@
|
||||
# ADR 004: Historical Validators
|
||||
|
||||
## Context
|
||||
|
||||
Right now, we can query the present validator set, but there is no history.
|
||||
If you were offline for a long time, there is no way to reconstruct past validators. This is needed for the light client and we agreed needs enhancement of the API.
|
||||
|
||||
## Decision
|
||||
|
||||
For every block, store a new structure that contains either the latest validator set,
|
||||
or the height of the last block for which the validator set changed. Note this is not
|
||||
the height of the block which returned the validator set change itself, but the next block,
|
||||
ie. the first block it comes into effect for.
|
||||
|
||||
Storing the validators will be handled by the `state` package.
|
||||
|
||||
At some point in the future, we may consider more efficient storage in the case where the validators
|
||||
are updated frequently - for instance by only saving the diffs, rather than the whole set.
|
||||
|
||||
An alternative approach suggested keeping the validator set, or diffs of it, in a merkle IAVL tree.
|
||||
While it might afford cheaper proofs that a validator set has not changed, it would be more complex,
|
||||
and likely less efficient.
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Can query old validator sets, with proof.
|
||||
|
||||
### Negative
|
||||
|
||||
- Writes an extra structure to disk with every block.
|
||||
|
||||
### Neutral
|
||||
@@ -1,85 +0,0 @@
|
||||
# ADR 005: Consensus Params
|
||||
|
||||
## Context
|
||||
|
||||
Consensus critical parameters controlling blockchain capacity have until now been hard coded, loaded from a local config, or neglected.
|
||||
Since they may be need to be different in different networks, and potentially to evolve over time within
|
||||
networks, we seek to initialize them in a genesis file, and expose them through the ABCI.
|
||||
|
||||
While we have some specific parameters now, like maximum block and transaction size, we expect to have more in the future,
|
||||
such as a period over which evidence is valid, or the frequency of checkpoints.
|
||||
|
||||
## Decision
|
||||
|
||||
### ConsensusParams
|
||||
|
||||
No consensus critical parameters should ever be found in the `config.toml`.
|
||||
|
||||
A new `ConsensusParams` is optionally included in the `genesis.json` file,
|
||||
and loaded into the `State`. Any items not included are set to their default value.
|
||||
A value of 0 is undefined (see ABCI, below). A value of -1 is used to indicate the parameter does not apply.
|
||||
The parameters are used to determine the validity of a block (and tx) via the union of all relevant parameters.
|
||||
|
||||
```
|
||||
type ConsensusParams struct {
|
||||
BlockSize
|
||||
TxSize
|
||||
BlockGossip
|
||||
}
|
||||
|
||||
type BlockSize struct {
|
||||
MaxBytes int
|
||||
MaxTxs int
|
||||
MaxGas int
|
||||
}
|
||||
|
||||
type TxSize struct {
|
||||
MaxBytes int
|
||||
MaxGas int
|
||||
}
|
||||
|
||||
type BlockGossip struct {
|
||||
BlockPartSizeBytes int
|
||||
}
|
||||
```
|
||||
|
||||
The `ConsensusParams` can evolve over time by adding new structs that cover different aspects of the consensus rules.
|
||||
|
||||
The `BlockPartSizeBytes` and the `BlockSize.MaxBytes` are enforced to be greater than 0.
|
||||
The former because we need a part size, the latter so that we always have at least some sanity check over the size of blocks.
|
||||
|
||||
### ABCI
|
||||
|
||||
#### InitChain
|
||||
|
||||
InitChain currently takes the initial validator set. It should be extended to also take parts of the ConsensusParams.
|
||||
There is some case to be made for it to take the entire Genesis, except there may be things in the genesis,
|
||||
like the BlockPartSize, that the app shouldn't really know about.
|
||||
|
||||
#### EndBlock
|
||||
|
||||
The EndBlock response includes a `ConsensusParams`, which includes BlockSize and TxSize, but not BlockGossip.
|
||||
Other param struct can be added to `ConsensusParams` in the future.
|
||||
The `0` value is used to denote no change.
|
||||
Any other value will update that parameter in the `State.ConsensusParams`, to be applied for the next block.
|
||||
Tendermint should have hard-coded upper limits as sanity checks.
|
||||
|
||||
## Status
|
||||
|
||||
Proposed.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Alternative capacity limits and consensus parameters can be specified without re-compiling the software.
|
||||
- They can also change over time under the control of the application
|
||||
|
||||
### Negative
|
||||
|
||||
- More exposed parameters is more complexity
|
||||
- Different rules at different heights in the blockchain complicates fast sync
|
||||
|
||||
### Neutral
|
||||
|
||||
- The TxSize, which checks validity, may be in conflict with the config's `max_block_size_tx`, which determines proposal sizes
|
||||
@@ -1,229 +0,0 @@
|
||||
# ADR 006: Trust Metric Design
|
||||
|
||||
## Context
|
||||
|
||||
The proposed trust metric will allow Tendermint to maintain local trust rankings for peers it has directly interacted with, which can then be used to implement soft security controls. The calculations were obtained from the [TrustGuard](https://dl.acm.org/citation.cfm?id=1060808) project.
|
||||
|
||||
### Background
|
||||
|
||||
The Tendermint Core project developers would like to improve Tendermint security and reliability by keeping track of the level of trustworthiness peers have demonstrated within the peer-to-peer network. This way, undesirable outcomes from peers will not immediately result in them being dropped from the network (potentially causing drastic changes to take place). Instead, peers behavior can be monitored with appropriate metrics and be removed from the network once Tendermint Core is certain the peer is a threat. For example, when the PEXReactor makes a request for peers network addresses from a already known peer, and the returned network addresses are unreachable, this untrustworthy behavior should be tracked. Returning a few bad network addresses probably shouldn’t cause a peer to be dropped, while excessive amounts of this behavior does qualify the peer being dropped.
|
||||
|
||||
Trust metrics can be circumvented by malicious nodes through the use of strategic oscillation techniques, which adapts the malicious node’s behavior pattern in order to maximize its goals. For instance, if the malicious node learns that the time interval of the Tendermint trust metric is _X_ hours, then it could wait _X_ hours in-between malicious activities. We could try to combat this issue by increasing the interval length, yet this will make the system less adaptive to recent events.
|
||||
|
||||
Instead, having shorter intervals, but keeping a history of interval values, will give our metric the flexibility needed in order to keep the network stable, while also making it resilient against a strategic malicious node in the Tendermint peer-to-peer network. Also, the metric can access trust data over a rather long period of time while not greatly increasing its history size by aggregating older history values over a larger number of intervals, and at the same time, maintain great precision for the recent intervals. This approach is referred to as fading memories, and closely resembles the way human beings remember their experiences. The trade-off to using history data is that the interval values should be preserved in-between executions of the node.
|
||||
|
||||
### References
|
||||
|
||||
S. Mudhakar, L. Xiong, and L. Liu, “TrustGuard: Countering Vulnerabilities in Reputation Management for Decentralized Overlay Networks,” in _Proceedings of the 14th international conference on World Wide Web, pp. 422-431_, May 2005.
|
||||
|
||||
## Decision
|
||||
|
||||
The proposed trust metric will allow a developer to inform the trust metric store of all good and bad events relevant to a peer's behavior, and at any time, the metric can be queried for a peer's current trust ranking.
|
||||
|
||||
The three subsections below will cover the process being considered for calculating the trust ranking, the concept of the trust metric store, and the interface for the trust metric.
|
||||
|
||||
### Proposed Process
|
||||
|
||||
The proposed trust metric will count good and bad events relevant to the object, and calculate the percent of counters that are good over an interval with a predefined duration. This is the procedure that will continue for the life of the trust metric. When the trust metric is queried for the current **trust value**, a resilient equation will be utilized to perform the calculation.
|
||||
|
||||
The equation being proposed resembles a Proportional-Integral-Derivative (PID) controller used in control systems. The proportional component allows us to be sensitive to the value of the most recent interval, while the integral component allows us to incorporate trust values stored in the history data, and the derivative component allows us to give weight to sudden changes in the behavior of a peer. We compute the trust value of a peer in interval i based on its current trust ranking, its trust rating history prior to interval _i_ (over the past _maxH_ number of intervals) and its trust ranking fluctuation. We will break up the equation into the three components.
|
||||
|
||||
```math
|
||||
(1) Proportional Value = a * R[i]
|
||||
```
|
||||
|
||||
where _R_[*i*] denotes the raw trust value at time interval _i_ (where _i_ == 0 being current time) and _a_ is the weight applied to the contribution of the current reports. The next component of our equation uses a weighted sum over the last _maxH_ intervals to calculate the history value for time _i_:
|
||||
|
||||
`H[i] =` 
|
||||
|
||||
The weights can be chosen either optimistically or pessimistically. An optimistic weight creates larger weights for newer history data values, while the the pessimistic weight creates larger weights for time intervals with lower scores. The default weights used during the calculation of the history value are optimistic and calculated as _Wk_ = 0.8^_k_, for time interval _k_. With the history value available, we can now finish calculating the integral value:
|
||||
|
||||
```math
|
||||
(2) Integral Value = b * H[i]
|
||||
```
|
||||
|
||||
Where _H_[*i*] denotes the history value at time interval _i_ and _b_ is the weight applied to the contribution of past performance for the object being measured. The derivative component will be calculated as follows:
|
||||
|
||||
```math
|
||||
D[i] = R[i] – H[i]
|
||||
|
||||
(3) Derivative Value = c(D[i]) * D[i]
|
||||
```
|
||||
|
||||
Where the value of _c_ is selected based on the _D_[*i*] value relative to zero. The default selection process makes _c_ equal to 0 unless _D_[*i*] is a negative value, in which case c is equal to 1. The result is that the maximum penalty is applied when current behavior is lower than previously experienced behavior. If the current behavior is better than the previously experienced behavior, then the Derivative Value has no impact on the trust value. With the three components brought together, our trust value equation is calculated as follows:
|
||||
|
||||
```math
|
||||
TrustValue[i] = a * R[i] + b * H[i] + c(D[i]) * D[i]
|
||||
```
|
||||
|
||||
As a performance optimization that will keep the amount of raw interval data being saved to a reasonable size of _m_, while allowing us to represent 2^_m_ - 1 history intervals, we can employ the fading memories technique that will trade space and time complexity for the precision of the history data values by summarizing larger quantities of less recent values. While our equation above attempts to access up to _maxH_ (which can be 2^_m_ - 1), we will map those requests down to _m_ values using equation 4 below:
|
||||
|
||||
```math
|
||||
(4) j = index, where index > 0
|
||||
```
|
||||
|
||||
Where _j_ is one of _(0, 1, 2, … , m – 1)_ indices used to access history interval data. Now we can access the raw intervals using the following calculations:
|
||||
|
||||
```math
|
||||
R[0] = raw data for current time interval
|
||||
```
|
||||
|
||||
`R[j] =` 
|
||||
|
||||
### Trust Metric Store
|
||||
|
||||
Similar to the P2P subsystem AddrBook, the trust metric store will maintain information relevant to Tendermint peers. Additionally, the trust metric store will ensure that trust metrics will only be active for peers that a node is currently and directly engaged with.
|
||||
|
||||
Reactors will provide a peer key to the trust metric store in order to retrieve the associated trust metric. The trust metric can then record new positive and negative events experienced by the reactor, as well as provided the current trust score calculated by the metric.
|
||||
|
||||
When the node is shutting down, the trust metric store will save history data for trust metrics associated with all known peers. This saved information allows experiences with a peer to be preserved across node executions, which can span a tracking windows of days or weeks. The trust history data is loaded automatically during OnStart.
|
||||
|
||||
### Interface Detailed Design
|
||||
|
||||
Each trust metric allows for the recording of positive/negative events, querying the current trust value/score, and the stopping/pausing of tracking over time intervals. This can be seen below:
|
||||
|
||||
```go
|
||||
// TrustMetric - keeps track of peer reliability
|
||||
type TrustMetric struct {
|
||||
// Private elements.
|
||||
}
|
||||
|
||||
// Pause tells the metric to pause recording data over time intervals.
|
||||
// All method calls that indicate events will unpause the metric
|
||||
func (tm *TrustMetric) Pause() {}
|
||||
|
||||
// Stop tells the metric to stop recording data over time intervals
|
||||
func (tm *TrustMetric) Stop() {}
|
||||
|
||||
// BadEvents indicates that an undesirable event(s) took place
|
||||
func (tm *TrustMetric) BadEvents(num int) {}
|
||||
|
||||
// GoodEvents indicates that a desirable event(s) took place
|
||||
func (tm *TrustMetric) GoodEvents(num int) {}
|
||||
|
||||
// TrustValue gets the dependable trust value; always between 0 and 1
|
||||
func (tm *TrustMetric) TrustValue() float64 {}
|
||||
|
||||
// TrustScore gets a score based on the trust value always between 0 and 100
|
||||
func (tm *TrustMetric) TrustScore() int {}
|
||||
|
||||
// NewMetric returns a trust metric with the default configuration
|
||||
func NewMetric() *TrustMetric {}
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// For example
|
||||
|
||||
tm := NewMetric()
|
||||
|
||||
tm.BadEvents(1)
|
||||
score := tm.TrustScore()
|
||||
|
||||
tm.Stop()
|
||||
```
|
||||
|
||||
Some of the trust metric parameters can be configured. The weight values should probably be left alone in more cases, yet the time durations for the tracking window and individual time interval should be considered.
|
||||
|
||||
```go
|
||||
// TrustMetricConfig - Configures the weight functions and time intervals for the metric
|
||||
type TrustMetricConfig struct {
|
||||
// Determines the percentage given to current behavior
|
||||
ProportionalWeight float64
|
||||
|
||||
// Determines the percentage given to prior behavior
|
||||
IntegralWeight float64
|
||||
|
||||
// The window of time that the trust metric will track events across.
|
||||
// This can be set to cover many days without issue
|
||||
TrackingWindow time.Duration
|
||||
|
||||
// Each interval should be short for adapability.
|
||||
// Less than 30 seconds is too sensitive,
|
||||
// and greater than 5 minutes will make the metric numb
|
||||
IntervalLength time.Duration
|
||||
}
|
||||
|
||||
// DefaultConfig returns a config with values that have been tested and produce desirable results
|
||||
func DefaultConfig() TrustMetricConfig {}
|
||||
|
||||
// NewMetricWithConfig returns a trust metric with a custom configuration
|
||||
func NewMetricWithConfig(tmc TrustMetricConfig) *TrustMetric {}
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// For example
|
||||
|
||||
config := TrustMetricConfig{
|
||||
TrackingWindow: time.Minute * 60 * 24, // one day
|
||||
IntervalLength: time.Minute * 2,
|
||||
}
|
||||
|
||||
tm := NewMetricWithConfig(config)
|
||||
|
||||
tm.BadEvents(10)
|
||||
tm.Pause()
|
||||
tm.GoodEvents(1) // becomes active again
|
||||
```
|
||||
|
||||
A trust metric store should be created with a DB that has persistent storage so it can save history data across node executions. All trust metrics instantiated by the store will be created with the provided TrustMetricConfig configuration.
|
||||
|
||||
When you attempt to fetch the trust metric for a peer, and an entry does not exist in the trust metric store, a new metric is automatically created and the entry made within the store.
|
||||
|
||||
In additional to the fetching method, GetPeerTrustMetric, the trust metric store provides a method to call when a peer has disconnected from the node. This is so the metric can be paused (history data will not be saved) for periods of time when the node is not having direct experiences with the peer.
|
||||
|
||||
```go
|
||||
// TrustMetricStore - Manages all trust metrics for peers
|
||||
type TrustMetricStore struct {
|
||||
cmn.BaseService
|
||||
|
||||
// Private elements
|
||||
}
|
||||
|
||||
// OnStart implements Service
|
||||
func (tms *TrustMetricStore) OnStart() error {}
|
||||
|
||||
// OnStop implements Service
|
||||
func (tms *TrustMetricStore) OnStop() {}
|
||||
|
||||
// NewTrustMetricStore returns a store that saves data to the DB
|
||||
// and uses the config when creating new trust metrics
|
||||
func NewTrustMetricStore(db dbm.DB, tmc TrustMetricConfig) *TrustMetricStore {}
|
||||
|
||||
// Size returns the number of entries in the trust metric store
|
||||
func (tms *TrustMetricStore) Size() int {}
|
||||
|
||||
// GetPeerTrustMetric returns a trust metric by peer key
|
||||
func (tms *TrustMetricStore) GetPeerTrustMetric(key string) *TrustMetric {}
|
||||
|
||||
// PeerDisconnected pauses the trust metric associated with the peer identified by the key
|
||||
func (tms *TrustMetricStore) PeerDisconnected(key string) {}
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// For example
|
||||
|
||||
db := dbm.NewDB("trusthistory", "goleveldb", dirPathStr)
|
||||
tms := NewTrustMetricStore(db, DefaultConfig())
|
||||
|
||||
tm := tms.GetPeerTrustMetric(key)
|
||||
tm.BadEvents(1)
|
||||
|
||||
tms.PeerDisconnected(key)
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
Approved.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- The trust metric will allow Tendermint to make non-binary security and reliability decisions
|
||||
- Will help Tendermint implement deterrents that provide soft security controls, yet avoids disruption on the network
|
||||
- Will provide useful profiling information when analyzing performance over time related to peer interaction
|
||||
|
||||
### Negative
|
||||
|
||||
- Requires saving the trust metric history data across node executions
|
||||
|
||||
### Neutral
|
||||
|
||||
- Keep in mind that, good events need to be recorded just as bad events do using this implementation
|
||||
@@ -1,106 +0,0 @@
|
||||
# ADR 007: Trust Metric Usage Guide
|
||||
|
||||
## Context
|
||||
|
||||
Tendermint is required to monitor peer quality in order to inform its peer dialing and peer exchange strategies.
|
||||
|
||||
When a node first connects to the network, it is important that it can quickly find good peers.
|
||||
Thus, while a node has fewer connections, it should prioritize connecting to higher quality peers.
|
||||
As the node becomes well connected to the rest of the network, it can dial lesser known or lesser
|
||||
quality peers and help assess their quality. Similarly, when queried for peers, a node should make
|
||||
sure they dont return low quality peers.
|
||||
|
||||
Peer quality can be tracked using a trust metric that flags certain behaviours as good or bad. When enough
|
||||
bad behaviour accumulates, we can mark the peer as bad and disconnect.
|
||||
For example, when the PEXReactor makes a request for peers network addresses from an already known peer, and the returned network addresses are unreachable, this undesirable behavior should be tracked. Returning a few bad network addresses probably shouldn’t cause a peer to be dropped, while excessive amounts of this behavior does qualify the peer for removal. The originally proposed approach and design document for the trust metric can be found in the [ADR 006](adr-006-trust-metric.md) document.
|
||||
|
||||
The trust metric implementation allows a developer to obtain a peer's trust metric from a trust metric store, and track good and bad events relevant to a peer's behavior, and at any time, the peer's metric can be queried for a current trust value. The current trust value is calculated with a formula that utilizes current behavior, previous behavior, and change between the two. Current behavior is calculated as the percentage of good behavior within a time interval. The time interval is short; probably set between 30 seconds and 5 minutes. On the other hand, the historic data can estimate a peer's behavior over days worth of tracking. At the end of a time interval, the current behavior becomes part of the historic data, and a new time interval begins with the good and bad counters reset to zero.
|
||||
|
||||
These are some important things to keep in mind regarding how the trust metrics handle time intervals and scoring:
|
||||
|
||||
- Each new time interval begins with a perfect score
|
||||
- Bad events quickly bring the score down and good events cause the score to slowly rise
|
||||
- When the time interval is over, the percentage of good events becomes historic data.
|
||||
|
||||
Some useful information about the inner workings of the trust metric:
|
||||
|
||||
- When a trust metric is first instantiated, a timer (ticker) periodically fires in order to handle transitions between trust metric time intervals
|
||||
- If a peer is disconnected from a node, the timer should be paused, since the node is no longer connected to that peer
|
||||
- The ability to pause the metric is supported with the store **PeerDisconnected** method and the metric **Pause** method
|
||||
- After a pause, if a good or bad event method is called on a metric, it automatically becomes unpaused and begins a new time interval.
|
||||
|
||||
## Decision
|
||||
|
||||
The trust metric capability is now available, yet, it still leaves the question of how should it be applied throughout Tendermint in order to properly track the quality of peers?
|
||||
|
||||
### Proposed Process
|
||||
|
||||
Peers are managed using an address book and a trust metric:
|
||||
|
||||
- The address book keeps a record of peers and provides selection methods
|
||||
- The trust metric tracks the quality of the peers
|
||||
|
||||
#### Presence in Address Book
|
||||
|
||||
Outbound peers are added to the address book before they are dialed,
|
||||
and inbound peers are added once the peer connection is set up.
|
||||
Peers are also added to the address book when they are received in response to
|
||||
a pexRequestMessage.
|
||||
|
||||
While a node has less than `needAddressThreshold`, it will periodically request more,
|
||||
via pexRequestMessage, from randomly selected peers and from newly dialed outbound peers.
|
||||
|
||||
When a new address is added to an address book that has more than `0.5*needAddressThreshold` addresses,
|
||||
then with some low probability, a randomly chosen low quality peer is removed.
|
||||
|
||||
#### Outbound Peers
|
||||
|
||||
Peers attempt to maintain a minimum number of outbound connections by
|
||||
repeatedly querying the address book for peers to connect to.
|
||||
While a node has few to no outbound connections, the address book is biased to return
|
||||
higher quality peers. As the node increases the number of outbound connections,
|
||||
the address book is biased to return less-vetted or lower-quality peers.
|
||||
|
||||
#### Inbound Peers
|
||||
|
||||
Peers also maintain a maximum number of total connections, MaxNumPeers.
|
||||
If a peer has MaxNumPeers, new incoming connections will be accepted with low probability.
|
||||
When such a new connection is accepted, the peer disconnects from a probabilistically chosen low ranking peer
|
||||
so it does not exceed MaxNumPeers.
|
||||
|
||||
#### Peer Exchange
|
||||
|
||||
When a peer receives a pexRequestMessage, it returns a random sample of high quality peers from the address book. Peers with no score or low score should not be inclided in a response to pexRequestMessage.
|
||||
|
||||
#### Peer Quality
|
||||
|
||||
Peer quality is tracked in the connection and across the reactors by storing the TrustMetric in the peer's
|
||||
thread safe Data store.
|
||||
|
||||
Peer behaviour is then defined as one of the following:
|
||||
|
||||
- Fatal - something outright malicious that causes us to disconnect the peer and ban it from the address book for some amount of time
|
||||
- Bad - Any kind of timeout, messages that don't unmarshal, fail other validity checks, or messages we didn't ask for or aren't expecting (usually worth one bad event)
|
||||
- Neutral - Unknown channels/message types/version upgrades (no good or bad events recorded)
|
||||
- Correct - Normal correct behavior (worth one good event)
|
||||
- Good - some random majority of peers per reactor sending us useful messages (worth more than one good event).
|
||||
|
||||
Note that Fatal behaviour causes us to remove the peer, and neutral behaviour does not affect the score.
|
||||
|
||||
## Status
|
||||
|
||||
Proposed.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Bringing the address book and trust metric store together will cause the network to be built in a way that encourages greater security and reliability.
|
||||
|
||||
### Negative
|
||||
|
||||
- TBD
|
||||
|
||||
### Neutral
|
||||
|
||||
- Keep in mind that, good events need to be recorded just as bad events do using this implementation.
|
||||
@@ -1,35 +0,0 @@
|
||||
# ADR 008: SocketPV
|
||||
|
||||
Tendermint node's should support only two in-process PrivValidator
|
||||
implementations:
|
||||
|
||||
- FilePV uses an unencrypted private key in a "priv_validator.json" file - no
|
||||
configuration required (just `tendermint init`).
|
||||
- TCPVal and IPCVal use TCP and Unix sockets respectively to send signing requests
|
||||
to another process - the user is responsible for starting that process themselves.
|
||||
|
||||
Both TCPVal and IPCVal addresses can be provided via flags at the command line
|
||||
or in the configuration file; TCPVal addresses must be of the form
|
||||
`tcp://<ip_address>:<port>` and IPCVal addresses `unix:///path/to/file.sock` -
|
||||
doing so will cause Tendermint to ignore any private validator files.
|
||||
|
||||
TCPVal will listen on the given address for incoming connections from an external
|
||||
private validator process. It will halt any operation until at least one external
|
||||
process successfully connected.
|
||||
|
||||
The external priv_validator process will dial the address to connect to
|
||||
Tendermint, and then Tendermint will send requests on the ensuing connection to
|
||||
sign votes and proposals. Thus the external process initiates the connection,
|
||||
but the Tendermint process makes all requests. In a later stage we're going to
|
||||
support multiple validators for fault tolerance. To prevent double signing they
|
||||
need to be synced, which is deferred to an external solution (see #1185).
|
||||
|
||||
Conversely, IPCVal will make an outbound connection to an existing socket opened
|
||||
by the external validator process.
|
||||
|
||||
In addition, Tendermint will provide implementations that can be run in that
|
||||
external process. These include:
|
||||
|
||||
- FilePV will encrypt the private key, and the user must enter password to
|
||||
decrypt key when process is started.
|
||||
- LedgerPV uses a Ledger Nano S to handle all signing.
|
||||
@@ -1,271 +0,0 @@
|
||||
# ADR 009: ABCI UX Improvements
|
||||
|
||||
## Changelog
|
||||
|
||||
23-06-2018: Some minor fixes from review
|
||||
07-06-2018: Some updates based on discussion with Jae
|
||||
07-06-2018: Initial draft to match what was released in ABCI v0.11
|
||||
|
||||
## Context
|
||||
|
||||
The ABCI was first introduced in late 2015. It's purpose is to be:
|
||||
|
||||
- a generic interface between state machines and their replication engines
|
||||
- agnostic to the language the state machine is written in
|
||||
- agnostic to the replication engine that drives it
|
||||
|
||||
This means ABCI should provide an interface for both pluggable applications and
|
||||
pluggable consensus engines.
|
||||
|
||||
To achieve this, it uses Protocol Buffers (proto3) for message types. The dominant
|
||||
implementation is in Go.
|
||||
|
||||
After some recent discussions with the community on github, the following were
|
||||
identified as pain points:
|
||||
|
||||
- Amino encoded types
|
||||
- Managing validator sets
|
||||
- Imports in the protobuf file
|
||||
|
||||
See the [references](#references) for more.
|
||||
|
||||
### Imports
|
||||
|
||||
The native proto library in Go generates inflexible and verbose code.
|
||||
Many in the Go community have adopted a fork called
|
||||
[gogoproto](https://github.com/gogo/protobuf) that provides a
|
||||
variety of features aimed to improve the developer experience.
|
||||
While `gogoproto` is nice, it creates an additional dependency, and compiling
|
||||
the protobuf types for other languages has been reported to fail when `gogoproto` is used.
|
||||
|
||||
### Amino
|
||||
|
||||
Amino is an encoding protocol designed to improve over insufficiencies of protobuf.
|
||||
It's goal is to be proto4.
|
||||
|
||||
Many people are frustrated by incompatibility with protobuf,
|
||||
and with the requirement for Amino to be used at all within ABCI.
|
||||
|
||||
We intend to make Amino successful enough that we can eventually use it for ABCI
|
||||
message types directly. By then it should be called proto4. In the meantime,
|
||||
we want it to be easy to use.
|
||||
|
||||
### PubKey
|
||||
|
||||
PubKeys are encoded using Amino (and before that, go-wire).
|
||||
Ideally, PubKeys are an interface type where we don't know all the
|
||||
implementation types, so its unfitting to use `oneof` or `enum`.
|
||||
|
||||
### Addresses
|
||||
|
||||
The address for ED25519 pubkey is the RIPEMD160 of the Amino
|
||||
encoded pubkey. This introduces an Amino dependency in the address generation,
|
||||
a functionality that is widely required and should be easy to compute as
|
||||
possible.
|
||||
|
||||
### Validators
|
||||
|
||||
To change the validator set, applications can return a list of validator updates
|
||||
with ResponseEndBlock. In these updates, the public key _must_ be included,
|
||||
because Tendermint requires the public key to verify validator signatures. This
|
||||
means ABCI developers have to work with PubKeys. That said, it would also be
|
||||
convenient to work with address information, and for it to be simple to do so.
|
||||
|
||||
### AbsentValidators
|
||||
|
||||
Tendermint also provides a list of validators in BeginBlock who did not sign the
|
||||
last block. This allows applications to reflect availability behaviour in the
|
||||
application, for instance by punishing validators for not having votes included
|
||||
in commits.
|
||||
|
||||
### InitChain
|
||||
|
||||
Tendermint passes in a list of validators here, and nothing else. It would
|
||||
benefit the application to be able to control the initial validator set. For
|
||||
instance the genesis file could include application-based information about the
|
||||
initial validator set that the application could process to determine the
|
||||
initial validator set. Additionally, InitChain would benefit from getting all
|
||||
the genesis information.
|
||||
|
||||
### Header
|
||||
|
||||
ABCI provides the Header in RequestBeginBlock so the application can have
|
||||
important information about the latest state of the blockchain.
|
||||
|
||||
## Decision
|
||||
|
||||
### Imports
|
||||
|
||||
Move away from gogoproto. In the short term, we will just maintain a second
|
||||
protobuf file without the gogoproto annotations. In the medium term, we will
|
||||
make copies of all the structs in Golang and shuttle back and forth. In the long
|
||||
term, we will use Amino.
|
||||
|
||||
### Amino
|
||||
|
||||
To simplify ABCI application development in the short term,
|
||||
Amino will be completely removed from the ABCI:
|
||||
|
||||
- It will not be required for PubKey encoding
|
||||
- It will not be required for computing PubKey addresses
|
||||
|
||||
That said, we are working to make Amino a huge success, and to become proto4.
|
||||
To facilitate adoption and cross-language compatibility in the near-term, Amino
|
||||
v1 will:
|
||||
|
||||
- be fully compatible with the subset of proto3 that excludes `oneof`
|
||||
- use the Amino prefix system to provide interface types, as opposed to `oneof`
|
||||
style union types.
|
||||
|
||||
That said, an Amino v2 will be worked on to improve the performance of the
|
||||
format and its useability in cryptographic applications.
|
||||
|
||||
### PubKey
|
||||
|
||||
Encoding schemes infect software. As a generic middleware, ABCI aims to have
|
||||
some cross scheme compatibility. For this it has no choice but to include opaque
|
||||
bytes from time to time. While we will not enforce Amino encoding for these
|
||||
bytes yet, we need to provide a type system. The simplest way to do this is to
|
||||
use a type string.
|
||||
|
||||
PubKey will now look like:
|
||||
|
||||
```
|
||||
message PubKey {
|
||||
string type
|
||||
bytes data
|
||||
}
|
||||
```
|
||||
|
||||
where `type` can be:
|
||||
|
||||
- "ed225519", with `data = <raw 32-byte pubkey>`
|
||||
- "secp256k1", with `data = <33-byte OpenSSL compressed pubkey>`
|
||||
|
||||
As we want to retain flexibility here, and since ideally, PubKey would be an
|
||||
interface type, we do not use `enum` or `oneof`.
|
||||
|
||||
### Addresses
|
||||
|
||||
To simplify and improve computing addresses, we change it to the first 20-bytes of the SHA256
|
||||
of the raw 32-byte public key.
|
||||
|
||||
We continue to use the Bitcoin address scheme for secp256k1 keys.
|
||||
|
||||
### Validators
|
||||
|
||||
Add a `bytes address` field:
|
||||
|
||||
```
|
||||
message Validator {
|
||||
bytes address
|
||||
PubKey pub_key
|
||||
int64 power
|
||||
}
|
||||
```
|
||||
|
||||
### RequestBeginBlock and AbsentValidators
|
||||
|
||||
To simplify this, RequestBeginBlock will include the complete validator set,
|
||||
including the address, and voting power of each validator, along
|
||||
with a boolean for whether or not they voted:
|
||||
|
||||
```
|
||||
message RequestBeginBlock {
|
||||
bytes hash
|
||||
Header header
|
||||
LastCommitInfo last_commit_info
|
||||
repeated Evidence byzantine_validators
|
||||
}
|
||||
|
||||
message LastCommitInfo {
|
||||
int32 CommitRound
|
||||
repeated SigningValidator validators
|
||||
}
|
||||
|
||||
message SigningValidator {
|
||||
Validator validator
|
||||
bool signed_last_block
|
||||
}
|
||||
```
|
||||
|
||||
Note that in Validators in RequestBeginBlock, we DO NOT include public keys. Public keys are
|
||||
larger than addresses and in the future, with quantum computers, will be much
|
||||
larger. The overhead of passing them, especially during fast-sync, is
|
||||
significant.
|
||||
|
||||
Additional, addresses are changing to be simpler to compute, further removing
|
||||
the need to include pubkeys here.
|
||||
|
||||
In short, ABCI developers must be aware of both addresses and public keys.
|
||||
|
||||
### ResponseEndBlock
|
||||
|
||||
Since ResponseEndBlock includes Validator, it must now include their address.
|
||||
|
||||
### InitChain
|
||||
|
||||
Change RequestInitChain to give the app all the information from the genesis file:
|
||||
|
||||
```
|
||||
message RequestInitChain {
|
||||
int64 time
|
||||
string chain_id
|
||||
ConsensusParams consensus_params
|
||||
repeated Validator validators
|
||||
bytes app_state_bytes
|
||||
}
|
||||
```
|
||||
|
||||
Change ResponseInitChain to allow the app to specify the initial validator set
|
||||
and consensus parameters.
|
||||
|
||||
```
|
||||
message ResponseInitChain {
|
||||
ConsensusParams consensus_params
|
||||
repeated Validator validators
|
||||
}
|
||||
```
|
||||
|
||||
### Header
|
||||
|
||||
Now that Tendermint Amino will be compatible with proto3, the Header in ABCI
|
||||
should exactly match the Tendermint header - they will then be encoded
|
||||
identically in ABCI and in Tendermint Core.
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Easier for developers to build on the ABCI
|
||||
- ABCI and Tendermint headers are identically serialized
|
||||
|
||||
### Negative
|
||||
|
||||
- Maintenance overhead of alternative type encoding scheme
|
||||
- Performance overhead of passing all validator info every block (at least its
|
||||
only addresses, and not also pubkeys)
|
||||
- Maintenance overhead of duplicate types
|
||||
|
||||
### Neutral
|
||||
|
||||
- ABCI developers must know about validator addresses
|
||||
|
||||
## References
|
||||
|
||||
- [ABCI v0.10.3 Specification (before this
|
||||
proposal)](https://github.com/tendermint/abci/blob/v0.10.3/specification.rst)
|
||||
- [ABCI v0.11.0 Specification (implementing first draft of this
|
||||
proposal)](https://github.com/tendermint/abci/blob/v0.11.0/specification.md)
|
||||
- [Ed25519 addresses](https://github.com/tendermint/go-crypto/issues/103)
|
||||
- [InitChain contains the
|
||||
Genesis](https://github.com/tendermint/abci/issues/216)
|
||||
- [PubKeys](https://github.com/tendermint/tendermint/issues/1524)
|
||||
- [Notes on
|
||||
Header](https://github.com/tendermint/tendermint/issues/1605)
|
||||
- [Gogoproto issues](https://github.com/tendermint/abci/issues/256)
|
||||
- [Absent Validators](https://github.com/tendermint/abci/issues/231)
|
||||
@@ -1,75 +0,0 @@
|
||||
# ADR 010: Crypto Changes
|
||||
|
||||
## Context
|
||||
|
||||
Tendermint is a cryptographic protocol that uses and composes a variety of cryptographic primitives.
|
||||
|
||||
After nearly 4 years of development, Tendermint has recently undergone multiple security reviews to search for vulnerabilities and to assess the the use and composition of cryptographic primitives.
|
||||
|
||||
### Hash Functions
|
||||
|
||||
Tendermint uses RIPEMD160 universally as a hash function, most notably in its Merkle tree implementation.
|
||||
|
||||
RIPEMD160 was chosen because it provides the shortest fingerprint that is long enough to be considered secure (ie. birthday bound of 80-bits).
|
||||
It was also developed in the open academic community, unlike NSA-designed algorithms like SHA256.
|
||||
|
||||
That said, the cryptographic community appears to unanimously agree on the security of SHA256. It has become a universal standard, especially now that SHA1 is broken, being required in TLS connections and having optimized support in hardware.
|
||||
|
||||
### Merkle Trees
|
||||
|
||||
Tendermint uses a simple Merkle tree to compute digests of large structures like transaction batches
|
||||
and even blockchain headers. The Merkle tree length prefixes byte arrays before concatenating and hashing them.
|
||||
It uses RIPEMD160.
|
||||
|
||||
### Addresses
|
||||
|
||||
ED25519 addresses are computed using the RIPEMD160 of the Amino encoding of the public key.
|
||||
RIPEMD160 is generally considered an outdated hash function, and is much slower
|
||||
than more modern functions like SHA256 or Blake2.
|
||||
|
||||
### Authenticated Encryption
|
||||
|
||||
Tendermint P2P connections use authenticated encryption to provide privacy and authentication in the communications.
|
||||
This is done using the simple Station-to-Station protocol with the NaCL Ed25519 library.
|
||||
|
||||
While there have been no vulnerabilities found in the implementation, there are some concerns:
|
||||
|
||||
- NaCL uses Salsa20, a not-widely used and relatively out-dated stream cipher that has been obsoleted by ChaCha20
|
||||
- Connections use RIPEMD160 to compute a value that is used for the encryption nonce with subtle requirements on how it's used
|
||||
|
||||
## Decision
|
||||
|
||||
### Hash Functions
|
||||
|
||||
Use the first 20-bytes of the SHA256 hash instead of RIPEMD160 for everything
|
||||
|
||||
### Merkle Trees
|
||||
|
||||
TODO
|
||||
|
||||
### Addresses
|
||||
|
||||
Compute ED25519 addresses as the first 20-bytes of the SHA256 of the raw 32-byte public key
|
||||
|
||||
### Authenticated Encryption
|
||||
|
||||
Make the following changes:
|
||||
|
||||
- Use xChaCha20 instead of xSalsa20 - https://github.com/tendermint/tendermint/issues/1124
|
||||
- Use an HKDF instead of RIPEMD160 to compute nonces - https://github.com/tendermint/tendermint/issues/1165
|
||||
|
||||
## Status
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- More modern and standard cryptographic functions with wider adoption and hardware acceleration
|
||||
|
||||
### Negative
|
||||
|
||||
- Exact authenticated encryption construction isn't already provided in a well-used library
|
||||
|
||||
### Neutral
|
||||
|
||||
## References
|
||||
@@ -1,116 +0,0 @@
|
||||
# ADR 011: Monitoring
|
||||
|
||||
## Changelog
|
||||
|
||||
08-06-2018: Initial draft
|
||||
11-06-2018: Reorg after @xla comments
|
||||
13-06-2018: Clarification about usage of labels
|
||||
|
||||
## Context
|
||||
|
||||
In order to bring more visibility into Tendermint, we would like it to report
|
||||
metrics and, maybe later, traces of transactions and RPC queries. See
|
||||
https://github.com/tendermint/tendermint/issues/986.
|
||||
|
||||
A few solutions were considered:
|
||||
|
||||
1. [Prometheus](https://prometheus.io)
|
||||
a) Prometheus API
|
||||
b) [go-kit metrics package](https://github.com/go-kit/kit/tree/master/metrics) as an interface plus Prometheus
|
||||
c) [telegraf](https://github.com/influxdata/telegraf)
|
||||
d) new service, which will listen to events emitted by pubsub and report metrics
|
||||
2. [OpenCensus](https://opencensus.io/introduction/)
|
||||
|
||||
### 1. Prometheus
|
||||
|
||||
Prometheus seems to be the most popular product out there for monitoring. It has
|
||||
a Go client library, powerful queries, alerts.
|
||||
|
||||
**a) Prometheus API**
|
||||
|
||||
We can commit to using Prometheus in Tendermint, but I think Tendermint users
|
||||
should be free to choose whatever monitoring tool they feel will better suit
|
||||
their needs (if they don't have existing one already). So we should try to
|
||||
abstract interface enough so people can switch between Prometheus and other
|
||||
similar tools.
|
||||
|
||||
**b) go-kit metrics package as an interface**
|
||||
|
||||
metrics package provides a set of uniform interfaces for service
|
||||
instrumentation and offers adapters to popular metrics packages:
|
||||
|
||||
https://godoc.org/github.com/go-kit/kit/metrics#pkg-subdirectories
|
||||
|
||||
Comparing to Prometheus API, we're losing customisability and control, but gaining
|
||||
freedom in choosing any instrument from the above list given we will extract
|
||||
metrics creation into a separate function (see "providers" in node/node.go).
|
||||
|
||||
**c) telegraf**
|
||||
|
||||
Unlike already discussed options, telegraf does not require modifying Tendermint
|
||||
source code. You create something called an input plugin, which polls
|
||||
Tendermint RPC every second and calculates the metrics itself.
|
||||
|
||||
While it may sound good, but some metrics we want to report are not exposed via
|
||||
RPC or pubsub, therefore can't be accessed externally.
|
||||
|
||||
**d) service, listening to pubsub**
|
||||
|
||||
Same issue as the above.
|
||||
|
||||
### 2. opencensus
|
||||
|
||||
opencensus provides both metrics and tracing, which may be important in the
|
||||
future. It's API looks different from go-kit and Prometheus, but looks like it
|
||||
covers everything we need.
|
||||
|
||||
Unfortunately, OpenCensus go client does not define any
|
||||
interfaces, so if we want to abstract away metrics we
|
||||
will need to write interfaces ourselves.
|
||||
|
||||
### List of metrics
|
||||
|
||||
| | Name | Type | Description |
|
||||
| --- | ------------------------------------ | ------ | ----------------------------------------------------------------------------- |
|
||||
| A | consensus_height | Gauge | |
|
||||
| A | consensus_validators | Gauge | Number of validators who signed |
|
||||
| A | consensus_validators_power | Gauge | Total voting power of all validators |
|
||||
| A | consensus_missing_validators | Gauge | Number of validators who did not sign |
|
||||
| A | consensus_missing_validators_power | Gauge | Total voting power of the missing validators |
|
||||
| A | consensus_byzantine_validators | Gauge | Number of validators who tried to double sign |
|
||||
| A | consensus_byzantine_validators_power | Gauge | Total voting power of the byzantine validators |
|
||||
| A | consensus_block_interval | Timing | Time between this and last block (Block.Header.Time) |
|
||||
| | consensus_block_time | Timing | Time to create a block (from creating a proposal to commit) |
|
||||
| | consensus_time_between_blocks | Timing | Time between committing last block and (receiving proposal creating proposal) |
|
||||
| A | consensus_rounds | Gauge | Number of rounds |
|
||||
| | consensus_prevotes | Gauge | |
|
||||
| | consensus_precommits | Gauge | |
|
||||
| | consensus_prevotes_total_power | Gauge | |
|
||||
| | consensus_precommits_total_power | Gauge | |
|
||||
| A | consensus_num_txs | Gauge | |
|
||||
| A | mempool_size | Gauge | |
|
||||
| A | consensus_total_txs | Gauge | |
|
||||
| A | consensus_block_size | Gauge | In bytes |
|
||||
| A | p2p_peers | Gauge | Number of peers node's connected to |
|
||||
|
||||
`A` - will be implemented in the fist place.
|
||||
|
||||
**Proposed solution**
|
||||
|
||||
## Status
|
||||
|
||||
Proposed.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
Better visibility, support of variety of monitoring backends
|
||||
|
||||
### Negative
|
||||
|
||||
One more library to audit, messing metrics reporting code with business domain.
|
||||
|
||||
### Neutral
|
||||
|
||||
-
|
||||
@@ -1,113 +0,0 @@
|
||||
# ADR 012: PeerTransport
|
||||
|
||||
## Context
|
||||
|
||||
One of the more apparent problems with the current architecture in the p2p
|
||||
package is that there is no clear separation of concerns between different
|
||||
components. Most notably the `Switch` is currently doing physical connection
|
||||
handling. An artifact is the dependency of the Switch on
|
||||
`[config.P2PConfig`](https://github.com/tendermint/tendermint/blob/05a76fb517f50da27b4bfcdc7b4cf185fc61eff6/config/config.go#L272-L339).
|
||||
|
||||
Addresses:
|
||||
|
||||
- [#2046](https://github.com/tendermint/tendermint/issues/2046)
|
||||
- [#2047](https://github.com/tendermint/tendermint/issues/2047)
|
||||
|
||||
First iteraton in [#2067](https://github.com/tendermint/tendermint/issues/2067)
|
||||
|
||||
## Decision
|
||||
|
||||
Transport concerns will be handled by a new component (`PeerTransport`) which
|
||||
will provide Peers at its boundary to the caller. In turn `Switch` will use
|
||||
this new component accept new `Peer`s and dial them based on `NetAddress`.
|
||||
|
||||
### PeerTransport
|
||||
|
||||
Responsible for emitting and connecting to Peers. The implementation of `Peer`
|
||||
is left to the transport, which implies that the chosen transport dictates the
|
||||
characteristics of the implementation handed back to the `Switch`. Each
|
||||
transport implementation is responsible to filter establishing peers specific
|
||||
to its domain, for the default multiplexed implementation the following will
|
||||
apply:
|
||||
|
||||
- connections from our own node
|
||||
- handshake fails
|
||||
- upgrade to secret connection fails
|
||||
- prevent duplicate ip
|
||||
- prevent duplicate id
|
||||
- nodeinfo incompatibility
|
||||
|
||||
```go
|
||||
// PeerTransport proxies incoming and outgoing peer connections.
|
||||
type PeerTransport interface {
|
||||
// Accept returns a newly connected Peer.
|
||||
Accept() (Peer, error)
|
||||
|
||||
// Dial connects to a Peer.
|
||||
Dial(NetAddress) (Peer, error)
|
||||
}
|
||||
|
||||
// EXAMPLE OF DEFAULT IMPLEMENTATION
|
||||
|
||||
// multiplexTransport accepts tcp connections and upgrades to multiplexted
|
||||
// peers.
|
||||
type multiplexTransport struct {
|
||||
listener net.Listener
|
||||
|
||||
acceptc chan accept
|
||||
closec <-chan struct{}
|
||||
listenc <-chan struct{}
|
||||
|
||||
dialTimeout time.Duration
|
||||
handshakeTimeout time.Duration
|
||||
nodeAddr NetAddress
|
||||
nodeInfo NodeInfo
|
||||
nodeKey NodeKey
|
||||
|
||||
// TODO(xla): Remove when MConnection is refactored into mPeer.
|
||||
mConfig conn.MConnConfig
|
||||
}
|
||||
|
||||
var _ PeerTransport = (*multiplexTransport)(nil)
|
||||
|
||||
// NewMTransport returns network connected multiplexed peers.
|
||||
func NewMTransport(
|
||||
nodeAddr NetAddress,
|
||||
nodeInfo NodeInfo,
|
||||
nodeKey NodeKey,
|
||||
) *multiplexTransport
|
||||
```
|
||||
|
||||
### Switch
|
||||
|
||||
From now the Switch will depend on a fully setup `PeerTransport` to
|
||||
retrieve/reach out to its peers. As the more low-level concerns are pushed to
|
||||
the transport, we can omit passing the `config.P2PConfig` to the Switch.
|
||||
|
||||
```go
|
||||
func NewSwitch(transport PeerTransport, opts ...SwitchOption) *Switch
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
In Review.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- free Switch from transport concerns - simpler implementation
|
||||
- pluggable transport implementation - simpler test setup
|
||||
- remove Switch dependency on P2PConfig - easier to test
|
||||
|
||||
### Negative
|
||||
|
||||
- more setup for tests which depend on Switches
|
||||
|
||||
### Neutral
|
||||
|
||||
- multiplexed will be the default implementation
|
||||
|
||||
[0] These guards could be potentially extended to be pluggable much like
|
||||
middlewares to express different concerns required by differentally configured
|
||||
environments.
|
||||
@@ -1,99 +0,0 @@
|
||||
# ADR 013: Need for symmetric cryptography
|
||||
|
||||
## Context
|
||||
|
||||
We require symmetric ciphers to handle how we encrypt keys in the sdk,
|
||||
and to potentially encrypt `priv_validator.json` in tendermint.
|
||||
|
||||
Currently we use AEAD's to support symmetric encryption,
|
||||
which is great since we want data integrity in addition to privacy and authenticity.
|
||||
We don't currently have a scenario where we want to encrypt without data integrity,
|
||||
so it is fine to optimize our code to just use AEAD's.
|
||||
Currently there is not a way to switch out AEAD's easily, this ADR outlines a way
|
||||
to easily swap these out.
|
||||
|
||||
### How do we encrypt with AEAD's
|
||||
|
||||
AEAD's typically require a nonce in addition to the key.
|
||||
For the purposes we require symmetric cryptography for,
|
||||
we need encryption to be stateless.
|
||||
Because of this we use random nonces.
|
||||
(Thus the AEAD must support random nonces)
|
||||
|
||||
We currently construct a random nonce, and encrypt the data with it.
|
||||
The returned value is `nonce || encrypted data`.
|
||||
The limitation of this is that does not provide a way to identify
|
||||
which algorithm was used in encryption.
|
||||
Consequently decryption with multiple algoritms is sub-optimal.
|
||||
(You have to try them all)
|
||||
|
||||
## Decision
|
||||
|
||||
We should create the following two methods in a new `crypto/encoding/symmetric` package:
|
||||
|
||||
```golang
|
||||
func Encrypt(aead cipher.AEAD, plaintext []byte) (ciphertext []byte, err error)
|
||||
func Decrypt(key []byte, ciphertext []byte) (plaintext []byte, err error)
|
||||
func Register(aead cipher.AEAD, algo_name string, NewAead func(key []byte) (cipher.Aead, error)) error
|
||||
```
|
||||
|
||||
This allows you to specify the algorithm in encryption, but not have to specify
|
||||
it in decryption.
|
||||
This is intended for ease of use in downstream applications, in addition to people
|
||||
looking at the file directly.
|
||||
One downside is that for the encrypt function you must have already initialized an AEAD,
|
||||
but I don't really see this as an issue.
|
||||
|
||||
If there is no error in encryption, Encrypt will return `algo_name || nonce || aead_ciphertext`.
|
||||
`algo_name` should be length prefixed, using standard varuint encoding.
|
||||
This will be binary data, but thats not a problem considering the nonce and ciphertext are also binary.
|
||||
|
||||
This solution requires a mapping from aead type to name.
|
||||
We can achieve this via reflection.
|
||||
|
||||
```golang
|
||||
func getType(myvar interface{}) string {
|
||||
if t := reflect.TypeOf(myvar); t.Kind() == reflect.Ptr {
|
||||
return "*" + t.Elem().Name()
|
||||
} else {
|
||||
return t.Name()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then we maintain a map from the name returned from `getType(aead)` to `algo_name`.
|
||||
|
||||
In decryption, we read the `algo_name`, and then instantiate a new AEAD with the key.
|
||||
Then we call the AEAD's decrypt method on the provided nonce/ciphertext.
|
||||
|
||||
`Register` allows a downstream user to add their own desired AEAD to the symmetric package.
|
||||
It will error if the AEAD name is already registered.
|
||||
This prevents a malicious import from modifying / nullifying an AEAD at runtime.
|
||||
|
||||
## Implementation strategy
|
||||
|
||||
The golang implementation of what is proposed is rather straight forward.
|
||||
The concern is that we will break existing private keys if we just switch to this.
|
||||
If this is concerning, we can make a simple script which doesn't require decoding privkeys,
|
||||
for converting from the old format to the new one.
|
||||
|
||||
## Status
|
||||
|
||||
Proposed.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Allows us to support new AEAD's, in a way that makes decryption easier
|
||||
- Allows downstream users to add their own AEAD
|
||||
|
||||
### Negative
|
||||
|
||||
- We will have to break all private keys stored on disk.
|
||||
They can be recovered using seed words, and upgrade scripts are simple.
|
||||
|
||||
### Neutral
|
||||
|
||||
- Caller has to instantiate the AEAD with the private key.
|
||||
However it forces them to be aware of what signing algorithm they are using, which is a positive.
|
||||
@@ -1,63 +0,0 @@
|
||||
# ADR 014: Secp256k1 Signature Malleability
|
||||
|
||||
## Context
|
||||
|
||||
Secp256k1 has two layers of malleability.
|
||||
The signer has a random nonce, and thus can produce many different valid signatures.
|
||||
This ADR is not concerned with that.
|
||||
The second layer of malleability basically allows one who is given a signature
|
||||
to produce exactly one more valid signature for the same message from the same public key.
|
||||
(They don't even have to know the message!)
|
||||
The math behind this will be explained in the subsequent section.
|
||||
|
||||
Note that in many downstream applications, signatures will appear in a transaction, and therefore in the tx hash.
|
||||
This means that if someone broadcasts a transaction with secp256k1 signature, the signature can be altered into the other form by anyone in the p2p network.
|
||||
Thus the tx hash will change, and this altered tx hash may be committed instead.
|
||||
This breaks the assumption that you can broadcast a valid transaction and just wait for its hash to be included on chain.
|
||||
One example is if you are broadcasting a tx in cosmos,
|
||||
and you wait for it to appear on chain before incrementing your sequence number.
|
||||
You may never increment your sequence number if a different tx hash got committed.
|
||||
Removing this second layer of signature malleability concerns could ease downstream development.
|
||||
|
||||
### ECDSA context
|
||||
|
||||
Secp256k1 is ECDSA over a particular curve.
|
||||
The signature is of the form `(r, s)`, where `s` is a field element.
|
||||
(The particular field is the `Z_n`, where the elliptic curve has order `n`)
|
||||
However `(r, -s)` is also another valid solution.
|
||||
Note that anyone can negate a group element, and therefore can get this second signature.
|
||||
|
||||
## Decision
|
||||
|
||||
We can just distinguish a canonical form for the ECDSA signatures.
|
||||
Then we require that all ECDSA signatures be in the form which we defined as canonical.
|
||||
We reject signatures in non-canonical form.
|
||||
|
||||
A canonical form is rather easy to define and check.
|
||||
It would just be the smaller of the two values for `s`, defined lexicographically.
|
||||
This is a simple check, instead of checking if `s < n`, instead check `s <= (n - 1)/2`.
|
||||
An example of another cryptosystem using this
|
||||
is the parity definition here https://github.com/zkcrypto/pairing/pull/30#issuecomment-372910663.
|
||||
|
||||
This is the same solution Ethereum has chosen for solving secp malleability.
|
||||
|
||||
## Proposed Implementation
|
||||
|
||||
Fork https://github.com/btcsuite/btcd, and just update the [parse sig method](https://github.com/btcsuite/btcd/blob/master/btcec/signature.go#195) and serialize functions to enforce our canonical form.
|
||||
|
||||
## Status
|
||||
|
||||
Implemented
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Lets us maintain the ability to expect a tx hash to appear in the blockchain.
|
||||
|
||||
### Negative
|
||||
|
||||
- More work in all future implementations (Though this is a very simple check)
|
||||
- Requires us to maintain another fork
|
||||
|
||||
### Neutral
|
||||
@@ -1,84 +0,0 @@
|
||||
# ADR 015: Crypto encoding
|
||||
|
||||
## Context
|
||||
|
||||
We must standardize our method for encoding public keys and signatures on chain.
|
||||
Currently we amino encode the public keys and signatures.
|
||||
The reason we are using amino here is primarily due to ease of support in
|
||||
parsing for other languages.
|
||||
We don't need its upgradability properties in cryptosystems, as a change in
|
||||
the crypto that requires adapting the encoding, likely warrants being deemed
|
||||
a new cryptosystem.
|
||||
(I.e. using new public parameters)
|
||||
|
||||
## Decision
|
||||
|
||||
### Public keys
|
||||
|
||||
For public keys, we will continue to use amino encoding on the canonical
|
||||
representation of the pubkey.
|
||||
(Canonical as defined by the cryptosystem itself)
|
||||
This has two significant drawbacks.
|
||||
Amino encoding is less space-efficient, due to requiring support for upgradability.
|
||||
Amino encoding support requires forking protobuf and adding this new interface support
|
||||
option in the language of choice.
|
||||
|
||||
The reason for continuing to use amino however is that people can create code
|
||||
more easily in languages that already have an up to date amino library.
|
||||
It is possible that this will change in the future, if it is deemed that
|
||||
requiring amino for interacting with Tendermint cryptography is unnecessary.
|
||||
|
||||
The arguments for space efficiency here are refuted on the basis that there are
|
||||
far more egregious wastages of space in the SDK.
|
||||
The space requirement of the public keys doesn't cause many problems beyond
|
||||
increasing the space attached to each validator / account.
|
||||
|
||||
The alternative to using amino here would be for us to create an enum type.
|
||||
Switching to just an enum type is worthy of investigation post-launch.
|
||||
For reference, part of amino encoding interfaces is basically a 4 byte enum
|
||||
type definition.
|
||||
Enum types would just change that 4 bytes to be a variant, and it would remove
|
||||
the protobuf overhead, but it would be hard to integrate into the existing API.
|
||||
|
||||
### Signatures
|
||||
|
||||
Signatures should be switched to be `[]byte`.
|
||||
Spatial efficiency in the signatures is quite important,
|
||||
as it directly affects the gas cost of every transaction,
|
||||
and the throughput of the chain.
|
||||
Signatures don't need to encode what type they are for (unlike public keys)
|
||||
since public keys must already be known.
|
||||
Therefore we can validate the signature without needing to encode its type.
|
||||
|
||||
When placed in state, signatures will still be amino encoded, but it will be the
|
||||
primitive type `[]byte` getting encoded.
|
||||
|
||||
#### Ed25519
|
||||
|
||||
Use the canonical representation for signatures.
|
||||
|
||||
#### Secp256k1
|
||||
|
||||
There isn't a clear canonical representation here.
|
||||
Signatures have two elements `r,s`.
|
||||
These bytes are encoded as `r || s`, where `r` and `s` are both exactly
|
||||
32 bytes long, encoded big-endian.
|
||||
This is basically Ethereum's encoding, but without the leading recovery bit.
|
||||
|
||||
## Status
|
||||
|
||||
Implemented
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- More space efficient signatures
|
||||
|
||||
### Negative
|
||||
|
||||
- We have an amino dependency for cryptography.
|
||||
|
||||
### Neutral
|
||||
|
||||
- No change to public keys
|
||||
@@ -1,308 +0,0 @@
|
||||
# ADR 016: Protocol Versions
|
||||
|
||||
## TODO
|
||||
|
||||
- How to / should we version the authenticated encryption handshake itself (ie.
|
||||
upfront protocol negotiation for the P2PVersion)
|
||||
- How to / should we version ABCI itself? Should it just be absorbed by the
|
||||
BlockVersion?
|
||||
|
||||
## Changelog
|
||||
|
||||
- 18-09-2018: Updates after working a bit on implementation
|
||||
- ABCI Handshake needs to happen independently of starting the app
|
||||
conns so we can see the result
|
||||
- Add question about ABCI protocol version
|
||||
- 16-08-2018: Updates after discussion with SDK team
|
||||
- Remove signalling for next version from Header/ABCI
|
||||
- 03-08-2018: Updates from discussion with Jae:
|
||||
- ProtocolVersion contains Block/AppVersion, not Current/Next
|
||||
- signal upgrades to Tendermint using EndBlock fields
|
||||
- dont restrict peer compatibilty by version to simplify syncing old nodes
|
||||
- 28-07-2018: Updates from review
|
||||
- split into two ADRs - one for protocol, one for chains
|
||||
- include signalling for upgrades in header
|
||||
- 16-07-2018: Initial draft - was originally joint ADR for protocol and chain
|
||||
versions
|
||||
|
||||
## Context
|
||||
|
||||
Here we focus on software-agnostic protocol versions.
|
||||
|
||||
The Software Version is covered by SemVer and described elsewhere.
|
||||
It is not relevant to the protocol description, suffice to say that if any protocol version
|
||||
changes, the software version changes, but not necessarily vice versa.
|
||||
|
||||
Software version should be included in NodeInfo for convenience/diagnostics.
|
||||
|
||||
We are also interested in versioning across different blockchains in a
|
||||
meaningful way, for instance to differentiate branches of a contentious
|
||||
hard-fork. We leave that for a later ADR.
|
||||
|
||||
## Requirements
|
||||
|
||||
We need to version components of the blockchain that may be independently upgraded.
|
||||
We need to do it in a way that is scalable and maintainable - we can't just litter
|
||||
the code with conditionals.
|
||||
|
||||
We can consider the complete version of the protocol to contain the following sub-versions:
|
||||
BlockVersion, P2PVersion, AppVersion. These versions reflect the major sub-components
|
||||
of the software that are likely to evolve together, at different rates, and in different ways,
|
||||
as described below.
|
||||
|
||||
The BlockVersion defines the core of the blockchain data structures and
|
||||
should change infrequently.
|
||||
|
||||
The P2PVersion defines how peers connect and communicate with eachother - it's
|
||||
not part of the blockchain data structures, but defines the protocols used to build the
|
||||
blockchain. It may change gradually.
|
||||
|
||||
The AppVersion determines how we compute app specific information, like the
|
||||
AppHash and the Results.
|
||||
|
||||
All of these versions may change over the life of a blockchain, and we need to
|
||||
be able to help new nodes sync up across version changes. This means we must be willing
|
||||
to connect to peers with older version.
|
||||
|
||||
### BlockVersion
|
||||
|
||||
- All tendermint hashed data-structures (headers, votes, txs, responses, etc.).
|
||||
- Note the semantic meaning of a transaction may change according to the AppVersion, but the way txs are merklized into the header is part of the BlockVersion
|
||||
- It should be the least frequent/likely to change.
|
||||
- Tendermint should be stabilizing - it's just Atomic Broadcast.
|
||||
- We can start considering for Tendermint v2.0 in a year
|
||||
- It's easy to determine the version of a block from its serialized form
|
||||
|
||||
### P2PVersion
|
||||
|
||||
- All p2p and reactor messaging (messages, detectable behaviour)
|
||||
- Will change gradually as reactors evolve to improve performance and support new features - eg proposed new message types BatchTx in the mempool and HasBlockPart in the consensus
|
||||
- It's easy to determine the version of a peer from its first serialized message/s
|
||||
- New versions must be compatible with at least one old version to allow gradual upgrades
|
||||
|
||||
### AppVersion
|
||||
|
||||
- The ABCI state machine (txs, begin/endblock behaviour, commit hashing)
|
||||
- Behaviour and message types will change abruptly in the course of the life of a chain
|
||||
- Need to minimize complexity of the code for supporting different AppVersions at different heights
|
||||
- Ideally, each version of the software supports only a _single_ AppVersion at one time
|
||||
- this means we checkout different versions of the software at different heights instead of littering the code
|
||||
with conditionals
|
||||
- minimize the number of data migrations required across AppVersion (ie. most AppVersion should be able to read the same state from disk as previous AppVersion).
|
||||
|
||||
## Ideal
|
||||
|
||||
Each component of the software is independently versioned in a modular way and its easy to mix and match and upgrade.
|
||||
|
||||
## Proposal
|
||||
|
||||
Each of BlockVersion, AppVersion, P2PVersion, is a monotonically increasing uint64.
|
||||
|
||||
To use these versions, we need to update the block Header, the p2p NodeInfo, and the ABCI.
|
||||
|
||||
### Header
|
||||
|
||||
Block Header should include a `Version` struct as its first field like:
|
||||
|
||||
```
|
||||
type Version struct {
|
||||
Block uint64
|
||||
App uint64
|
||||
}
|
||||
```
|
||||
|
||||
Here, `Version.Block` defines the rules for the current block, while
|
||||
`Version.App` defines the app version that processed the last block and computed
|
||||
the `AppHash` in the current block. Together they provide a complete description
|
||||
of the consensus-critical protocol.
|
||||
|
||||
Since we have settled on a proto3 header, the ability to read the BlockVersion out of the serialized header is unanimous.
|
||||
|
||||
Using a Version struct gives us more flexibility to add fields without breaking
|
||||
the header.
|
||||
|
||||
The ProtocolVersion struct includes both the Block and App versions - it should
|
||||
serve as a complete description of the consensus-critical protocol.
|
||||
|
||||
### NodeInfo
|
||||
|
||||
NodeInfo should include a Version struct as its first field like:
|
||||
|
||||
```
|
||||
type Version struct {
|
||||
P2P uint64
|
||||
Block uint64
|
||||
App uint64
|
||||
|
||||
Other []string
|
||||
}
|
||||
```
|
||||
|
||||
Note this effectively makes `Version.P2P` the first field in the NodeInfo, so it
|
||||
should be easy to read this out of the serialized header if need be to facilitate an upgrade.
|
||||
|
||||
The `Version.Other` here should include additional information like the name of the software client and
|
||||
it's SemVer version - this is for convenience only. Eg.
|
||||
`tendermint-core/v0.22.8`. It's a `[]string` so it can include information about
|
||||
the version of Tendermint, of the app, of Tendermint libraries, etc.
|
||||
|
||||
### ABCI
|
||||
|
||||
Since the ABCI is responsible for keeping Tendermint and the App in sync, we
|
||||
need to communicate version information through it.
|
||||
|
||||
On startup, we use Info to perform a basic handshake. It should include all the
|
||||
version information.
|
||||
|
||||
We also need to be able to update versions in the life of a blockchain. The
|
||||
natural place to do this is EndBlock.
|
||||
|
||||
Note that currently the result of the Handshake isn't exposed anywhere, as the
|
||||
handshaking happens inside the `proxy.AppConns` abstraction. We will need to
|
||||
remove the handshaking from the `proxy` package so we can call it independently
|
||||
and get the result, which should contain the application version.
|
||||
|
||||
#### Info
|
||||
|
||||
RequestInfo should add support for protocol versions like:
|
||||
|
||||
```
|
||||
message RequestInfo {
|
||||
string version
|
||||
uint64 block_version
|
||||
uint64 p2p_version
|
||||
}
|
||||
```
|
||||
|
||||
Similarly, ResponseInfo should return the versions:
|
||||
|
||||
```
|
||||
message ResponseInfo {
|
||||
string data
|
||||
|
||||
string version
|
||||
uint64 app_version
|
||||
|
||||
int64 last_block_height
|
||||
bytes last_block_app_hash
|
||||
}
|
||||
```
|
||||
|
||||
The existing `version` fields should be called `software_version` but we leave
|
||||
them for now to reduce the number of breaking changes.
|
||||
|
||||
#### EndBlock
|
||||
|
||||
Updating the version could be done either with new fields or by using the
|
||||
existing `tags`. Since we're trying to communicate information that will be
|
||||
included in Tendermint block Headers, it should be native to the ABCI, and not
|
||||
something embedded through some scheme in the tags. Thus, version updates should
|
||||
be communicated through EndBlock.
|
||||
|
||||
EndBlock already contains `ConsensusParams`. We can add version information to
|
||||
the ConsensusParams as well:
|
||||
|
||||
```
|
||||
message ConsensusParams {
|
||||
|
||||
BlockSize block_size
|
||||
EvidenceParams evidence_params
|
||||
VersionParams version
|
||||
}
|
||||
|
||||
message VersionParams {
|
||||
uint64 block_version
|
||||
uint64 app_version
|
||||
}
|
||||
```
|
||||
|
||||
For now, the `block_version` will be ignored, as we do not allow block version
|
||||
to be updated live. If the `app_version` is set, it signals that the app's
|
||||
protocol version has changed, and the new `app_version` will be included in the
|
||||
`Block.Header.Version.App` for the next block.
|
||||
|
||||
### BlockVersion
|
||||
|
||||
BlockVersion is included in both the Header and the NodeInfo.
|
||||
|
||||
Changing BlockVersion should happen quite infrequently and ideally only for
|
||||
critical upgrades. For now, it is not encoded in ABCI, though it's always
|
||||
possible to use tags to signal an external process to co-ordinate an upgrade.
|
||||
|
||||
Note Ethereum has not had to make an upgrade like this (everything has been at state machine level, AFAIK).
|
||||
|
||||
### P2PVersion
|
||||
|
||||
P2PVersion is not included in the block Header, just the NodeInfo.
|
||||
|
||||
P2PVersion is the first field in the NodeInfo. NodeInfo is also proto3 so this is easy to read out.
|
||||
|
||||
Note we need the peer/reactor protocols to take the versions of peers into account when sending messages:
|
||||
|
||||
- don't send messages they don't understand
|
||||
- don't send messages they don't expect
|
||||
|
||||
Doing this will be specific to the upgrades being made.
|
||||
|
||||
Note we also include the list of reactor channels in the NodeInfo and already don't send messages for channels the peer doesn't understand.
|
||||
If upgrades always use new channels, this simplifies the development cost of backwards compatibility.
|
||||
|
||||
Note NodeInfo is only exchanged after the authenticated encryption handshake to ensure that it's private.
|
||||
Doing any version exchange before encrypting could be considered information leakage, though I'm not sure
|
||||
how much that matters compared to being able to upgrade the protocol.
|
||||
|
||||
XXX: if needed, can we change the meaning of the first byte of the first message to encode a handshake version?
|
||||
this is the first byte of a 32-byte ed25519 pubkey.
|
||||
|
||||
### AppVersion
|
||||
|
||||
AppVersion is also included in the block Header and the NodeInfo.
|
||||
|
||||
AppVersion essentially defines how the AppHash and LastResults are computed.
|
||||
|
||||
### Peer Compatibility
|
||||
|
||||
Restricting peer compatibility based on version is complicated by the need to
|
||||
help old peers, possibly on older versions, sync the blockchain.
|
||||
|
||||
We might be tempted to say that we only connect to peers with the same
|
||||
AppVersion and BlockVersion (since these define the consensus critical
|
||||
computations), and a select list of P2PVersions (ie. those compatible with
|
||||
ours), but then we'd need to make accomodations for connecting to peers with the
|
||||
right Block/AppVersion for the height they're on.
|
||||
|
||||
For now, we will connect to peers with any version and restrict compatibility
|
||||
solely based on the ChainID. We leave more restrictive rules on peer
|
||||
compatibiltiy to a future proposal.
|
||||
|
||||
### Future Changes
|
||||
|
||||
It may be valuable to support an `/unsafe_stop?height=_` endpoint to tell Tendermint to shutdown at a given height.
|
||||
This could be use by an external manager process that oversees upgrades by
|
||||
checking out and installing new software versions and restarting the process. It
|
||||
would subscribe to the relevant upgrade event (needs to be implemented) and call `/unsafe_stop` at
|
||||
the correct height (of course only after getting approval from its user!)
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Make tendermint and application versions native to the ABCI to more clearly
|
||||
communicate about them
|
||||
- Distinguish clearly between protocol versions and software version to
|
||||
facilitate implementations in other languages
|
||||
- Versions included in key data structures in easy to discern way
|
||||
- Allows proposers to signal for upgrades and apps to decide when to actually change the
|
||||
version (and start signalling for a new version)
|
||||
|
||||
### Neutral
|
||||
|
||||
- Unclear how to version the initial P2P handshake itself
|
||||
- Versions aren't being used (yet) to restrict peer compatibility
|
||||
- Signalling for a new version happens through the proposer and must be
|
||||
tallied/tracked in the app.
|
||||
|
||||
### Negative
|
||||
|
||||
- Adds more fields to the ABCI
|
||||
- Implies that a single codebase must be able to handle multiple versions
|
||||
@@ -1,99 +0,0 @@
|
||||
# ADR 017: Chain Versions
|
||||
|
||||
## TODO
|
||||
|
||||
- clarify how to handle slashing when ChainID changes
|
||||
|
||||
## Changelog
|
||||
|
||||
- 28-07-2018: Updates from review
|
||||
- split into two ADRs - one for protocol, one for chains
|
||||
- 16-07-2018: Initial draft - was originally joint ADR for protocol and chain
|
||||
versions
|
||||
|
||||
## Context
|
||||
|
||||
Software and Protocol versions are covered in a separate ADR.
|
||||
|
||||
Here we focus on chain versions.
|
||||
|
||||
## Requirements
|
||||
|
||||
We need to version blockchains across protocols, networks, forks, etc.
|
||||
We need chain identifiers and descriptions so we can talk about a multitude of chains,
|
||||
and especially the differences between them, in a meaningful way.
|
||||
|
||||
### Networks
|
||||
|
||||
We need to support many independent networks running the same version of the software,
|
||||
even possibly starting from the same initial state.
|
||||
They must have distinct identifiers so that peers know which one they are joining and so
|
||||
validators and users can prevent replay attacks.
|
||||
|
||||
Call this the `NetworkName` (note we currently call this `ChainID` in the software. In this
|
||||
ADR, ChainID has a different meaning).
|
||||
It represents both the application being run and the community or intention
|
||||
of running it.
|
||||
|
||||
Peers only connect to other peers with the same NetworkName.
|
||||
|
||||
### Forks
|
||||
|
||||
We need to support existing networks upgrading and forking, wherein they may do any of:
|
||||
|
||||
- revert back to some height, continue with the same versions but new blocks
|
||||
- arbitrarily mutate state at some height, continue with the same versions (eg. Dao Fork)
|
||||
- change the AppVersion at some height
|
||||
|
||||
Note because of Tendermint's voting power threshold rules, a chain can only be extended under the "original" rules and under the new rules
|
||||
if 1/3 or more is double signing, which is expressly prohibited, and is supposed to result in their punishment on both chains. Since they can censor
|
||||
the punishment, the chain is expected to be hardforked to remove the validators. Thus, if both branches are to continue after a fork,
|
||||
they will each require a new identifier, and the old chain identifier will be retired (ie. only useful for syncing history, not for new blocks)..
|
||||
|
||||
TODO: explain how to handle slashing when chain id changed!
|
||||
|
||||
We need a consistent way to describe forks.
|
||||
|
||||
## Proposal
|
||||
|
||||
### ChainDescription
|
||||
|
||||
ChainDescription is a complete immutable description of a blockchain. It takes the following form:
|
||||
|
||||
```
|
||||
ChainDescription = <NetworkName>/<BlockVersion>/<AppVersion>/<StateHash>/<ValHash>/<ConsensusParamsHash>
|
||||
```
|
||||
|
||||
Here, StateHash is the merkle root of the initial state, ValHash is the merkle root of the initial Tendermint validator set,
|
||||
and ConsensusParamsHash is the merkle root of the initial Tendermint consensus parameters.
|
||||
|
||||
The `genesis.json` file must contain enough information to compute this value. It need not contain the StateHash or ValHash itself,
|
||||
but contain the state from which they can be computed with the given protocol versions.
|
||||
|
||||
NOTE: consider splitting NetworkName into NetworkName and AppName - this allows
|
||||
folks to independently use the same application for different networks (ie we
|
||||
could imagine multiple communities of validators wanting to put up a Hub using
|
||||
the same app but having a distinct network name. Arguably not needed if
|
||||
differences will come via different initial state / validators).
|
||||
|
||||
#### ChainID
|
||||
|
||||
Define `ChainID = TMHASH(ChainDescriptor)`. It's the unique ID of a blockchain.
|
||||
|
||||
It should be Bech32 encoded when handled by users, eg. with `cosmoschain` prefix.
|
||||
|
||||
#### Forks and Uprades
|
||||
|
||||
When a chain forks or upgrades but continues the same history, it takes a new ChainDescription as follows:
|
||||
|
||||
```
|
||||
ChainDescription = <ChainID>/x/<Height>/<ForkDescription>
|
||||
```
|
||||
|
||||
Where
|
||||
|
||||
- ChainID is the ChainID from the previous ChainDescription (ie. its hash)
|
||||
- `x` denotes that a change occured
|
||||
- `Height` is the height the change occured
|
||||
- ForkDescription has the same form as ChainDescription but for the fork
|
||||
- this allows forks to specify new versions for tendermint or the app, as well as arbitrary changes to the state or validator set
|
||||
@@ -1,100 +0,0 @@
|
||||
# ADR 018: ABCI Validator Improvements
|
||||
|
||||
## Changelog
|
||||
|
||||
016-08-2018: Follow up from review: - Revert changes to commit round - Remind about justification for removing pubkey - Update pros/cons
|
||||
05-08-2018: Initial draft
|
||||
|
||||
## Context
|
||||
|
||||
ADR 009 introduced major improvements to the ABCI around validators and the use
|
||||
of Amino. Here we follow up with some additional changes to improve the naming
|
||||
and expected use of Validator messages.
|
||||
|
||||
## Decision
|
||||
|
||||
### Validator
|
||||
|
||||
Currently a Validator contains `address` and `pub_key`, and one or the other is
|
||||
optional/not-sent depending on the use case. Instead, we should have a
|
||||
`Validator` (with just the address, used for RequestBeginBlock)
|
||||
and a `ValidatorUpdate` (with the pubkey, used for ResponseEndBlock):
|
||||
|
||||
```
|
||||
message Validator {
|
||||
bytes address
|
||||
int64 power
|
||||
}
|
||||
|
||||
message ValidatorUpdate {
|
||||
PubKey pub_key
|
||||
int64 power
|
||||
}
|
||||
```
|
||||
|
||||
As noted in [ADR-009](adr-009-ABCI-design.md),
|
||||
the `Validator` does not contain a pubkey because quantum public keys are
|
||||
quite large and it would be wasteful to send them all over ABCI with every block.
|
||||
Thus, applications that want to take advantage of the information in BeginBlock
|
||||
are _required_ to store pubkeys in state (or use much less efficient lazy means
|
||||
of verifying BeginBlock data).
|
||||
|
||||
### RequestBeginBlock
|
||||
|
||||
LastCommitInfo currently has an array of `SigningValidator` that contains
|
||||
information for each validator in the entire validator set.
|
||||
Instead, this should be called `VoteInfo`, since it is information about the
|
||||
validator votes.
|
||||
|
||||
Note that all votes in a commit must be from the same round.
|
||||
|
||||
```
|
||||
message LastCommitInfo {
|
||||
int64 round
|
||||
repeated VoteInfo commit_votes
|
||||
}
|
||||
|
||||
message VoteInfo {
|
||||
Validator validator
|
||||
bool signed_last_block
|
||||
}
|
||||
```
|
||||
|
||||
### ResponseEndBlock
|
||||
|
||||
Use ValidatorUpdates instead of Validators. Then it's clear we don't need an
|
||||
address, and we do need a pubkey.
|
||||
|
||||
We could require the address here as well as a sanity check, but it doesn't seem
|
||||
necessary.
|
||||
|
||||
### InitChain
|
||||
|
||||
Use ValidatorUpdates for both Request and Response. InitChain
|
||||
is about setting/updating the initial validator set, unlike BeginBlock
|
||||
which is just informational.
|
||||
|
||||
## Status
|
||||
|
||||
Proposal.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Clarifies the distinction between the different uses of validator information
|
||||
|
||||
### Negative
|
||||
|
||||
- Apps must still store the public keys in state to utilize the RequestBeginBlock info
|
||||
|
||||
### Neutral
|
||||
|
||||
- ResponseEndBlock does not require an address
|
||||
|
||||
## References
|
||||
|
||||
- [Latest ABCI Spec](https://github.com/tendermint/tendermint/blob/v0.22.8/docs/app-dev/abci-spec.md)
|
||||
- [ADR-009](https://github.com/tendermint/tendermint/blob/v0.22.8/docs/architecture/adr-009-ABCI-design.md)
|
||||
- [Issue #1712 - Don't send PubKey in
|
||||
RequestBeginBlock](https://github.com/tendermint/tendermint/issues/1712)
|
||||
@@ -1,160 +0,0 @@
|
||||
# ADR 019: Encoding standard for Multisignatures
|
||||
|
||||
## Changelog
|
||||
|
||||
06-08-2018: Minor updates
|
||||
|
||||
27-07-2018: Update draft to use amino encoding
|
||||
|
||||
11-07-2018: Initial Draft
|
||||
|
||||
## Context
|
||||
|
||||
Multisignatures, or technically _Accountable Subgroup Multisignatures_ (ASM),
|
||||
are signature schemes which enable any subgroup of a set of signers to sign any message,
|
||||
and reveal to the verifier exactly who the signers were.
|
||||
This allows for complex conditionals of when to validate a signature.
|
||||
|
||||
Suppose the set of signers is of size _n_.
|
||||
If we validate a signature if any subgroup of size _k_ signs a message,
|
||||
this becomes what is commonly reffered to as a _k of n multisig_ in Bitcoin.
|
||||
|
||||
This ADR specifies the encoding standard for general accountable subgroup multisignatures,
|
||||
k of n accountable subgroup multisignatures, and its weighted variant.
|
||||
|
||||
In the future, we can also allow for more complex conditionals on the accountable subgroup.
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
### New structs
|
||||
|
||||
Every ASM will then have its own struct, implementing the crypto.Pubkey interface.
|
||||
|
||||
This ADR assumes that [replacing crypto.Signature with []bytes](https://github.com/tendermint/tendermint/issues/1957) has been accepted.
|
||||
|
||||
#### K of N threshold signature
|
||||
|
||||
The pubkey is the following struct:
|
||||
|
||||
```golang
|
||||
type ThresholdMultiSignaturePubKey struct { // K of N threshold multisig
|
||||
K uint `json:"threshold"`
|
||||
Pubkeys []crypto.Pubkey `json:"pubkeys"`
|
||||
}
|
||||
```
|
||||
|
||||
We will derive N from the length of pubkeys. (For spatial efficiency in encoding)
|
||||
|
||||
`Verify` will expect an `[]byte` encoded version of the Multisignature.
|
||||
(Multisignature is described in the next section)
|
||||
The multisignature will be rejected if the bitmap has less than k indices,
|
||||
or if any signature at any of the k indices is not a valid signature from
|
||||
the kth public key on the message.
|
||||
(If more than k signatures are included, all must be valid)
|
||||
|
||||
`Bytes` will be the amino encoded version of the pubkey.
|
||||
|
||||
Address will be `Hash(amino_encoded_pubkey)`
|
||||
|
||||
The reason this doesn't use `log_8(n)` bytes per signer is because that heavily optimizes for the case where a very small number of signers are required.
|
||||
e.g. for `n` of size `24`, that would only be more space efficient for `k < 3`.
|
||||
This seems less likely, and that it should not be the case optimized for.
|
||||
|
||||
#### Weighted threshold signature
|
||||
|
||||
The pubkey is the following struct:
|
||||
|
||||
```golang
|
||||
type WeightedThresholdMultiSignaturePubKey struct {
|
||||
Weights []uint `json:"weights"`
|
||||
Threshold uint `json:"threshold"`
|
||||
Pubkeys []crypto.Pubkey `json:"pubkeys"`
|
||||
}
|
||||
```
|
||||
|
||||
Weights and Pubkeys must be of the same length.
|
||||
Everything else proceeds identically to the K of N multisig,
|
||||
except the multisig fails if the sum of the weights is less than the threshold.
|
||||
|
||||
#### Multisignature
|
||||
|
||||
The inter-mediate phase of the signatures (as it accrues more signatures) will be the following struct:
|
||||
|
||||
```golang
|
||||
type Multisignature struct {
|
||||
BitArray CryptoBitArray // Documented later
|
||||
Sigs [][]byte
|
||||
```
|
||||
|
||||
It is important to recall that each private key will output a signature on the provided message itself.
|
||||
So no signing algorithm ever outputs the multisignature.
|
||||
The UI will take a signature, cast into a multisignature, and then keep adding
|
||||
new signatures into it, and when done marshal into `[]byte`.
|
||||
This will require the following helper methods:
|
||||
|
||||
```golang
|
||||
func SigToMultisig(sig []byte, n int)
|
||||
func GetIndex(pk crypto.Pubkey, []crypto.Pubkey)
|
||||
func AddSignature(sig Signature, index int, multiSig *Multisignature)
|
||||
```
|
||||
|
||||
The multisignature will be converted to an `[]byte` using amino.MarshalBinaryBare. \*
|
||||
|
||||
#### Bit Array
|
||||
|
||||
We would be using a new implementation of a bitarray. The struct it would be encoded/decoded from is
|
||||
|
||||
```golang
|
||||
type CryptoBitArray struct {
|
||||
ExtraBitsStored byte `json:"extra_bits"` // The number of extra bits in elems.
|
||||
Elems []byte `json:"elems"`
|
||||
}
|
||||
```
|
||||
|
||||
The reason for not using the BitArray currently implemented in `libs/common/bit_array.go`
|
||||
is that it is less space efficient, due to a space / time trade-off.
|
||||
Evidence for this is outlined in [this issue](https://github.com/tendermint/tendermint/issues/2077).
|
||||
|
||||
In the multisig, we will not be performing arithmetic operations,
|
||||
so there is no performance increase with the current implementation,
|
||||
and just loss of spatial efficiency.
|
||||
Implementing this new bit array with `[]byte` _should_ be simple, as no
|
||||
arithmetic operations between bit arrays are required, and save a couple of bytes.
|
||||
(Explained in that same issue)
|
||||
|
||||
When this bit array encoded, the number of elements is encoded due to amino.
|
||||
However we may be encoding a full byte for what we actually only need 1-7 bits for.
|
||||
We store that difference in ExtraBitsStored.
|
||||
This allows for us to have an unbounded number of signers, and is more space efficient than what is currently used in `libs/common`.
|
||||
Again the implementation of this space saving feature is straight forward.
|
||||
|
||||
### Encoding the structs
|
||||
|
||||
We will use straight forward amino encoding. This is chosen for ease of compatibility in other languages.
|
||||
|
||||
### Future points of discussion
|
||||
|
||||
If desired, we can use ed25519 batch verification for all ed25519 keys.
|
||||
This is a future point of discussion, but would be backwards compatible as this information won't need to be marshalled.
|
||||
(There may even be cofactor concerns without ristretto)
|
||||
Aggregation of pubkeys / sigs in Schnorr sigs / BLS sigs is not backwards compatible, and would need to be a new ASM type.
|
||||
|
||||
## Status
|
||||
|
||||
Proposed.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Supports multisignatures, in a way that won't require any special cases in our downstream verification code.
|
||||
- Easy to serialize / deserialize
|
||||
- Unbounded number of signers
|
||||
|
||||
### Negative
|
||||
|
||||
- Larger codebase, however this should reside in a subfolder of tendermint/crypto, as it provides no new interfaces. (Ref #https://github.com/tendermint/go-crypto/issues/136)
|
||||
- Space inefficient due to utilization of amino encoding
|
||||
- Suggested implementation requires a new struct for every ASM.
|
||||
|
||||
### Neutral
|
||||
@@ -1,104 +0,0 @@
|
||||
# ADR 020: Limiting txs size inside a block
|
||||
|
||||
## Changelog
|
||||
|
||||
13-08-2018: Initial Draft
|
||||
15-08-2018: Second version after Dev's comments
|
||||
28-08-2018: Third version after Ethan's comments
|
||||
30-08-2018: AminoOverheadForBlock => MaxAminoOverheadForBlock
|
||||
31-08-2018: Bounding evidence and chain ID
|
||||
13-01-2019: Add section on MaxBytes vs MaxDataBytes
|
||||
|
||||
## Context
|
||||
|
||||
We currently use MaxTxs to reap txs from the mempool when proposing a block,
|
||||
but enforce MaxBytes when unmarshalling a block, so we could easily propose a
|
||||
block thats too large to be valid.
|
||||
|
||||
We should just remove MaxTxs all together and stick with MaxBytes, and have a
|
||||
`mempool.ReapMaxBytes`.
|
||||
|
||||
But we can't just reap BlockSize.MaxBytes, since MaxBytes is for the entire block,
|
||||
not for the txs inside the block. There's extra amino overhead + the actual
|
||||
headers on top of the actual transactions + evidence + last commit.
|
||||
We could also consider using a MaxDataBytes instead of or in addition to MaxBytes.
|
||||
|
||||
## MaxBytes vs MaxDataBytes
|
||||
|
||||
The [PR #3045](https://github.com/tendermint/tendermint/pull/3045) suggested
|
||||
additional clarity/justification was necessary here, wither respect to the use
|
||||
of MaxDataBytes in addition to, or instead of, MaxBytes.
|
||||
|
||||
MaxBytes provides a clear limit on the total size of a block that requires no
|
||||
additional calculation if you want to use it to bound resource usage, and there
|
||||
has been considerable discussions about optimizing tendermint around 1MB blocks.
|
||||
Regardless, we need some maximum on the size of a block so we can avoid
|
||||
unmarshalling blocks that are too big during the consensus, and it seems more
|
||||
straightforward to provide a single fixed number for this rather than a
|
||||
computation of "MaxDataBytes + everything else you need to make room for
|
||||
(signatures, evidence, header)". MaxBytes provides a simple bound so we can
|
||||
always say "blocks are less than X MB".
|
||||
|
||||
Having both MaxBytes and MaxDataBytes feels like unnecessary complexity. It's
|
||||
not particularly surprising for MaxBytes to imply the maximum size of the
|
||||
entire block (not just txs), one just has to know that a block includes header,
|
||||
txs, evidence, votes. For more fine grained control over the txs included in the
|
||||
block, there is the MaxGas. In practice, the MaxGas may be expected to do most of
|
||||
the tx throttling, and the MaxBytes to just serve as an upper bound on the total
|
||||
size. Applications can use MaxGas as a MaxDataBytes by just taking the gas for
|
||||
every tx to be its size in bytes.
|
||||
|
||||
## Proposed solution
|
||||
|
||||
Therefore, we should
|
||||
|
||||
1) Get rid of MaxTxs.
|
||||
2) Rename MaxTxsBytes to MaxBytes.
|
||||
|
||||
When we need to ReapMaxBytes from the mempool, we calculate the upper bound as follows:
|
||||
|
||||
```
|
||||
ExactLastCommitBytes = {number of validators currently enabled} * {MaxVoteBytes}
|
||||
MaxEvidenceBytesPerBlock = MaxBytes / 10
|
||||
ExactEvidenceBytes = cs.evpool.PendingEvidence(MaxEvidenceBytesPerBlock) * MaxEvidenceBytes
|
||||
|
||||
mempool.ReapMaxBytes(MaxBytes - MaxAminoOverheadForBlock - ExactLastCommitBytes - ExactEvidenceBytes - MaxHeaderBytes)
|
||||
```
|
||||
|
||||
where MaxVoteBytes, MaxEvidenceBytes, MaxHeaderBytes and MaxAminoOverheadForBlock
|
||||
are constants defined inside the `types` package:
|
||||
|
||||
- MaxVoteBytes - 170 bytes
|
||||
- MaxEvidenceBytes - 364 bytes
|
||||
- MaxHeaderBytes - 476 bytes (~276 bytes hashes + 200 bytes - 50 UTF-8 encoded
|
||||
symbols of chain ID 4 bytes each in the worst case + amino overhead)
|
||||
- MaxAminoOverheadForBlock - 8 bytes (assuming MaxHeaderBytes includes amino
|
||||
overhead for encoding header, MaxVoteBytes - for encoding vote, etc.)
|
||||
|
||||
ChainID needs to bound to 50 symbols max.
|
||||
|
||||
When reaping evidence, we use MaxBytes to calculate the upper bound (e.g. 1/10)
|
||||
to save some space for transactions.
|
||||
|
||||
NOTE while reaping the `max int` bytes in mempool, we should account that every
|
||||
transaction will take `len(tx)+aminoOverhead`, where aminoOverhead=1-4 bytes.
|
||||
|
||||
We should write a test that fails if the underlying structs got changed, but
|
||||
MaxXXX stayed the same.
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
* one way to limit the size of a block
|
||||
* less variables to configure
|
||||
|
||||
### Negative
|
||||
|
||||
* constants that need to be adjusted if the underlying structs got changed
|
||||
|
||||
### Neutral
|
||||
@@ -1,52 +0,0 @@
|
||||
# ADR 012: ABCI Events
|
||||
|
||||
## Changelog
|
||||
|
||||
- *2018-09-02* Remove ABCI errors component. Update description for events
|
||||
- *2018-07-12* Initial version
|
||||
|
||||
## Context
|
||||
|
||||
ABCI tags were first described in [ADR 002](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-002-event-subscription.md).
|
||||
They are key-value pairs that can be used to index transactions.
|
||||
|
||||
Currently, ABCI messages return a list of tags to describe an
|
||||
"event" that took place during the Check/DeliverTx/Begin/EndBlock,
|
||||
where each tag refers to a different property of the event, like the sending and receiving account addresses.
|
||||
|
||||
Since there is only one list of tags, recording data for multiple such events in
|
||||
a single Check/DeliverTx/Begin/EndBlock must be done using prefixes in the key
|
||||
space.
|
||||
|
||||
Alternatively, groups of tags that constitute an event can be separated by a
|
||||
special tag that denotes a break between the events. This would allow
|
||||
straightforward encoding of multiple events into a single list of tags without
|
||||
prefixing, at the cost of these "special" tags to separate the different events.
|
||||
|
||||
TODO: brief description of how the indexing works
|
||||
|
||||
## Decision
|
||||
|
||||
Instead of returning a list of tags, return a list of events, where
|
||||
each event is a list of tags. This way we naturally capture the concept of
|
||||
multiple events happening during a single ABCI message.
|
||||
|
||||
TODO: describe impact on indexing and querying
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Ability to track distinct events separate from ABCI calls (DeliverTx/BeginBlock/EndBlock)
|
||||
- More powerful query abilities
|
||||
|
||||
### Negative
|
||||
|
||||
- More complex query syntax
|
||||
- More complex search implementation
|
||||
|
||||
### Neutral
|
||||
@@ -1,64 +0,0 @@
|
||||
# ADR 023: ABCI Codespaces
|
||||
|
||||
## Changelog
|
||||
|
||||
- *2018-09-01* Initial version
|
||||
|
||||
## Context
|
||||
|
||||
ABCI errors should provide an abstraction between application details
|
||||
and the client interface responsible for formatting & displaying errors to the user.
|
||||
|
||||
Currently, this abstraction consists of a single integer (the `code`), where any
|
||||
`code > 0` is considered an error (ie. invalid transaction) and all type
|
||||
information about the error is contained in the code. This integer is
|
||||
expected to be decoded by the client into a known error string, where any
|
||||
more specific data is contained in the `data`.
|
||||
|
||||
In a [previous conversation](https://github.com/tendermint/abci/issues/165#issuecomment-353704015),
|
||||
it was suggested that not all non-zero codes need to be errors, hence why it's called `code` and not `error code`.
|
||||
It is unclear exactly how the semantics of the `code` field will evolve, though
|
||||
better lite-client proofs (like discussed for tags
|
||||
[here](https://github.com/tendermint/tendermint/issues/1007#issuecomment-413917763))
|
||||
may play a role.
|
||||
|
||||
Note that having all type information in a single integer
|
||||
precludes an easy coordination method between "module implementers" and "client
|
||||
implementers", especially for apps with many "modules". With an unbounded error domain (such as a string), module
|
||||
implementers can pick a globally unique prefix & error code set, so client
|
||||
implementers could easily implement support for "module A" regardless of which
|
||||
particular blockchain network it was running in and which other modules were running with it. With
|
||||
only error codes, globally unique codes are difficult/impossible, as the space
|
||||
is finite and collisions are likely without an easy way to coordinate.
|
||||
|
||||
For instance, while trying to build an ecosystem of modules that can be composed into a single
|
||||
ABCI application, the Cosmos-SDK had to hack a higher level "codespace" into the
|
||||
single integer so that each module could have its own space to express its
|
||||
errors.
|
||||
|
||||
## Decision
|
||||
|
||||
Include a `string code_space` in all ABCI messages that have a `code`.
|
||||
This allows applications to namespace the codes so they can experiment with
|
||||
their own code schemes.
|
||||
|
||||
It is the responsibility of applications to limit the size of the `code_space`
|
||||
string.
|
||||
|
||||
How the codespace is hashed into block headers (ie. so it can be queried
|
||||
efficiently by lite clients) is left for a separate ADR.
|
||||
|
||||
## Consequences
|
||||
|
||||
## Positive
|
||||
|
||||
- No need for complex codespacing on a single integer
|
||||
- More expressive type system for errors
|
||||
|
||||
## Negative
|
||||
|
||||
- Another field in the response needs to be accounted for
|
||||
- Some redundancy with `code` field
|
||||
- May encourage more error/code type info to move to the `codespace` string, which
|
||||
could impact lite clients.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user