mirror of
https://github.com/tendermint/tendermint.git
synced 2026-07-20 06:52:30 +00:00
spec: merge v0.35 spec into tendermint (#9018)
This commit is contained in:
+3
-1
@@ -7,4 +7,6 @@
|
||||
# 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 @cmwaters @tychoish @williambanfield @creachadair
|
||||
* @ebuchman @cmwaters @tychoish @williambanfield @creachadair @sergio-mena @jmalicevic @thanethomson @samricotta
|
||||
|
||||
/spec @josef-widder @milosevic @cason @sergio-mena @jmalicevic
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Check Markdown links
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
markdown-link-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.md
|
||||
- uses: creachadair/github-action-markdown-link-check@master
|
||||
with:
|
||||
check-modified-files-only: 'yes'
|
||||
config-file: '.md-link-check.json'
|
||||
if: env.GIT_DIFF
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Proto Check
|
||||
# Protobuf runs buf (https://buf.build/) lint and check-breakage
|
||||
# This workflow is only run when a file in the proto directory
|
||||
# has been modified.
|
||||
on:
|
||||
workflow_dispatch: # allow running workflow manually
|
||||
pull_request:
|
||||
paths:
|
||||
- "proto/**"
|
||||
jobs:
|
||||
proto-lint:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 4
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- uses: bufbuild/buf-setup-action@v1.6.0
|
||||
- uses: bufbuild/buf-lint-action@v1
|
||||
with:
|
||||
input: 'proto'
|
||||
|
||||
proto-breakage:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 4
|
||||
steps:
|
||||
- uses: actions/checkout@v2.4.0
|
||||
- uses: bufbuild/buf-setup-action@v1.6.0
|
||||
- uses: bufbuild/buf-breaking-action@v1
|
||||
with:
|
||||
against: 'https://github.com/tendermint/tendermint.git#branch=v0.35.x'
|
||||
@@ -1,23 +0,0 @@
|
||||
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:
|
||||
workflow_dispatch: # allow running workflow manually
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.proto"
|
||||
jobs:
|
||||
proto-lint:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 4
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: lint
|
||||
run: make proto-lint
|
||||
proto-breakage:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 4
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: check-breakage
|
||||
run: make proto-check-breaking-ci
|
||||
+8
-1
@@ -10,7 +10,7 @@
|
||||
.idea/
|
||||
.revision
|
||||
.tendermint
|
||||
.tendermint-lite
|
||||
.tendermint-light
|
||||
.terraform
|
||||
.vagrant
|
||||
.vendor-new/
|
||||
@@ -47,3 +47,10 @@ test/fuzz/**/corpus
|
||||
test/fuzz/**/crashers
|
||||
test/fuzz/**/suppressions
|
||||
test/fuzz/**/*.zip
|
||||
*.aux
|
||||
*.bbl
|
||||
*.blg
|
||||
*.pdf
|
||||
*.gz
|
||||
*.dvi
|
||||
.idea
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
default: true
|
||||
MD001: false
|
||||
MD007: { indent: 4 }
|
||||
MD013: false
|
||||
MD024: { siblings_only: true }
|
||||
MD025: false
|
||||
MD033: false
|
||||
MD036: false
|
||||
MD010: false
|
||||
MD012: false
|
||||
MD028: false
|
||||
+1
-1
@@ -1091,7 +1091,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/v0.35.x/spec/consensus/light-client.md) for complete details.
|
||||
`lite` package is now deprecated and will be removed in v0.34 release.
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
+1
-1
@@ -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)
|
||||
Comment](https://github.com/tendermint/tendermint/tree/master/docs/rfc)
|
||||
in the Tendermint spec repo. Discussion
|
||||
at the RFC stage will build collective understanding of the dimensions
|
||||
of the problems and help structure conversations around trade-offs.
|
||||
|
||||
@@ -83,14 +83,29 @@ $(BUILDDIR)/:
|
||||
proto-all: proto-gen proto-lint proto-check-breaking
|
||||
.PHONY: proto-all
|
||||
|
||||
proto-gen:
|
||||
@echo "Generating Go packages for .proto files"
|
||||
@$(DOCKER_PROTO) sh ./scripts/protocgen.sh
|
||||
check-proto-deps:
|
||||
ifeq (,$(shell which protoc-gen-gogofaster))
|
||||
$(error "gogofaster plugin for protoc is required. Run 'go install github.com/gogo/protobuf/protoc-gen-gogofaster@latest' to install")
|
||||
endif
|
||||
.PHONY: check-proto-deps
|
||||
|
||||
check-proto-format-deps:
|
||||
ifeq (,$(shell which clang-format))
|
||||
$(error "clang-format is required for Protobuf formatting. See instructions for your platform on how to install it.")
|
||||
endif
|
||||
.PHONY: check-proto-format-deps
|
||||
|
||||
proto-gen: check-proto-deps
|
||||
@echo "Generating Protobuf files"
|
||||
@go run github.com/bufbuild/buf/cmd/buf generate
|
||||
@mv ./proto/tendermint/abci/types.pb.go ./abci/types/
|
||||
.PHONY: proto-gen
|
||||
|
||||
proto-lint:
|
||||
@echo "Running lint checks for .proto files"
|
||||
@$(DOCKER_PROTO) buf lint --error-format=json
|
||||
# These targets are provided for convenience and are intended for local
|
||||
# execution only.
|
||||
proto-lint: check-proto-deps
|
||||
@echo "Linting Protobuf files"
|
||||
@go run github.com/bufbuild/buf/cmd/buf lint
|
||||
.PHONY: proto-lint
|
||||
|
||||
proto-format:
|
||||
@@ -98,16 +113,14 @@ proto-format:
|
||||
@$(DOCKER_PROTO) find ./ -not -path "./third_party/*" -name '*.proto' -exec clang-format -i {} \;
|
||||
.PHONY: proto-format
|
||||
|
||||
proto-check-breaking:
|
||||
@echo "Checking for breaking changes in .proto files"
|
||||
@$(DOCKER_PROTO) buf breaking --against .git#branch=$(BASE_BRANCH)
|
||||
proto-check-breaking: check-proto-deps
|
||||
@echo "Checking for breaking changes in Protobuf files against local branch"
|
||||
@echo "Note: This is only useful if your changes have not yet been committed."
|
||||
@echo " Otherwise read up on buf's \"breaking\" command usage:"
|
||||
@echo " https://docs.buf.build/breaking/usage"
|
||||
@go run github.com/bufbuild/buf/cmd/buf breaking --against ".git"
|
||||
.PHONY: proto-check-breaking
|
||||
|
||||
proto-check-breaking-ci:
|
||||
@echo "Checking for breaking changes in .proto files"
|
||||
@$(DOCKER_PROTO) buf breaking --against $(HTTPS_GIT)#branch=$(BASE_BRANCH)
|
||||
.PHONY: proto-check-breaking-ci
|
||||
|
||||
###############################################################################
|
||||
### Build ABCI ###
|
||||
###############################################################################
|
||||
|
||||
@@ -20,7 +20,7 @@ Or [Blockchain](<https://en.wikipedia.org/wiki/Blockchain_(database)>), for shor
|
||||
|
||||
Tendermint Core is a 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/blob/v0.35.x/spec/README.md).
|
||||
|
||||
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)".
|
||||
@@ -70,7 +70,7 @@ Please abide by the [Code of Conduct](CODE_OF_CONDUCT.md) in all interactions.
|
||||
|
||||
Before contributing to the project, please take a look at the [contributing guidelines](CONTRIBUTING.md)
|
||||
and the [style guide](STYLE_GUIDE.md). You may also find it helpful to read the
|
||||
[specifications](https://github.com/tendermint/spec), watch the [Developer Sessions](/docs/DEV_SESSIONS.md),
|
||||
[specifications](https://github.com/tendermint/tendermint/blob/v0.35.x/spec/README.md), watch the [Developer Sessions](/docs/DEV_SESSIONS.md),
|
||||
and familiarize yourself with our
|
||||
[Architectural Decision Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture).
|
||||
|
||||
|
||||
+3
-3
@@ -713,14 +713,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/v0.35.x/spec/blockchain/encoding.md#merkle-trees)
|
||||
* [ConsensusParams](https://github.com/tendermint/tendermint/blob/v0.35.x/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/v0.35.x/spec/consensus/signing.md#votes)
|
||||
|
||||
Finally, the proposer selection algorithm continues to evolve. See the
|
||||
[work-in-progress
|
||||
|
||||
+1
-1
@@ -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/master/spec/abci/abci.md)
|
||||
- [A protobuf file](../proto/tendermint/abci/types.proto)
|
||||
- [A Go interface](./types/application.go)
|
||||
|
||||
|
||||
+160
-40
@@ -7715,7 +7715,10 @@ func (m *Request) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -7797,7 +7800,10 @@ func (m *RequestEcho) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -7847,7 +7853,10 @@ func (m *RequestFlush) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -7999,7 +8008,10 @@ func (m *RequestInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -8237,7 +8249,10 @@ func (m *RequestInitChain) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -8392,7 +8407,10 @@ func (m *RequestQuery) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -8576,7 +8594,10 @@ func (m *RequestBeginBlock) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -8679,7 +8700,10 @@ func (m *RequestCheckTx) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -8763,7 +8787,10 @@ func (m *RequestDeliverTx) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -8832,7 +8859,10 @@ func (m *RequestEndBlock) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -8882,7 +8912,10 @@ func (m *RequestCommit) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -8932,7 +8965,10 @@ func (m *RequestListSnapshots) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -9052,7 +9088,10 @@ func (m *RequestOfferSnapshot) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -9159,7 +9198,10 @@ func (m *RequestLoadSnapshotChunk) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -9294,7 +9336,10 @@ func (m *RequestApplySnapshotChunk) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -9869,7 +9914,10 @@ func (m *Response) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -9951,7 +9999,10 @@ func (m *ResponseException) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -10033,7 +10084,10 @@ func (m *ResponseEcho) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -10083,7 +10137,10 @@ func (m *ResponseFlush) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -10269,7 +10326,10 @@ func (m *ResponseInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -10423,7 +10483,10 @@ func (m *ResponseInitChain) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -10730,7 +10793,10 @@ func (m *ResponseQuery) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -10814,7 +10880,10 @@ func (m *ResponseBeginBlock) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -11168,7 +11237,10 @@ func (m *ResponseCheckTx) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -11439,7 +11511,10 @@ func (m *ResponseDeliverTx) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -11593,7 +11668,10 @@ func (m *ResponseEndBlock) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -11696,7 +11774,10 @@ func (m *ResponseCommit) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -11780,7 +11861,10 @@ func (m *ResponseListSnapshots) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -11849,7 +11933,10 @@ func (m *ResponseOfferSnapshot) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -11933,7 +12020,10 @@ func (m *ResponseLoadSnapshotChunk) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -12110,7 +12200,10 @@ func (m *ResponseApplySnapshotChunk) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -12213,7 +12306,10 @@ func (m *LastCommitInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -12329,7 +12425,10 @@ func (m *Event) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -12463,7 +12562,10 @@ func (m *EventAttribute) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -12618,7 +12720,10 @@ func (m *TxResult) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -12721,7 +12826,10 @@ func (m *Validator) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -12823,7 +12931,10 @@ func (m *ValidatorUpdate) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -12926,7 +13037,10 @@ func (m *VoteInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -13099,7 +13213,10 @@ func (m *Evidence) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -13274,7 +13391,10 @@ func (m *Snapshot) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version: v1
|
||||
directories:
|
||||
- proto
|
||||
@@ -15,7 +15,7 @@ the block itself is never stored.
|
||||
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)
|
||||
[ABCI](https://github.com/tendermint/tendermint/blob/v0.35.x/spec/abci/abci.md#events)
|
||||
documentation.
|
||||
|
||||
An `Event` has a composite key associated with it. A `compositeKey` is
|
||||
|
||||
@@ -120,7 +120,7 @@ Next follows a standard block creation cycle, where we enter a new
|
||||
round, propose a block, receive more than 2/3 of prevotes, then
|
||||
precommits and finally have a chance to commit a block. For details,
|
||||
please refer to [Byzantine Consensus
|
||||
Algorithm](https://github.com/tendermint/spec/blob/master/spec/consensus/consensus.md).
|
||||
Algorithm](https://github.com/tendermint/tendermint/blob/v0.35.x/spec/consensus/consensus.md).
|
||||
|
||||
```sh
|
||||
I[10-04|13:54:30.393] enterNewRound(91/0). Current: 91/0/RoundStepNewHeight module=consensus
|
||||
|
||||
@@ -8,7 +8,7 @@ order: 1
|
||||
|
||||
This document endeavours to inform the wider Tendermint community about development plans and priorities for Tendermint Core, and when we expect features to be delivered. It is intended to broadly inform all users of Tendermint, including application developers, node operators, integrators, and the engineering and research teams.
|
||||
|
||||
Anyone wishing to propose work to be a part of this roadmap should do so by opening an [issue](https://github.com/tendermint/spec/issues/new/choose) in the spec. Bug reports and other implementation concerns should be brought up in the [core repository](https://github.com/tendermint/tendermint).
|
||||
Anyone wishing to propose work to be a part of this roadmap should do so by opening an [issue](https://github.com/tendermint/tendermint/issues/new/choose) in the spec. Bug reports and other implementation concerns should be brought up in the [core repository](https://github.com/tendermint/tendermint).
|
||||
|
||||
This roadmap should be read as a high-level guide to plans and priorities, rather than a commitment to schedules and deliverables. Features earlier on the roadmap will generally be more specific and detailed than those later on. We will update this document periodically to reflect the current status.
|
||||
|
||||
@@ -43,7 +43,7 @@ Added a new `EventSink` interface to allow alternatives to Tendermint's propriet
|
||||
|
||||
### ABCI++
|
||||
|
||||
An overhaul of the existing interface between the application and consensus, to give the application more control over block construction. ABCI++ adds new hooks allowing modification of transactions before they get into a block, verification of a block before voting, injection of signed information into votes, and more compact delivery of blocks after agreement (to allow for concurrent execution). [More](https://github.com/tendermint/spec/blob/master/rfc/004-abci%2B%2B.md)
|
||||
An overhaul of the existing interface between the application and consensus, to give the application more control over block construction. ABCI++ adds new hooks allowing modification of transactions before they get into a block, verification of a block before voting, injection of signed information into votes, and more compact delivery of blocks after agreement (to allow for concurrent execution). [More](https://github.com/tendermint/tendermint/blob/v0.35.x/rfc/004-abci%2B%2B.md)
|
||||
|
||||
### Proposer-Based Timestamps
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ explained in a forthcoming document.
|
||||
For efficiency reasons, validators in Tendermint consensus protocol do not agree directly on the
|
||||
block as the block size is big, i.e., they don't embed the block inside `Proposal` and
|
||||
`VoteMessage`. Instead, they reach agreement on the `BlockID` (see `BlockID` definition in
|
||||
[Blockchain](https://github.com/tendermint/spec/blob/master/spec/core/data_structures.md#blockid) section)
|
||||
[Blockchain](https://github.com/tendermint/tendermint/blob/v0.35.x/spec/core/data_structures.md#blockid) section)
|
||||
that uniquely identifies each block. The block itself is
|
||||
disseminated to validator processes using peer-to-peer gossiping protocol. It starts by having a
|
||||
proposer first splitting a block into a number of block parts, that are then gossiped between
|
||||
|
||||
@@ -43,7 +43,7 @@ transactions](../app-dev/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/v0.35.x/spec/abci/abci.md#endblock) in
|
||||
the ABCI spec).
|
||||
|
||||
Response:
|
||||
|
||||
@@ -49,7 +49,7 @@ definition](https://github.com/tendermint/tendermint/blob/master/types/genesis.g
|
||||
chain IDs, you will have a bad time. The ChainID must be less than 50 symbols.
|
||||
- `initial_height`: Height at which Tendermint should begin at. If a blockchain is conducting a network upgrade,
|
||||
starting from the stopped height brings uniqueness to previous heights.
|
||||
- `consensus_params` [spec](https://github.com/tendermint/spec/blob/master/spec/core/state.md#consensusparams)
|
||||
- `consensus_params` [spec](https://github.com/tendermint/tendermint/blob/v0.35.x/spec/core/state.md#consensusparams)
|
||||
- `block`
|
||||
- `max_bytes`: Max block size, in bytes.
|
||||
- `max_gas`: Max gas per block.
|
||||
|
||||
@@ -8,6 +8,7 @@ require (
|
||||
github.com/adlio/schema v1.3.3
|
||||
github.com/btcsuite/btcd v0.22.1
|
||||
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
|
||||
github.com/bufbuild/buf v1.3.1
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/creachadair/atomicfile v0.2.6
|
||||
github.com/creachadair/taskgroup v0.3.2
|
||||
@@ -47,9 +48,8 @@ require (
|
||||
github.com/vektra/mockery/v2 v2.14.0
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
|
||||
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
|
||||
google.golang.org/grpc v1.48.0
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
|
||||
google.golang.org/grpc v1.47.0
|
||||
gotest.tools v2.2.0+incompatible // indirect
|
||||
pgregory.net/rapid v0.4.8
|
||||
)
|
||||
|
||||
@@ -148,6 +148,8 @@ github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnO
|
||||
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -179,6 +181,8 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku
|
||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/bufbuild/buf v1.3.1 h1:AelWcENnbNEjwxmQXIZaU51GHgnWQ8Mc94kZdDUKgRs=
|
||||
github.com/bufbuild/buf v1.3.1/go.mod h1:CTRUb23N+zlm1U8ZIBKz0Sqluk++qQloB2i/MZNZHIs=
|
||||
github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY=
|
||||
github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc=
|
||||
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
||||
@@ -231,10 +235,12 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
||||
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creachadair/atomicfile v0.2.6 h1:FgYxYvGcqREApTY8Nxg8msM6P/KVKK3ob5h9FaRUTNg=
|
||||
github.com/creachadair/atomicfile v0.2.6/go.mod h1:BRq8Une6ckFneYXZQ+kO7p1ZZP3I2fzVzf28JxrIkBc=
|
||||
@@ -384,6 +390,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
|
||||
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
@@ -401,6 +409,7 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
@@ -617,11 +626,17 @@ github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jdxcode/netrc v0.0.0-20210204082910-926c7f70242a h1:d4+I1YEKVmWZrgkt6jpXBnLgV2ZjO0YxEtLDdfIZfH4=
|
||||
github.com/jdxcode/netrc v0.0.0-20210204082910-926c7f70242a/go.mod h1:Zi/ZFkEqFHTm7qkjyNJjaWH4LQA9LQhGJyF0lTYGpxw=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jgautheron/goconst v1.5.1 h1:HxVbL1MhydKs8R8n/HE5NPvzfaYmQJA3o879lE4+WcM=
|
||||
github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
|
||||
github.com/jhump/protocompile v0.0.0-20220216033700-d705409f108f h1:BNuUg9k2EiJmlMwjoef3e8vZLHplbVw6DrjGFjLL+Yo=
|
||||
github.com/jhump/protocompile v0.0.0-20220216033700-d705409f108f/go.mod h1:qr2b5kx4HbFS7/g4uYO5qv9ei8303JMsC7ESbYiqr2Q=
|
||||
github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4=
|
||||
github.com/jhump/protoreflect v1.11.1-0.20220213155251-0c2aedc66cf4 h1:E2CdxLXYSn6Zrj2+u8DWrwMJW3YZLSWtM/7kIL8OL18=
|
||||
github.com/jhump/protoreflect v1.11.1-0.20220213155251-0c2aedc66cf4/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48=
|
||||
@@ -664,6 +679,10 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6
|
||||
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A=
|
||||
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -815,6 +834,7 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20210609091139-0a56a4bca00b h1:MKwruh+HeCSKWphkxuzvRzU4QzDkg7yiPkDVV0cDFgI=
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20210609091139-0a56a4bca00b/go.mod h1:TLJifjWF6eotcfzDjKZsDqWJ+73Uvj/N85MvVyrvynM=
|
||||
github.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
|
||||
@@ -864,6 +884,7 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
@@ -880,12 +901,16 @@ github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7
|
||||
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
||||
github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM=
|
||||
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -958,8 +983,10 @@ github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs=
|
||||
github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryancurrah/gomodguard v1.2.3 h1:ww2fsjqocGCAFamzvv/b8IsRduuHHeK2MHTcTxZTQX8=
|
||||
github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg=
|
||||
@@ -1165,6 +1192,7 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
@@ -1175,18 +1203,24 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=
|
||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
|
||||
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -1362,8 +1396,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
|
||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1445,6 +1480,7 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1789,8 +1825,8 @@ google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
|
||||
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
|
||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
@@ -1804,6 +1840,7 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Package evidence handles all evidence storage and gossiping from detection to block proposal.
|
||||
For the different types of evidence refer to the `evidence.go` file in the types package
|
||||
or https://github.com/tendermint/spec/blob/master/spec/consensus/light-client/accountability.md.
|
||||
or https://github.com/tendermint/tendermint/blob/v0.35.x/spec/consensus/light-client/accountability.md.
|
||||
|
||||
Gossiping
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# we need python2 support, which was dropped after buster:
|
||||
FROM debian:buster
|
||||
|
||||
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y apt-utils
|
||||
|
||||
# Install and configure locale `en_US.UTF-8`
|
||||
RUN apt-get install -y locales && \
|
||||
sed -i -e "s/# $en_US.*/en_US.UTF-8 UTF-8/" /etc/locale.gen && \
|
||||
dpkg-reconfigure --frontend=noninteractive locales && \
|
||||
update-locale LANG=en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y git python2 python-pip g++ cmake python-ply python-tk tix pkg-config libssl-dev python-setuptools
|
||||
|
||||
# create a user:
|
||||
RUN useradd -ms /bin/bash user
|
||||
USER user
|
||||
WORKDIR /home/user
|
||||
|
||||
RUN git clone --recurse-submodules https://github.com/kenmcmil/ivy.git
|
||||
WORKDIR /home/user/ivy/
|
||||
RUN git checkout 271ee38980699115508eb90a0dd01deeb750a94b
|
||||
|
||||
RUN python2.7 build_submodules.py
|
||||
RUN mkdir -p "/home/user/python/lib/python2.7/site-packages"
|
||||
ENV PYTHONPATH="/home/user/python/lib/python2.7/site-packages"
|
||||
# need to install pyparsing manually because otherwise wrong version found
|
||||
RUN pip install pyparsing
|
||||
RUN python2.7 setup.py install --prefix="/home/user/python/"
|
||||
ENV PATH=$PATH:"/home/user/python/bin/"
|
||||
WORKDIR /home/user/tendermint-proof/
|
||||
|
||||
ENTRYPOINT ["/home/user/tendermint-proof/check_proofs.sh"]
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Ivy Proofs
|
||||
|
||||
```copyright
|
||||
Copyright (c) 2020 Galois, Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
```
|
||||
|
||||
## Contents
|
||||
|
||||
This folder contains:
|
||||
|
||||
* `tendermint.ivy`, a specification of Tendermint algorithm as described in *The latest gossip on BFT consensus* by E. Buchman, J. Kwon, Z. Milosevic.
|
||||
* `abstract_tendermint.ivy`, a more abstract specification of Tendermint that is more verification-friendly.
|
||||
* `classic_safety.ivy`, a proof that Tendermint satisfies the classic safety property of BFT consensus: if every two quorums have a well-behaved node in common, then no two well-behaved nodes ever disagree.
|
||||
* `accountable_safety_1.ivy`, a proof that, assuming every quorum contains at least one well-behaved node, if two well-behaved nodes disagree, then there is evidence demonstrating at least f+1 nodes misbehaved.
|
||||
* `accountable_safety_2.ivy`, a proof that, regardless of any assumption about quorums, well-behaved nodes cannot be framed by malicious nodes. In other words, malicious nodes can never construct evidence that incriminates a well-behaved node.
|
||||
* `network_shim.ivy`, the network model and a convenience `shim` object to interface with the Tendermint specification.
|
||||
* `domain_model.ivy`, a specification of the domain model underlying the Tendermint specification, i.e. rounds, value, quorums, etc.
|
||||
|
||||
All specifications and proofs are written in [Ivy](https://github.com/kenmcmil/ivy).
|
||||
|
||||
The license above applies to all files in this folder.
|
||||
|
||||
|
||||
## Building and running
|
||||
|
||||
The easiest way to check the proofs is to use [Docker](https://www.docker.com/).
|
||||
|
||||
1. Install [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/).
|
||||
2. Build a Docker image: `docker-compose build`
|
||||
3. Run the proofs inside the Docker container: `docker-compose run
|
||||
tendermint-proof`. This will check all the proofs with the `ivy_check`
|
||||
command and write the output of `ivy_check` to a subdirectory of `./output/'
|
||||
@@ -0,0 +1,178 @@
|
||||
#lang ivy1.7
|
||||
# ---
|
||||
# layout: page
|
||||
# title: Abstract specification of Tendermint in Ivy
|
||||
# ---
|
||||
|
||||
# Here we define an abstract version of the Tendermint specification. We use
|
||||
# two main forms of abstraction: a) We abstract over how information is
|
||||
# transmitted (there is no network). b) We abstract functions using relations.
|
||||
# For example, we abstract over a node's current round, instead only tracking
|
||||
# with a relation which rounds the node has left. We do something similar for
|
||||
# the `lockedRound` variable. This is in order to avoid using a function from
|
||||
# node to round, and it allows us to emit verification conditions that are
|
||||
# efficiently solvable by Z3.
|
||||
|
||||
# This specification also defines the observations that are used to adjudicate
|
||||
# misbehavior. Well-behaved nodes faithfully observe every message that they
|
||||
# use to take a step, while Byzantine nodes can fake observations about
|
||||
# themselves (including withholding observations). Misbehavior is defined using
|
||||
# the collection of all observations made (in reality, those observations must
|
||||
# be collected first, but we do not model this process).
|
||||
|
||||
include domain_model
|
||||
|
||||
module abstract_tendermint = {
|
||||
|
||||
# Protocol state
|
||||
# ##############
|
||||
|
||||
relation left_round(N:node, R:round)
|
||||
relation prevoted(N:node, R:round, V:value)
|
||||
relation precommitted(N:node, R:round, V:value)
|
||||
relation decided(N:node, R:round, V:value)
|
||||
relation locked(N:node, R:round, V:value)
|
||||
|
||||
# Accountability relations
|
||||
# ########################
|
||||
|
||||
relation observed_prevoted(N:node, R:round, V:value)
|
||||
relation observed_precommitted(N:node, R:round, V:value)
|
||||
|
||||
# relations that are defined in terms of the previous two:
|
||||
relation observed_equivocation(N:node)
|
||||
relation observed_unlawful_prevote(N:node)
|
||||
relation agreement
|
||||
relation accountability_violation
|
||||
|
||||
object defs = { # we hide those definitions and use them only when needed
|
||||
private {
|
||||
definition [observed_equivocation_def] observed_equivocation(N) = exists V1,V2,R .
|
||||
V1 ~= V2 & (observed_precommitted(N,R,V1) & observed_precommitted(N,R,V2) | observed_prevoted(N,R,V1) & observed_prevoted(N,R,V2))
|
||||
|
||||
definition [observed_unlawful_prevote_def] observed_unlawful_prevote(N) = exists V1,V2,R1,R2 .
|
||||
V1 ~= value.nil & V2 ~= value.nil & V1 ~= V2 & R1 < R2 & observed_precommitted(N,R1,V1) & observed_prevoted(N,R2,V2)
|
||||
& forall Q,R . R1 <= R & R < R2 & nset.is_quorum(Q) -> exists N2 . nset.member(N2,Q) & ~observed_prevoted(N2,R,V2)
|
||||
|
||||
definition [agreement_def] agreement = forall N1,N2,R1,R2,V1,V2 . well_behaved(N1) & well_behaved(N2) & decided(N1,R1,V1) & decided(N2,R2,V2) -> V1 = V2
|
||||
|
||||
definition [accountability_violation_def] accountability_violation = exists Q1,Q2 . nset.is_quorum(Q1) & nset.is_quorum(Q2) & (forall N . nset.member(N,Q1) & nset.member(N,Q2) -> observed_equivocation(N) | observed_unlawful_prevote(N))
|
||||
}
|
||||
}
|
||||
|
||||
# Protocol transitions
|
||||
# ####################
|
||||
|
||||
after init {
|
||||
left_round(N,R) := R < 0;
|
||||
prevoted(N,R,V) := false;
|
||||
precommitted(N,R,V) := false;
|
||||
decided(N,R,V) := false;
|
||||
locked(N,R,V) := false;
|
||||
|
||||
observed_prevoted(N,R,V) := false;
|
||||
observed_precommitted(N,R,V) := false;
|
||||
}
|
||||
|
||||
# Actions are named after the corresponding line numbers in the Tendermint
|
||||
# arXiv paper.
|
||||
|
||||
action l_11(n:node, r:round) = { # start round r
|
||||
require ~left_round(n,r);
|
||||
left_round(n,R) := R < r;
|
||||
}
|
||||
|
||||
action l_22(n:node, rp:round, v:value) = {
|
||||
require ~left_round(n,rp);
|
||||
require ~prevoted(n,rp,V) & ~precommitted(n,rp,V);
|
||||
require (forall R,V . locked(n,R,V) -> V = v) | v = value.nil;
|
||||
prevoted(n, rp, v) := true;
|
||||
left_round(n, R) := R < rp; # leave all lower rounds.
|
||||
|
||||
observed_prevoted(n, rp, v) := observed_prevoted(n, rp, v) | well_behaved(n); # the node observes itself
|
||||
}
|
||||
|
||||
action l_28(n:node, rp:round, v:value, vr:round, q:nset) = {
|
||||
require ~left_round(n,rp) & ~prevoted(n,rp,V);
|
||||
require ~prevoted(n,rp,V) & ~precommitted(n,rp,V);
|
||||
require vr < rp;
|
||||
require nset.is_quorum(q) & (forall N . nset.member(N,q) -> (prevoted(N,vr,v) | ~well_behaved(N)));
|
||||
var proposal:value;
|
||||
if value.valid(v) & ((forall R0,V0 . locked(n,R0,V0) -> R0 <= vr) | (forall R,V . locked(n,R,V) -> V = v)) {
|
||||
proposal := v;
|
||||
}
|
||||
else {
|
||||
proposal := value.nil;
|
||||
};
|
||||
prevoted(n, rp, proposal) := true;
|
||||
left_round(n, R) := R < rp; # leave all lower rounds
|
||||
|
||||
observed_prevoted(N, vr, v) := observed_prevoted(N, vr, v) | (well_behaved(n) & nset.member(N,q)); # the node observes the prevotes of quorum q
|
||||
observed_prevoted(n, rp, proposal) := observed_prevoted(n, rp, proposal) | well_behaved(n); # the node observes itself
|
||||
}
|
||||
|
||||
action l_36(n:node, rp:round, v:value, q:nset) = {
|
||||
require v ~= value.nil;
|
||||
require ~left_round(n,rp);
|
||||
require exists V . prevoted(n,rp,V);
|
||||
require ~precommitted(n,rp,V);
|
||||
require nset.is_quorum(q) & (forall N . nset.member(N,q) -> (prevoted(N,rp,v) | ~well_behaved(N)));
|
||||
precommitted(n, rp, v) := true;
|
||||
left_round(n, R) := R < rp; # leave all lower rounds
|
||||
locked(n,R,V) := R <= rp & V = v;
|
||||
|
||||
observed_prevoted(N, rp, v) := observed_prevoted(N, rp, v) | (well_behaved(n) & nset.member(N,q)); # the node observes the prevotes of quorum q
|
||||
observed_precommitted(n, rp, v) := observed_precommitted(n, rp, v) | well_behaved(n); # the node observes itself
|
||||
}
|
||||
|
||||
action l_44(n:node, rp:round, q:nset) = {
|
||||
require ~left_round(n,rp);
|
||||
require ~precommitted(n,rp,V);
|
||||
require nset.is_quorum(q) & (forall N .nset.member(N,q) -> (prevoted(N,rp,value.nil) | ~well_behaved(N)));
|
||||
precommitted(n, rp, value.nil) := true;
|
||||
left_round(n, R) := R < rp; # leave all lower rounds
|
||||
|
||||
observed_prevoted(N, rp, value.nil) := observed_prevoted(N, rp, value.nil) | (well_behaved(n) & nset.member(N,q)); # the node observes the prevotes of quorum q
|
||||
observed_precommitted(n, rp, value.nil) := observed_precommitted(n, rp, value.nil) | well_behaved(n); # the node observes itself
|
||||
}
|
||||
|
||||
action l_57(n:node, rp:round) = {
|
||||
require ~left_round(n,rp);
|
||||
require ~prevoted(n,rp,V);
|
||||
prevoted(n, rp, value.nil) := true;
|
||||
left_round(n, R) := R < rp; # leave all lower rounds
|
||||
|
||||
observed_prevoted(n, rp, value.nil) := observed_prevoted(n, rp, value.nil) | well_behaved(n); # the node observes itself
|
||||
}
|
||||
|
||||
action l_61(n:node, rp:round) = {
|
||||
require ~left_round(n,rp);
|
||||
require ~precommitted(n,rp,V);
|
||||
precommitted(n, rp, value.nil) := true;
|
||||
left_round(n, R) := R < rp; # leave all lower rounds
|
||||
|
||||
observed_precommitted(n, rp, value.nil) := observed_precommitted(n, rp, value.nil) | well_behaved(n); # the node observes itself
|
||||
}
|
||||
|
||||
action decide(n:node, r:round, v:value, q:nset) = {
|
||||
require v ~= value.nil;
|
||||
require nset.is_quorum(q) & (forall N . nset.member(N, q) -> (precommitted(N, r, v) | ~well_behaved(N)));
|
||||
decided(n, r, v) := true;
|
||||
|
||||
observed_precommitted(N, r, v) := observed_precommitted(N, r, v) | (well_behaved(n) & nset.member(N,q)); # the node observes the precommits of quorum q
|
||||
|
||||
}
|
||||
|
||||
action misbehave = {
|
||||
# Byzantine nodes can claim they observed whatever they want about themselves,
|
||||
# but they cannot remove observations. Note that we use assume because we don't
|
||||
# want those to be checked; we just want them to be true (that's the model of
|
||||
# Byzantine behavior).
|
||||
observed_prevoted(N,R,V) := *;
|
||||
assume (old observed_prevoted(N,R,V)) -> observed_prevoted(N,R,V);
|
||||
assume well_behaved(N) -> old observed_prevoted(N,R,V) = observed_prevoted(N,R,V);
|
||||
observed_precommitted(N,R,V) := *;
|
||||
assume (old observed_precommitted(N,R,V)) -> observed_precommitted(N,R,V);
|
||||
assume well_behaved(N) -> old observed_precommitted(N,R,V) = observed_precommitted(N,R,V);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
#lang ivy1.7
|
||||
# ---
|
||||
# layout: page
|
||||
# title: Proof of Classic Safety
|
||||
# ---
|
||||
|
||||
include tendermint
|
||||
include abstract_tendermint
|
||||
|
||||
# Here we prove the first accountability property: if two well-behaved nodes
|
||||
# disagree, then there are two quorums Q1 and Q2 such that all members of the
|
||||
# intersection of Q1 and Q2 have violated the accountability properties.
|
||||
|
||||
# The proof is done in two steps: first we prove the abstract specification
|
||||
# satisfies the property, and then we show by refinement that this property
|
||||
# also holds in the concrete specification.
|
||||
|
||||
# To see what is checked in the refinement proof, use `ivy_show isolate=accountable_safety_1 accountable_safety_1.ivy`
|
||||
# To see what is checked in the abstract correctness proof, use `ivy_show isolate=abstract_accountable_safety_1 accountable_safety_1.ivy`
|
||||
# To check the whole proof, use `ivy_check accountable_safety_1.ivy`.
|
||||
|
||||
|
||||
# Proof of the accountability property in the abstract specification
|
||||
# ==================================================================
|
||||
|
||||
# We prove with tactics (see `lemma_1` and `lemma_2`) that, if some basic
|
||||
# invariants hold (see `invs` below), then the accountability property holds.
|
||||
|
||||
isolate abstract_accountable_safety = {
|
||||
|
||||
instantiate abstract_tendermint
|
||||
|
||||
# The main property
|
||||
# -----------------
|
||||
|
||||
# If there is disagreement, then there is evidence that a third of the nodes
|
||||
# have violated the protocol:
|
||||
invariant [accountability] agreement | accountability_violation
|
||||
proof {
|
||||
apply lemma_1.thm # this reduces to goal to three subgoals: p1, p2, and p3 (see their definition below)
|
||||
proof [p1] {
|
||||
assume invs.inv1
|
||||
}
|
||||
proof [p2] {
|
||||
assume invs.inv2
|
||||
}
|
||||
proof [p3] {
|
||||
assume invs.inv3
|
||||
}
|
||||
}
|
||||
|
||||
# The invariants
|
||||
# --------------
|
||||
|
||||
isolate invs = {
|
||||
|
||||
# well-behaved nodes observe their own actions faithfully:
|
||||
invariant [inv1] well_behaved(N) -> (observed_precommitted(N,R,V) = precommitted(N,R,V))
|
||||
# if a value is precommitted by a well-behaved node, then a quorum is observed to prevote it:
|
||||
invariant [inv2] (exists N . well_behaved(N) & precommitted(N,R,V)) & V ~= value.nil -> exists Q . nset.is_quorum(Q) & forall N2 . nset.member(N2,Q) -> observed_prevoted(N2,R,V)
|
||||
# if a value is decided by a well-behaved node, then a quorum is observed to precommit it:
|
||||
invariant [inv3] (exists N . well_behaved(N) & decided(N,R,V)) -> 0 <= R & V ~= value.nil & exists Q . nset.is_quorum(Q) & forall N2 . nset.member(N2,Q) -> observed_precommitted(N2,R,V)
|
||||
private {
|
||||
invariant (precommitted(N,R,V) | prevoted(N,R,V)) -> 0 <= R
|
||||
invariant R < 0 -> left_round(N,R)
|
||||
}
|
||||
|
||||
} with this, nset, round, accountable_bft.max_2f_byzantine
|
||||
|
||||
# The theorems proved with tactics
|
||||
# --------------------------------
|
||||
|
||||
# Using complete induction on rounds, we prove that, assuming that the
|
||||
# invariants inv1, inv2, and inv3 hold, the accountability property holds.
|
||||
|
||||
# For technical reasons, we separate the proof in two steps
|
||||
isolate lemma_1 = {
|
||||
|
||||
specification {
|
||||
theorem [thm] {
|
||||
property [p1] forall N,R,V . well_behaved(N) -> (observed_precommitted(N,R,V) = precommitted(N,R,V))
|
||||
property [p2] forall R,V . (exists N . well_behaved(N) & precommitted(N,R,V)) & V ~= value.nil -> exists Q . nset.is_quorum(Q) & forall N2 . nset.member(N2,Q) -> observed_prevoted(N2,R,V)
|
||||
property [p3] forall R,V. (exists N . well_behaved(N) & decided(N,R,V)) -> 0 <= R & V ~= value.nil & exists Q . nset.is_quorum(Q) & forall N2 . nset.member(N2,Q) -> observed_precommitted(N2,R,V)
|
||||
#-------------------------------------------------------------------------------------------------------------------------------------------
|
||||
property agreement | accountability_violation
|
||||
}
|
||||
proof {
|
||||
assume inductive_property # the theorem follows from what we prove by induction below
|
||||
}
|
||||
}
|
||||
|
||||
implementation {
|
||||
# complete induction is not built-in, so we introduce it with an axiom. Note that this only holds for a type where 0 is the smallest element
|
||||
axiom [complete_induction] {
|
||||
relation p(X:round)
|
||||
{ # base case
|
||||
property p(0)
|
||||
}
|
||||
{ # inductive step: show that if the property is true for all X lower or equal to x and y=x+1, then the property is true of y
|
||||
individual a:round
|
||||
individual b:round
|
||||
property (forall X. 0 <= X & X <= a -> p(X)) & round.succ(a,b) -> p(b)
|
||||
}
|
||||
#--------------------------
|
||||
property forall X . 0 <= X -> p(X)
|
||||
}
|
||||
|
||||
# The main lemma: if inv1 and inv2 below hold and a quorum is observed to
|
||||
# precommit V1 at R1 and another quorum is observed to precommit V2~=V1 at
|
||||
# R2>=R1, then the intersection of two quorums (i.e. f+1 nodes) is observed to
|
||||
# violate the protocol. We prove this by complete induction on R2.
|
||||
theorem [inductive_property] {
|
||||
property [p1] forall N,R,V . well_behaved(N) -> (observed_precommitted(N,R,V) = precommitted(N,R,V))
|
||||
property [p2] forall R,V . (exists N . well_behaved(N) & precommitted(N,R,V)) -> V = value.nil | exists Q . nset.is_quorum(Q) & forall N2 . nset.member(N2,Q) -> observed_prevoted(N2,R,V)
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
property forall R2. 0 <= R2 -> ((exists V2,Q1,R1,V1,Q1 . V1 ~= value.nil & V2 ~= value.nil & V1 ~= V2 & 0 <= R1 & R1 <= R2 & nset.is_quorum(Q1) & (forall N . nset.member(N,Q1) -> observed_precommitted(N,R1,V1)) & (exists Q2 . nset.is_quorum(Q2) & forall N . nset.member(N,Q2) -> observed_prevoted(N,R2,V2))) -> accountability_violation)
|
||||
}
|
||||
proof {
|
||||
apply complete_induction # the two subgoals (base case and inductive case) are then discharged automatically
|
||||
# NOTE: this can take a long time depending on the SMT random seed (to try a different seed, use `ivy_check seed=$RANDOM`
|
||||
}
|
||||
}
|
||||
} with this, round, nset, accountable_bft.max_2f_byzantine, defs.observed_equivocation_def, defs.observed_unlawful_prevote_def, defs.accountability_violation_def, defs.agreement_def
|
||||
|
||||
} with round
|
||||
|
||||
# The final proof
|
||||
# ===============
|
||||
|
||||
isolate accountable_safety_1 = {
|
||||
|
||||
# First we instantiate the concrete protocol:
|
||||
instantiate tendermint(abstract_accountable_safety)
|
||||
|
||||
# We then define what we mean by agreement
|
||||
relation agreement
|
||||
definition [agreement_def] agreement = forall N1,N2. well_behaved(N1) & well_behaved(N2) & server.decision(N1) ~= value.nil & server.decision(N2) ~= value.nil -> server.decision(N1) = server.decision(N2)
|
||||
|
||||
invariant abstract_accountable_safety.agreement -> agreement
|
||||
|
||||
invariant [accountability] agreement | abstract_accountable_safety.accountability_violation
|
||||
|
||||
} with value, round, proposers, shim, abstract_accountable_safety, abstract_accountable_safety.defs.agreement_def, accountable_safety_1.agreement_def
|
||||
@@ -0,0 +1,52 @@
|
||||
#lang ivy1.7
|
||||
|
||||
include tendermint
|
||||
include abstract_tendermint
|
||||
|
||||
# Here we prove the second accountability property: no well-behaved node is
|
||||
# ever observed to violate the accountability properties.
|
||||
|
||||
# The proof is done in two steps: first we prove the the abstract specification
|
||||
# satisfies the property, and then we show by refinement that this property
|
||||
# also holds in the concrete specification.
|
||||
|
||||
# To see what is checked in the refinement proof, use `ivy_show isolate=accountable_safety_2 accountable_safety_2.ivy`
|
||||
# To see what is checked in the abstract correctness proof, use `ivy_show isolate=abstract_accountable_safety_2 accountable_safety_2.ivy`
|
||||
# To check the whole proof, use `ivy_check complete=fo accountable_safety_2.ivy`.
|
||||
|
||||
# Proof that the property holds in the abstract specification
|
||||
# ============================================================
|
||||
|
||||
isolate abstract_accountable_safety_2 = {
|
||||
|
||||
instantiate abstract_tendermint
|
||||
|
||||
# the main property:
|
||||
invariant [wb_never_punished] well_behaved(N) -> ~(observed_equivocation(N) | observed_unlawful_prevote(N))
|
||||
|
||||
# the main invariant for proving wb_not_punished:
|
||||
invariant well_behaved(N) & precommitted(N,R,V) & ~locked(N,R,V) & V ~= value.nil -> exists R2,V2 . V2 ~= value.nil & R < R2 & precommitted(N,R2,V2) & locked(N,R2,V2)
|
||||
|
||||
invariant (exists N . well_behaved(N) & precommitted(N,R,V) & V ~= value.nil) -> exists Q . nset.is_quorum(Q) & forall N . nset.member(N,Q) -> observed_prevoted(N,R,V)
|
||||
|
||||
invariant well_behaved(N) -> (observed_prevoted(N,R,V) <-> prevoted(N,R,V))
|
||||
invariant well_behaved(N) -> (observed_precommitted(N,R,V) <-> precommitted(N,R,V))
|
||||
|
||||
# nodes stop prevoting or precommitting in lower rounds when doing so in a higher round:
|
||||
invariant well_behaved(N) & prevoted(N,R2,V2) & R1 < R2 -> left_round(N,R1)
|
||||
invariant well_behaved(N) & locked(N,R2,V2) & R1 < R2 -> left_round(N,R1)
|
||||
|
||||
invariant [precommit_unique_per_round] well_behaved(N) & precommitted(N,R,V1) & precommitted(N,R,V2) -> V1 = V2
|
||||
|
||||
} with nset, round, abstract_accountable_safety_2.defs.observed_equivocation_def, abstract_accountable_safety_2.defs.observed_unlawful_prevote_def
|
||||
|
||||
# Proof that the property holds in the concrete specification
|
||||
# ===========================================================
|
||||
|
||||
isolate accountable_safety_2 = {
|
||||
|
||||
instantiate tendermint(abstract_accountable_safety_2)
|
||||
|
||||
invariant well_behaved(N) -> ~(abstract_accountable_safety_2.observed_equivocation(N) | abstract_accountable_safety_2.observed_unlawful_prevote(N))
|
||||
|
||||
} with round, value, shim, abstract_accountable_safety_2, abstract_accountable_safety_2.defs.observed_equivocation_def, abstract_accountable_safety_2.defs.observed_unlawful_prevote_def
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
|
||||
# returns non-zero error code if any proof fails
|
||||
|
||||
success=0
|
||||
log_dir=$(cat /dev/urandom | tr -cd 'a-f0-9' | head -c 6)
|
||||
cmd="ivy_check seed=$RANDOM"
|
||||
mkdir -p output/$log_dir
|
||||
|
||||
echo "Checking classic safety:"
|
||||
res=$($cmd classic_safety.ivy | tee "output/$log_dir/classic_safety.txt" | tail -n 1)
|
||||
if [ "$res" = "OK" ]; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAILED"
|
||||
success=1
|
||||
fi
|
||||
|
||||
echo "Checking accountable safety 1:"
|
||||
res=$($cmd accountable_safety_1.ivy | tee "output/$log_dir/accountable_safety_1.txt" | tail -n 1)
|
||||
if [ "$res" = "OK" ]; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAILED"
|
||||
success=1
|
||||
fi
|
||||
|
||||
echo "Checking accountable safety 2:"
|
||||
res=$($cmd complete=fo accountable_safety_2.ivy | tee "output/$log_dir/accountable_safety_2.txt" | tail -n 1)
|
||||
if [ "$res" = "OK" ]; then
|
||||
echo "OK"
|
||||
else
|
||||
echo "FAILED"
|
||||
success=1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "See ivy_check output in the output/ folder"
|
||||
exit $success
|
||||
@@ -0,0 +1,85 @@
|
||||
#lang ivy1.7
|
||||
# ---
|
||||
# layout: page
|
||||
# title: Proof of Classic Safety
|
||||
# ---
|
||||
|
||||
include tendermint
|
||||
include abstract_tendermint
|
||||
|
||||
# Here we prove the classic safety property: assuming that every two quorums
|
||||
# have a well-behaved node in common, no two well-behaved nodes ever disagree.
|
||||
|
||||
# The proof is done in two steps: first we prove the the abstract specification
|
||||
# satisfies the property, and then we show by refinement that this property
|
||||
# also holds in the concrete specification.
|
||||
|
||||
# To see what is checked in the refinement proof, use `ivy_show isolate=classic_safety classic_safety.ivy`
|
||||
# To see what is checked in the abstract correctness proof, use `ivy_show isolate=abstract_classic_safety classic_safety.ivy`
|
||||
|
||||
# To check the whole proof, use `ivy_check classic_safety.ivy`.
|
||||
|
||||
# Note that all the verification conditions sent to Z3 for this proof are in
|
||||
# EPR.
|
||||
|
||||
# Classic safety in the abstract model
|
||||
# ====================================
|
||||
|
||||
# We start by proving that classic safety holds in the abstract model.
|
||||
|
||||
isolate abstract_classic_safety = {
|
||||
|
||||
instantiate abstract_tendermint
|
||||
|
||||
invariant [classic_safety] classic_bft.quorum_intersection & decided(N1,R1,V1) & decided(N2,R2,V2) -> V1 = V2
|
||||
|
||||
# The notion of choosable value
|
||||
# -----------------------------
|
||||
|
||||
relation choosable(R:round, V:value)
|
||||
definition choosable(R,V) = exists Q . nset.is_quorum(Q) & forall N . well_behaved(N) & nset.member(N,Q) -> ~left_round(N,R) | precommitted(N,R,V)
|
||||
|
||||
# Main invariants
|
||||
# ---------------
|
||||
|
||||
# `classic_safety` is inductive relative to those invariants
|
||||
|
||||
invariant [decision_is_quorum_precommit] (exists N1 . decided(N1,R,V)) -> exists Q. nset.is_quorum(Q) & forall N2. well_behaved(N2) & nset.member(N2, Q) -> precommitted(N2,R,V)
|
||||
|
||||
invariant [precommitted_is_quorum_prevote] V ~= value.nil & (exists N1 . precommitted(N1,R,V)) -> exists Q. nset.is_quorum(Q) & forall N2. well_behaved(N2) & nset.member(N2, Q) -> prevoted(N2,R,V)
|
||||
|
||||
invariant [prevote_unique_per_round] prevoted(N,R,V1) & prevoted(N,R,V2) -> V1 = V2
|
||||
|
||||
# This is the core invariant: as long as a precommitted value is still choosable, it remains protected by a lock and prevents any new value from being prevoted:
|
||||
invariant [locks] classic_bft.quorum_intersection & V ~= value.nil & precommitted(N,R,V) & choosable(R,V) -> locked(N,R,V) & forall R2,V2 . R < R2 & prevoted(N,R2,V2) -> V2 = V | V2 = value.nil
|
||||
|
||||
# Supporting invariants
|
||||
# ---------------------
|
||||
|
||||
# The main invariants are inductive relative to those
|
||||
|
||||
invariant decided(N,R,V) -> V ~= value.nil
|
||||
|
||||
invariant left_round(N,R2) & R1 < R2 -> left_round(N,R1) # if a node left round R2>R1, then it also left R1:
|
||||
|
||||
invariant prevoted(N,R2,V2) & R1 < R2 -> left_round(N,R1)
|
||||
invariant precommitted(N,R2,V2) & R1 < R2 -> left_round(N,R1)
|
||||
|
||||
} with round, nset, classic_bft.quorum_intersection_def
|
||||
|
||||
# The refinement proof
|
||||
# ====================
|
||||
|
||||
# Now, thanks to the refinement relation that we establish in
|
||||
# `concrete_tendermint.ivy`, we prove that classic safety transfers to the
|
||||
# concrete specification:
|
||||
isolate classic_safety = {
|
||||
|
||||
# We instantiate the `tendermint` module providing `abstract_classic_safety` as abstract model.
|
||||
instantiate tendermint(abstract_classic_safety)
|
||||
|
||||
# We prove that if every two quorums have a well-behaved node in common,
|
||||
# then well-behaved nodes never disagree:
|
||||
invariant [classic_safety] classic_bft.quorum_intersection & server.decision(N1) ~= value.nil & server.decision(N2) ~= value.nil -> server.decision(N1) = server.decision(N2)
|
||||
|
||||
} with value, round, proposers, shim, abstract_classic_safety # here we list all the specifications that we rely on for this proof
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
r='^\s*$\|^\s*\#\|^\s*\}\s*$\|^\s*{\s*$' # removes comments and blank lines and lines that contain only { or }
|
||||
N1=`cat tendermint.ivy domain_model.ivy network_shim.ivy | grep -v $r'\|.*invariant.*' | wc -l`
|
||||
N2=`cat abstract_tendermint.ivy | grep "observed_" | wc -l` # the observed_* variables specify the observations of the nodes
|
||||
SPEC_LINES=`expr $N1 + $N2`
|
||||
echo "spec lines: $SPEC_LINES"
|
||||
N3=`cat abstract_tendermint.ivy | grep -v $r'\|.*observed_.*' | wc -l`
|
||||
N4=`cat accountable_safety_1.ivy | grep -v $r | wc -l`
|
||||
PROOF_LINES=`expr $N3 + $N4`
|
||||
echo "proof lines: $PROOF_LINES"
|
||||
RATIO=`bc <<< "scale=2;$PROOF_LINES / $SPEC_LINES"`
|
||||
echo "proof-to-code ratio for the accountable-safety property: $RATIO"
|
||||
@@ -0,0 +1,8 @@
|
||||
version: '3'
|
||||
services:
|
||||
tendermint-proof:
|
||||
build: .
|
||||
volumes:
|
||||
- ./:/home/user/tendermint-proof:ro
|
||||
- ./output:/home/user/tendermint-proof/output:rw
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
#lang ivy1.7
|
||||
|
||||
include order # this is a file from the standard library (`ivy/ivy/include/1.7/order.ivy`)
|
||||
|
||||
isolate round = {
|
||||
type this
|
||||
individual minus_one:this
|
||||
relation succ(R1:round, R2:round)
|
||||
action incr(i:this) returns (j:this)
|
||||
specification {
|
||||
# to simplify verification, we treat rounds as an abstract totally ordered set with a successor relation.
|
||||
instantiate totally_ordered(this)
|
||||
property minus_one < 0
|
||||
property succ(X,Z) -> (X < Z & ~(X < Y & Y < Z))
|
||||
after incr {
|
||||
ensure succ(i,j)
|
||||
}
|
||||
}
|
||||
implementation {
|
||||
# here we prove that the abstraction is sound.
|
||||
interpret this -> int # rounds are integers in the Tendermint specification.
|
||||
definition minus_one = 0-1
|
||||
definition succ(R1,R2) = R2 = R1 + 1
|
||||
implement incr {
|
||||
j := i+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instance node : iterable # nodes are a set with an order, that can be iterated over (see order.ivy in the standard library)
|
||||
|
||||
relation well_behaved(N:node) # whether a node is well-behaved or not. NOTE: Used only in the proof and the Byzantine model; Nodes do know know who is well-behaved and who is not.
|
||||
|
||||
isolate proposers = {
|
||||
# each round has a unique proposer in Tendermint. In order to avoid a
|
||||
# function from round to node (which makes verification more difficult), we
|
||||
# abstract over this function using a relation.
|
||||
relation is_proposer(N:node, R:round)
|
||||
export action get_proposer(r:round) returns (n:node)
|
||||
specification {
|
||||
property is_proposer(N1,R) & is_proposer(N2,R) -> N1 = N2
|
||||
after get_proposer {
|
||||
ensure is_proposer(n,r);
|
||||
}
|
||||
}
|
||||
implementation {
|
||||
function f(R:round):node
|
||||
definition f(r:round) = <<<r % `node.size`>>>
|
||||
definition is_proposer(N,R) = N = f(R)
|
||||
implement get_proposer {
|
||||
n := f(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isolate value = { # the type of values
|
||||
type this
|
||||
relation valid(V:value)
|
||||
individual nil:value
|
||||
specification {
|
||||
property ~valid(nil)
|
||||
}
|
||||
implementation {
|
||||
interpret value -> bv[2]
|
||||
definition nil = <<< -1 >>> # let's say nil is -1
|
||||
definition valid(V) = V ~= nil
|
||||
}
|
||||
}
|
||||
|
||||
object nset = { # the type of node sets
|
||||
type this # a set of N=3f+i nodes for 0<i<=3
|
||||
relation member(N:node, S:nset) # set-membership relation
|
||||
relation is_quorum(S:nset) # intent: sets of cardinality at least 2f+i+1
|
||||
relation is_blocking(S:nset) # intent: at least f+1 nodes
|
||||
export action insert(s:nset, n:node) returns (t:nset)
|
||||
export action empty returns (s:nset)
|
||||
implementation {
|
||||
# NOTE: this is not checked at all by Ivy; it is however useful to generate C++ code and run it for debugging purposes
|
||||
<<< header
|
||||
#include <set>
|
||||
#include <exception>
|
||||
namespace hash_space {
|
||||
template <typename T>
|
||||
class hash<std::set<T> > {
|
||||
public:
|
||||
size_t operator()(const std::set<T> &s) const {
|
||||
hash<T> h;
|
||||
size_t res = 0;
|
||||
for (const T &e : s)
|
||||
res += h(e);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
}
|
||||
>>>
|
||||
interpret nset -> <<< std::set<`node`> >>>
|
||||
definition member(n:node, s:nset) = <<< `s`.find(`n`) != `s`.end() >>>
|
||||
definition is_quorum(s:nset) = <<< 3*`s`.size() > 2*`node.size` >>>
|
||||
definition is_blocking(s:nset) = <<< 3*`s`.size() > `node.size` >>>
|
||||
implement empty {
|
||||
<<<
|
||||
>>>
|
||||
}
|
||||
implement insert {
|
||||
<<<
|
||||
`t` = `s`;
|
||||
`t`.insert(`n`);
|
||||
>>>
|
||||
}
|
||||
<<< encode `nset`
|
||||
|
||||
std::ostream &operator <<(std::ostream &s, const `nset` &a) {
|
||||
s << "{";
|
||||
for (auto iter = a.begin(); iter != a.end(); iter++) {
|
||||
if (iter != a.begin()) s << ", ";
|
||||
s << *iter;
|
||||
}
|
||||
s << "}";
|
||||
return s;
|
||||
}
|
||||
|
||||
template <>
|
||||
`nset` _arg<`nset`>(std::vector<ivy_value> &args, unsigned idx, long long bound) {
|
||||
throw std::invalid_argument("Not implemented"); // no syntax for nset values in the REPL
|
||||
}
|
||||
|
||||
>>>
|
||||
}
|
||||
}
|
||||
|
||||
object classic_bft = {
|
||||
relation quorum_intersection
|
||||
private {
|
||||
definition [quorum_intersection_def] quorum_intersection = forall Q1,Q2. exists N. well_behaved(N) & nset.member(N, Q1) & nset.member(N, Q2) # every two quorums have a well-behaved node in common
|
||||
}
|
||||
}
|
||||
|
||||
trusted isolate accountable_bft = {
|
||||
# this is our baseline assumption about quorums:
|
||||
private {
|
||||
property [max_2f_byzantine] exists N . well_behaved(N) & nset.member(N,Q) # every quorum has a well-behaved member
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
#lang ivy1.7
|
||||
# ---
|
||||
# layout: page
|
||||
# title: Network model and network shim
|
||||
# ---
|
||||
|
||||
# Here we define a network module, which is our model of the network, and a
|
||||
# shim module that sits on top of the network and which, upon receiving a
|
||||
# message, calls the appropriate protocol handler.
|
||||
|
||||
include domain_model
|
||||
|
||||
# Here we define an enumeration type for identifying the 3 different types of
|
||||
# messages that nodes send.
|
||||
object msg_kind = { # TODO: merge with step_t
|
||||
type this = {proposal, prevote, precommit}
|
||||
}
|
||||
|
||||
# Here we define the type of messages `msg`. Its members are structs with the fields described below.
|
||||
object msg = {
|
||||
type this = struct {
|
||||
m_kind : msg_kind,
|
||||
m_src : node,
|
||||
m_round : round,
|
||||
m_value : value,
|
||||
m_vround : round
|
||||
}
|
||||
}
|
||||
|
||||
# This is our model of the network:
|
||||
isolate net = {
|
||||
|
||||
export action recv(dst:node,v:msg)
|
||||
action send(src:node,dst:node,v:msg)
|
||||
# Note that the `recv` action is exported, meaning that it can be called
|
||||
# non-deterministically by the environment any time it is enabled. In other
|
||||
# words, a packet that is in flight can be received at any time. In this
|
||||
# sense, the network is fully asynchronous. Moreover, there is no
|
||||
# requirement that a given message will be received at all.
|
||||
|
||||
# The state of the network consists of all the packets that have been
|
||||
# sent so far, along with their destination.
|
||||
relation sent(V:msg, N:node)
|
||||
|
||||
after init {
|
||||
sent(V, N) := false
|
||||
}
|
||||
|
||||
before send {
|
||||
sent(v,dst) := true
|
||||
}
|
||||
|
||||
before recv {
|
||||
require sent(v,dst) # only sent messages can be received.
|
||||
}
|
||||
}
|
||||
|
||||
# The network shim sits on top of the network and, upon receiving a message,
|
||||
# calls the appropriate protocol handler. It also exposes a `broadcast` action
|
||||
# that sends to all nodes.
|
||||
|
||||
isolate shim = {
|
||||
|
||||
# In order not repeat the same code for each handler, we use a handler
|
||||
# module parameterized by the type of message it will handle. Below we
|
||||
# instantiate this module for the 3 types of messages of Tendermint
|
||||
module handler(p_kind) = {
|
||||
action handle(dst:node,m:msg)
|
||||
object spec = {
|
||||
before handle {
|
||||
assert sent(m,dst) & m.m_kind = p_kind
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instance proposal_handler : handler(msg_kind.proposal)
|
||||
instance prevote_handler : handler(msg_kind.prevote)
|
||||
instance precommit_handler : handler(msg_kind.precommit)
|
||||
|
||||
relation sent(M:msg,N:node)
|
||||
|
||||
action broadcast(src:node,m:msg)
|
||||
action send(src:node,dst:node,m:msg)
|
||||
|
||||
specification {
|
||||
after init {
|
||||
sent(M,D) := false;
|
||||
}
|
||||
before broadcast {
|
||||
sent(m,D) := true
|
||||
}
|
||||
before send {
|
||||
sent(m,dst) := true
|
||||
}
|
||||
}
|
||||
|
||||
# Here we give an implementation of it that satisfies its specification:
|
||||
implementation {
|
||||
|
||||
implement net.recv(dst:node,m:msg) {
|
||||
|
||||
if m.m_kind = msg_kind.proposal {
|
||||
call proposal_handler.handle(dst,m)
|
||||
}
|
||||
else if m.m_kind = msg_kind.prevote {
|
||||
call prevote_handler.handle(dst,m)
|
||||
}
|
||||
else if m.m_kind = msg_kind.precommit {
|
||||
call precommit_handler.handle(dst,m)
|
||||
}
|
||||
}
|
||||
|
||||
implement broadcast { # broadcast sends to all nodes, including the sender.
|
||||
var iter := node.iter.create(0);
|
||||
while ~iter.is_end
|
||||
invariant net.sent(M,D) -> sent(M,D)
|
||||
{
|
||||
var n := iter.val;
|
||||
call net.send(src,n,m);
|
||||
iter := iter.next;
|
||||
}
|
||||
}
|
||||
|
||||
implement send {
|
||||
call net.send(src,dst,m)
|
||||
}
|
||||
|
||||
private {
|
||||
invariant net.sent(M,D) -> sent(M,D)
|
||||
}
|
||||
}
|
||||
|
||||
} with net, node # to prove that the shim implementation satisfies the shim specification, we rely on the specification of net and node.
|
||||
@@ -0,0 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
@@ -0,0 +1,420 @@
|
||||
#lang ivy1.7
|
||||
# ---
|
||||
# layout: page
|
||||
# title: Specification of Tendermint in Ivy
|
||||
# ---
|
||||
|
||||
# This specification closely follows the pseudo-code given in "The latest
|
||||
# gossip on BFT consensus" by E. Buchman, J. Kwon, Z. Milosevic
|
||||
# <https://arxiv.org/abs/1807.04938>
|
||||
|
||||
include domain_model
|
||||
include network_shim
|
||||
|
||||
# We model the Tendermint protocol as an Ivy object. Like in Object-Oriented
|
||||
# Programming, the basic structuring unit in Ivy is the object. Objects have
|
||||
# internal state and actions (i.e. methods in OO parlance) that modify their
|
||||
# state. We model Tendermint as an object whose actions represent steps taken
|
||||
# by individual nodes in the protocol. Actions in Ivy can have preconditions,
|
||||
# and a valid execution is a sequence of actions whose preconditions are all
|
||||
# satisfied in the state in which they are called.
|
||||
|
||||
# For technical reasons, we define below a `tendermint` module instead of an
|
||||
# object. Ivy modules are a little bit like classes in OO programs, and like
|
||||
# classes they can be instantiated to obtain objects. To instantiate the
|
||||
# `tendermint` module, we must provide an abstract-protocol object. This allows
|
||||
# us to use different abstract-protocol objects for different parts of the
|
||||
# proof, and to do so without too much notational burden (we could have used
|
||||
# Ivy monitors, but then we would need to prefix every variable name by the
|
||||
# name of the object containing it, which clutters things a bit compared to the
|
||||
# approach we took).
|
||||
|
||||
# The abstract-protocol object is called by the resulting tendermint object so
|
||||
# as to run the abstract protocol alongside the concrete protocol. This allows
|
||||
# us to transfer properties proved of the abstract protocol to the concrete
|
||||
# protocol, as follows. First, we prove that running the abstract protocol in
|
||||
# this way results in a valid execution of the abstract protocol. This is done
|
||||
# by checking that all preconditions of the abstract actions are satisfied at
|
||||
# their call sites. Second, we establish a relation between abstract state and
|
||||
# concrete state (in the form of invariants of the resulting, two-object
|
||||
# transition system) that allow us to transfer properties proved in the
|
||||
# abstract protocol to the concrete protocol (for example, we prove that any
|
||||
# decision made in the Tendermint protocol is also made in the abstract
|
||||
# protocol; if the abstract protocol satisfies the agreement property, this
|
||||
# allows us to conclude that the Tendermint protocol also does).
|
||||
|
||||
# The abstract protocol object that we will use is always the same, and only
|
||||
# the abstract properties that we prove about it change in the different
|
||||
# instantiations of the `tendermint` module. Thus we provide common invariants
|
||||
# that a) allow to prove that the abstract preconditions are met, and b)
|
||||
# provide a refinement relation (see end of the module) relating the state of
|
||||
# Tendermint to the state of the abstract protocol.
|
||||
|
||||
# In the model, Byzantine nodes can send whatever messages they want, except
|
||||
# that they cannot forge sender identities. This reflects the fact that, in
|
||||
# practice, nodes use public key cryptography to sign their messages.
|
||||
|
||||
# Finally, note that the observations that serve to adjudicate misbehavior are
|
||||
# defined only in the abstract protocol (they happen in the abstract actions).
|
||||
|
||||
module tendermint(abstract_protocol) = {
|
||||
|
||||
# the initial value of a node:
|
||||
function init_val(N:node): value
|
||||
|
||||
# the three type of steps
|
||||
object step_t = {
|
||||
type this = {propose, prevote, precommit}
|
||||
} # refer to those e.g. as step_t.propose
|
||||
|
||||
object server(n:node) = {
|
||||
|
||||
# the current round of a node
|
||||
individual round_p: round
|
||||
|
||||
individual step: step_t
|
||||
|
||||
individual decision: value
|
||||
|
||||
individual lockedValue: value
|
||||
individual lockedRound: round
|
||||
|
||||
individual validValue: value
|
||||
individual validRound: round
|
||||
|
||||
|
||||
relation done_l34(R:round)
|
||||
relation done_l36(R:round, V:value)
|
||||
relation done_l47(R:round)
|
||||
|
||||
# variables for scheduling request
|
||||
relation propose_timer_scheduled(R:round)
|
||||
relation prevote_timer_scheduled(R:round)
|
||||
relation precommit_timer_scheduled(R:round)
|
||||
|
||||
relation _recved_proposal(Sender:node, R:round, V:value, VR:round)
|
||||
relation _recved_prevote(Sender:node, R:round, V:value)
|
||||
relation _recved_precommit(Sender:node, R:round, V:value)
|
||||
|
||||
relation _has_started
|
||||
|
||||
after init {
|
||||
round_p := 0;
|
||||
step := step_t.propose;
|
||||
decision := value.nil;
|
||||
|
||||
lockedValue := value.nil;
|
||||
lockedRound := round.minus_one;
|
||||
|
||||
validValue := value.nil;
|
||||
validRound := round.minus_one;
|
||||
|
||||
done_l34(R) := false;
|
||||
done_l36(R, V) := false;
|
||||
done_l47(R) := false;
|
||||
|
||||
propose_timer_scheduled(R) := false;
|
||||
prevote_timer_scheduled(R) := false;
|
||||
precommit_timer_scheduled(R) := false;
|
||||
|
||||
_recved_proposal(Sender, R, V, VR) := false;
|
||||
_recved_prevote(Sender, R, V) := false;
|
||||
_recved_precommit(Sender, R, V) := false;
|
||||
|
||||
_has_started := false;
|
||||
}
|
||||
|
||||
action getValue returns (v:value) = {
|
||||
v := init_val(n)
|
||||
}
|
||||
|
||||
export action start = {
|
||||
require ~_has_started;
|
||||
_has_started := true;
|
||||
# line 10
|
||||
call startRound(0);
|
||||
}
|
||||
|
||||
# line 11-21
|
||||
action startRound(r:round) = {
|
||||
# line 12
|
||||
round_p := r;
|
||||
|
||||
# line 13
|
||||
step := step_t.propose;
|
||||
|
||||
var proposal : value;
|
||||
|
||||
# line 14
|
||||
if (proposers.get_proposer(r) = n) {
|
||||
if validValue ~= value.nil { # line 15
|
||||
proposal := validValue; # line 16
|
||||
} else {
|
||||
proposal := getValue(); # line 18
|
||||
};
|
||||
call broadcast_proposal(r, proposal, validRound); # line 19
|
||||
} else {
|
||||
propose_timer_scheduled(r) := true; # line 21
|
||||
};
|
||||
|
||||
call abstract_protocol.l_11(n, r);
|
||||
}
|
||||
|
||||
# This action, as not exported, can only be called at specific call sites.
|
||||
action broadcast_proposal(r:round, v:value, vr:round) = {
|
||||
var m: msg;
|
||||
m.m_kind := msg_kind.proposal;
|
||||
m.m_src := n;
|
||||
m.m_round := r;
|
||||
m.m_value := v;
|
||||
m.m_vround := vr;
|
||||
call shim.broadcast(n,m);
|
||||
}
|
||||
|
||||
implement shim.proposal_handler.handle(msg:msg) {
|
||||
_recved_proposal(msg.m_src, msg.m_round, msg.m_value, msg.m_vround) := true;
|
||||
}
|
||||
|
||||
# line 22-27
|
||||
export action l_22(v:value) = {
|
||||
require _has_started;
|
||||
require _recved_proposal(proposers.get_proposer(round_p), round_p, v, round.minus_one);
|
||||
require step = step_t.propose;
|
||||
|
||||
if (value.valid(v) & (lockedRound = round.minus_one | lockedValue = v)) {
|
||||
call broadcast_prevote(round_p, v); # line 24
|
||||
call abstract_protocol.l_22(n, round_p, v);
|
||||
} else {
|
||||
call broadcast_prevote(round_p, value.nil); # line 26
|
||||
call abstract_protocol.l_22(n, round_p, value.nil);
|
||||
};
|
||||
|
||||
# line 27
|
||||
step := step_t.prevote;
|
||||
}
|
||||
|
||||
# line 28-33
|
||||
export action l_28(r:round, v:value, vr:round, q:nset) = {
|
||||
require _has_started;
|
||||
require r = round_p;
|
||||
require _recved_proposal(proposers.get_proposer(r), r, v, vr);
|
||||
require nset.is_quorum(q);
|
||||
require nset.member(N,q) -> _recved_prevote(N,vr,v);
|
||||
require step = step_t.propose;
|
||||
require vr >= 0 & vr < r;
|
||||
|
||||
# line 29
|
||||
if (value.valid(v) & (lockedRound <= vr | lockedValue = v)) {
|
||||
call broadcast_prevote(r, v);
|
||||
} else {
|
||||
call broadcast_prevote(r, value.nil);
|
||||
};
|
||||
|
||||
call abstract_protocol.l_28(n,r,v,vr,q);
|
||||
step := step_t.prevote;
|
||||
}
|
||||
|
||||
action broadcast_prevote(r:round, v:value) = {
|
||||
var m: msg;
|
||||
m.m_kind := msg_kind.prevote;
|
||||
m.m_src := n;
|
||||
m.m_round := r;
|
||||
m.m_value := v;
|
||||
call shim.broadcast(n,m);
|
||||
}
|
||||
|
||||
implement shim.prevote_handler.handle(msg:msg) {
|
||||
_recved_prevote(msg.m_src, msg.m_round, msg.m_value) := true;
|
||||
}
|
||||
|
||||
# line 34-35
|
||||
export action l_34(r:round, q:nset) = {
|
||||
require _has_started;
|
||||
require round_p = r;
|
||||
require nset.is_quorum(q);
|
||||
require exists V . nset.member(N,q) -> _recved_prevote(N,r,V);
|
||||
require step = step_t.prevote;
|
||||
require ~done_l34(r);
|
||||
done_l34(r) := true;
|
||||
|
||||
prevote_timer_scheduled(r) := true;
|
||||
}
|
||||
|
||||
|
||||
# line 36-43
|
||||
export action l_36(r:round, v:value, q:nset) = {
|
||||
require _has_started;
|
||||
require r = round_p;
|
||||
require exists VR . round.minus_one <= VR & VR < r & _recved_proposal(proposers.get_proposer(r), r, v, VR);
|
||||
require nset.is_quorum(q);
|
||||
require nset.member(N,q) -> _recved_prevote(N,r,v);
|
||||
require value.valid(v);
|
||||
require step = step_t.prevote | step = step_t.precommit;
|
||||
|
||||
require ~done_l36(r,v);
|
||||
done_l36(r, v) := true;
|
||||
|
||||
if step = step_t.prevote {
|
||||
lockedValue := v; # line 38
|
||||
lockedRound := r; # line 39
|
||||
call broadcast_precommit(r, v); # line 40
|
||||
step := step_t.precommit; # line 41
|
||||
call abstract_protocol.l_36(n, r, v, q);
|
||||
};
|
||||
|
||||
validValue := v; # line 42
|
||||
validRound := r; # line 43
|
||||
}
|
||||
|
||||
# line 44-46
|
||||
export action l_44(r:round, q:nset) = {
|
||||
require _has_started;
|
||||
require r = round_p;
|
||||
require nset.is_quorum(q);
|
||||
require nset.member(N,q) -> _recved_prevote(N,r,value.nil);
|
||||
require step = step_t.prevote;
|
||||
|
||||
call broadcast_precommit(r, value.nil); # line 45
|
||||
step := step_t.precommit; # line 46
|
||||
|
||||
call abstract_protocol.l_44(n, r, q);
|
||||
}
|
||||
|
||||
action broadcast_precommit(r:round, v:value) = {
|
||||
var m: msg;
|
||||
m.m_kind := msg_kind.precommit;
|
||||
m.m_src := n;
|
||||
m.m_round := r;
|
||||
m.m_value := v;
|
||||
call shim.broadcast(n,m);
|
||||
}
|
||||
|
||||
implement shim.precommit_handler.handle(msg:msg) {
|
||||
_recved_precommit(msg.m_src, msg.m_round, msg.m_value) := true;
|
||||
}
|
||||
|
||||
|
||||
# line 47-48
|
||||
export action l_47(r:round, q:nset) = {
|
||||
require _has_started;
|
||||
require round_p = r;
|
||||
require nset.is_quorum(q);
|
||||
require nset.member(N,q) -> exists V . _recved_precommit(N,r,V);
|
||||
require ~done_l47(r);
|
||||
done_l47(r) := true;
|
||||
|
||||
precommit_timer_scheduled(r) := true;
|
||||
}
|
||||
|
||||
|
||||
# line 49-54
|
||||
export action l_49_decide(r:round, v:value, q:nset) = {
|
||||
require _has_started;
|
||||
require exists VR . round.minus_one <= VR & VR < r & _recved_proposal(proposers.get_proposer(r), r, v, VR);
|
||||
require nset.is_quorum(q);
|
||||
require nset.member(N,q) -> _recved_precommit(N,r,v);
|
||||
require decision = value.nil;
|
||||
|
||||
if value.valid(v) {
|
||||
decision := v;
|
||||
# MORE for next height
|
||||
call abstract_protocol.decide(n, r, v, q);
|
||||
}
|
||||
}
|
||||
|
||||
# line 55-56
|
||||
export action l_55(r:round, b:nset) = {
|
||||
require _has_started;
|
||||
require nset.is_blocking(b);
|
||||
require nset.member(N,b) -> exists VR . round.minus_one <= VR & VR < r & exists V . _recved_proposal(N,r,V,VR) | _recved_prevote(N,r,V) | _recved_precommit(N,r,V);
|
||||
require r > round_p;
|
||||
call startRound(r); # line 56
|
||||
}
|
||||
|
||||
# line 57-60
|
||||
export action onTimeoutPropose(r:round) = {
|
||||
require _has_started;
|
||||
require propose_timer_scheduled(r);
|
||||
require r = round_p;
|
||||
require step = step_t.propose;
|
||||
call broadcast_prevote(r,value.nil);
|
||||
step := step_t.prevote;
|
||||
|
||||
call abstract_protocol.l_57(n,r);
|
||||
|
||||
propose_timer_scheduled(r) := false;
|
||||
}
|
||||
|
||||
# line 61-64
|
||||
export action onTimeoutPrevote(r:round) = {
|
||||
require _has_started;
|
||||
require prevote_timer_scheduled(r);
|
||||
require r = round_p;
|
||||
require step = step_t.prevote;
|
||||
call broadcast_precommit(r,value.nil);
|
||||
step := step_t.precommit;
|
||||
|
||||
call abstract_protocol.l_61(n,r);
|
||||
|
||||
prevote_timer_scheduled(r) := false;
|
||||
}
|
||||
|
||||
# line 65-67
|
||||
export action onTimeoutPrecommit(r:round) = {
|
||||
require _has_started;
|
||||
require precommit_timer_scheduled(r);
|
||||
require r = round_p;
|
||||
call startRound(round.incr(r));
|
||||
|
||||
precommit_timer_scheduled(r) := false;
|
||||
}
|
||||
|
||||
# The Byzantine actions
|
||||
# ---------------------
|
||||
|
||||
# Byzantine nodes can send whatever they want, but they cannot send
|
||||
# messages on behalf of well-behaved nodes. In practice this is implemented
|
||||
# using cryptography (e.g. public-key cryptography).
|
||||
|
||||
export action byzantine_send(m:msg, dst:node) = {
|
||||
require ~well_behaved(n);
|
||||
require ~well_behaved(m.m_src); # cannot forge the identity of well-behaved nodes
|
||||
call shim.send(n,dst,m);
|
||||
}
|
||||
|
||||
# Byzantine nodes can also report fake observations, as defined in the abstract protocol.
|
||||
export action fake_observations = {
|
||||
call abstract_protocol.misbehave
|
||||
}
|
||||
|
||||
# Invariants
|
||||
# ----------
|
||||
|
||||
# We provide common invariants that a) allow to prove that the abstract
|
||||
# preconditions are met, and b) provide a refinement relation.
|
||||
|
||||
|
||||
specification {
|
||||
|
||||
invariant 0 <= round_p
|
||||
invariant abstract_protocol.left_round(n,R) <-> R < round_p
|
||||
|
||||
invariant lockedRound ~= round.minus_one -> forall R,V . abstract_protocol.locked(n,R,V) <-> R <= lockedRound & lockedValue = V
|
||||
invariant lockedRound = round.minus_one -> forall R,V . ~abstract_protocol.locked(n,R,V)
|
||||
|
||||
invariant forall M:msg . well_behaved(M.m_src) & M.m_kind = msg_kind.prevote & shim.sent(M,N) -> abstract_protocol.prevoted(M.m_src,M.m_round,M.m_value)
|
||||
invariant well_behaved(N) & _recved_prevote(N,R,V) -> abstract_protocol.prevoted(N,R,V)
|
||||
invariant forall M:msg . well_behaved(M.m_src) & M.m_kind = msg_kind.precommit & shim.sent(M,N) -> abstract_protocol.precommitted(M.m_src,M.m_round,M.m_value)
|
||||
invariant well_behaved(N) & _recved_precommit(N,R,V) -> abstract_protocol.precommitted(N,R,V)
|
||||
|
||||
invariant (step = step_t.prevote | step = step_t.propose) -> ~abstract_protocol.precommitted(n,round_p,V)
|
||||
invariant step = step_t.propose -> ~abstract_protocol.prevoted(n,round_p,V)
|
||||
invariant step = step_t.prevote -> exists V . abstract_protocol.prevoted(n,round_p,V)
|
||||
|
||||
invariant round_p < R -> ~(abstract_protocol.prevoted(n,R,V) | abstract_protocol.precommitted(n,R,V))
|
||||
invariant ~_has_started -> step = step_t.propose & ~(abstract_protocol.prevoted(n,R,V) | abstract_protocol.precommitted(n,R,V)) & round_p = 0
|
||||
|
||||
invariant decision ~= value.nil -> exists R . abstract_protocol.decided(n,R,decision)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#lang ivy1.7
|
||||
|
||||
include tendermint
|
||||
include abstract_tendermint
|
||||
|
||||
isolate ghost_ = {
|
||||
instantiate abstract_tendermint
|
||||
}
|
||||
|
||||
isolate protocol = {
|
||||
instantiate tendermint(ghost_) # here we instantiate the parameter of the tendermint module with `ghost_`; however note that we don't extract any code for `ghost_` (it's not in the list of object in the extract, and it's thus sliced away).
|
||||
implementation {
|
||||
definition init_val(n:node) = <<< `n`%2 >>>
|
||||
}
|
||||
# attribute test = impl
|
||||
} with ghost_, shim, value, round, proposers
|
||||
|
||||
# Here we run a simple scenario that exhibits an execution in which nodes make
|
||||
# a decision. We do this to rule out trivial modeling errors.
|
||||
|
||||
# One option to check that this scenario is valid is to run it in Ivy's REPL.
|
||||
# For this, first compile the scenario:
|
||||
#```ivyc target=repl isolate=code trace=true tendermint_test.ivy
|
||||
# Then, run the produced binary (e.g. for 4 nodes):
|
||||
#``` ./tendermint_test 4
|
||||
# Finally, call the action:
|
||||
#``` scenarios.scenario_1
|
||||
# Note that Ivy will check at runtime that all action preconditions are
|
||||
# satisfied. For example, runing the scenario twice will cause a violation of
|
||||
# the precondition of the `start` action, because a node cannot start twice
|
||||
# (see `require ~_has_started` in action `start`).
|
||||
|
||||
# Another possibility would be to run `ivy_check` on the scenario, but that
|
||||
# does not seem to work at the moment.
|
||||
|
||||
isolate scenarios = {
|
||||
individual all:nset # will be used as parameter to actions requiring a quorum
|
||||
|
||||
after init {
|
||||
var iter := node.iter.create(0);
|
||||
while ~iter.is_end
|
||||
{
|
||||
all := all.insert(iter.val);
|
||||
iter := iter.next;
|
||||
};
|
||||
assert nset.is_quorum(all); # we can also use asserts to make sure we are getting what we expect
|
||||
}
|
||||
|
||||
export action scenario_1 = {
|
||||
# all nodes start:
|
||||
var iter := node.iter.create(0);
|
||||
while ~iter.is_end
|
||||
{
|
||||
call protocol.server.start(iter.val);
|
||||
iter := iter.next;
|
||||
};
|
||||
# all nodes receive the leader's proposal:
|
||||
var m:msg;
|
||||
m.m_kind := msg_kind.proposal;
|
||||
m.m_src := 0;
|
||||
m.m_round := 0;
|
||||
m.m_value := 0;
|
||||
m.m_vround := round.minus_one;
|
||||
iter := node.iter.create(0);
|
||||
while ~iter.is_end
|
||||
{
|
||||
call net.recv(iter.val,m);
|
||||
iter := iter.next;
|
||||
};
|
||||
# all nodes prevote:
|
||||
iter := node.iter.create(0);
|
||||
while ~iter.is_end
|
||||
{
|
||||
call protocol.server.l_22(iter.val,0);
|
||||
iter := iter.next;
|
||||
};
|
||||
# all nodes receive each other's prevote messages;
|
||||
m.m_kind := msg_kind.prevote;
|
||||
m.m_vround := 0;
|
||||
iter := node.iter.create(0);
|
||||
while ~iter.is_end
|
||||
{
|
||||
var iter2 := node.iter.create(0); # the sender
|
||||
while ~iter2.is_end
|
||||
{
|
||||
m.m_src := iter2.val;
|
||||
call net.recv(iter.val,m);
|
||||
iter2 := iter2.next;
|
||||
};
|
||||
iter := iter.next;
|
||||
};
|
||||
# all nodes precommit:
|
||||
iter := node.iter.create(0);
|
||||
while ~iter.is_end
|
||||
{
|
||||
call protocol.server.l_36(iter.val,0,0,all);
|
||||
iter := iter.next;
|
||||
};
|
||||
# all nodes receive each other's pre-commits
|
||||
m.m_kind := msg_kind.precommit;
|
||||
iter := node.iter.create(0);
|
||||
while ~iter.is_end
|
||||
{
|
||||
var iter2 := node.iter.create(0); # the sender
|
||||
while ~iter2.is_end
|
||||
{
|
||||
m.m_src := iter2.val;
|
||||
call net.recv(iter.val,m);
|
||||
iter2 := iter2.next;
|
||||
};
|
||||
iter := iter.next;
|
||||
};
|
||||
# now all nodes can decide:
|
||||
iter := node.iter.create(0);
|
||||
while ~iter.is_end
|
||||
{
|
||||
call protocol.server.l_49_decide(iter.val,0,0,all);
|
||||
iter := iter.next;
|
||||
};
|
||||
}
|
||||
|
||||
# TODO: add more scenarios
|
||||
|
||||
} with round, node, proposers, value, nset, protocol, shim, net
|
||||
|
||||
# extract code = protocol, shim, round, node
|
||||
extract code = round, node, proposers, value, nset, protocol, shim, net, scenarios
|
||||
+1
-1
@@ -428,7 +428,7 @@ func (c *Client) VerifyLightBlockAtHeight(ctx context.Context, height int64, now
|
||||
// headers are not adjacent, verifySkipping is performed and necessary (not all)
|
||||
// intermediate headers will be requested. See the specification for details.
|
||||
// Intermediate headers are not saved to database.
|
||||
// https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md
|
||||
// https://github.com/tendermint/tendermint/blob/v0.35.x/spec/consensus/light-client.md
|
||||
//
|
||||
// If the header, which is older than the currently trusted header, is
|
||||
// requested and the light client does not have it, VerifyHeader will perform:
|
||||
|
||||
+1
-1
@@ -94,7 +94,7 @@ Check out other examples in example_test.go
|
||||
## 2. Pure functions to verify a new header (see verifier.go)
|
||||
|
||||
Verify function verifies a new header against some trusted header. See
|
||||
https://github.com/tendermint/spec/blob/master/spec/light-client/verification/README.md
|
||||
https://github.com/tendermint/tendermint/blob/v0.35.x/spec/light-client/verification/README.md
|
||||
for details.
|
||||
|
||||
There are two methods of verification: sequential and bisection
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# This Dockerfile defines an image containing tools for linting, formatting,
|
||||
# and compiling the Tendermint protos.
|
||||
FROM golang:1.17-alpine
|
||||
|
||||
# Install a commonly used set of programs for use with our protos.
|
||||
# clang-extra-tools is included here because it provides clang-format,
|
||||
# used to format the .proto files.
|
||||
RUN apk add --no-cache build-base clang-extra-tools curl git
|
||||
|
||||
ENV GOLANG_PROTOBUF_VERSION=1.3.1 \
|
||||
GOGO_PROTOBUF_VERSION=1.3.2
|
||||
|
||||
# Retrieve the go protoc programs and copy them into the PATH
|
||||
RUN go install github.com/golang/protobuf/protoc-gen-go@v${GOLANG_PROTOBUF_VERSION} && \
|
||||
go install github.com/gogo/protobuf/protoc-gen-gogo@v${GOGO_PROTOBUF_VERSION} && \
|
||||
go install github.com/gogo/protobuf/protoc-gen-gogofaster@v${GOGO_PROTOBUF_VERSION} && \
|
||||
mv "$(go env GOPATH)"/bin/* /usr/local/bin/
|
||||
|
||||
# Copy the 'buf' program out of the buildbuf/buf container.
|
||||
COPY --from=bufbuild/buf:latest /usr/local/bin/* /usr/local/bin/
|
||||
@@ -0,0 +1,21 @@
|
||||
# Protocol Buffers
|
||||
|
||||
This sections defines the protocol buffers used in Tendermint. This is split into two directories: `spec`, the types required for all implementations and `tendermint`, a set of types internal to the Go implementation. All generated go code is also stored in `tendermint`.
|
||||
More descriptions of the data structures are located in the spec directory as follows:
|
||||
|
||||
- [Block](../spec/core/data_structures.md)
|
||||
- [ABCI](../spec/abci/README.md)
|
||||
- [P2P](../spec/p2p/messages/README.md)
|
||||
|
||||
## Process to generate protos
|
||||
|
||||
The `.proto` files within this section are core to the protocol and updates must be treated as such.
|
||||
|
||||
### Steps
|
||||
|
||||
1. Make an issue with the proposed change.
|
||||
- Within the issue members, from the Tendermint team will leave comments. If there is not consensus on the change an [RFC](../docs/rfc/README.md) may be requested.
|
||||
1a. Submission of an RFC as a pull request should be made to facilitate further discussion.
|
||||
1b. Merge the RFC.
|
||||
2. Make the necessary changes to the `.proto` file(s), [core data structures](../spec/core/data_structures.md) and/or [ABCI protocol](../spec/abci/apps.md).
|
||||
3. Rebuild the Go protocol buffers by running `make proto-gen`. Ensure that the project builds correctly by running `make build`.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Generated by buf. DO NOT EDIT.
|
||||
version: v1
|
||||
deps:
|
||||
- remote: buf.build
|
||||
owner: gogo
|
||||
repository: protobuf
|
||||
commit: 5461a3dfa9d941da82028ab185dc2a0e
|
||||
@@ -1,16 +1,11 @@
|
||||
version: v1beta1
|
||||
|
||||
build:
|
||||
roots:
|
||||
- proto
|
||||
- third_party/proto
|
||||
version: v1
|
||||
deps:
|
||||
- buf.build/gogo/protobuf
|
||||
breaking:
|
||||
use:
|
||||
- FILE
|
||||
lint:
|
||||
use:
|
||||
- BASIC
|
||||
- FILE_LOWER_SNAKE_CASE
|
||||
- UNARY_RPC
|
||||
ignore:
|
||||
- gogoproto
|
||||
breaking:
|
||||
use:
|
||||
- FILE
|
||||
@@ -899,7 +899,10 @@ func (m *BlockRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -968,7 +971,10 @@ func (m *NoBlockResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1054,7 +1060,10 @@ func (m *BlockResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1104,7 +1113,10 @@ func (m *StatusRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1192,7 +1204,10 @@ func (m *StatusResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1417,7 +1432,10 @@ func (m *Message) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -1932,7 +1932,10 @@ func (m *NewRoundStep) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2109,7 +2112,10 @@ func (m *NewValidBlock) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2192,7 +2198,10 @@ func (m *Proposal) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2313,7 +2322,10 @@ func (m *ProposalPOL) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2434,7 +2446,10 @@ func (m *BlockPart) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2520,7 +2535,10 @@ func (m *Vote) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2646,7 +2664,10 @@ func (m *HasVote) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2786,7 +2807,10 @@ func (m *VoteSetMaj23) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2959,7 +2983,10 @@ func (m *VoteSetBits) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -3324,7 +3351,10 @@ func (m *Message) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -921,7 +921,10 @@ func (m *MsgInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthWal
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthWal
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1061,7 +1064,10 @@ func (m *TimeoutInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthWal
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthWal
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1130,7 +1136,10 @@ func (m *EndHeight) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthWal
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthWal
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1320,7 +1329,10 @@ func (m *WALMessage) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthWal
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthWal
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1439,7 +1451,10 @@ func (m *TimedWALMessage) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthWal
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthWal
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -687,7 +687,10 @@ func (m *PublicKey) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthKeys
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthKeys
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -820,7 +820,10 @@ func (m *Proof) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthProof
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthProof
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -940,7 +943,10 @@ func (m *ValueOp) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthProof
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthProof
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1086,7 +1092,10 @@ func (m *DominoOp) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthProof
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthProof
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1236,7 +1245,10 @@ func (m *ProofOp) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthProof
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthProof
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1320,7 +1332,10 @@ func (m *ProofOps) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthProof
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthProof
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -307,7 +307,10 @@ func (m *BitArray) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -370,7 +370,10 @@ func (m *Txs) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -455,7 +458,10 @@ func (m *Message) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -723,7 +723,10 @@ func (m *PacketPing) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthConn
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthConn
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -773,7 +776,10 @@ func (m *PacketPong) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthConn
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthConn
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -896,7 +902,10 @@ func (m *PacketMsg) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthConn
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthConn
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1051,7 +1060,10 @@ func (m *Packet) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthConn
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthConn
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1168,7 +1180,10 @@ func (m *AuthSigMessage) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthConn
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthConn
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -1025,7 +1025,10 @@ func (m *PexAddress) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1075,7 +1078,10 @@ func (m *PexRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1159,7 +1165,10 @@ func (m *PexResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1241,7 +1250,10 @@ func (m *PexAddressV2) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1291,7 +1303,10 @@ func (m *PexRequestV2) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1375,7 +1390,10 @@ func (m *PexResponseV2) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1565,7 +1583,10 @@ func (m *PexMessage) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthPex
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -938,7 +938,10 @@ func (m *ProtocolVersion) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1248,7 +1251,10 @@ func (m *NodeInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1362,7 +1368,10 @@ func (m *NodeInfoOther) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1534,7 +1543,10 @@ func (m *PeerInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1707,7 +1719,10 @@ func (m *PeerAddressInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -1708,7 +1708,10 @@ func (m *RemoteSignerError) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1790,7 +1793,10 @@ func (m *PubKeyRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1909,7 +1915,10 @@ func (m *PubKeyResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2027,7 +2036,10 @@ func (m *SignVoteRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2146,7 +2158,10 @@ func (m *SignedVoteResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2264,7 +2279,10 @@ func (m *SignProposalRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2383,7 +2401,10 @@ func (m *SignedProposalResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2433,7 +2454,10 @@ func (m *PingRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2483,7 +2507,10 @@ func (m *PingResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2813,7 +2840,10 @@ func (m *Message) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2930,7 +2960,10 @@ func (m *AuthSigMessage) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -0,0 +1,936 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: tendermint/rpc/grpc/types.proto
|
||||
|
||||
package coregrpc
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
types "github.com/tendermint/tendermint/abci/types"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type RequestPing struct {
|
||||
}
|
||||
|
||||
func (m *RequestPing) Reset() { *m = RequestPing{} }
|
||||
func (m *RequestPing) String() string { return proto.CompactTextString(m) }
|
||||
func (*RequestPing) ProtoMessage() {}
|
||||
func (*RequestPing) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0ffff5682c662b95, []int{0}
|
||||
}
|
||||
func (m *RequestPing) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *RequestPing) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_RequestPing.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *RequestPing) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_RequestPing.Merge(m, src)
|
||||
}
|
||||
func (m *RequestPing) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *RequestPing) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_RequestPing.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_RequestPing proto.InternalMessageInfo
|
||||
|
||||
type RequestBroadcastTx struct {
|
||||
Tx []byte `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"`
|
||||
}
|
||||
|
||||
func (m *RequestBroadcastTx) Reset() { *m = RequestBroadcastTx{} }
|
||||
func (m *RequestBroadcastTx) String() string { return proto.CompactTextString(m) }
|
||||
func (*RequestBroadcastTx) ProtoMessage() {}
|
||||
func (*RequestBroadcastTx) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0ffff5682c662b95, []int{1}
|
||||
}
|
||||
func (m *RequestBroadcastTx) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *RequestBroadcastTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_RequestBroadcastTx.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *RequestBroadcastTx) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_RequestBroadcastTx.Merge(m, src)
|
||||
}
|
||||
func (m *RequestBroadcastTx) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *RequestBroadcastTx) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_RequestBroadcastTx.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_RequestBroadcastTx proto.InternalMessageInfo
|
||||
|
||||
func (m *RequestBroadcastTx) GetTx() []byte {
|
||||
if m != nil {
|
||||
return m.Tx
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResponsePing struct {
|
||||
}
|
||||
|
||||
func (m *ResponsePing) Reset() { *m = ResponsePing{} }
|
||||
func (m *ResponsePing) String() string { return proto.CompactTextString(m) }
|
||||
func (*ResponsePing) ProtoMessage() {}
|
||||
func (*ResponsePing) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0ffff5682c662b95, []int{2}
|
||||
}
|
||||
func (m *ResponsePing) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *ResponsePing) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_ResponsePing.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *ResponsePing) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ResponsePing.Merge(m, src)
|
||||
}
|
||||
func (m *ResponsePing) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *ResponsePing) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ResponsePing.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ResponsePing proto.InternalMessageInfo
|
||||
|
||||
type ResponseBroadcastTx struct {
|
||||
CheckTx *types.ResponseCheckTx `protobuf:"bytes,1,opt,name=check_tx,json=checkTx,proto3" json:"check_tx,omitempty"`
|
||||
DeliverTx *types.ResponseDeliverTx `protobuf:"bytes,2,opt,name=deliver_tx,json=deliverTx,proto3" json:"deliver_tx,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ResponseBroadcastTx) Reset() { *m = ResponseBroadcastTx{} }
|
||||
func (m *ResponseBroadcastTx) String() string { return proto.CompactTextString(m) }
|
||||
func (*ResponseBroadcastTx) ProtoMessage() {}
|
||||
func (*ResponseBroadcastTx) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_0ffff5682c662b95, []int{3}
|
||||
}
|
||||
func (m *ResponseBroadcastTx) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *ResponseBroadcastTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_ResponseBroadcastTx.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *ResponseBroadcastTx) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ResponseBroadcastTx.Merge(m, src)
|
||||
}
|
||||
func (m *ResponseBroadcastTx) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *ResponseBroadcastTx) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ResponseBroadcastTx.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ResponseBroadcastTx proto.InternalMessageInfo
|
||||
|
||||
func (m *ResponseBroadcastTx) GetCheckTx() *types.ResponseCheckTx {
|
||||
if m != nil {
|
||||
return m.CheckTx
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ResponseBroadcastTx) GetDeliverTx() *types.ResponseDeliverTx {
|
||||
if m != nil {
|
||||
return m.DeliverTx
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*RequestPing)(nil), "tendermint.rpc.grpc.RequestPing")
|
||||
proto.RegisterType((*RequestBroadcastTx)(nil), "tendermint.rpc.grpc.RequestBroadcastTx")
|
||||
proto.RegisterType((*ResponsePing)(nil), "tendermint.rpc.grpc.ResponsePing")
|
||||
proto.RegisterType((*ResponseBroadcastTx)(nil), "tendermint.rpc.grpc.ResponseBroadcastTx")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("tendermint/rpc/grpc/types.proto", fileDescriptor_0ffff5682c662b95) }
|
||||
|
||||
var fileDescriptor_0ffff5682c662b95 = []byte{
|
||||
// 316 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2f, 0x49, 0xcd, 0x4b,
|
||||
0x49, 0x2d, 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x2f, 0x2a, 0x48, 0xd6, 0x4f, 0x07, 0x11, 0x25, 0x95,
|
||||
0x05, 0xa9, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xc2, 0x08, 0x05, 0x7a, 0x45, 0x05,
|
||||
0xc9, 0x7a, 0x20, 0x05, 0x52, 0xd2, 0x48, 0xba, 0x12, 0x93, 0x92, 0x33, 0x91, 0x75, 0x28, 0xf1,
|
||||
0x72, 0x71, 0x07, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x04, 0x64, 0xe6, 0xa5, 0x2b, 0xa9, 0x70,
|
||||
0x09, 0x41, 0xb9, 0x4e, 0x45, 0xf9, 0x89, 0x29, 0xc9, 0x89, 0xc5, 0x25, 0x21, 0x15, 0x42, 0x7c,
|
||||
0x5c, 0x4c, 0x25, 0x15, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x4c, 0x25, 0x15, 0x4a, 0x7c,
|
||||
0x5c, 0x3c, 0x41, 0xa9, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x60, 0x5d, 0x53, 0x19, 0xb9, 0x84,
|
||||
0x61, 0x02, 0xc8, 0xfa, 0xac, 0xb9, 0x38, 0x92, 0x33, 0x52, 0x93, 0xb3, 0xe3, 0xa1, 0xba, 0xb9,
|
||||
0x8d, 0x14, 0xf4, 0x90, 0x5c, 0x08, 0x72, 0x8c, 0x1e, 0x4c, 0x9f, 0x33, 0x48, 0x61, 0x48, 0x45,
|
||||
0x10, 0x7b, 0x32, 0x84, 0x21, 0xe4, 0xc8, 0xc5, 0x95, 0x92, 0x9a, 0x93, 0x59, 0x96, 0x5a, 0x04,
|
||||
0xd2, 0xce, 0x04, 0xd6, 0xae, 0x84, 0x53, 0xbb, 0x0b, 0x44, 0x69, 0x48, 0x45, 0x10, 0x67, 0x0a,
|
||||
0x8c, 0x69, 0xb4, 0x97, 0x91, 0x8b, 0x07, 0xee, 0x1e, 0xc7, 0x00, 0x4f, 0x21, 0x6f, 0x2e, 0x16,
|
||||
0x90, 0x83, 0x85, 0x50, 0x9c, 0x01, 0x0b, 0x28, 0x3d, 0xa4, 0x80, 0x90, 0x52, 0xc4, 0xa1, 0x02,
|
||||
0xe1, 0x6b, 0xa1, 0x04, 0x2e, 0x6e, 0x64, 0xcf, 0xaa, 0xe3, 0x33, 0x13, 0x49, 0xa1, 0x94, 0x06,
|
||||
0x5e, 0xa3, 0x91, 0x54, 0x3a, 0xf9, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83,
|
||||
0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43,
|
||||
0x94, 0x51, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0x52, 0xf4, 0x62,
|
||||
0x49, 0x1f, 0xd6, 0xc9, 0xf9, 0x45, 0xa9, 0x20, 0x46, 0x12, 0x1b, 0x38, 0xc6, 0x8d, 0x01, 0x01,
|
||||
0x00, 0x00, 0xff, 0xff, 0xf6, 0x4b, 0x02, 0xd8, 0x46, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// BroadcastAPIClient is the client API for BroadcastAPI service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type BroadcastAPIClient interface {
|
||||
Ping(ctx context.Context, in *RequestPing, opts ...grpc.CallOption) (*ResponsePing, error)
|
||||
BroadcastTx(ctx context.Context, in *RequestBroadcastTx, opts ...grpc.CallOption) (*ResponseBroadcastTx, error)
|
||||
}
|
||||
|
||||
type broadcastAPIClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewBroadcastAPIClient(cc *grpc.ClientConn) BroadcastAPIClient {
|
||||
return &broadcastAPIClient{cc}
|
||||
}
|
||||
|
||||
func (c *broadcastAPIClient) Ping(ctx context.Context, in *RequestPing, opts ...grpc.CallOption) (*ResponsePing, error) {
|
||||
out := new(ResponsePing)
|
||||
err := c.cc.Invoke(ctx, "/tendermint.rpc.grpc.BroadcastAPI/Ping", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *broadcastAPIClient) BroadcastTx(ctx context.Context, in *RequestBroadcastTx, opts ...grpc.CallOption) (*ResponseBroadcastTx, error) {
|
||||
out := new(ResponseBroadcastTx)
|
||||
err := c.cc.Invoke(ctx, "/tendermint.rpc.grpc.BroadcastAPI/BroadcastTx", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// BroadcastAPIServer is the server API for BroadcastAPI service.
|
||||
type BroadcastAPIServer interface {
|
||||
Ping(context.Context, *RequestPing) (*ResponsePing, error)
|
||||
BroadcastTx(context.Context, *RequestBroadcastTx) (*ResponseBroadcastTx, error)
|
||||
}
|
||||
|
||||
// UnimplementedBroadcastAPIServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedBroadcastAPIServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedBroadcastAPIServer) Ping(ctx context.Context, req *RequestPing) (*ResponsePing, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
|
||||
}
|
||||
func (*UnimplementedBroadcastAPIServer) BroadcastTx(ctx context.Context, req *RequestBroadcastTx) (*ResponseBroadcastTx, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method BroadcastTx not implemented")
|
||||
}
|
||||
|
||||
func RegisterBroadcastAPIServer(s *grpc.Server, srv BroadcastAPIServer) {
|
||||
s.RegisterService(&_BroadcastAPI_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _BroadcastAPI_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RequestPing)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BroadcastAPIServer).Ping(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/tendermint.rpc.grpc.BroadcastAPI/Ping",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BroadcastAPIServer).Ping(ctx, req.(*RequestPing))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _BroadcastAPI_BroadcastTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RequestBroadcastTx)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BroadcastAPIServer).BroadcastTx(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/tendermint.rpc.grpc.BroadcastAPI/BroadcastTx",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BroadcastAPIServer).BroadcastTx(ctx, req.(*RequestBroadcastTx))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _BroadcastAPI_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "tendermint.rpc.grpc.BroadcastAPI",
|
||||
HandlerType: (*BroadcastAPIServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Ping",
|
||||
Handler: _BroadcastAPI_Ping_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "BroadcastTx",
|
||||
Handler: _BroadcastAPI_BroadcastTx_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "tendermint/rpc/grpc/types.proto",
|
||||
}
|
||||
|
||||
func (m *RequestPing) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *RequestPing) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *RequestPing) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *RequestBroadcastTx) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *RequestBroadcastTx) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *RequestBroadcastTx) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Tx) > 0 {
|
||||
i -= len(m.Tx)
|
||||
copy(dAtA[i:], m.Tx)
|
||||
i = encodeVarintTypes(dAtA, i, uint64(len(m.Tx)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *ResponsePing) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *ResponsePing) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *ResponsePing) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *ResponseBroadcastTx) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *ResponseBroadcastTx) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *ResponseBroadcastTx) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.DeliverTx != nil {
|
||||
{
|
||||
size, err := m.DeliverTx.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTypes(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if m.CheckTx != nil {
|
||||
{
|
||||
size, err := m.CheckTx.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTypes(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintTypes(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovTypes(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return base
|
||||
}
|
||||
func (m *RequestPing) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *RequestBroadcastTx) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Tx)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ResponsePing) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *ResponseBroadcastTx) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.CheckTx != nil {
|
||||
l = m.CheckTx.Size()
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
if m.DeliverTx != nil {
|
||||
l = m.DeliverTx.Size()
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovTypes(x uint64) (n int) {
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozTypes(x uint64) (n int) {
|
||||
return sovTypes(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *RequestPing) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: RequestPing: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: RequestPing: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipTypes(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *RequestBroadcastTx) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: RequestBroadcastTx: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: RequestBroadcastTx: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Tx", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Tx = append(m.Tx[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.Tx == nil {
|
||||
m.Tx = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipTypes(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ResponsePing) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: ResponsePing: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ResponsePing: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipTypes(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *ResponseBroadcastTx) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: ResponseBroadcastTx: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ResponseBroadcastTx: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field CheckTx", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.CheckTx == nil {
|
||||
m.CheckTx = &types.ResponseCheckTx{}
|
||||
}
|
||||
if err := m.CheckTx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field DeliverTx", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if m.DeliverTx == nil {
|
||||
m.DeliverTx = &types.ResponseDeliverTx{}
|
||||
}
|
||||
if err := m.DeliverTx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipTypes(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipTypes(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthTypes
|
||||
}
|
||||
iNdEx += length
|
||||
case 3:
|
||||
depth++
|
||||
case 4:
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupTypes
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthTypes
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthTypes = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupTypes = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
||||
@@ -1069,7 +1069,10 @@ func (m *ABCIResponses) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1174,7 +1177,10 @@ func (m *ValidatorsInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1276,7 +1282,10 @@ func (m *ConsensusParamsInfo) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1391,7 +1400,10 @@ func (m *Version) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1857,7 +1869,10 @@ func (m *State) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -1740,7 +1740,10 @@ func (m *Message) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1790,7 +1793,10 @@ func (m *SnapshotsRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1965,7 +1971,10 @@ func (m *SnapshotsResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2072,7 +2081,10 @@ func (m *ChunkRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2233,7 +2245,10 @@ func (m *ChunkResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2302,7 +2317,10 @@ func (m *LightBlockRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2388,7 +2406,10 @@ func (m *LightBlockResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2457,7 +2478,10 @@ func (m *ParamsRequest) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2559,7 +2583,10 @@ func (m *ParamsResponse) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -59,4 +59,4 @@ message ParamsRequest {
|
||||
message ParamsResponse {
|
||||
uint64 height = 1;
|
||||
tendermint.types.ConsensusParams consensus_params = 2 [(gogoproto.nullable) = false];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,7 +389,10 @@ func (m *Block) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthBlock
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthBlock
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -775,7 +775,10 @@ func (m *CanonicalBlockID) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthCanonical
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthCanonical
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -878,7 +881,10 @@ func (m *CanonicalPartSetHeader) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthCanonical
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthCanonical
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1087,7 +1093,10 @@ func (m *CanonicalProposal) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthCanonical
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthCanonical
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1277,7 +1286,10 @@ func (m *CanonicalVote) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthCanonical
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthCanonical
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -285,7 +285,10 @@ func (m *EventDataRoundState) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthEvents
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthEvents
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -825,7 +825,10 @@ func (m *Evidence) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthEvidence
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthEvidence
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1018,7 +1021,10 @@ func (m *DuplicateVoteEvidence) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthEvidence
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthEvidence
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1209,7 +1215,10 @@ func (m *LightClientAttackEvidence) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthEvidence
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthEvidence
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1293,7 +1302,10 @@ func (m *EvidenceList) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthEvidence
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthEvidence
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -1123,7 +1123,10 @@ func (m *ConsensusParams) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1211,7 +1214,10 @@ func (m *BlockParams) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1332,7 +1338,10 @@ func (m *EvidenceParams) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1414,7 +1423,10 @@ func (m *ValidatorParams) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1483,7 +1495,10 @@ func (m *VersionParams) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -1571,7 +1586,10 @@ func (m *HashedParams) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthParams
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -2267,7 +2267,10 @@ func (m *PartSetHeader) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2403,7 +2406,10 @@ func (m *Part) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -2520,7 +2526,10 @@ func (m *BlockID) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -3026,7 +3035,10 @@ func (m *Header) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -3108,7 +3120,10 @@ func (m *Data) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -3368,7 +3383,10 @@ func (m *Vote) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -3523,7 +3541,10 @@ func (m *Commit) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -3693,7 +3714,10 @@ func (m *CommitSig) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -3919,7 +3943,10 @@ func (m *Proposal) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -4041,7 +4068,10 @@ func (m *SignedHeader) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -4163,7 +4193,10 @@ func (m *LightBlock) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -4317,7 +4350,10 @@ func (m *BlockMeta) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -4471,7 +4507,10 @@ func (m *TxProof) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -583,7 +583,10 @@ func (m *ValidatorSet) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthValidator
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthValidator
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -738,7 +741,10 @@ func (m *Validator) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthValidator
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthValidator
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
@@ -843,7 +849,10 @@ func (m *SimpleValidator) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthValidator
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthValidator
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -265,7 +265,10 @@ func (m *Consensus) Unmarshal(dAtA []byte) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
buf generate --path proto/tendermint
|
||||
|
||||
mv ./proto/tendermint/abci/types.pb.go ./abci/types
|
||||
|
||||
mv ./proto/tendermint/rpc/grpc/types.pb.go ./rpc/grpc
|
||||
@@ -0,0 +1,93 @@
|
||||
# Tendermint Specifications
|
||||
|
||||
This directory hosts the canonical Markdown specifications of the Tendermint Protocol.
|
||||
|
||||
It shall be used to describe protocol semantics, namely the BFT consensus engine, leader election, block propagation and light client verification. The specification includes encoding descriptions used in interprocess communication to comply with the protocol. It defines the interface between the application and Tendermint. The english specifications are often accompanies with a TLA+ specification.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
|
||||
### Data Structures
|
||||
|
||||
- [Encoding and Digests](./core/encoding.md)
|
||||
- [Blockchain](./core/data_structures.md)
|
||||
- [State](./core/state.md)
|
||||
|
||||
### Consensus Protocol
|
||||
|
||||
- [Consensus Algorithm](./consensus/consensus.md)
|
||||
- [Creating a proposal](./consensus/creating-proposal.md)
|
||||
- [Time](./consensus/bft-time.md)
|
||||
- [Light-Client](./consensus/light-client/README.md)
|
||||
|
||||
### P2P and Network Protocols
|
||||
|
||||
- [The Base P2P Layer](./p2p/node.md): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections
|
||||
- [Peer Exchange (PEX)](./p2p/messages/pex.md): gossip known peer addresses so peers can find each other
|
||||
- [Block Sync](./p2p/messages/block-sync.md): gossip blocks so peers can catch up quickly
|
||||
- [Consensus](./p2p/messages/consensus.md): gossip votes and block parts so new blocks can be committed
|
||||
- [Mempool](./p2p/messages/mempool.md): gossip transactions so they get included in blocks
|
||||
- [Evidence](./p2p/messages/evidence.md): sending invalid evidence will stop the peer
|
||||
|
||||
### RPC
|
||||
|
||||
- [RPC SPEC](./rpc/README.md): Specification of the Tendermint remote procedure call interface.
|
||||
|
||||
### Software
|
||||
|
||||
- [ABCI](./abci/README.md): Details about interactions between the
|
||||
application and consensus engine over ABCI
|
||||
- [Write-Ahead Log](./consensus/wal.md): Details about how the consensus
|
||||
engine preserves data and recovers from crash failures
|
||||
|
||||
## Contibuting
|
||||
|
||||
Contributions are welcome.
|
||||
|
||||
Proposals at an early stage can first be drafted as Github issues. To progress, a proposal will often need to be written out and approved as a [Request For Comment (RFC)](../docs/rfc/README.md).
|
||||
|
||||
The standard language for coding blocks is Golang.
|
||||
|
||||
If you find discrepancies between the spec and the code that
|
||||
do not have an associated issue or pull request on github,
|
||||
please submit them to our [bug bounty](https://tendermint.com/security)!
|
||||
|
||||
## Overview
|
||||
|
||||
Tendermint provides Byzantine Fault Tolerant State Machine Replication using
|
||||
hash-linked batches of transactions. Such transaction batches are called "blocks".
|
||||
Hence, Tendermint defines a "blockchain".
|
||||
|
||||
Each block in Tendermint has a unique index - its Height.
|
||||
Height's in the blockchain are monotonic.
|
||||
Each block is committed by a known set of weighted Validators.
|
||||
Membership and weighting within this validator set may change over time.
|
||||
Tendermint guarantees the safety and liveness of the blockchain
|
||||
so long as less than 1/3 of the total weight of the Validator set
|
||||
is malicious or faulty.
|
||||
|
||||
A commit in Tendermint is a set of signed messages from more than 2/3 of
|
||||
the total weight of the current Validator set. Validators take turns proposing
|
||||
blocks and voting on them. Once enough votes are received, the block is considered
|
||||
committed. These votes are included in the _next_ block as proof that the previous block
|
||||
was committed - they cannot be included in the current block, as that block has already been
|
||||
created.
|
||||
|
||||
Once a block is committed, it can be executed against an application.
|
||||
The application returns results for each of the transactions in the block.
|
||||
The application can also return changes to be made to the validator set,
|
||||
as well as a cryptographic digest of its latest state.
|
||||
|
||||
Tendermint is designed to enable efficient verification and authentication
|
||||
of the latest state of the blockchain. To achieve this, it embeds
|
||||
cryptographic commitments to certain information in the block "header".
|
||||
This information includes the contents of the block (eg. the transactions),
|
||||
the validator set committing the block, as well as the various results returned by the application.
|
||||
Note, however, that block execution only occurs _after_ a block is committed.
|
||||
Thus, application results can only be included in the _next_ block.
|
||||
|
||||
Also note that information like the transaction results and the validator set are never
|
||||
directly included in the block - only their cryptographic digests (Merkle roots) are.
|
||||
Hence, verification of a block requires a separate data structure to store this information.
|
||||
We call this the `State`. Block verification also requires access to the previous block.
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
order: 1
|
||||
parent:
|
||||
title: ABCI
|
||||
order: 2
|
||||
---
|
||||
|
||||
# ABCI
|
||||
|
||||
ABCI stands for "**A**pplication **B**lock**c**hain **I**nterface".
|
||||
ABCI is the interface between Tendermint (a state-machine replication engine)
|
||||
and your application (the actual state machine). It consists of a set of
|
||||
_methods_, each with a corresponding `Request` and `Response`message type.
|
||||
To perform state-machine replication, Tendermint calls the ABCI methods on the
|
||||
ABCI application by sending the `Request*` messages and receiving the `Response*` messages in return.
|
||||
|
||||
All ABCI messages and methods are defined in [protocol buffers](https://github.com/tendermint/tendermint/blob/v0.35.x/proto/abci/types.proto).
|
||||
This allows Tendermint to run with applications written in many programming languages.
|
||||
|
||||
This specification is split as follows:
|
||||
|
||||
- [Methods and Types](./abci.md) - complete details on all ABCI methods and
|
||||
message types
|
||||
- [Applications](./apps.md) - how to manage ABCI application state and other
|
||||
details about building ABCI applications
|
||||
- [Client and Server](./client-server.md) - for those looking to implement their
|
||||
own ABCI application servers
|
||||
@@ -0,0 +1,775 @@
|
||||
---
|
||||
order: 1
|
||||
title: Method and Types
|
||||
---
|
||||
|
||||
# Methods and Types
|
||||
|
||||
## Connections
|
||||
|
||||
ABCI applications can run either within the _same_ process as the Tendermint
|
||||
state-machine replication engine, or as a _separate_ process from the state-machine
|
||||
replication engine. When run within the same process, Tendermint will call the ABCI
|
||||
application methods directly as Go method calls.
|
||||
|
||||
When Tendermint and the ABCI application are run as separate processes, Tendermint
|
||||
opens four connections to the application for ABCI methods. The connections each
|
||||
handle a subset of the ABCI method calls. These subsets are defined as follows:
|
||||
|
||||
#### **Consensus** connection
|
||||
|
||||
* Driven by a consensus protocol and is responsible for block execution.
|
||||
* Handles the `InitChain`, `BeginBlock`, `DeliverTx`, `EndBlock`, and `Commit` method
|
||||
calls.
|
||||
|
||||
#### **Mempool** connection
|
||||
|
||||
* For validating new transactions, before they're shared or included in a block.
|
||||
* Handles the `CheckTx` calls.
|
||||
|
||||
#### **Info** connection
|
||||
|
||||
* For initialization and for queries from the user.
|
||||
* Handles the `Info` and `Query` calls.
|
||||
|
||||
#### **Snapshot** connection
|
||||
|
||||
* For serving and restoring [state sync snapshots](apps.md#state-sync).
|
||||
* Handles the `ListSnapshots`, `LoadSnapshotChunk`, `OfferSnapshot`, and `ApplySnapshotChunk` calls.
|
||||
|
||||
Additionally, there is a `Flush` method that is called on every connection,
|
||||
and an `Echo` method that is just for debugging.
|
||||
|
||||
More details on managing state across connections can be found in the section on
|
||||
[ABCI Applications](apps.md).
|
||||
|
||||
## Errors
|
||||
|
||||
The `Query`, `CheckTx` and `DeliverTx` methods include a `Code` field in their `Response*`.
|
||||
This field is meant to contain an application-specific response code.
|
||||
A response code of `0` indicates no error. Any other response code
|
||||
indicates to Tendermint that an error occurred.
|
||||
|
||||
These methods also return a `Codespace` string to Tendermint. This field is
|
||||
used to disambiguate `Code` values returned by different domains of the
|
||||
application. The `Codespace` is a namespace for the `Code`.
|
||||
|
||||
The `Echo`, `Info`, `InitChain`, `BeginBlock`, `EndBlock`, `Commit` methods
|
||||
do not return errors. An error in any of these methods represents a critical
|
||||
issue that Tendermint has no reasonable way to handle. If there is an error in one
|
||||
of these methods, the application must crash to ensure that the error is safely
|
||||
handled by an operator.
|
||||
|
||||
The handling of non-zero response codes by Tendermint is described below
|
||||
|
||||
### CheckTx
|
||||
|
||||
The `CheckTx` ABCI method controls what transactions are considered for inclusion in a block.
|
||||
When Tendermint receives a `ResponseCheckTx` with a non-zero `Code`, the associated
|
||||
transaction will be not be added to Tendermint's mempool or it will be removed if
|
||||
it is already included.
|
||||
|
||||
### DeliverTx
|
||||
|
||||
The `DeliverTx` ABCI method delivers transactions from Tendermint to the application.
|
||||
When Tendermint recieves a `ResponseDeliverTx` with a non-zero `Code`, the response code is logged.
|
||||
The transaction was already included in a block, so the `Code` does not influence
|
||||
Tendermint consensus.
|
||||
|
||||
### Query
|
||||
|
||||
The `Query` ABCI method query queries the application for information about application state.
|
||||
When Tendermint receives a `ResponseQuery` with a non-zero `Code`, this code is
|
||||
returned directly to the client that initiated the query.
|
||||
|
||||
## Events
|
||||
|
||||
The `CheckTx`, `BeginBlock`, `DeliverTx`, `EndBlock` methods include an `Events`
|
||||
field in their `Response*`. Applications may respond to these ABCI methods with a set of events.
|
||||
Events allow applications to associate metadata about ABCI method execution with the
|
||||
transactions and blocks this metadata relates to.
|
||||
Events returned via these ABCI methods do not impact Tendermint consensus in any way
|
||||
and instead exist to power subscriptions and queries of Tendermint state.
|
||||
|
||||
An `Event` contains a `type` and a list of `EventAttributes`, which are key-value
|
||||
string pairs denoting metadata about what happened during the method's execution.
|
||||
`Event` values can be used to index transactions and blocks according to what happened
|
||||
during their execution. Note that the set of events returned for a block from
|
||||
`BeginBlock` and `EndBlock` are merged. In case both methods return the same
|
||||
key, only the value defined in `EndBlock` is used.
|
||||
|
||||
Each event has a `type` which is meant to categorize the event for a particular
|
||||
`Response*` or `Tx`. A `Response*` or `Tx` may contain multiple events with duplicate
|
||||
`type` values, where each distinct entry is meant to categorize attributes for a
|
||||
particular event. Every key and value in an event's attributes must be UTF-8
|
||||
encoded strings along with the event type itself.
|
||||
|
||||
```protobuf
|
||||
message Event {
|
||||
string type = 1;
|
||||
repeated EventAttribute attributes = 2;
|
||||
}
|
||||
```
|
||||
|
||||
The attributes of an `Event` consist of a `key`, a `value`, and an `index` flag. The
|
||||
index flag notifies the Tendermint indexer to index the attribute. The value of
|
||||
the `index` flag is non-deterministic and may vary across different nodes in the network.
|
||||
|
||||
```protobuf
|
||||
message EventAttribute {
|
||||
bytes key = 1;
|
||||
bytes value = 2;
|
||||
bool index = 3; // nondeterministic
|
||||
}
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
abci.ResponseDeliverTx{
|
||||
// ...
|
||||
Events: []abci.Event{
|
||||
{
|
||||
Type: "validator.provisions",
|
||||
Attributes: []abci.EventAttribute{
|
||||
abci.EventAttribute{Key: []byte("address"), Value: []byte("..."), Index: true},
|
||||
abci.EventAttribute{Key: []byte("amount"), Value: []byte("..."), Index: true},
|
||||
abci.EventAttribute{Key: []byte("balance"), Value: []byte("..."), Index: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "validator.provisions",
|
||||
Attributes: []abci.EventAttribute{
|
||||
abci.EventAttribute{Key: []byte("address"), Value: []byte("..."), Index: true},
|
||||
abci.EventAttribute{Key: []byte("amount"), Value: []byte("..."), Index: false},
|
||||
abci.EventAttribute{Key: []byte("balance"), Value: []byte("..."), Index: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "validator.slashed",
|
||||
Attributes: []abci.EventAttribute{
|
||||
abci.EventAttribute{Key: []byte("address"), Value: []byte("..."), Index: false},
|
||||
abci.EventAttribute{Key: []byte("amount"), Value: []byte("..."), Index: true},
|
||||
abci.EventAttribute{Key: []byte("reason"), Value: []byte("..."), Index: true},
|
||||
},
|
||||
},
|
||||
// ...
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## EvidenceType
|
||||
|
||||
Tendermint's security model relies on the use of "evidence". Evidence is proof of
|
||||
malicious behaviour by a network participant. It is the responsibility of Tendermint
|
||||
to detect such malicious behaviour. When malicious behavior is detected, Tendermint
|
||||
will gossip evidence of the behavior to other nodes and commit the evidence to
|
||||
the chain once it is verified by all validators. This evidence will then be
|
||||
passed it on to the application through the ABCI. It is the responsibility of the
|
||||
application to handle the evidence and exercise punishment.
|
||||
|
||||
EvidenceType has the following protobuf format:
|
||||
|
||||
```proto
|
||||
enum EvidenceType {
|
||||
UNKNOWN = 0;
|
||||
DUPLICATE_VOTE = 1;
|
||||
LIGHT_CLIENT_ATTACK = 2;
|
||||
}
|
||||
```
|
||||
|
||||
There are two forms of evidence: Duplicate Vote and Light Client Attack. More
|
||||
information can be found in either [data structures](https://github.com/tendermint/tendermint/blob/v0.35.x/spec/core/data_structures.md)
|
||||
or [accountability](https://github.com/tendermint/tendermint/blob/v0.35.x/spec/light-client/accountability/)
|
||||
|
||||
## Determinism
|
||||
|
||||
ABCI applications must implement deterministic finite-state machines to be
|
||||
securely replicated by the Tendermint consensus engine. This means block execution
|
||||
over the Consensus Connection must be strictly deterministic: given the same
|
||||
ordered set of requests, all nodes will compute identical responses, for all
|
||||
BeginBlock, DeliverTx, EndBlock, and Commit. This is critical, because the
|
||||
responses are included in the header of the next block, either via a Merkle root
|
||||
or directly, so all nodes must agree on exactly what they are.
|
||||
|
||||
For this reason, it is recommended that applications not be exposed to any
|
||||
external user or process except via the ABCI connections to a consensus engine
|
||||
like Tendermint Core. The application must only change its state based on input
|
||||
from block execution (BeginBlock, DeliverTx, EndBlock, Commit), and not through
|
||||
any other kind of request. This is the only way to ensure all nodes see the same
|
||||
transactions and compute the same results.
|
||||
|
||||
If there is some non-determinism in the state machine, consensus will eventually
|
||||
fail as nodes disagree over the correct values for the block header. The
|
||||
non-determinism must be fixed and the nodes restarted.
|
||||
|
||||
Sources of non-determinism in applications may include:
|
||||
|
||||
* Hardware failures
|
||||
* Cosmic rays, overheating, etc.
|
||||
* Node-dependent state
|
||||
* Random numbers
|
||||
* Time
|
||||
* Underspecification
|
||||
* Library version changes
|
||||
* Race conditions
|
||||
* Floating point numbers
|
||||
* JSON serialization
|
||||
* Iterating through hash-tables/maps/dictionaries
|
||||
* External Sources
|
||||
* Filesystem
|
||||
* Network calls (eg. some external REST API service)
|
||||
|
||||
See [#56](https://github.com/tendermint/abci/issues/56) for original discussion.
|
||||
|
||||
Note that some methods (`Query, CheckTx, DeliverTx`) return
|
||||
explicitly non-deterministic data in the form of `Info` and `Log` fields. The `Log` is
|
||||
intended for the literal output from the application's logger, while the
|
||||
`Info` is any additional info that should be returned. These are the only fields
|
||||
that are not included in block header computations, so we don't need agreement
|
||||
on them. All other fields in the `Response*` must be strictly deterministic.
|
||||
|
||||
## Block Execution
|
||||
|
||||
The first time a new blockchain is started, Tendermint calls
|
||||
`InitChain`. From then on, the following sequence of methods is executed for each
|
||||
block:
|
||||
|
||||
`BeginBlock, [DeliverTx], EndBlock, Commit`
|
||||
|
||||
where one `DeliverTx` is called for each transaction in the block.
|
||||
The result is an updated application state.
|
||||
Cryptographic commitments to the results of DeliverTx, EndBlock, and
|
||||
Commit are included in the header of the next block.
|
||||
|
||||
## State Sync
|
||||
|
||||
State sync allows new nodes to rapidly bootstrap by discovering, fetching, and applying
|
||||
state machine snapshots instead of replaying historical blocks. For more details, see the
|
||||
[state sync section](../spec/p2p/messages/state-sync.md).
|
||||
|
||||
New nodes will discover and request snapshots from other nodes in the P2P network.
|
||||
A Tendermint node that receives a request for snapshots from a peer will call
|
||||
`ListSnapshots` on its application to retrieve any local state snapshots. After receiving
|
||||
snapshots from peers, the new node will offer each snapshot received from a peer
|
||||
to its local application via the `OfferSnapshot` method.
|
||||
|
||||
Snapshots may be quite large and are thus broken into smaller "chunks" that can be
|
||||
assembled into the whole snapshot. Once the application accepts a snapshot and
|
||||
begins restoring it, Tendermint will fetch snapshot "chunks" from existing nodes.
|
||||
The node providing "chunks" will fetch them from its local application using
|
||||
the `LoadSnapshotChunk` method.
|
||||
|
||||
As the new node receives "chunks" it will apply them sequentially to the local
|
||||
application with `ApplySnapshotChunk`. When all chunks have been applied, the application
|
||||
`AppHash` is retrieved via an `Info` query. The `AppHash` is then compared to
|
||||
the blockchain's `AppHash` which is verified via [light client verification](../spec/light-client/verification/README.md).
|
||||
|
||||
## Messages
|
||||
|
||||
### Echo
|
||||
|
||||
* **Request**:
|
||||
* `Message (string)`: A string to echo back
|
||||
* **Response**:
|
||||
* `Message (string)`: The input string
|
||||
* **Usage**:
|
||||
* Echo a string to test an abci client/server implementation
|
||||
|
||||
### Flush
|
||||
|
||||
* **Usage**:
|
||||
* Signals that messages queued on the client should be flushed to
|
||||
the server. It is called periodically by the client
|
||||
implementation to ensure asynchronous requests are actually
|
||||
sent, and is called immediately to make a synchronous request,
|
||||
which returns when the Flush response comes back.
|
||||
|
||||
### Info
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|---------------|--------|------------------------------------------|--------------|
|
||||
| version | string | The Tendermint software semantic version | 1 |
|
||||
| block_version | uint64 | The Tendermint Block Protocol version | 2 |
|
||||
| p2p_version | uint64 | The Tendermint P2P Protocol version | 3 |
|
||||
| abci_version | string | The Tendermint ABCI semantic version | 4 |
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|---------------------|--------|--------------------------------------------------|--------------|
|
||||
| data | string | Some arbitrary information | 1 |
|
||||
| version | string | The application software semantic version | 2 |
|
||||
| app_version | uint64 | The application protocol version | 3 |
|
||||
| last_block_height | int64 | Latest block for which the app has called Commit | 4 |
|
||||
| last_block_app_hash | bytes | Latest result of Commit | 5 |
|
||||
|
||||
* **Usage**:
|
||||
* Return information about the application state.
|
||||
* Used to sync Tendermint with the application during a handshake
|
||||
that happens on startup.
|
||||
* The returned `app_version` will be included in the Header of every block.
|
||||
* Tendermint expects `last_block_app_hash` and `last_block_height` to
|
||||
be updated during `Commit`, ensuring that `Commit` is never
|
||||
called twice for the same block height.
|
||||
|
||||
> Note: Semantic version is a reference to [semantic versioning](https://semver.org/). Semantic versions in info will be displayed as X.X.x.
|
||||
|
||||
### InitChain
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|------------------|--------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------|--------------|
|
||||
| time | [google.protobuf.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp) | Genesis time | 1 |
|
||||
| chain_id | string | ID of the blockchain. | 2 |
|
||||
| consensus_params | [ConsensusParams](#consensusparams) | Initial consensus-critical parameters. | 3 |
|
||||
| validators | repeated [ValidatorUpdate](#validatorupdate) | Initial genesis validators, sorted by voting power. | 4 |
|
||||
| app_state_bytes | bytes | Serialized initial application state. JSON bytes. | 5 |
|
||||
| initial_height | int64 | Height of the initial block (typically `1`). | 6 |
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|------------------|----------------------------------------------|-------------------------------------------------|--------------|
|
||||
| consensus_params | [ConsensusParams](#consensusparams) | Initial consensus-critical parameters (optional | 1 |
|
||||
| validators | repeated [ValidatorUpdate](#validatorupdate) | Initial validator set (optional). | 2 |
|
||||
| app_hash | bytes | Initial application hash. | 3 |
|
||||
|
||||
* **Usage**:
|
||||
* Called once upon genesis.
|
||||
* If ResponseInitChain.Validators is empty, the initial validator set will be the RequestInitChain.Validators
|
||||
* If ResponseInitChain.Validators is not empty, it will be the initial
|
||||
validator set (regardless of what is in RequestInitChain.Validators).
|
||||
* This allows the app to decide if it wants to accept the initial validator
|
||||
set proposed by tendermint (ie. in the genesis file), or if it wants to use
|
||||
a different one (perhaps computed based on some application specific
|
||||
information in the genesis file).
|
||||
|
||||
### Query
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|--------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| data | bytes | Raw query bytes. Can be used with or in lieu of Path. | 1 |
|
||||
| path | string | Path field of the request URI. Can be used with or in lieu of `data`. Apps MUST interpret `/store` as a query by key on the underlying store. The key SHOULD be specified in the `data` field. Apps SHOULD allow queries over specific types like `/accounts/...` or `/votes/...` | 2 |
|
||||
| height | int64 | The block height for which you want the query (default=0 returns data for the latest committed block). Note that this is the height of the block containing the application's Merkle root hash, which represents the state as it was after committing the block at Height-1 | 3 |
|
||||
| prove | bool | Return Merkle proof with response if possible | 4 |
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|-----------|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| code | uint32 | Response code. | 1 |
|
||||
| log | string | The output of the application's logger. **May be non-deterministic.** | 3 |
|
||||
| info | string | Additional information. **May be non-deterministic.** | 4 |
|
||||
| index | int64 | The index of the key in the tree. | 5 |
|
||||
| key | bytes | The key of the matching data. | 6 |
|
||||
| value | bytes | The value of the matching data. | 7 |
|
||||
| proof_ops | [ProofOps](#proofops) | Serialized proof for the value data, if requested, to be verified against the `app_hash` for the given Height. | 8 |
|
||||
| height | int64 | The block height from which data was derived. Note that this is the height of the block containing the application's Merkle root hash, which represents the state as it was after committing the block at Height-1 | 9 |
|
||||
| codespace | string | Namespace for the `code`. | 10 |
|
||||
|
||||
* **Usage**:
|
||||
* Query for data from the application at current or past height.
|
||||
* Optionally return Merkle proof.
|
||||
* Merkle proof includes self-describing `type` field to support many types
|
||||
of Merkle trees and encoding formats.
|
||||
|
||||
### BeginBlock
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|----------------------|---------------------------------------------|-------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| hash | bytes | The block's hash. This can be derived from the block header. | 1 |
|
||||
| header | [Header](../core/data_structures.md#header) | The block header. | 2 |
|
||||
| last_commit_info | [LastCommitInfo](#lastcommitinfo) | Info about the last commit, including the round, and the list of validators and which ones signed the last block. | 3 |
|
||||
| byzantine_validators | repeated [Evidence](#evidence) | List of evidence of validators that acted maliciously. | 4 |
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|--------|---------------------------|-------------------------------------|--------------|
|
||||
| events | repeated [Event](#events) | type & Key-Value events for indexing | 1 |
|
||||
|
||||
* **Usage**:
|
||||
* Signals the beginning of a new block.
|
||||
* Called prior to any `DeliverTx` method calls.
|
||||
* The header contains the height, timestamp, and more - it exactly matches the
|
||||
Tendermint block header. We may seek to generalize this in the future.
|
||||
* The `LastCommitInfo` and `ByzantineValidators` can be used to determine
|
||||
rewards and punishments for the validators.
|
||||
|
||||
### CheckTx
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|------|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| tx | bytes | The request transaction bytes | 1 |
|
||||
| type | CheckTxType | One of `CheckTx_New` or `CheckTx_Recheck`. `CheckTx_New` is the default and means that a full check of the tranasaction is required. `CheckTx_Recheck` types are used when the mempool is initiating a normal recheck of a transaction. | 2 |
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|------------|---------------------------|-----------------------------------------------------------------------|--------------|
|
||||
| code | uint32 | Response code. | 1 |
|
||||
| data | bytes | Result bytes, if any. | 2 |
|
||||
| log | string | The output of the application's logger. **May be non-deterministic.** | 3 |
|
||||
| info | string | Additional information. **May be non-deterministic.** | 4 |
|
||||
| gas_wanted | int64 | Amount of gas requested for transaction. | 5 |
|
||||
| gas_used | int64 | Amount of gas consumed by transaction. | 6 |
|
||||
| events | repeated [Event](#events) | Type & Key-Value events for indexing transactions (eg. by account). | 7 |
|
||||
| codespace | string | Namespace for the `code`. | 8 |
|
||||
| sender | string | The transaction's sender (e.g. the signer) | 9 |
|
||||
| priority | int64 | The transaction's priority (for mempool ordering) | 10 |
|
||||
|
||||
* **Usage**:
|
||||
|
||||
* Technically optional - not involved in processing blocks.
|
||||
* Guardian of the mempool: every node runs `CheckTx` before letting a
|
||||
transaction into its local mempool.
|
||||
* The transaction may come from an external user or another node
|
||||
* `CheckTx` validates the transaction against the current state of the application,
|
||||
for example, checking signatures and account balances, but does not apply any
|
||||
of the state changes described in the transaction.
|
||||
not running code in a virtual machine.
|
||||
* Transactions where `ResponseCheckTx.Code != 0` will be rejected - they will not be broadcast to
|
||||
other nodes or included in a proposal block.
|
||||
* Tendermint attributes no other value to the response code
|
||||
|
||||
### DeliverTx
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|------|-------|--------------------------------|--------------|
|
||||
| tx | bytes | The request transaction bytes. | 1 |
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|------------|---------------------------|-----------------------------------------------------------------------|--------------|
|
||||
| code | uint32 | Response code. | 1 |
|
||||
| data | bytes | Result bytes, if any. | 2 |
|
||||
| log | string | The output of the application's logger. **May be non-deterministic.** | 3 |
|
||||
| info | string | Additional information. **May be non-deterministic.** | 4 |
|
||||
| gas_wanted | int64 | Amount of gas requested for transaction. | 5 |
|
||||
| gas_used | int64 | Amount of gas consumed by transaction. | 6 |
|
||||
| events | repeated [Event](#events) | Type & Key-Value events for indexing transactions (eg. by account). | 7 |
|
||||
| codespace | string | Namespace for the `code`. | 8 |
|
||||
|
||||
* **Usage**:
|
||||
* [**Required**] The core method of the application.
|
||||
* When `DeliverTx` is called, the application must execute the transaction in full before returning control to Tendermint.
|
||||
* `ResponseDeliverTx.Code == 0` only if the transaction is fully valid.
|
||||
|
||||
### EndBlock
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|--------|-------|------------------------------------|--------------|
|
||||
| height | int64 | Height of the block just executed. | 1 |
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|-------------------------|----------------------------------------------|-----------------------------------------------------------------|--------------|
|
||||
| validator_updates | repeated [ValidatorUpdate](#validatorupdate) | Changes to validator set (set voting power to 0 to remove). | 1 |
|
||||
| consensus_param_updates | [ConsensusParams](#consensusparams) | Changes to consensus-critical time, size, and other parameters. | 2 |
|
||||
| events | repeated [Event](#events) | Type & Key-Value events for indexing | 3 |
|
||||
|
||||
* **Usage**:
|
||||
* Signals the end of a block.
|
||||
* Called after all the transactions for the current block have been delivered, prior to the block's `Commit` message.
|
||||
* Optional `validator_updates` triggered by block `H`. These updates affect validation
|
||||
for blocks `H+1`, `H+2`, and `H+3`.
|
||||
* Heights following a validator update are affected in the following way:
|
||||
* `H+1`: `NextValidatorsHash` includes the new `validator_updates` value.
|
||||
* `H+2`: The validator set change takes effect and `ValidatorsHash` is updated.
|
||||
* `H+3`: `LastCommitInfo` is changed to include the altered validator set.
|
||||
* `consensus_param_updates` returned for block `H` apply to the consensus
|
||||
params for block `H+1`. For more information on the consensus parameters,
|
||||
see the [application spec entry on consensus parameters](../spec/abci/apps.md#consensus-parameters).
|
||||
|
||||
### Commit
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|--------|-------|------------------------------------|--------------|
|
||||
|
||||
Commit signals the application to persist application state. It takes no parameters.
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|---------------|-------|------------------------------------------------------------------------|--------------|
|
||||
| data | bytes | The Merkle root hash of the application state. | 2 |
|
||||
| retain_height | int64 | Blocks below this height may be removed. Defaults to `0` (retain all). | 3 |
|
||||
|
||||
* **Usage**:
|
||||
* Signal the application to persist the application state.
|
||||
* Return an (optional) Merkle root hash of the application state
|
||||
* `ResponseCommit.Data` is included as the `Header.AppHash` in the next block
|
||||
* it may be empty
|
||||
* Later calls to `Query` can return proofs about the application state anchored
|
||||
in this Merkle root hash
|
||||
* Note developers can return whatever they want here (could be nothing, or a
|
||||
constant string, etc.), so long as it is deterministic - it must not be a
|
||||
function of anything that did not come from the
|
||||
BeginBlock/DeliverTx/EndBlock methods.
|
||||
* Use `RetainHeight` with caution! If all nodes in the network remove historical
|
||||
blocks then this data is permanently lost, and no new nodes will be able to
|
||||
join the network and bootstrap. Historical blocks may also be required for
|
||||
other purposes, e.g. auditing, replay of non-persisted heights, light client
|
||||
verification, and so on.
|
||||
|
||||
### ListSnapshots
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|--------|-------|------------------------------------|--------------|
|
||||
|
||||
Empty request asking the application for a list of snapshots.
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|-----------|--------------------------------|--------------------------------|--------------|
|
||||
| snapshots | repeated [Snapshot](#snapshot) | List of local state snapshots. | 1 |
|
||||
|
||||
* **Usage**:
|
||||
* Used during state sync to discover available snapshots on peers.
|
||||
* See `Snapshot` data type for details.
|
||||
|
||||
### LoadSnapshotChunk
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|--------|--------|-----------------------------------------------------------------------|--------------|
|
||||
| height | uint64 | The height of the snapshot the chunks belongs to. | 1 |
|
||||
| format | uint32 | The application-specific format of the snapshot the chunk belongs to. | 2 |
|
||||
| chunk | uint32 | The chunk index, starting from `0` for the initial chunk. | 3 |
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|-------|-------|-------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| chunk | bytes | The binary chunk contents, in an arbitray format. Chunk messages cannot be larger than 16 MB _including metadata_, so 10 MB is a good starting point. | 1 |
|
||||
|
||||
* **Usage**:
|
||||
* Used during state sync to retrieve snapshot chunks from peers.
|
||||
|
||||
### OfferSnapshot
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|----------|-----------------------|--------------------------------------------------------------------------|--------------|
|
||||
| snapshot | [Snapshot](#snapshot) | The snapshot offered for restoration. | 1 |
|
||||
| app_hash | bytes | The light client-verified app hash for this height, from the blockchain. | 2 |
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|--------|-------------------|-----------------------------------|--------------|
|
||||
| result | [Result](#result) | The result of the snapshot offer. | 1 |
|
||||
|
||||
#### Result
|
||||
|
||||
```proto
|
||||
enum Result {
|
||||
UNKNOWN = 0; // Unknown result, abort all snapshot restoration
|
||||
ACCEPT = 1; // Snapshot is accepted, start applying chunks.
|
||||
ABORT = 2; // Abort snapshot restoration, and don't try any other snapshots.
|
||||
REJECT = 3; // Reject this specific snapshot, try others.
|
||||
REJECT_FORMAT = 4; // Reject all snapshots with this `format`, try others.
|
||||
REJECT_SENDER = 5; // Reject all snapshots from all senders of this snapshot, try others.
|
||||
}
|
||||
```
|
||||
|
||||
* **Usage**:
|
||||
* `OfferSnapshot` is called when bootstrapping a node using state sync. The application may
|
||||
accept or reject snapshots as appropriate. Upon accepting, Tendermint will retrieve and
|
||||
apply snapshot chunks via `ApplySnapshotChunk`. The application may also choose to reject a
|
||||
snapshot in the chunk response, in which case it should be prepared to accept further
|
||||
`OfferSnapshot` calls.
|
||||
* Only `AppHash` can be trusted, as it has been verified by the light client. Any other data
|
||||
can be spoofed by adversaries, so applications should employ additional verification schemes
|
||||
to avoid denial-of-service attacks. The verified `AppHash` is automatically checked against
|
||||
the restored application at the end of snapshot restoration.
|
||||
* For more information, see the `Snapshot` data type or the [state sync section](../spec/p2p/messages/state-sync.md).
|
||||
|
||||
### ApplySnapshotChunk
|
||||
|
||||
* **Request**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|--------|--------|-----------------------------------------------------------------------------|--------------|
|
||||
| index | uint32 | The chunk index, starting from `0`. Tendermint applies chunks sequentially. | 1 |
|
||||
| chunk | bytes | The binary chunk contents, as returned by `LoadSnapshotChunk`. | 2 |
|
||||
| sender | string | The P2P ID of the node who sent this chunk. | 3 |
|
||||
|
||||
* **Response**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|----------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| result | Result (see below) | The result of applying this chunk. | 1 |
|
||||
| refetch_chunks | repeated uint32 | Refetch and reapply the given chunks, regardless of `result`. Only the listed chunks will be refetched, and reapplied in sequential order. | 2 |
|
||||
| reject_senders | repeated string | Reject the given P2P senders, regardless of `Result`. Any chunks already applied will not be refetched unless explicitly requested, but queued chunks from these senders will be discarded, and new chunks or other snapshots rejected. | 3 |
|
||||
|
||||
```proto
|
||||
enum Result {
|
||||
UNKNOWN = 0; // Unknown result, abort all snapshot restoration
|
||||
ACCEPT = 1; // The chunk was accepted.
|
||||
ABORT = 2; // Abort snapshot restoration, and don't try any other snapshots.
|
||||
RETRY = 3; // Reapply this chunk, combine with `RefetchChunks` and `RejectSenders` as appropriate.
|
||||
RETRY_SNAPSHOT = 4; // Restart this snapshot from `OfferSnapshot`, reusing chunks unless instructed otherwise.
|
||||
REJECT_SNAPSHOT = 5; // Reject this snapshot, try a different one.
|
||||
}
|
||||
```
|
||||
|
||||
* **Usage**:
|
||||
* The application can choose to refetch chunks and/or ban P2P peers as appropriate. Tendermint
|
||||
will not do this unless instructed by the application.
|
||||
* The application may want to verify each chunk, e.g. by attaching chunk hashes in
|
||||
`Snapshot.Metadata` and/or incrementally verifying contents against `AppHash`.
|
||||
* When all chunks have been accepted, Tendermint will make an ABCI `Info` call to verify that
|
||||
`LastBlockAppHash` and `LastBlockHeight` matches the expected values, and record the
|
||||
`AppVersion` in the node state. It then switches to fast sync or consensus and joins the
|
||||
network.
|
||||
* If Tendermint is unable to retrieve the next chunk after some time (e.g. because no suitable
|
||||
peers are available), it will reject the snapshot and try a different one via `OfferSnapshot`.
|
||||
The application should be prepared to reset and accept it or abort as appropriate.
|
||||
|
||||
## Data Types
|
||||
|
||||
Most of the data structures used in ABCI are shared [common data structures](../spec/core/data_structures.md). In certain cases, ABCI uses different data structures which are documented here:
|
||||
|
||||
### Validator
|
||||
|
||||
* **Fields**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|---------|-------|---------------------------------------------------------------------|--------------|
|
||||
| address | bytes | [Address](../core/data_structures.md#address) of validator | 1 |
|
||||
| power | int64 | Voting power of the validator | 3 |
|
||||
|
||||
* **Usage**:
|
||||
* Validator identified by address
|
||||
* Used in RequestBeginBlock as part of VoteInfo
|
||||
* Does not include PubKey to avoid sending potentially large quantum pubkeys
|
||||
over the ABCI
|
||||
|
||||
### ValidatorUpdate
|
||||
|
||||
* **Fields**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|---------|--------------------------------------------------|-------------------------------|--------------|
|
||||
| pub_key | [Public Key](../core/data_structures.md#pub_key) | Public key of the validator | 1 |
|
||||
| power | int64 | Voting power of the validator | 2 |
|
||||
|
||||
* **Usage**:
|
||||
* Validator identified by PubKey
|
||||
* Used to tell Tendermint to update the validator set
|
||||
|
||||
### VoteInfo
|
||||
|
||||
* **Fields**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|-------------------|-------------------------|--------------------------------------------------------------|--------------|
|
||||
| validator | [Validator](#validator) | A validator | 1 |
|
||||
| signed_last_block | bool | Indicates whether or not the validator signed the last block | 2 |
|
||||
|
||||
* **Usage**:
|
||||
* Indicates whether a validator signed the last block, allowing for rewards
|
||||
based on validator availability
|
||||
|
||||
### Evidence
|
||||
|
||||
* **Fields**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|--------------------|--------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------|--------------|
|
||||
| type | [EvidenceType](#evidencetype) | Type of the evidence. An enum of possible evidence's. | 1 |
|
||||
| validator | [Validator](#validator) | The offending validator | 2 |
|
||||
| height | int64 | Height when the offense occurred | 3 |
|
||||
| time | [google.protobuf.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp) | Time of the block that was committed at the height that the offense occurred | 4 |
|
||||
| total_voting_power | int64 | Total voting power of the validator set at height `Height` | 5 |
|
||||
|
||||
#### EvidenceType
|
||||
|
||||
* **Fields**
|
||||
|
||||
EvidenceType is an enum with the listed fields:
|
||||
|
||||
| Name | Field Number |
|
||||
|---------------------|--------------|
|
||||
| UNKNOWN | 0 |
|
||||
| DUPLICATE_VOTE | 1 |
|
||||
| LIGHT_CLIENT_ATTACK | 2 |
|
||||
|
||||
### LastCommitInfo
|
||||
|
||||
* **Fields**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|-------|--------------------------------|-----------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| round | int32 | Commit round. Reflects the total amount of rounds it took to come to consensus for the current block. | 1 |
|
||||
| votes | repeated [VoteInfo](#voteinfo) | List of validators addresses in the last validator set with their voting power and whether or not they signed a vote. | 2 |
|
||||
|
||||
### ConsensusParams
|
||||
|
||||
* **Fields**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|-----------|---------------------------------------------------------------|------------------------------------------------------------------------------|--------------|
|
||||
| block | [BlockParams](../core/data_structures.md#blockparams) | Parameters limiting the size of a block and time between consecutive blocks. | 1 |
|
||||
| evidence | [EvidenceParams](../core/data_structures.md#evidenceparams) | Parameters limiting the validity of evidence of byzantine behaviour. | 2 |
|
||||
| validator | [ValidatorParams](../core/data_structures.md#validatorparams) | Parameters limiting the types of public keys validators can use. | 3 |
|
||||
| version | [VersionsParams](../core/data_structures.md#versionparams) | The ABCI application version. | 4 |
|
||||
|
||||
### ProofOps
|
||||
|
||||
* **Fields**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|------|------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| ops | repeated [ProofOp](#proofop) | List of chained Merkle proofs, of possibly different types. The Merkle root of one op is the value being proven in the next op. The Merkle root of the final op should equal the ultimate root hash being verified against.. | 1 |
|
||||
|
||||
### ProofOp
|
||||
|
||||
* **Fields**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|------|--------|------------------------------------------------|--------------|
|
||||
| type | string | Type of Merkle proof and how it's encoded. | 1 |
|
||||
| key | bytes | Key in the Merkle tree that this proof is for. | 2 |
|
||||
| data | bytes | Encoded Merkle proof for the key. | 3 |
|
||||
|
||||
### Snapshot
|
||||
|
||||
* **Fields**:
|
||||
|
||||
| Name | Type | Description | Field Number |
|
||||
|----------|--------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
|
||||
| height | uint64 | The height at which the snapshot was taken (after commit). | 1 |
|
||||
| format | uint32 | An application-specific snapshot format, allowing applications to version their snapshot data format and make backwards-incompatible changes. Tendermint does not interpret this. | 2 |
|
||||
| chunks | uint32 | The number of chunks in the snapshot. Must be at least 1 (even if empty). | 3 |
|
||||
| hash | bytes | TAn arbitrary snapshot hash. Must be equal only for identical snapshots across nodes. Tendermint does not interpret the hash, it only compares them. | 3 |
|
||||
| metadata | bytes | Arbitrary application metadata, for example chunk hashes or other verification data. | 3 |
|
||||
|
||||
* **Usage**:
|
||||
* Used for state sync snapshots, see the [state sync section](../spec/p2p/messages/state-sync.md) for details.
|
||||
* A snapshot is considered identical across nodes only if _all_ fields are equal (including
|
||||
`Metadata`). Chunks may be retrieved from all nodes that have the same snapshot.
|
||||
* When sent across the network, a snapshot message can be at most 4 MB.
|
||||
@@ -0,0 +1,671 @@
|
||||
---
|
||||
order: 2
|
||||
title: Applications
|
||||
---
|
||||
|
||||
# Applications
|
||||
|
||||
Please ensure you've first read the spec for [ABCI Methods and Types](abci.md)
|
||||
|
||||
Here we cover the following components of ABCI applications:
|
||||
|
||||
- [Connection State](#connection-state) - the interplay between ABCI connections and application state
|
||||
and the differences between `CheckTx` and `DeliverTx`.
|
||||
- [Transaction Results](#transaction-results) - rules around transaction
|
||||
results and validity
|
||||
- [Validator Set Updates](#validator-updates) - how validator sets are
|
||||
changed during `InitChain` and `EndBlock`
|
||||
- [Query](#query) - standards for using the `Query` method and proofs about the
|
||||
application state
|
||||
- [Crash Recovery](#crash-recovery) - handshake protocol to synchronize
|
||||
Tendermint and the application on startup.
|
||||
- [State Sync](#state-sync) - rapid bootstrapping of new nodes by restoring state machine snapshots
|
||||
|
||||
## Connection State
|
||||
|
||||
Since Tendermint maintains four concurrent ABCI connections, it is typical
|
||||
for an application to maintain a distinct state for each, and for the states to
|
||||
be synchronized during `Commit`.
|
||||
|
||||
### Concurrency
|
||||
|
||||
In principle, each of the four ABCI connections operate concurrently with one
|
||||
another. This means applications need to ensure access to state is
|
||||
thread safe. In practice, both the
|
||||
[default in-process ABCI client](https://github.com/tendermint/tendermint/blob/v0.34.4/abci/client/local_client.go#L18)
|
||||
and the
|
||||
[default Go ABCI
|
||||
server](https://github.com/tendermint/tendermint/blob/v0.34.4/abci/server/socket_server.go#L32)
|
||||
use global locks across all connections, so they are not
|
||||
concurrent at all. This means if your app is written in Go, and compiled in-process with Tendermint
|
||||
using the default `NewLocalClient`, or run out-of-process using the default `SocketServer`,
|
||||
ABCI messages from all connections will be linearizable (received one at a
|
||||
time).
|
||||
|
||||
The existence of this global mutex means Go application developers can get
|
||||
thread safety for application state by routing *all* reads and writes through the ABCI
|
||||
system. Thus it may be *unsafe* to expose application state directly to an RPC
|
||||
interface, and unless explicit measures are taken, all queries should be routed through the ABCI Query method.
|
||||
|
||||
### BeginBlock
|
||||
|
||||
The BeginBlock request can be used to run some code at the beginning of
|
||||
every block. It also allows Tendermint to send the current block hash
|
||||
and header to the application, before it sends any of the transactions.
|
||||
|
||||
The app should remember the latest height and header (ie. from which it
|
||||
has run a successful Commit) so that it can tell Tendermint where to
|
||||
pick up from when it restarts. See information on the Handshake, below.
|
||||
|
||||
### Commit
|
||||
|
||||
Application state should only be persisted to disk during `Commit`.
|
||||
|
||||
Before `Commit` is called, Tendermint locks and flushes the mempool so that no new messages will
|
||||
be received on the mempool connection. This provides an opportunity to safely update all four connection
|
||||
states to the latest committed state at once.
|
||||
|
||||
When `Commit` completes, it unlocks the mempool.
|
||||
|
||||
WARNING: if the ABCI app logic processing the `Commit` message sends a
|
||||
`/broadcast_tx_sync` or `/broadcast_tx_commit` and waits for the response
|
||||
before proceeding, it will deadlock. Executing those `broadcast_tx` calls
|
||||
involves acquiring a lock that is held during the `Commit` call, so it's not
|
||||
possible. If you make the call to the `broadcast_tx` endpoints concurrently,
|
||||
that's no problem, it just can't be part of the sequential logic of the
|
||||
`Commit` function.
|
||||
|
||||
### Consensus Connection
|
||||
|
||||
The Consensus Connection should maintain a `DeliverTxState` - the working state
|
||||
for block execution. It should be updated by the calls to `BeginBlock`, `DeliverTx`,
|
||||
and `EndBlock` during block execution and committed to disk as the "latest
|
||||
committed state" during `Commit`.
|
||||
|
||||
Updates made to the `DeliverTxState` by each method call must be readable by each subsequent method -
|
||||
ie. the updates are linearizable.
|
||||
|
||||
### Mempool Connection
|
||||
|
||||
The mempool Connection should maintain a `CheckTxState`
|
||||
to sequentially process pending transactions in the mempool that have
|
||||
not yet been committed. It should be initialized to the latest committed state
|
||||
at the end of every `Commit`.
|
||||
|
||||
Before calling `Commit`, Tendermint will lock and flush the mempool connection,
|
||||
ensuring that all existing CheckTx are responded to and no new ones can begin.
|
||||
The `CheckTxState` may be updated concurrently with the `DeliverTxState`, as
|
||||
messages may be sent concurrently on the Consensus and Mempool connections.
|
||||
|
||||
After `Commit`, while still holding the mempool lock, CheckTx is run again on all transactions that remain in the
|
||||
node's local mempool after filtering those included in the block.
|
||||
An additional `Type` parameter is made available to the CheckTx function that
|
||||
indicates whether an incoming transaction is new (`CheckTxType_New`), or a
|
||||
recheck (`CheckTxType_Recheck`).
|
||||
|
||||
Finally, after re-checking transactions in the mempool, Tendermint will unlock
|
||||
the mempool connection. New transactions are once again able to be processed through CheckTx.
|
||||
|
||||
Note that CheckTx is just a weak filter to keep invalid transactions out of the block chain.
|
||||
CheckTx doesn't have to check everything that affects transaction validity; the
|
||||
expensive things can be skipped. It's weak because a Byzantine node doesn't
|
||||
care about CheckTx; it can propose a block full of invalid transactions if it wants.
|
||||
|
||||
#### Replay Protection
|
||||
|
||||
To prevent old transactions from being replayed, CheckTx must implement
|
||||
replay protection.
|
||||
|
||||
It is possible for old transactions to be sent to the application. So
|
||||
it is important CheckTx implements some logic to handle them.
|
||||
|
||||
### Query Connection
|
||||
|
||||
The Info Connection should maintain a `QueryState` for answering queries from the user,
|
||||
and for initialization when Tendermint first starts up (both described further
|
||||
below).
|
||||
It should always contain the latest committed state associated with the
|
||||
latest committed block.
|
||||
|
||||
`QueryState` should be set to the latest `DeliverTxState` at the end of every `Commit`,
|
||||
after the full block has been processed and the state committed to disk.
|
||||
Otherwise it should never be modified.
|
||||
|
||||
Tendermint Core currently uses the Query connection to filter peers upon
|
||||
connecting, according to IP address or node ID. For instance,
|
||||
returning non-OK ABCI response to either of the following queries will
|
||||
cause Tendermint to not connect to the corresponding peer:
|
||||
|
||||
- `p2p/filter/addr/<ip addr>`, where `<ip addr>` is an IP address.
|
||||
- `p2p/filter/id/<id>`, where `<is>` is the hex-encoded node ID (the hash of
|
||||
the node's p2p pubkey).
|
||||
|
||||
Note: these query formats are subject to change!
|
||||
|
||||
### Snapshot Connection
|
||||
|
||||
The Snapshot Connection is optional, and is only used to serve state sync snapshots for other nodes
|
||||
and/or restore state sync snapshots to a local node being bootstrapped.
|
||||
|
||||
For more information, see [the state sync section of this document](#state-sync).
|
||||
|
||||
## Transaction Results
|
||||
|
||||
The `Info` and `Log` fields are non-deterministic values for debugging/convenience purposes
|
||||
that are otherwise ignored.
|
||||
|
||||
The `Data` field must be strictly deterministic, but can be arbitrary data.
|
||||
|
||||
### Gas
|
||||
|
||||
Ethereum introduced the notion of `gas` as an abstract representation of the
|
||||
cost of resources used by nodes when processing transactions. Every operation in the
|
||||
Ethereum Virtual Machine uses some amount of gas, and gas can be accepted at a market-variable price.
|
||||
Users propose a maximum amount of gas for their transaction; if the tx uses less, they get
|
||||
the difference credited back. Tendermint adopts a similar abstraction,
|
||||
though uses it only optionally and weakly, allowing applications to define
|
||||
their own sense of the cost of execution.
|
||||
|
||||
In Tendermint, the [ConsensusParams.Block.MaxGas](../proto/types/params.proto) limits the amount of `gas` that can be used in a block.
|
||||
The default value is `-1`, meaning no limit, or that the concept of gas is
|
||||
meaningless.
|
||||
|
||||
Responses contain a `GasWanted` and `GasUsed` field. The former is the maximum
|
||||
amount of gas the sender of a tx is willing to use, and the later is how much it actually
|
||||
used. Applications should enforce that `GasUsed <= GasWanted` - ie. tx execution
|
||||
should halt before it can use more resources than it requested.
|
||||
|
||||
When `MaxGas > -1`, Tendermint enforces the following rules:
|
||||
|
||||
- `GasWanted <= MaxGas` for all txs in the mempool
|
||||
- `(sum of GasWanted in a block) <= MaxGas` when proposing a block
|
||||
|
||||
If `MaxGas == -1`, no rules about gas are enforced.
|
||||
|
||||
Note that Tendermint does not currently enforce anything about Gas in the consensus, only the mempool.
|
||||
This means it does not guarantee that committed blocks satisfy these rules!
|
||||
It is the application's responsibility to return non-zero response codes when gas limits are exceeded.
|
||||
|
||||
The `GasUsed` field is ignored completely by Tendermint. That said, applications should enforce:
|
||||
|
||||
- `GasUsed <= GasWanted` for any given transaction
|
||||
- `(sum of GasUsed in a block) <= MaxGas` for every block
|
||||
|
||||
In the future, we intend to add a `Priority` field to the responses that can be
|
||||
used to explicitly prioritize txs in the mempool for inclusion in a block
|
||||
proposal. See [#1861](https://github.com/tendermint/tendermint/issues/1861).
|
||||
|
||||
### CheckTx
|
||||
|
||||
If `Code != 0`, it will be rejected from the mempool and hence
|
||||
not broadcasted to other peers and not included in a proposal block.
|
||||
|
||||
`Data` contains the result of the CheckTx transaction execution, if any. It is
|
||||
semantically meaningless to Tendermint.
|
||||
|
||||
`Events` include any events for the execution, though since the transaction has not
|
||||
been committed yet, they are effectively ignored by Tendermint.
|
||||
|
||||
### DeliverTx
|
||||
|
||||
DeliverTx is the workhorse of the blockchain. Tendermint sends the
|
||||
DeliverTx requests asynchronously but in order, and relies on the
|
||||
underlying socket protocol (ie. TCP) to ensure they are received by the
|
||||
app in order. They have already been ordered in the global consensus by
|
||||
the Tendermint protocol.
|
||||
|
||||
If DeliverTx returns `Code != 0`, the transaction will be considered invalid,
|
||||
though it is still included in the block.
|
||||
|
||||
DeliverTx also returns a [Code, Data, and Log](../../proto/abci/types.proto#L189-L191).
|
||||
|
||||
`Data` contains the result of the CheckTx transaction execution, if any. It is
|
||||
semantically meaningless to Tendermint.
|
||||
|
||||
Both the `Code` and `Data` are included in a structure that is hashed into the
|
||||
`LastResultsHash` of the next block header.
|
||||
|
||||
`Events` include any events for the execution, which Tendermint will use to index
|
||||
the transaction by. This allows transactions to be queried according to what
|
||||
events took place during their execution.
|
||||
|
||||
## Updating the Validator Set
|
||||
|
||||
The application may set the validator set during InitChain, and may update it during
|
||||
EndBlock.
|
||||
|
||||
Note that the maximum total power of the validator set is bounded by
|
||||
`MaxTotalVotingPower = MaxInt64 / 8`. Applications are responsible for ensuring
|
||||
they do not make changes to the validator set that cause it to exceed this
|
||||
limit.
|
||||
|
||||
Additionally, applications must ensure that a single set of updates does not contain any duplicates -
|
||||
a given public key can only appear once within a given update. If an update includes
|
||||
duplicates, the block execution will fail irrecoverably.
|
||||
|
||||
### InitChain
|
||||
|
||||
The `InitChain` method can return a list of validators.
|
||||
If the list is empty, Tendermint will use the validators loaded in the genesis
|
||||
file.
|
||||
If the list returned by `InitChain` is not empty, Tendermint will use its contents as the validator set.
|
||||
This way the application can set the initial validator set for the
|
||||
blockchain.
|
||||
|
||||
### EndBlock
|
||||
|
||||
Updates to the Tendermint validator set can be made by returning
|
||||
`ValidatorUpdate` objects in the `ResponseEndBlock`:
|
||||
|
||||
```protobuf
|
||||
message ValidatorUpdate {
|
||||
tendermint.crypto.keys.PublicKey pub_key
|
||||
int64 power
|
||||
}
|
||||
|
||||
message PublicKey {
|
||||
oneof {
|
||||
ed25519 bytes = 1;
|
||||
}
|
||||
```
|
||||
|
||||
The `pub_key` currently supports only one type:
|
||||
|
||||
- `type = "ed25519"`
|
||||
|
||||
The `power` is the new voting power for the validator, with the
|
||||
following rules:
|
||||
|
||||
- power must be non-negative
|
||||
- if power is 0, the validator must already exist, and will be removed from the
|
||||
validator set
|
||||
- if power is non-0:
|
||||
- if the validator does not already exist, it will be added to the validator
|
||||
set with the given power
|
||||
- if the validator does already exist, its power will be adjusted to the given power
|
||||
- the total power of the new validator set must not exceed MaxTotalVotingPower
|
||||
|
||||
Note the updates returned in block `H` will only take effect at block `H+2`.
|
||||
|
||||
## Consensus Parameters
|
||||
|
||||
ConsensusParams enforce certain limits in the blockchain, like the maximum size
|
||||
of blocks, amount of gas used in a block, and the maximum acceptable age of
|
||||
evidence. They can be set in InitChain and updated in EndBlock.
|
||||
|
||||
### BlockParams.MaxBytes
|
||||
|
||||
The maximum size of a complete Protobuf encoded block.
|
||||
This is enforced by Tendermint consensus.
|
||||
|
||||
This implies a maximum transaction size that is this MaxBytes, less the expected size of
|
||||
the header, the validator set, and any included evidence in the block.
|
||||
|
||||
Must have `0 < MaxBytes < 100 MB`.
|
||||
|
||||
### BlockParams.MaxGas
|
||||
|
||||
The maximum of the sum of `GasWanted` that will be allowed in a proposed block.
|
||||
This is *not* enforced by Tendermint consensus.
|
||||
It is left to the app to enforce (ie. if txs are included past the
|
||||
limit, they should return non-zero codes). It is used by Tendermint to limit the
|
||||
txs included in a proposed block.
|
||||
|
||||
Must have `MaxGas >= -1`.
|
||||
If `MaxGas == -1`, no limit is enforced.
|
||||
|
||||
### EvidenceParams.MaxAgeDuration
|
||||
|
||||
This is the maximum age of evidence in time units.
|
||||
This is enforced by Tendermint consensus.
|
||||
|
||||
If a block includes evidence older than this (AND the evidence was created more
|
||||
than `MaxAgeNumBlocks` ago), the block will be rejected (validators won't vote
|
||||
for it).
|
||||
|
||||
Must have `MaxAgeDuration > 0`.
|
||||
|
||||
### EvidenceParams.MaxAgeNumBlocks
|
||||
|
||||
This is the maximum age of evidence in blocks.
|
||||
This is enforced by Tendermint consensus.
|
||||
|
||||
If a block includes evidence older than this (AND the evidence was created more
|
||||
than `MaxAgeDuration` ago), the block will be rejected (validators won't vote
|
||||
for it).
|
||||
|
||||
Must have `MaxAgeNumBlocks > 0`.
|
||||
|
||||
### EvidenceParams.MaxNum
|
||||
|
||||
This is the maximum number of evidence that can be committed to a single block.
|
||||
|
||||
The product of this and the `MaxEvidenceBytes` must not exceed the size of
|
||||
a block minus it's overhead ( ~ `MaxBytes`).
|
||||
|
||||
Must have `MaxNum > 0`.
|
||||
|
||||
### Updates
|
||||
|
||||
The application may set the ConsensusParams during InitChain, and update them during
|
||||
EndBlock. If the ConsensusParams is empty, it will be ignored. Each field
|
||||
that is not empty will be applied in full. For instance, if updating the
|
||||
Block.MaxBytes, applications must also set the other Block fields (like
|
||||
Block.MaxGas), even if they are unchanged, as they will otherwise cause the
|
||||
value to be updated to 0.
|
||||
|
||||
#### InitChain
|
||||
|
||||
ResponseInitChain includes a ConsensusParams.
|
||||
If ConsensusParams is nil, Tendermint will use the params loaded in the genesis
|
||||
file. If ConsensusParams is not nil, Tendermint will use it.
|
||||
This way the application can determine the initial consensus params for the
|
||||
blockchain.
|
||||
|
||||
#### EndBlock
|
||||
|
||||
ResponseEndBlock includes a ConsensusParams.
|
||||
If ConsensusParams nil, Tendermint will do nothing.
|
||||
If ConsensusParam is not nil, Tendermint will use it.
|
||||
This way the application can update the consensus params over time.
|
||||
|
||||
Note the updates returned in block `H` will take effect right away for block
|
||||
`H+1`.
|
||||
|
||||
## Query
|
||||
|
||||
Query is a generic method with lots of flexibility to enable diverse sets
|
||||
of queries on application state. Tendermint makes use of Query to filter new peers
|
||||
based on ID and IP, and exposes Query to the user over RPC.
|
||||
|
||||
Note that calls to Query are not replicated across nodes, but rather query the
|
||||
local node's state - hence they may return stale reads. For reads that require
|
||||
consensus, use a transaction.
|
||||
|
||||
The most important use of Query is to return Merkle proofs of the application state at some height
|
||||
that can be used for efficient application-specific light-clients.
|
||||
|
||||
Note Tendermint has technically no requirements from the Query
|
||||
message for normal operation - that is, the ABCI app developer need not implement
|
||||
Query functionality if they do not wish too.
|
||||
|
||||
### Query Proofs
|
||||
|
||||
The Tendermint block header includes a number of hashes, each providing an
|
||||
anchor for some type of proof about the blockchain. The `ValidatorsHash` enables
|
||||
quick verification of the validator set, the `DataHash` gives quick
|
||||
verification of the transactions included in the block, etc.
|
||||
|
||||
The `AppHash` is unique in that it is application specific, and allows for
|
||||
application-specific Merkle proofs about the state of the application.
|
||||
While some applications keep all relevant state in the transactions themselves
|
||||
(like Bitcoin and its UTXOs), others maintain a separated state that is
|
||||
computed deterministically *from* transactions, but is not contained directly in
|
||||
the transactions themselves (like Ethereum contracts and accounts).
|
||||
For such applications, the `AppHash` provides a much more efficient way to verify light-client proofs.
|
||||
|
||||
ABCI applications can take advantage of more efficient light-client proofs for
|
||||
their state as follows:
|
||||
|
||||
- return the Merkle root of the deterministic application state in
|
||||
`ResponseCommit.Data`. This Merkle root will be included as the `AppHash` in the next block.
|
||||
- return efficient Merkle proofs about that application state in `ResponseQuery.Proof`
|
||||
that can be verified using the `AppHash` of the corresponding block.
|
||||
|
||||
For instance, this allows an application's light-client to verify proofs of
|
||||
absence in the application state, something which is much less efficient to do using the block hash.
|
||||
|
||||
Some applications (eg. Ethereum, Cosmos-SDK) have multiple "levels" of Merkle trees,
|
||||
where the leaves of one tree are the root hashes of others. To support this, and
|
||||
the general variability in Merkle proofs, the `ResponseQuery.Proof` has some minimal structure:
|
||||
|
||||
```protobuf
|
||||
message ProofOps {
|
||||
repeated ProofOp ops
|
||||
}
|
||||
|
||||
message ProofOp {
|
||||
string type = 1;
|
||||
bytes key = 2;
|
||||
bytes data = 3;
|
||||
}
|
||||
```
|
||||
|
||||
Each `ProofOp` contains a proof for a single key in a single Merkle tree, of the specified `type`.
|
||||
This allows ABCI to support many different kinds of Merkle trees, encoding
|
||||
formats, and proofs (eg. of presence and absence) just by varying the `type`.
|
||||
The `data` contains the actual encoded proof, encoded according to the `type`.
|
||||
When verifying the full proof, the root hash for one ProofOp is the value being
|
||||
verified for the next ProofOp in the list. The root hash of the final ProofOp in
|
||||
the list should match the `AppHash` being verified against.
|
||||
|
||||
### Peer Filtering
|
||||
|
||||
When Tendermint connects to a peer, it sends two queries to the ABCI application
|
||||
using the following paths, with no additional data:
|
||||
|
||||
- `/p2p/filter/addr/<IP:PORT>`, where `<IP:PORT>` denote the IP address and
|
||||
the port of the connection
|
||||
- `p2p/filter/id/<ID>`, where `<ID>` is the peer node ID (ie. the
|
||||
pubkey.Address() for the peer's PubKey)
|
||||
|
||||
If either of these queries return a non-zero ABCI code, Tendermint will refuse
|
||||
to connect to the peer.
|
||||
|
||||
### Paths
|
||||
|
||||
Queries are directed at paths, and may optionally include additional data.
|
||||
|
||||
The expectation is for there to be some number of high level paths
|
||||
differentiating concerns, like `/p2p`, `/store`, and `/app`. Currently,
|
||||
Tendermint only uses `/p2p`, for filtering peers. For more advanced use, see the
|
||||
implementation of
|
||||
[Query in the Cosmos-SDK](https://github.com/cosmos/cosmos-sdk/blob/v0.23.1/baseapp/baseapp.go#L333).
|
||||
|
||||
## Crash Recovery
|
||||
|
||||
On startup, Tendermint calls the `Info` method on the Info Connection to get the latest
|
||||
committed state of the app. The app MUST return information consistent with the
|
||||
last block it succesfully completed Commit for.
|
||||
|
||||
If the app succesfully committed block H, then `last_block_height = H` and `last_block_app_hash = <hash returned by Commit for block H>`. If the app
|
||||
failed during the Commit of block H, then `last_block_height = H-1` and
|
||||
`last_block_app_hash = <hash returned by Commit for block H-1, which is the hash in the header of block H>`.
|
||||
|
||||
We now distinguish three heights, and describe how Tendermint syncs itself with
|
||||
the app.
|
||||
|
||||
```md
|
||||
storeBlockHeight = height of the last block Tendermint saw a commit for
|
||||
stateBlockHeight = height of the last block for which Tendermint completed all
|
||||
block processing and saved all ABCI results to disk
|
||||
appBlockHeight = height of the last block for which ABCI app succesfully
|
||||
completed Commit
|
||||
|
||||
```
|
||||
|
||||
Note we always have `storeBlockHeight >= stateBlockHeight` and `storeBlockHeight >= appBlockHeight`
|
||||
Note also Tendermint never calls Commit on an ABCI app twice for the same height.
|
||||
|
||||
The procedure is as follows.
|
||||
|
||||
First, some simple start conditions:
|
||||
|
||||
If `appBlockHeight == 0`, then call InitChain.
|
||||
|
||||
If `storeBlockHeight == 0`, we're done.
|
||||
|
||||
Now, some sanity checks:
|
||||
|
||||
If `storeBlockHeight < appBlockHeight`, error
|
||||
If `storeBlockHeight < stateBlockHeight`, panic
|
||||
If `storeBlockHeight > stateBlockHeight+1`, panic
|
||||
|
||||
Now, the meat:
|
||||
|
||||
If `storeBlockHeight == stateBlockHeight && appBlockHeight < storeBlockHeight`,
|
||||
replay all blocks in full from `appBlockHeight` to `storeBlockHeight`.
|
||||
This happens if we completed processing the block, but the app forgot its height.
|
||||
|
||||
If `storeBlockHeight == stateBlockHeight && appBlockHeight == storeBlockHeight`, we're done.
|
||||
This happens if we crashed at an opportune spot.
|
||||
|
||||
If `storeBlockHeight == stateBlockHeight+1`
|
||||
This happens if we started processing the block but didn't finish.
|
||||
|
||||
If `appBlockHeight < stateBlockHeight`
|
||||
replay all blocks in full from `appBlockHeight` to `storeBlockHeight-1`,
|
||||
and replay the block at `storeBlockHeight` using the WAL.
|
||||
This happens if the app forgot the last block it committed.
|
||||
|
||||
If `appBlockHeight == stateBlockHeight`,
|
||||
replay the last block (storeBlockHeight) in full.
|
||||
This happens if we crashed before the app finished Commit
|
||||
|
||||
If `appBlockHeight == storeBlockHeight`
|
||||
update the state using the saved ABCI responses but dont run the block against the real app.
|
||||
This happens if we crashed after the app finished Commit but before Tendermint saved the state.
|
||||
|
||||
## State Sync
|
||||
|
||||
A new node joining the network can simply join consensus at the genesis height and replay all
|
||||
historical blocks until it is caught up. However, for large chains this can take a significant
|
||||
amount of time, often on the order of days or weeks.
|
||||
|
||||
State sync is an alternative mechanism for bootstrapping a new node, where it fetches a snapshot
|
||||
of the state machine at a given height and restores it. Depending on the application, this can
|
||||
be several orders of magnitude faster than replaying blocks.
|
||||
|
||||
Note that state sync does not currently backfill historical blocks, so the node will have a
|
||||
truncated block history - users are advised to consider the broader network implications of this in
|
||||
terms of block availability and auditability. This functionality may be added in the future.
|
||||
|
||||
For details on the specific ABCI calls and types, see the [methods and types section](abci.md).
|
||||
|
||||
### Taking Snapshots
|
||||
|
||||
Applications that want to support state syncing must take state snapshots at regular intervals. How
|
||||
this is accomplished is entirely up to the application. A snapshot consists of some metadata and
|
||||
a set of binary chunks in an arbitrary format:
|
||||
|
||||
- `Height (uint64)`: The height at which the snapshot is taken. It must be taken after the given
|
||||
height has been committed, and must not contain data from any later heights.
|
||||
|
||||
- `Format (uint32)`: An arbitrary snapshot format identifier. This can be used to version snapshot
|
||||
formats, e.g. to switch from Protobuf to MessagePack for serialization. The application can use
|
||||
this when restoring to choose whether to accept or reject a snapshot.
|
||||
|
||||
- `Chunks (uint32)`: The number of chunks in the snapshot. Each chunk contains arbitrary binary
|
||||
data, and should be less than 16 MB; 10 MB is a good starting point.
|
||||
|
||||
- `Hash ([]byte)`: An arbitrary hash of the snapshot. This is used to check whether a snapshot is
|
||||
the same across nodes when downloading chunks.
|
||||
|
||||
- `Metadata ([]byte)`: Arbitrary snapshot metadata, e.g. chunk hashes for verification or any other
|
||||
necessary info.
|
||||
|
||||
For a snapshot to be considered the same across nodes, all of these fields must be identical. When
|
||||
sent across the network, snapshot metadata messages are limited to 4 MB.
|
||||
|
||||
When a new node is running state sync and discovering snapshots, Tendermint will query an existing
|
||||
application via the ABCI `ListSnapshots` method to discover available snapshots, and load binary
|
||||
snapshot chunks via `LoadSnapshotChunk`. The application is free to choose how to implement this
|
||||
and which formats to use, but must provide the following guarantees:
|
||||
|
||||
- **Consistent:** A snapshot must be taken at a single isolated height, unaffected by
|
||||
concurrent writes. This can be accomplished by using a data store that supports ACID
|
||||
transactions with snapshot isolation.
|
||||
|
||||
- **Asynchronous:** Taking a snapshot can be time-consuming, so it must not halt chain progress,
|
||||
for example by running in a separate thread.
|
||||
|
||||
- **Deterministic:** A snapshot taken at the same height in the same format must be identical
|
||||
(at the byte level) across nodes, including all metadata. This ensures good availability of
|
||||
chunks, and that they fit together across nodes.
|
||||
|
||||
A very basic approach might be to use a datastore with MVCC transactions (such as RocksDB),
|
||||
start a transaction immediately after block commit, and spawn a new thread which is passed the
|
||||
transaction handle. This thread can then export all data items, serialize them using e.g.
|
||||
Protobuf, hash the byte stream, split it into chunks, and store the chunks in the file system
|
||||
along with some metadata - all while the blockchain is applying new blocks in parallel.
|
||||
|
||||
A more advanced approach might include incremental verification of individual chunks against the
|
||||
chain app hash, parallel or batched exports, compression, and so on.
|
||||
|
||||
Old snapshots should be removed after some time - generally only the last two snapshots are needed
|
||||
(to prevent the last one from being removed while a node is restoring it).
|
||||
|
||||
### Bootstrapping a Node
|
||||
|
||||
An empty node can be state synced by setting the configuration option `statesync.enabled =
|
||||
true`. The node also needs the chain genesis file for basic chain info, and configuration for
|
||||
light client verification of the restored snapshot: a set of Tendermint RPC servers, and a
|
||||
trusted header hash and corresponding height from a trusted source, via the `statesync`
|
||||
configuration section.
|
||||
|
||||
Once started, the node will connect to the P2P network and begin discovering snapshots. These
|
||||
will be offered to the local application via the `OfferSnapshot` ABCI method. Once a snapshot
|
||||
is accepted Tendermint will fetch and apply the snapshot chunks. After all chunks have been
|
||||
successfully applied, Tendermint verifies the app's `AppHash` against the chain using the light
|
||||
client, then switches the node to normal consensus operation.
|
||||
|
||||
#### Snapshot Discovery
|
||||
|
||||
When the empty node join the P2P network, it asks all peers to report snapshots via the
|
||||
`ListSnapshots` ABCI call (limited to 10 per node). After some time, the node picks the most
|
||||
suitable snapshot (generally prioritized by height, format, and number of peers), and offers it
|
||||
to the application via `OfferSnapshot`. The application can choose a number of responses,
|
||||
including accepting or rejecting it, rejecting the offered format, rejecting the peer who sent
|
||||
it, and so on. Tendermint will keep discovering and offering snapshots until one is accepted or
|
||||
the application aborts.
|
||||
|
||||
#### Snapshot Restoration
|
||||
|
||||
Once a snapshot has been accepted via `OfferSnapshot`, Tendermint begins downloading chunks from
|
||||
any peers that have the same snapshot (i.e. that have identical metadata fields). Chunks are
|
||||
spooled in a temporary directory, and then given to the application in sequential order via
|
||||
`ApplySnapshotChunk` until all chunks have been accepted.
|
||||
|
||||
The method for restoring snapshot chunks is entirely up to the application.
|
||||
|
||||
During restoration, the application can respond to `ApplySnapshotChunk` with instructions for how
|
||||
to continue. This will typically be to accept the chunk and await the next one, but it can also
|
||||
ask for chunks to be refetched (either the current one or any number of previous ones), P2P peers
|
||||
to be banned, snapshots to be rejected or retried, and a number of other responses - see the ABCI
|
||||
reference for details.
|
||||
|
||||
If Tendermint fails to fetch a chunk after some time, it will reject the snapshot and try a
|
||||
different one via `OfferSnapshot` - the application can choose whether it wants to support
|
||||
restarting restoration, or simply abort with an error.
|
||||
|
||||
#### Snapshot Verification
|
||||
|
||||
Once all chunks have been accepted, Tendermint issues an `Info` ABCI call to retrieve the
|
||||
`LastBlockAppHash`. This is compared with the trusted app hash from the chain, retrieved and
|
||||
verified using the light client. Tendermint also checks that `LastBlockHeight` corresponds to the
|
||||
height of the snapshot.
|
||||
|
||||
This verification ensures that an application is valid before joining the network. However, the
|
||||
snapshot restoration may take a long time to complete, so applications may want to employ additional
|
||||
verification during the restore to detect failures early. This might e.g. include incremental
|
||||
verification of each chunk against the app hash (using bundled Merkle proofs), checksums to
|
||||
protect against data corruption by the disk or network, and so on. However, it is important to
|
||||
note that the only trusted information available is the app hash, and all other snapshot metadata
|
||||
can be spoofed by adversaries.
|
||||
|
||||
Apps may also want to consider state sync denial-of-service vectors, where adversaries provide
|
||||
invalid or harmful snapshots to prevent nodes from joining the network. The application can
|
||||
counteract this by asking Tendermint to ban peers. As a last resort, node operators can use
|
||||
P2P configuration options to whitelist a set of trusted peers that can provide valid snapshots.
|
||||
|
||||
#### Transition to Consensus
|
||||
|
||||
Once the snapshots have all been restored, Tendermint gathers additional information necessary for
|
||||
bootstrapping the node (e.g. chain ID, consensus parameters, validator sets, and block headers)
|
||||
from the genesis file and light client RPC servers. It also fetches and records the `AppVersion`
|
||||
from the ABCI application.
|
||||
|
||||
Once the state machine has been restored and Tendermint has gathered this additional
|
||||
information, it transitions to block sync (if enabled) to fetch any remaining blocks up the chain
|
||||
head, and then transitions to regular consensus operation. At this point the node operates like
|
||||
any other node, apart from having a truncated block history at the height of the restored snapshot.
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
order: 3
|
||||
title: Client and Server
|
||||
---
|
||||
|
||||
# Client and Server
|
||||
|
||||
This section is for those looking to implement their own ABCI Server, perhaps in
|
||||
a new programming language.
|
||||
|
||||
You are expected to have read [ABCI Methods and Types](./abci.md) and [ABCI
|
||||
Applications](./apps.md).
|
||||
|
||||
## Message Protocol
|
||||
|
||||
The message protocol consists of pairs of requests and responses defined in the
|
||||
[protobuf file](https://github.com/tendermint/tendermint/blob/master/proto/tendermint/abci/types.proto).
|
||||
|
||||
Some messages have no fields, while others may include byte-arrays, strings, integers,
|
||||
or custom protobuf types.
|
||||
|
||||
For more details on protobuf, see the [documentation](https://developers.google.com/protocol-buffers/docs/overview).
|
||||
|
||||
For each request, a server should respond with the corresponding
|
||||
response, where the order of requests is preserved in the order of
|
||||
responses.
|
||||
|
||||
## Server Implementations
|
||||
|
||||
To use ABCI in your programming language of choice, there must be a ABCI
|
||||
server in that language. Tendermint supports three implementations of the ABCI, written in Go:
|
||||
|
||||
- In-process ([Golang](https://github.com/tendermint/tendermint/tree/master/abci), [Rust](https://github.com/tendermint/rust-abci))
|
||||
- ABCI-socket
|
||||
- GRPC
|
||||
|
||||
The latter two can be tested using the `abci-cli` by setting the `--abci` flag
|
||||
appropriately (ie. to `socket` or `grpc`).
|
||||
|
||||
See examples, in various stages of maintenance, in
|
||||
[Go](https://github.com/tendermint/tendermint/tree/master/abci/server),
|
||||
[JavaScript](https://github.com/tendermint/js-abci),
|
||||
[C++](https://github.com/mdyring/cpp-tmsp), and
|
||||
[Java](https://github.com/jTendermint/jabci).
|
||||
|
||||
### In Process
|
||||
|
||||
The simplest implementation uses function calls within Golang.
|
||||
This means ABCI applications written in Golang can be compiled with Tendermint Core and run as a single binary.
|
||||
|
||||
### GRPC
|
||||
|
||||
If GRPC is available in your language, this is the easiest approach,
|
||||
though it will have significant performance overhead.
|
||||
|
||||
To get started with GRPC, copy in the [protobuf
|
||||
file](https://github.com/tendermint/tendermint/blob/master/proto/tendermint/abci/types.proto)
|
||||
and compile it using the GRPC plugin for your language. For instance,
|
||||
for golang, the command is `protoc --go_out=plugins=grpc:. types.proto`.
|
||||
See the [grpc documentation for more details](http://www.grpc.io/docs/).
|
||||
`protoc` will autogenerate all the necessary code for ABCI client and
|
||||
server in your language, including whatever interface your application
|
||||
must satisfy to be used by the ABCI server for handling requests.
|
||||
|
||||
Note the length-prefixing used in the socket implementation (TSP) does not apply for GRPC.
|
||||
|
||||
### TSP
|
||||
|
||||
Tendermint Socket Protocol is an asynchronous, raw socket server which provides ordered message passing over unix or tcp.
|
||||
Messages are serialized using Protobuf3 and length-prefixed with a [signed Varint](https://developers.google.com/protocol-buffers/docs/encoding?csw=1#signed-integers)
|
||||
|
||||
If GRPC is not available in your language, or you require higher
|
||||
performance, or otherwise enjoy programming, you may implement your own
|
||||
ABCI server using the Tendermint Socket Protocol. The first step is still to auto-generate the relevant data
|
||||
types and codec in your language using `protoc`. In addition to being proto3 encoded, messages coming over
|
||||
the socket are length-prefixed to facilitate use as a streaming protocol. proto3 doesn't have an
|
||||
official length-prefix standard, so we use our own. The first byte in
|
||||
the prefix represents the length of the Big Endian encoded length. The
|
||||
remaining bytes in the prefix are the Big Endian encoded length.
|
||||
|
||||
For example, if the proto3 encoded ABCI message is 0xDEADBEEF (4
|
||||
bytes), the length-prefixed message is 0x0104DEADBEEF. If the proto3
|
||||
encoded ABCI message is 65535 bytes long, the length-prefixed message
|
||||
would be like 0x02FFFF....
|
||||
|
||||
The benefit of using this `varint` encoding over the old version (where integers were encoded as `<len of len><big endian len>` is that
|
||||
it is the standard way to encode integers in Protobuf. It is also generally shorter.
|
||||
|
||||
As noted above, this prefixing does not apply for GRPC.
|
||||
|
||||
An ABCI server must also be able to support multiple connections, as
|
||||
Tendermint uses four connections.
|
||||
|
||||
### Async vs Sync
|
||||
|
||||
The main ABCI server (ie. non-GRPC) provides ordered asynchronous messages.
|
||||
This is useful for DeliverTx and CheckTx, since it allows Tendermint to forward
|
||||
transactions to the app before it's finished processing previous ones.
|
||||
|
||||
Thus, DeliverTx and CheckTx messages are sent asynchronously, while all other
|
||||
messages are sent synchronously.
|
||||
|
||||
## Client
|
||||
|
||||
There are currently two use-cases for an ABCI client. One is a testing
|
||||
tool, as in the `abci-cli`, which allows ABCI requests to be sent via
|
||||
command line. The other is a consensus engine, such as Tendermint Core,
|
||||
which makes requests to the application every time a new transaction is
|
||||
received or a block is committed.
|
||||
|
||||
It is unlikely that you will need to implement a client. For details of
|
||||
our client, see
|
||||
[here](https://github.com/tendermint/tendermint/tree/master/abci/client).
|
||||
@@ -0,0 +1,3 @@
|
||||
# Blockchain
|
||||
|
||||
Deprecated see [core/data_structures.md](../core/data_structures.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
# Encoding
|
||||
|
||||
Deprecated see [core/data_structures.md](../core/encoding.md)
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
order: 1
|
||||
parent:
|
||||
title: Blockchain
|
||||
order: false
|
||||
---
|
||||
|
||||
# Blockchain
|
||||
|
||||
This section describes the core types and functionality of the Tendermint protocol implementation.
|
||||
|
||||
[Core Data Structures](../core/data_structures.md)
|
||||
[Encoding](../core/encoding.md)
|
||||
[State](../core/state.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
# State
|
||||
|
||||
Deprecated see [core/state.md](../core/state.md)
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
order: 2
|
||||
---
|
||||
# BFT Time
|
||||
|
||||
Tendermint provides a deterministic, Byzantine fault-tolerant, source of time.
|
||||
Time in Tendermint is defined with the Time field of the block header.
|
||||
|
||||
It satisfies the following properties:
|
||||
|
||||
- Time Monotonicity: Time is monotonically increasing, i.e., given
|
||||
a header H1 for height h1 and a header H2 for height `h2 = h1 + 1`, `H1.Time < H2.Time`.
|
||||
- Time Validity: Given a set of Commit votes that forms the `block.LastCommit` field, a range of
|
||||
valid values for the Time field of the block header is defined only by
|
||||
Precommit messages (from the LastCommit field) sent by correct processes, i.e.,
|
||||
a faulty process cannot arbitrarily increase the Time value.
|
||||
|
||||
In the context of Tendermint, time is of type int64 and denotes UNIX time in milliseconds, i.e.,
|
||||
corresponds to the number of milliseconds since January 1, 1970. Before defining rules that need to be enforced by the
|
||||
Tendermint consensus protocol, so the properties above holds, we introduce the following definition:
|
||||
|
||||
- median of a Commit is equal to the median of `Vote.Time` fields of the `Vote` messages,
|
||||
where the value of `Vote.Time` is counted number of times proportional to the process voting power. As in Tendermint
|
||||
the voting power is not uniform (one process one vote), a vote message is actually an aggregator of the same votes whose
|
||||
number is equal to the voting power of the process that has casted the corresponding votes message.
|
||||
|
||||
Let's consider the following example:
|
||||
|
||||
- we have four processes p1, p2, p3 and p4, with the following voting power distribution (p1, 23), (p2, 27), (p3, 10)
|
||||
and (p4, 10). The total voting power is 70 (`N = 3f+1`, where `N` is the total voting power, and `f` is the maximum voting
|
||||
power of the faulty processes), so we assume that the faulty processes have at most 23 of voting power.
|
||||
Furthermore, we have the following vote messages in some LastCommit field (we ignore all fields except Time field):
|
||||
- (p1, 100), (p2, 98), (p3, 1000), (p4, 500). We assume that p3 and p4 are faulty processes. Let's assume that the
|
||||
`block.LastCommit` message contains votes of processes p2, p3 and p4. Median is then chosen the following way:
|
||||
the value 98 is counted 27 times, the value 1000 is counted 10 times and the value 500 is counted also 10 times.
|
||||
So the median value will be the value 98. No matter what set of messages with at least `2f+1` voting power we
|
||||
choose, the median value will always be between the values sent by correct processes.
|
||||
|
||||
We ensure Time Monotonicity and Time Validity properties by the following rules:
|
||||
|
||||
- let rs denotes `RoundState` (consensus internal state) of some process. Then
|
||||
`rs.ProposalBlock.Header.Time == median(rs.LastCommit) &&
|
||||
rs.Proposal.Timestamp == rs.ProposalBlock.Header.Time`.
|
||||
|
||||
- Furthermore, when creating the `vote` message, the following rules for determining `vote.Time` field should hold:
|
||||
|
||||
- if `rs.LockedBlock` is defined then
|
||||
`vote.Time = max(rs.LockedBlock.Timestamp + time.Millisecond, time.Now())`, where `time.Now()`
|
||||
denotes local Unix time in milliseconds
|
||||
|
||||
- else if `rs.Proposal` is defined then
|
||||
`vote.Time = max(rs.Proposal.Timestamp + time.Millisecond,, time.Now())`,
|
||||
|
||||
- otherwise, `vote.Time = time.Now())`. In this case vote is for `nil` so it is not taken into account for
|
||||
the timestamp of the next block.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
# Tendermint-spec
|
||||
|
||||
The repository contains the specification (and the proofs) of the Tendermint
|
||||
consensus protocol.
|
||||
|
||||
## How to install Latex on Mac OS
|
||||
|
||||
MacTex is Latex distribution for Mac OS. You can download it [here](http://www.tug.org/mactex/mactex-download.html).
|
||||
|
||||
Popular IDE for Latex-based projects is TexStudio. It can be downloaded
|
||||
[here](https://www.texstudio.org/).
|
||||
|
||||
## How to build project
|
||||
|
||||
In order to compile the latex files (and write bibliography), execute
|
||||
|
||||
`$ pdflatex paper` <br/>
|
||||
`$ bibtex paper` <br/>
|
||||
`$ pdflatex paper` <br/>
|
||||
`$ pdflatex paper` <br/>
|
||||
|
||||
The generated file is paper.pdf. You can open it with
|
||||
|
||||
`$ open paper.pdf`
|
||||
@@ -0,0 +1,195 @@
|
||||
% ALGORITHMICPLUS STYLE
|
||||
% for LaTeX version 2e
|
||||
% Original ``algorithmic.sty'' by -- 1994 Peter Williams <pwil3058@bigpond.net.au>
|
||||
% Bug fix (13 July 2004) by Arnaud Giersch <giersch@icps.u-strasbg.fr>
|
||||
% Includes ideas from 'algorithmicext' by Martin Biely
|
||||
% <biely@ecs.tuwien.ac.at> and 'distribalgo' by Xavier Defago
|
||||
% Modifications: Martin Hutle <martin.hutle@epfl.ch>
|
||||
%
|
||||
% This style file is free software; you can redistribute it and/or
|
||||
% modify it under the terms of the GNU Lesser General Public
|
||||
% License as published by the Free Software Foundation; either
|
||||
% version 2 of the License, or (at your option) any later version.
|
||||
%
|
||||
% This style file is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
% Lesser General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU Lesser General Public
|
||||
% License along with this style file; if not, write to the
|
||||
% Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
% Boston, MA 02111-1307, USA.
|
||||
%
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{algorithmicplus}
|
||||
\typeout{Document Style `algorithmicplus' - environment, replaces `algorithmic'}
|
||||
%
|
||||
\RequirePackage{ifthen}
|
||||
\RequirePackage{calc}
|
||||
\newboolean{ALC@noend}
|
||||
\setboolean{ALC@noend}{false}
|
||||
\newcounter{ALC@line}
|
||||
\newcounter{ALC@rem}
|
||||
\newcounter{ALC@depth}
|
||||
\newcounter{ALCPLUS@lastline}
|
||||
\newlength{\ALC@tlm}
|
||||
%
|
||||
\DeclareOption{noend}{\setboolean{ALC@noend}{true}}
|
||||
%
|
||||
\ProcessOptions
|
||||
%
|
||||
% ALGORITHMIC
|
||||
\newcommand{\algorithmiclnosize}{\small}
|
||||
\newcommand{\algorithmiclnofont}{\tt}
|
||||
\newcommand{\algorithmiclnodelimiter}{:}
|
||||
%
|
||||
\newcommand{\algorithmicrequire}{\textbf{Require:}}
|
||||
\newcommand{\algorithmicensure}{\textbf{Ensure:}}
|
||||
\newcommand{\algorithmiccomment}[1]{\{#1\}}
|
||||
\newcommand{\algorithmicend}{\textbf{end}}
|
||||
\newcommand{\algorithmicif}{\textbf{if}}
|
||||
\newcommand{\algorithmicthen}{\textbf{then}}
|
||||
\newcommand{\algorithmicelse}{\textbf{else}}
|
||||
\newcommand{\algorithmicelsif}{\algorithmicelse\ \algorithmicif}
|
||||
\newcommand{\algorithmicendif}{\algorithmicend\ \algorithmicif}
|
||||
\newcommand{\algorithmicfor}{\textbf{for}}
|
||||
\newcommand{\algorithmicforall}{\textbf{for all}}
|
||||
\newcommand{\algorithmicdo}{\textbf{do}}
|
||||
\newcommand{\algorithmicendfor}{\algorithmicend\ \algorithmicfor}
|
||||
\newcommand{\algorithmicwhile}{\textbf{while}}
|
||||
\newcommand{\algorithmicendwhile}{\algorithmicend\ \algorithmicwhile}
|
||||
\newcommand{\algorithmicloop}{\textbf{loop}}
|
||||
\newcommand{\algorithmicendloop}{\algorithmicend\ \algorithmicloop}
|
||||
\newcommand{\algorithmicrepeat}{\textbf{repeat}}
|
||||
\newcommand{\algorithmicuntil}{\textbf{until}}
|
||||
\def\ALC@item[#1]{%
|
||||
\if@noparitem \@donoparitem
|
||||
\else \if@inlabel \indent \par \fi
|
||||
\ifhmode \unskip\unskip \par \fi
|
||||
\if@newlist \if@nobreak \@nbitem \else
|
||||
\addpenalty\@beginparpenalty
|
||||
\addvspace\@topsep \addvspace{-\parskip}\fi
|
||||
\else \addpenalty\@itempenalty \addvspace\itemsep
|
||||
\fi
|
||||
\global\@inlabeltrue
|
||||
\fi
|
||||
\everypar{\global\@minipagefalse\global\@newlistfalse
|
||||
\if@inlabel\global\@inlabelfalse \hskip -\parindent \box\@labels
|
||||
\penalty\z@ \fi
|
||||
\everypar{}}\global\@nobreakfalse
|
||||
\if@noitemarg \@noitemargfalse \if@nmbrlist \refstepcounter{\@listctr}\fi \fi
|
||||
\sbox\@tempboxa{\makelabel{#1}}%
|
||||
\global\setbox\@labels
|
||||
\hbox{\unhbox\@labels \hskip \itemindent
|
||||
\hskip -\labelwidth \hskip -\ALC@tlm
|
||||
\ifdim \wd\@tempboxa >\labelwidth
|
||||
\box\@tempboxa
|
||||
\else \hbox to\labelwidth {\unhbox\@tempboxa}\fi
|
||||
\hskip \ALC@tlm}\ignorespaces}
|
||||
%
|
||||
\newenvironment{algorithmic}[1][0]{
|
||||
\setcounter{ALC@depth}{\@listdepth}%
|
||||
\let\@listdepth\c@ALC@depth%
|
||||
\let\@item\ALC@item
|
||||
\newcommand{\ALC@lno}{%
|
||||
\ifthenelse{\equal{\arabic{ALC@rem}}{0}}
|
||||
{{\algorithmiclnosize\algorithmiclnofont \arabic{ALC@line}\algorithmiclnodelimiter}}{}%
|
||||
}
|
||||
\let\@listii\@listi
|
||||
\let\@listiii\@listi
|
||||
\let\@listiv\@listi
|
||||
\let\@listv\@listi
|
||||
\let\@listvi\@listi
|
||||
\let\@listvii\@listi
|
||||
\newenvironment{ALC@g}{
|
||||
\begin{list}{\ALC@lno}{ \itemsep\z@ \itemindent\z@
|
||||
\listparindent\z@ \rightmargin\z@
|
||||
\topsep\z@ \partopsep\z@ \parskip\z@\parsep\z@
|
||||
\leftmargin 1em
|
||||
\addtolength{\ALC@tlm}{\leftmargin}
|
||||
}
|
||||
}
|
||||
{\end{list}}
|
||||
\newcommand{\ALC@it}{\refstepcounter{ALC@line}\addtocounter{ALC@rem}{1}\ifthenelse{\equal{\arabic{ALC@rem}}{#1}}{\setcounter{ALC@rem}{0}}{}\item}
|
||||
\newcommand{\ALC@com}[1]{\ifthenelse{\equal{##1}{default}}%
|
||||
{}{\ \algorithmiccomment{##1}}}
|
||||
\newcommand{\REQUIRE}{\item[\algorithmicrequire]}
|
||||
\newcommand{\ENSURE}{\item[\algorithmicensure]}
|
||||
\newcommand{\STATE}{\ALC@it}
|
||||
\newcommand{\COMMENT}[1]{\algorithmiccomment{##1}}
|
||||
\newenvironment{ALC@if}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@for}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@whl}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@loop}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newenvironment{ALC@rpt}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\renewcommand{\\}{\@centercr}
|
||||
\newcommand{\IF}[2][default]{\ALC@it\algorithmicif\ ##2\ \algorithmicthen%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\ELSE}[1][default]{\end{ALC@if}\ALC@it\algorithmicelse%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\ELSIF}[2][default]%
|
||||
{\end{ALC@if}\ALC@it\algorithmicelsif\ ##2\ \algorithmicthen%
|
||||
\ALC@com{##1}\begin{ALC@if}}
|
||||
\newcommand{\FOR}[2][default]{\ALC@it\algorithmicfor\ ##2\ \algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@for}}
|
||||
\newcommand{\FORALL}[2][default]{\ALC@it\algorithmicforall\ ##2\ %
|
||||
\algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@for}}
|
||||
\newcommand{\WHILE}[2][default]{\ALC@it\algorithmicwhile\ ##2\ %
|
||||
\algorithmicdo%
|
||||
\ALC@com{##1}\begin{ALC@whl}}
|
||||
\newcommand{\LOOP}[1][default]{\ALC@it\algorithmicloop%
|
||||
\ALC@com{##1}\begin{ALC@loop}}
|
||||
\newcommand{\REPEAT}[1][default]{\ALC@it\algorithmicrepeat%
|
||||
\ALC@com{##1}\begin{ALC@rpt}}
|
||||
\newcommand{\UNTIL}[1]{\end{ALC@rpt}\ALC@it\algorithmicuntil\ ##1}
|
||||
\ifthenelse{\boolean{ALC@noend}}{
|
||||
\newcommand{\ENDIF}{\end{ALC@if}}
|
||||
\newcommand{\ENDFOR}{\end{ALC@for}}
|
||||
\newcommand{\ENDWHILE}{\end{ALC@whl}}
|
||||
\newcommand{\ENDLOOP}{\end{ALC@loop}}
|
||||
}{
|
||||
\newcommand{\ENDIF}{\end{ALC@if}\ALC@it\algorithmicendif}
|
||||
\newcommand{\ENDFOR}{\end{ALC@for}\ALC@it\algorithmicendfor}
|
||||
\newcommand{\ENDWHILE}{\end{ALC@whl}\ALC@it\algorithmicendwhile}
|
||||
\newcommand{\ENDLOOP}{\end{ALC@loop}\ALC@it\algorithmicendloop}
|
||||
}
|
||||
\renewcommand{\@toodeep}{}
|
||||
\begin{list}{\ALC@lno}{\setcounter{ALC@line}{0}\setcounter{ALC@rem}{0}%
|
||||
\itemsep\z@ \itemindent\z@ \listparindent\z@%
|
||||
\partopsep\z@ \parskip\z@ \parsep\z@%
|
||||
\labelsep 0.5em \topsep 0.2em%
|
||||
\ifthenelse{\equal{#1}{0}}
|
||||
{\labelwidth 0.5em }
|
||||
{\labelwidth 1.2em }
|
||||
\leftmargin\labelwidth \addtolength{\leftmargin}{\labelsep}
|
||||
\ALC@tlm\labelsep
|
||||
}
|
||||
}
|
||||
{%
|
||||
\setcounter{ALCPLUS@lastline}{\value{ALC@line}}%
|
||||
\end{list}}
|
||||
|
||||
\newcommand{\continuecounting}{\setcounter{ALC@line}{\value{ALCPLUS@lastline}}}
|
||||
\newcommand{\startcounting}[1]{\setcounter{ALC@line}{#1}\addtocounter{ALC@line}{-1}}
|
||||
|
||||
\newcommand{\EMPTY}{\item[]}
|
||||
\newcommand{\SPACE}{\vspace{3mm}}
|
||||
\newcommand{\SHORTSPACE}{\vspace{1mm}}
|
||||
\newcommand{\newlinetag}[3]{\newcommand{#1}[#2]{\item[#3]}}
|
||||
\newcommand{\newconstruct}[5]{%
|
||||
\newenvironment{ALC@\string#1}{\begin{ALC@g}}{\end{ALC@g}}
|
||||
\newcommand{#1}[2][default]{\ALC@it#2\ ##2\ #3%
|
||||
\ALC@com{##1}\begin{ALC@\string#1}}
|
||||
\ifthenelse{\boolean{ALC@noend}}{
|
||||
\newcommand{#4}{\end{ALC@\string#1}}
|
||||
}{
|
||||
\newcommand{#4}{\end{ALC@\string#1}\ALC@it#5}
|
||||
}
|
||||
}
|
||||
|
||||
\newconstruct{\INDENT}{}{}{\ENDINDENT}{}
|
||||
|
||||
\newcommand{\setlinenosize}[1]{\renewcommand{\algorithmiclnosize}{#1}}
|
||||
\newcommand{\setlinenofont}[1]{\renewcommand{\algorithmiclnofont}{#1}}
|
||||
@@ -0,0 +1,16 @@
|
||||
\section{Conclusion} \label{sec:conclusion}
|
||||
|
||||
We have proposed a new Byzantine-fault tolerant consensus algorithm that is the
|
||||
core of the Tendermint BFT SMR platform. The algorithm is designed for the wide
|
||||
area network with high number of mutually distrusted nodes that communicate
|
||||
over gossip based peer-to-peer network. It has only a single mode of execution
|
||||
and the communication pattern is very similar to the "normal" case of the
|
||||
state-of-the art PBFT algorithm. The algorithm ensures termination with a novel
|
||||
mechanism that takes advantage of the gossip based communication between nodes.
|
||||
The proposed algorithm and the proofs are simple and elegant, and we believe
|
||||
that this makes it easier to understand and implement correctly.
|
||||
|
||||
\section*{Acknowledgment}
|
||||
|
||||
We would like to thank Anton Kaliaev, Ismail Khoffi and Dahlia Malkhi for comments on an earlier version of the paper. We also want to thank Marko Vukolic, Ming Chuan Lin, Maria Potop-Butucaru, Sara Tucci, Antonella Del Pozzo and Yackolley Amoussou-Guenou for pointing out the liveness issues
|
||||
in the previous version of the algorithm. Finally, we want to thank the Tendermint team members and all project contributors for making Tendermint such a great platform.
|
||||
@@ -0,0 +1,397 @@
|
||||
|
||||
\section{Tendermint consensus algorithm} \label{sec:tendermint}
|
||||
|
||||
\newcommand\Disseminate{\textbf{Disseminate}}
|
||||
|
||||
\newcommand\Proposal{\mathsf{PROPOSAL}}
|
||||
\newcommand\ProposalPart{\mathsf{PROPOSAL\mbox{-}PART}}
|
||||
\newcommand\PrePrepare{\mathsf{INIT}} \newcommand\Prevote{\mathsf{PREVOTE}}
|
||||
\newcommand\Precommit{\mathsf{PRECOMMIT}}
|
||||
\newcommand\Decision{\mathsf{DECISION}}
|
||||
|
||||
\newcommand\ViewChange{\mathsf{VC}}
|
||||
\newcommand\ViewChangeAck{\mathsf{VC\mbox{-}ACK}}
|
||||
\newcommand\NewPrePrepare{\mathsf{VC\mbox{-}INIT}}
|
||||
\newcommand\coord{\mathsf{proposer}}
|
||||
|
||||
\newcommand\newHeight{newHeight} \newcommand\newRound{newRound}
|
||||
\newcommand\nil{nil} \newcommand\id{id} \newcommand{\propose}{propose}
|
||||
\newcommand\prevote{prevote} \newcommand\prevoteWait{prevoteWait}
|
||||
\newcommand\precommit{precommit} \newcommand\precommitWait{precommitWait}
|
||||
\newcommand\commit{commit}
|
||||
|
||||
\newcommand\timeoutPropose{timeoutPropose}
|
||||
\newcommand\timeoutPrevote{timeoutPrevote}
|
||||
\newcommand\timeoutPrecommit{timeoutPrecommit}
|
||||
\newcommand\proofOfLocking{proof\mbox{-}of\mbox{-}locking}
|
||||
|
||||
\begin{algorithm}[htb!] \def\baselinestretch{1} \scriptsize\raggedright
|
||||
\begin{algorithmic}[1]
|
||||
\SHORTSPACE
|
||||
\INIT{}
|
||||
\STATE $h_p := 0$
|
||||
\COMMENT{current height, or consensus instance we are currently executing}
|
||||
\STATE $round_p := 0$ \COMMENT{current round number}
|
||||
\STATE $step_p \in \set{\propose, \prevote, \precommit}$
|
||||
\STATE $decision_p[] := nil$
|
||||
\STATE $lockedValue_p := nil$
|
||||
\STATE $lockedRound_p := -1$
|
||||
\STATE $validValue_p := nil$
|
||||
\STATE $validRound_p := -1$
|
||||
\ENDINIT
|
||||
\SHORTSPACE
|
||||
\STATE \textbf{upon} start \textbf{do} $StartRound(0)$
|
||||
\SHORTSPACE
|
||||
\FUNCTION{$StartRound(round)$} \label{line:tab:startRound}
|
||||
\STATE $round_p \assign round$
|
||||
\STATE $step_p \assign \propose$
|
||||
\IF{$\coord(h_p, round_p) = p$}
|
||||
\IF{$validValue_p \neq \nil$} \label{line:tab:isThereLockedValue}
|
||||
\STATE $proposal \assign validValue_p$ \ELSE \STATE $proposal \assign
|
||||
getValue()$
|
||||
\label{line:tab:getValidValue}
|
||||
\ENDIF
|
||||
\STATE \Broadcast\ $\li{\Proposal,h_p, round_p, proposal, validRound_p}$
|
||||
\label{line:tab:send-proposal}
|
||||
\ELSE
|
||||
\STATE \textbf{schedule} $OnTimeoutPropose(h_p,
|
||||
round_p)$ to be executed \textbf{after} $\timeoutPropose(round_p)$
|
||||
\ENDIF
|
||||
\ENDFUNCTION
|
||||
|
||||
\SPACE
|
||||
\UPON{$\li{\Proposal,h_p,round_p, v, -1}$ \From\ $\coord(h_p,round_p)$
|
||||
\With\ $step_p = \propose$} \label{line:tab:recvProposal}
|
||||
\IF{$valid(v) \wedge (lockedRound_p = -1 \vee lockedValue_p = v$)}
|
||||
\label{line:tab:accept-proposal-2}
|
||||
\STATE \Broadcast \ $\li{\Prevote,h_p,round_p,id(v)}$
|
||||
\label{line:tab:prevote-proposal}
|
||||
\ELSE
|
||||
\label{line:tab:acceptProposal1}
|
||||
\STATE \Broadcast \ $\li{\Prevote,h_p,round_p,\nil}$
|
||||
\label{line:tab:prevote-nil}
|
||||
\ENDIF
|
||||
\STATE $step_p \assign \prevote$ \label{line:tab:setStateToPrevote1}
|
||||
\ENDUPON
|
||||
|
||||
\SPACE
|
||||
\UPON{$\li{\Proposal,h_p,round_p, v, vr}$ \From\ $\coord(h_p,round_p)$
|
||||
\textbf{AND} $2f+1$ $\li{\Prevote,h_p, vr,id(v)}$ \With\ $step_p = \propose \wedge (vr \ge 0 \wedge vr < round_p)$}
|
||||
\label{line:tab:acceptProposal}
|
||||
\IF{$valid(v) \wedge (lockedRound_p \le vr
|
||||
\vee lockedValue_p = v)$} \label{line:tab:cond-prevote-higher-proposal}
|
||||
\STATE \Broadcast \ $\li{\Prevote,h_p,round_p,id(v)}$
|
||||
\label{line:tab:prevote-higher-proposal}
|
||||
\ELSE
|
||||
\label{line:tab:acceptProposal2}
|
||||
\STATE \Broadcast \ $\li{\Prevote,h_p,round_p,\nil}$
|
||||
\label{line:tab:prevote-nil2}
|
||||
\ENDIF
|
||||
\STATE $step_p \assign \prevote$ \label{line:tab:setStateToPrevote3}
|
||||
\ENDUPON
|
||||
|
||||
\SPACE
|
||||
\UPON{$2f+1$ $\li{\Prevote,h_p, round_p,*}$ \With\ $step_p = \prevote$ for the first time}
|
||||
\label{line:tab:recvAny2/3Prevote}
|
||||
\STATE \textbf{schedule} $OnTimeoutPrevote(h_p, round_p)$ to be executed \textbf{after} $\timeoutPrevote(round_p)$ \label{line:tab:timeoutPrevote}
|
||||
\ENDUPON
|
||||
|
||||
\SPACE
|
||||
\UPON{$\li{\Proposal,h_p,round_p, v, *}$ \From\ $\coord(h_p,round_p)$
|
||||
\textbf{AND} $2f+1$ $\li{\Prevote,h_p, round_p,id(v)}$ \With\ $valid(v) \wedge step_p \ge \prevote$ for the first time}
|
||||
\label{line:tab:recvPrevote}
|
||||
\IF{$step_p = \prevote$}
|
||||
\STATE $lockedValue_p \assign v$ \label{line:tab:setLockedValue}
|
||||
\STATE $lockedRound_p \assign round_p$ \label{line:tab:setLockedRound}
|
||||
\STATE \Broadcast \ $\li{\Precommit,h_p,round_p,id(v))}$
|
||||
\label{line:tab:precommit-v}
|
||||
\STATE $step_p \assign \precommit$ \label{line:tab:setStateToCommit}
|
||||
\ENDIF
|
||||
\STATE $validValue_p \assign v$ \label{line:tab:setValidRound}
|
||||
\STATE $validRound_p \assign round_p$ \label{line:tab:setValidValue}
|
||||
\ENDUPON
|
||||
|
||||
\SHORTSPACE
|
||||
\UPON{$2f+1$ $\li{\Prevote,h_p,round_p, \nil}$
|
||||
\With\ $step_p = \prevote$}
|
||||
\STATE \Broadcast \ $\li{\Precommit,h_p,round_p, \nil}$
|
||||
\label{line:tab:precommit-v-1}
|
||||
\STATE $step_p \assign \precommit$
|
||||
\ENDUPON
|
||||
|
||||
\SPACE
|
||||
\UPON{$2f+1$ $\li{\Precommit,h_p,round_p,*}$ for the first time}
|
||||
\label{line:tab:startTimeoutPrecommit}
|
||||
\STATE \textbf{schedule} $OnTimeoutPrecommit(h_p, round_p)$ to be executed \textbf{after} $\timeoutPrecommit(round_p)$
|
||||
|
||||
\ENDUPON
|
||||
|
||||
\SPACE
|
||||
\UPON{$\li{\Proposal,h_p,r, v, *}$ \From\ $\coord(h_p,r)$ \textbf{AND}
|
||||
$2f+1$ $\li{\Precommit,h_p,r,id(v)}$ \With\ $decision_p[h_p] = \nil$}
|
||||
\label{line:tab:onDecideRule}
|
||||
\IF{$valid(v)$} \label{line:tab:validDecisionValue}
|
||||
\STATE $decision_p[h_p] = v$ \label{line:tab:decide}
|
||||
\STATE$h_p \assign h_p + 1$ \label{line:tab:increaseHeight}
|
||||
\STATE reset $lockedRound_p$, $lockedValue_p$, $validRound_p$ and $validValue_p$ to initial values
|
||||
and empty message log
|
||||
\STATE $StartRound(0)$
|
||||
\ENDIF
|
||||
\ENDUPON
|
||||
|
||||
\SHORTSPACE
|
||||
\UPON{$f+1$ $\li{*,h_p,round, *, *}$ \textbf{with} $round > round_p$}
|
||||
\label{line:tab:skipRounds}
|
||||
\STATE $StartRound(round)$ \label{line:tab:nextRound2}
|
||||
\ENDUPON
|
||||
|
||||
\SHORTSPACE
|
||||
\FUNCTION{$OnTimeoutPropose(height,round)$} \label{line:tab:onTimeoutPropose}
|
||||
\IF{$height = h_p \wedge round = round_p \wedge step_p = \propose$}
|
||||
\STATE \Broadcast \ $\li{\Prevote,h_p,round_p, \nil}$
|
||||
\label{line:tab:prevote-nil-on-timeout}
|
||||
\STATE $step_p \assign \prevote$
|
||||
\ENDIF
|
||||
\ENDFUNCTION
|
||||
|
||||
\SHORTSPACE
|
||||
\FUNCTION{$OnTimeoutPrevote(height,round)$} \label{line:tab:onTimeoutPrevote}
|
||||
\IF{$height = h_p \wedge round = round_p \wedge step_p = \prevote$}
|
||||
\STATE \Broadcast \ $\li{\Precommit,h_p,round_p,\nil}$
|
||||
\label{line:tab:precommit-nil-onTimeout}
|
||||
\STATE $step_p \assign \precommit$
|
||||
\ENDIF
|
||||
\ENDFUNCTION
|
||||
|
||||
\SHORTSPACE
|
||||
\FUNCTION{$OnTimeoutPrecommit(height,round)$} \label{line:tab:onTimeoutPrecommit}
|
||||
\IF{$height = h_p \wedge round = round_p$}
|
||||
\STATE $StartRound(round_p + 1)$ \label{line:tab:nextRound}
|
||||
\ENDIF
|
||||
\ENDFUNCTION
|
||||
\end{algorithmic} \caption{Tendermint consensus algorithm}
|
||||
\label{alg:tendermint}
|
||||
\end{algorithm}
|
||||
|
||||
In this section we present the Tendermint Byzantine fault-tolerant consensus
|
||||
algorithm. The algorithm is specified by the pseudo-code shown in
|
||||
Algorithm~\ref{alg:tendermint}. We present the algorithm as a set of \emph{upon
|
||||
rules} that are executed atomically\footnote{In case several rules are active
|
||||
at the same time, the first rule to be executed is picked randomly. The
|
||||
correctness of the algorithm does not depend on the order in which rules are
|
||||
executed.}. We assume that processes exchange protocol messages using a gossip
|
||||
protocol and that both sent and received messages are stored in a local message
|
||||
log for every process. An upon rule is triggered once the message log contains
|
||||
messages such that the corresponding condition evaluates to $\tt{true}$. The
|
||||
condition that assumes reception of $X$ messages of a particular type and
|
||||
content denotes reception of messages whose senders have aggregate voting power at
|
||||
least equal to $X$. For example, the condition $2f+1$ $\li{\Precommit,h_p,r,id(v)}$,
|
||||
evaluates to true upon reception of $\Precommit$ messages for height $h_p$,
|
||||
a round $r$ and with value equal to $id(v)$ whose senders have aggregate voting
|
||||
power at least equal to $2f+1$. Some of the rules ends with "for the first time" constraint
|
||||
to denote that it is triggered only the first time a corresponding condition evaluates
|
||||
to $\tt{true}$. This is because those rules do not always change the state of algorithm
|
||||
variables so without this constraint, the algorithm could keep
|
||||
executing those rules forever. The variables with index $p$ are process local state
|
||||
variables, while variables without index $p$ are value placeholders. The sign
|
||||
$*$ denotes any value.
|
||||
|
||||
We denote with $n$ the total voting power of processes in the system, and we
|
||||
assume that the total voting power of faulty processes in the system is bounded
|
||||
with a system parameter $f$. The algorithm assumes that $n > 3f$, i.e., it
|
||||
requires that the total voting power of faulty processes is smaller than one
|
||||
third of the total voting power. For simplicity we present the algorithm for
|
||||
the case $n = 3f + 1$.
|
||||
|
||||
The algorithm proceeds in rounds, where each round has a dedicated
|
||||
\emph{proposer}. The mapping of rounds to proposers is known to all processes
|
||||
and is given as a function $\coord(h, round)$, returning the proposer for
|
||||
the round $round$ in the consensus instance $h$. We
|
||||
assume that the proposer selection function is weighted round-robin, where
|
||||
processes are rotated proportional to their voting power\footnote{A validator
|
||||
with more voting power is selected more frequently, proportional to its power.
|
||||
More precisely, during a sequence of rounds of size $n$, every process is
|
||||
proposer in a number of rounds equal to its voting power.}.
|
||||
The internal protocol state transitions are triggered by message reception and
|
||||
by expiration of timeouts. There are three timeouts in Algorithm \ref{alg:tendermint}:
|
||||
$\timeoutPropose$, $\timeoutPrevote$ and $\timeoutPrecommit$.
|
||||
The timeouts prevent the algorithm from blocking and
|
||||
waiting forever for some condition to be true, ensure that processes continuously
|
||||
transition between rounds, and guarantee that eventually (after GST) communication
|
||||
between correct processes is timely and reliable so they can decide.
|
||||
The last role is achieved by increasing the timeouts with every new round $r$,
|
||||
i.e, $timeoutX(r) = initTimeoutX + r*timeoutDelta$;
|
||||
they are reset for every new height (consensus
|
||||
instance).
|
||||
|
||||
Processes exchange the following messages in Tendermint: $\Proposal$,
|
||||
$\Prevote$ and $\Precommit$. The $\Proposal$ message is used by the proposer of
|
||||
the current round to suggest a potential decision value, while $\Prevote$ and
|
||||
$\Precommit$ are votes for a proposed value. According to the classification of
|
||||
consensus algorithms from \cite{RMS10:dsn}, Tendermint, like PBFT
|
||||
\cite{CL02:tcs} and DLS \cite{DLS88:jacm}, belongs to class 3, so it requires
|
||||
two voting steps (three communication exchanges in total) to decide a value.
|
||||
The Tendermint consensus algorithm is designed for the blockchain context where
|
||||
the value to decide is a block of transactions (ie. it is potentially quite
|
||||
large, consisting of many transactions). Therefore, in the Algorithm
|
||||
\ref{alg:tendermint} (similar as in \cite{CL02:tcs}) we are explicit about
|
||||
sending a value (block of transactions) and a small, constant size value id (a
|
||||
unique value identifier, normally a hash of the value, i.e., if $\id(v) =
|
||||
\id(v')$, then $v=v'$). The $\Proposal$ message is the only one carrying the
|
||||
value; $\Prevote$ and $\Precommit$ messages carry the value id. A correct
|
||||
process decides on a value $v$ in Tendermint upon receiving the $\Proposal$ for
|
||||
$v$ and $2f+1$ voting-power equivalent $\Precommit$ messages for $\id(v)$ in
|
||||
some round $r$. In order to send $\Precommit$ message for $v$ in a round $r$, a
|
||||
correct process waits to receive the $\Proposal$ and $2f+1$ of the
|
||||
corresponding $\Prevote$ messages in the round $r$. Otherwise,
|
||||
it sends $\Precommit$ message with a special $\nil$ value.
|
||||
This ensures that correct processes can $\Precommit$ only a
|
||||
single value (or $\nil$) in a round. As
|
||||
proposers may be faulty, the proposed value is treated by correct processes as
|
||||
a suggestion (it is not blindly accepted), and a correct process tells others
|
||||
if it accepted the $\Proposal$ for value $v$ by sending $\Prevote$ message for
|
||||
$\id(v)$; otherwise it sends $\Prevote$ message with the special $\nil$ value.
|
||||
|
||||
Every process maintains the following variables in the Algorithm
|
||||
\ref{alg:tendermint}: $step$, $lockedValue$, $lockedRound$, $validValue$ and
|
||||
$validRound$. The $step$ denotes the current state of the internal Tendermint
|
||||
state machine, i.e., it reflects the stage of the algorithm execution in the
|
||||
current round. The $lockedValue$ stores the most recent value (with respect to
|
||||
a round number) for which a $\Precommit$ message has been sent. The
|
||||
$lockedRound$ is the last round in which the process sent a $\Precommit$
|
||||
message that is not $\nil$. We also say that a correct process locks a value
|
||||
$v$ in a round $r$ by setting $lockedValue = v$ and $lockedRound = r$ before
|
||||
sending $\Precommit$ message for $\id(v)$. As a correct process can decide a
|
||||
value $v$ only if $2f+1$ $\Precommit$ messages for $\id(v)$ are received, this
|
||||
implies that a possible decision value is a value that is locked by at least
|
||||
$f+1$ voting power equivalent of correct processes. Therefore, any value $v$
|
||||
for which $\Proposal$ and $2f+1$ of the corresponding $\Prevote$ messages are
|
||||
received in some round $r$ is a \emph{possible decision} value. The role of the
|
||||
$validValue$ variable is to store the most recent possible decision value; the
|
||||
$validRound$ is the last round in which $validValue$ is updated. Apart from
|
||||
those variables, a process also stores the current consensus instance ($h_p$,
|
||||
called \emph{height} in Tendermint), and the current round number ($round_p$)
|
||||
and attaches them to every message. Finally, a process also stores an array of
|
||||
decisions, $decision_p$ (Tendermint assumes a sequence of consensus instances,
|
||||
one for each height).
|
||||
|
||||
Every round starts by a proposer suggesting a value with the $\Proposal$
|
||||
message (see line \ref{line:tab:send-proposal}). In the initial round of each
|
||||
height, the proposer is free to chose the value to suggest. In the
|
||||
Algorithm~\ref{alg:tendermint}, a correct process obtains a value to propose
|
||||
using an external function $getValue()$ that returns a valid value to
|
||||
propose. In the following rounds, a correct proposer will suggest a new value
|
||||
only if $validValue = \nil$; otherwise $validValue$ is proposed (see
|
||||
lines~\ref{line:tab:isThereLockedValue}-\ref{line:tab:getValidValue}).
|
||||
In addition to the value proposed, the $\Proposal$ message also
|
||||
contains the $validRound$ so other processes are informed about the last round
|
||||
in which the proposer observed $validValue$ as a possible decision value.
|
||||
Note that if a correct proposer $p$ sends $validValue$ with the $validRound$ in the
|
||||
$\Proposal$, this implies that the process $p$ received $\Proposal$ and the
|
||||
corresponding $2f+1$ $\Prevote$ messages for $validValue$ in the round
|
||||
$validRound$.
|
||||
If a correct process sends $\Proposal$ message with $validValue$ ($validRound > -1$)
|
||||
at time $t > GST$, by the \emph{Gossip communication} property, the
|
||||
corresponding $\Proposal$ and the $\Prevote$ messages will be received by all
|
||||
correct processes before time $t+\Delta$. Therefore, all correct processes will
|
||||
be able to verify the correctness of the suggested value as it is supported by
|
||||
the $\Proposal$ and the corresponding $2f+1$ voting power equivalent $\Prevote$
|
||||
messages.
|
||||
|
||||
A correct process $p$ accepts the proposal for a value $v$ (send $\Prevote$
|
||||
for $id(v)$) if an external \emph{valid} function returns $true$ for the value
|
||||
$v$, and if $p$ hasn't locked any value ($lockedRound = -1$) or $p$ has locked
|
||||
the value $v$ ($lockedValue = v$); see the line
|
||||
\ref{line:tab:accept-proposal-2}. In case the proposed pair is $(v,vr \ge 0)$ and a
|
||||
correct process $p$ has locked some value, it will accept
|
||||
$v$ if it is a more recent possible decision value\footnote{As
|
||||
explained above, the possible decision value in a round $r$ is the one for
|
||||
which $\Proposal$ and the corresponding $2f+1$ $\Prevote$ messages are received
|
||||
for the round $r$.}, $vr > lockedRound_p$, or if $lockedValue = v$
|
||||
(see line~\ref{line:tab:cond-prevote-higher-proposal}). Otherwise, a correct
|
||||
process will reject the proposal by sending $\Prevote$ message with $\nil$
|
||||
value. A correct process will send $\Prevote$ message with $\nil$ value also in
|
||||
case $\timeoutPropose$ expired (it is triggered when a correct process starts a
|
||||
new round) and a process has not sent $\Prevote$ message in the current round
|
||||
yet (see the line \ref{line:tab:onTimeoutPropose}).
|
||||
|
||||
If a correct process receives $\Proposal$ message for some value $v$ and $2f+1$
|
||||
$\Prevote$ messages for $\id(v)$, then it sends $\Precommit$ message with
|
||||
$\id(v)$. Otherwise, it sends $\Precommit$ $\nil$. A correct process will send
|
||||
$\Precommit$ message with $\nil$ value also in case $\timeoutPrevote$ expired
|
||||
(it is started when a correct process sent $\Prevote$ message and received any
|
||||
$2f+1$ $\Prevote$ messages) and a process has not sent $\Precommit$ message in
|
||||
the current round yet (see the line \ref{line:tab:onTimeoutPrecommit}). A
|
||||
correct process decides on some value $v$ if it receives in some round $r$
|
||||
$\Proposal$ message for $v$ and $2f+1$ $\Precommit$ messages with $\id(v)$ (see
|
||||
the line \ref{line:tab:decide}). To prevent the algorithm from blocking and
|
||||
waiting forever for this condition to be true, the Algorithm
|
||||
\ref{alg:tendermint} relies on $\timeoutPrecommit$. It is triggered after a
|
||||
process receives any set of $2f+1$ $\Precommit$ messages for the current round.
|
||||
If the $\timeoutPrecommit$ expires and a process has not decided yet, the
|
||||
process starts the next round (see the line \ref{line:tab:onTimeoutPrecommit}).
|
||||
When a correct process $p$ decides, it starts the next consensus instance
|
||||
(for the next height). The \emph{Gossip communication} property ensures
|
||||
that $\Proposal$ and $2f+1$ $\Prevote$ messages that led $p$ to decide
|
||||
are eventually received by all correct processes, so they will also decide.
|
||||
|
||||
\subsection{Termination mechanism}
|
||||
|
||||
Tendermint ensures termination by a novel mechanism that benefits from the
|
||||
gossip based nature of communication (see \emph{Gossip communication}
|
||||
property). It requires managing two additional variables, $validValue$ and
|
||||
$validRound$ that are then used by the proposer during the propose step as
|
||||
explained above. The $validValue$ and $validRound$ are updated to $v$ and $r$
|
||||
by a correct process in a round $r$ when the process receives valid $\Proposal$
|
||||
message for the value $v$ and the corresponding $2f+1$ $\Prevote$ messages for
|
||||
$id(v)$ in the round $r$ (see the rule at line~\ref{line:tab:recvPrevote}).
|
||||
|
||||
We now give briefly the intuition how managing and proposing $validValue$
|
||||
and $validRound$ ensures termination. Formal treatment is left for
|
||||
Section~\ref{sec:proof}.
|
||||
|
||||
The first thing to note is that during good period, because of the
|
||||
\emph{Gossip communication} property, if a correct process $p$ locks a value
|
||||
$v$ in some round $r$, all correct processes will update $validValue$ to $v$
|
||||
and $validRound$ to $r$ before the end of the round $r$ (we prove this formally
|
||||
in the Section~\ref{sec:proof}). The intuition is that messages that led to $p$
|
||||
locking a value $v$ in the round $r$ will be gossiped to all correct processes
|
||||
before the end of the round $r$, so it will update $validValue$ and
|
||||
$validRound$ (the line~\ref{line:tab:recvPrevote}). Therefore, if a correct
|
||||
process locks some value during good period, $validValue$ and $validRound$ are
|
||||
updated by all correct processes so that the value proposed in the following
|
||||
rounds will be acceptable by all correct processes. Note
|
||||
that it could happen that during good period, no correct process locks a value,
|
||||
but some correct process $q$ updates $validValue$ and $validRound$ during some
|
||||
round. As no correct process locks a value in this case, $validValue_q$ and
|
||||
$validRound_q$ will also be acceptable by all correct processes as
|
||||
$validRound_q > lockedRound_c$ for every correct process $c$ and as the
|
||||
\emph{Gossip communication} property ensures that the corresponding $\Prevote$
|
||||
messages that $q$ received in the round $validRound_q$ are received by all
|
||||
correct processes $\Delta$ time later.
|
||||
|
||||
Finally, it could happen that after GST, there is a long sequence of rounds in which
|
||||
no correct process neither locks a value nor update $validValue$ and $validRound$.
|
||||
In this case, during this sequence of rounds, the proposed value suggested by correct
|
||||
processes was not accepted by all correct processes. Note that this sequence of rounds
|
||||
is always finite as at the beginning of every
|
||||
round there is at least a single correct process $c$ such that $validValue_c$
|
||||
and $validRound_c$ are acceptable by every correct process. This is true as
|
||||
there exists a correct process $c$ such that for every other correct process
|
||||
$p$, $validRound_c > lockedRound_p$ or $validValue_c = lockedValue_p$. This is
|
||||
true as $c$ is the process that has locked a value in the most recent round
|
||||
among all correct processes (or no correct process locked any value). Therefore,
|
||||
eventually $c$ will be the proper in some round and the proposed value will be accepted
|
||||
by all correct processes, terminating therefore this sequence of
|
||||
rounds.
|
||||
|
||||
Therefore, updating $validValue$ and $validRound$ variables, and the
|
||||
\emph{Gossip communication} property, together ensures that eventually, during
|
||||
the good period, there exists a round with a correct proposer whose proposed
|
||||
value will be accepted by all correct processes, and all correct processes will
|
||||
terminate in that round. Note that this mechanism, contrary to the common
|
||||
termination mechanism illustrated in the
|
||||
Figure~\ref{ch3:fig:coordinator-change}, does not require exchanging any
|
||||
additional information in addition to messages already sent as part of what is
|
||||
normally being called "normal" case.
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
\section{Definitions} \label{sec:definitions}
|
||||
|
||||
\subsection{Model}
|
||||
|
||||
We consider a system of processes that communicate by exchanging messages.
|
||||
Processes can be correct or faulty, where a faulty process can behave in an
|
||||
arbitrary way, i.e., we consider Byzantine faults. We assume that each process
|
||||
has some amount of voting power (voting power of a process can be $0$).
|
||||
Processes in our model are not part of a single administrative domain;
|
||||
therefore we cannot enforce a direct network connectivity between all
|
||||
processes. Instead, we assume that each process is connected to a subset of
|
||||
processes called peers, such that there is an indirect communication channel
|
||||
between all correct processes. Communication between processes is established
|
||||
using a gossip protocol \cite{Dem1987:gossip}.
|
||||
|
||||
Formally, we model the network communication using a variant of the \emph{partially
|
||||
synchronous system model}~\cite{DLS88:jacm}: in all executions of the system
|
||||
there is a bound $\Delta$ and an instant GST (Global Stabilization Time) such
|
||||
that all communication among correct processes after GST is reliable and
|
||||
$\Delta$-timely, i.e., if a correct process $p$ sends message $m$ at time $t
|
||||
\ge GST$ to a correct process $q$, then $q$ will receive $m$ before $t +
|
||||
\Delta$\footnote{Note that as we do not assume direct communication channels
|
||||
among all correct processes, this implies that before the message $m$
|
||||
reaches $q$, it might pass through a number of correct processes that will
|
||||
forward the message $m$ using gossip protocol towards $q$.}.
|
||||
In addition to the standard \emph{partially
|
||||
synchronous system model}~\cite{DLS88:jacm}, we assume an auxiliary property
|
||||
that captures gossip-based nature of communication\footnote{The details of the Tendermint gossip protocol will be discussed in a separate
|
||||
technical report. }:
|
||||
|
||||
|
||||
\begin{itemize} \item \emph{Gossip communication:} If a correct process $p$
|
||||
sends some message $m$ at time $t$, all correct processes will receive
|
||||
$m$ before $max\{t, GST\} + \Delta$. Furthermore, if a correct process $p$
|
||||
receives some message $m$ at time $t$, all correct processes will receive
|
||||
$m$ before $max\{t, GST\} + \Delta$. \end{itemize}
|
||||
|
||||
|
||||
The bound $\Delta$ and GST are system
|
||||
parameters whose values are not required to be known for the safety of our
|
||||
algorithm. Termination of the algorithm is guaranteed within a bounded duration
|
||||
after GST. In practice, the algorithm will work correctly in the slightly
|
||||
weaker variant of the model where the system alternates between (long enough)
|
||||
good periods (corresponds to the \emph{after} GST period where system is
|
||||
reliable and $\Delta$-timely) and bad periods (corresponds to the period
|
||||
\emph{before} GST during which the system is asynchronous and messages can be
|
||||
lost), but consideration of the GST model simplifies the discussion.
|
||||
|
||||
We assume that process steps (which might include sending and receiving
|
||||
messages) take zero time. Processes are equipped with clocks so they can
|
||||
measure local timeouts.
|
||||
Spoofing/impersonation attacks are assumed to be impossible at all times due to
|
||||
the use of public-key cryptography, i.e., we assume that all protocol messages contains a digital signature.
|
||||
Therefore, when a correct
|
||||
process $q$ receives a signed message $m$ from its peer, the process $q$ can
|
||||
verify who was the original sender of the message $m$ and if the message signature is valid.
|
||||
We do not explicitly state a signature verification step in the pseudo-code of the algorithm to improve readability;
|
||||
we assume that only messages with the valid signature are considered at that level (and messages with invalid signatures
|
||||
are dropped).
|
||||
|
||||
|
||||
|
||||
%Messages that are being gossiped are created by the consensus layer. We can
|
||||
%think about consensus protocol as a content creator, which %defines what
|
||||
%messages should be disseminated using the gossip protocol. A correct
|
||||
%process creates the message for dissemination either i) %explicitly, by
|
||||
%invoking \emph{send} function as part of the consensus protocol or ii)
|
||||
%implicitly, by receiving a message from some other %process. Note that in
|
||||
%the case ii) gossiping of messages is implicit, i.e., it happens without
|
||||
%explicit send clause in the consensus algorithm %whenever a correct
|
||||
%process receives some messages in the consensus algorithm\footnote{If a
|
||||
%message is received by a correct process at %the consensus level then it
|
||||
%is considered valid from the protocol point of view, i.e., it has a
|
||||
%correct signature, a proper message structure %and a valid height and
|
||||
%round number.}.
|
||||
|
||||
%\item Processes keep resending messages (in case of failures or message loss)
|
||||
%until all its peers get them. This ensures that every message %sent or
|
||||
%received by a correct process is eventually received by all correct
|
||||
%processes.
|
||||
|
||||
\subsection{State Machine Replication}
|
||||
|
||||
State machine replication (SMR) is a general approach for replicating services
|
||||
modeled as a deterministic state machine~\cite{Lam78:cacm,Sch90:survey}. The
|
||||
key idea of this approach is to guarantee that all replicas start in the same
|
||||
state and then apply requests from clients in the same order, thereby
|
||||
guaranteeing that the replicas' states will not diverge. Following
|
||||
Schneider~\cite{Sch90:survey}, we note that the following is key for
|
||||
implementing a replicated state machine tolerant to (Byzantine) faults:
|
||||
|
||||
\begin{itemize} \item \emph{Replica Coordination.} All [non-faulty] replicas
|
||||
receive and process the same sequence of requests. \end{itemize}
|
||||
|
||||
Moreover, as Schneider also notes, this property can be decomposed into two
|
||||
parts, \emph{Agreement} and \emph{Order}: Agreement requires all (non-faulty)
|
||||
replicas to receive all requests, and Order requires that the order of received
|
||||
requests is the same at all replicas.
|
||||
|
||||
There is an additional requirement that needs to be ensured by Byzantine
|
||||
tolerant state machine replication: only requests (called transactions in the
|
||||
Tendermint terminology) proposed by clients are executed. In Tendermint,
|
||||
transaction verification is the responsibility of the service that is being
|
||||
replicated; upon receiving a transaction from the client, the Tendermint
|
||||
process will ask the service if the request is valid, and only valid requests
|
||||
will be processed.
|
||||
|
||||
\subsection{Consensus} \label{sec:consensus}
|
||||
|
||||
Tendermint solves state machine replication by sequentially executing consensus
|
||||
instances to agree on each block of transactions that are
|
||||
then executed by the service being replicated. We consider a variant of the
|
||||
Byzantine consensus problem called Validity Predicate-based Byzantine consensus
|
||||
that is motivated by blockchain systems~\cite{GLR17:red-belly-bc}. The problem
|
||||
is defined by an agreement, a termination, and a validity property.
|
||||
|
||||
\begin{itemize} \item \emph{Agreement:} No two correct processes decide on
|
||||
different values. \item \emph{Termination:} All correct processes
|
||||
eventually decide on a value. \item \emph{Validity:} A decided value
|
||||
is valid, i.e., it satisfies the predefined predicate denoted
|
||||
\emph{valid()}. \end{itemize}
|
||||
|
||||
This variant of the Byzantine consensus problem has an application-specific
|
||||
\emph{valid()} predicate to indicate whether a value is valid. In the context
|
||||
of blockchain systems, for example, a value is not valid if it does not
|
||||
contain an appropriate hash of the last value (block) added to the blockchain.
|
||||
@@ -0,0 +1,32 @@
|
||||
\newcommand{\NC}{\mbox{\it NC}}
|
||||
\newcommand{\HO}{\mbox{\it HO}}
|
||||
\newcommand{\AS}{\mbox{\it AS}}
|
||||
\newcommand{\SK}{\mbox{\it SK}}
|
||||
\newcommand{\SHO}{\mbox{\it SHO}}
|
||||
\newcommand{\AHO}{\mbox{\it AHO}}
|
||||
\newcommand{\CONS}{\mbox{\it CONS}}
|
||||
\newcommand{\K}{\mbox{\it K}}
|
||||
|
||||
\newcommand{\Alg}{\mathcal{A}}
|
||||
\newcommand{\Pred}{\mathcal{P}}
|
||||
\newcommand{\Spr}{S_p^r}
|
||||
\newcommand{\Tpr}{T_p^r}
|
||||
\newcommand{\mupr}{\vec{\mu}_p^{\,r}}
|
||||
|
||||
\newcommand{\MSpr}{S_p^{\rho}}
|
||||
\newcommand{\MTpr}{T_p^{\rho}}
|
||||
|
||||
|
||||
|
||||
\newconstruct{\SEND}{$\Spr$:}{}{\ENDSEND}{}
|
||||
\newconstruct{\TRAN}{$\Tpr$:}{}{\ENDTRAN}{}
|
||||
\newconstruct{\ROUND}{\textbf{Round}}{\!\textbf{:}}{\ENDROUND}{}
|
||||
\newconstruct{\VARIABLES}{\textbf{Variables:}}{}{\ENDVARIABLES}{}
|
||||
\newconstruct{\INIT}{\textbf{Initialization:}}{}{\ENDINIT}{}
|
||||
|
||||
\newconstruct{\MSEND}{$\MSpr$:}{}{\ENDMSEND}{}
|
||||
\newconstruct{\MTRAN}{$\MTpr$:}{}{\ENDMTRAN}{}
|
||||
|
||||
\newconstruct{\SROUND}{\textbf{Selection Round}}{\!\textbf{:}}{\ENDSROUND}{}
|
||||
\newconstruct{\VROUND}{\textbf{Validation Round}}{\!\textbf{:}}{\ENDVROUND}{}
|
||||
\newconstruct{\DROUND}{\textbf{Decision Round}}{\!\textbf{:}}{\ENDDROUND}{}
|
||||
@@ -0,0 +1,138 @@
|
||||
\section{Introduction} \label{sec:tendermint}
|
||||
|
||||
Consensus is a fundamental problem in distributed computing. It
|
||||
is important because of it's role in State Machine Replication (SMR), a generic
|
||||
approach for replicating services that can be modeled as a deterministic state
|
||||
machine~\cite{Lam78:cacm, Sch90:survey}. The key idea of this approach is that
|
||||
service replicas start in the same initial state, and then execute requests
|
||||
(also called transactions) in the same order; thereby guaranteeing that
|
||||
replicas stay in sync with each other. The role of consensus in the SMR
|
||||
approach is ensuring that all replicas receive transactions in the same order.
|
||||
Traditionally, deployments of SMR based systems are in data-center settings
|
||||
(local area network), have a small number of replicas (three to seven) and are
|
||||
typically part of a single administration domain (e.g., Chubby
|
||||
\cite{Bur:osdi06}); therefore they handle benign (crash) failures only, as more
|
||||
general forms of failure (in particular, malicious or Byzantine faults) are
|
||||
considered to occur with only negligible probability.
|
||||
|
||||
The success of cryptocurrencies and blockchain systems in recent years (e.g.,
|
||||
\cite{Nak2012:bitcoin, But2014:ethereum}) pose a whole new set of challenges on
|
||||
the design and deployment of SMR based systems: reaching agreement over wide
|
||||
area network, among large number of nodes (hundreds or thousands) that are not
|
||||
part of the same administrative domain, and where a subset of nodes can behave
|
||||
maliciously (Byzantine faults). Furthermore, contrary to the previous
|
||||
data-center deployments where nodes are fully connected to each other, in
|
||||
blockchain systems, a node is only connected to a subset of other nodes, so
|
||||
communication is achieved by gossip-based peer-to-peer protocols.
|
||||
The new requirements demand designs and algorithms that are not necessarily
|
||||
present in the classical academic literature on Byzantine fault tolerant
|
||||
consensus (or SMR) systems (e.g., \cite{DLS88:jacm, CL02:tcs}) as the primary
|
||||
focus was different setup.
|
||||
|
||||
In this paper we describe a novel Byzantine-fault tolerant consensus algorithm
|
||||
that is the core of the BFT SMR platform called Tendermint\footnote{The
|
||||
Tendermint platform is available open source at
|
||||
https://github.com/tendermint/tendermint.}. The Tendermint platform consists of
|
||||
a high-performance BFT SMR implementation written in Go, a flexible interface
|
||||
for
|
||||
building arbitrary deterministic applications above the consensus, and a suite
|
||||
of tools for deployment and management.
|
||||
|
||||
The Tendermint consensus algorithm is inspired by the PBFT SMR
|
||||
algorithm~\cite{CL99:osdi} and the DLS algorithm for authenticated faults (the
|
||||
Algorithm 2 from \cite{DLS88:jacm}). Similar to DLS algorithm, Tendermint
|
||||
proceeds in
|
||||
rounds\footnote{Tendermint is not presented in the basic round model of
|
||||
\cite{DLS88:jacm}. Furthermore, we use the term round differently than in
|
||||
\cite{DLS88:jacm}; in Tendermint a round denotes a sequence of communication
|
||||
steps instead of a single communication step in \cite{DLS88:jacm}.}, where each
|
||||
round has a dedicated proposer (also called coordinator or
|
||||
leader) and a process proceeds to a new round as part of normal
|
||||
processing (not only in case the proposer is faulty or suspected as being faulty
|
||||
by enough processes as in PBFT).
|
||||
The communication pattern of each round is very similar to the "normal" case
|
||||
of PBFT. Therefore, in preferable conditions (correct proposer, timely and
|
||||
reliable communication between correct processes), Tendermint decides in three
|
||||
communication steps (the same as PBFT).
|
||||
|
||||
The major novelty and contribution of the Tendermint consensus algorithm is a
|
||||
new termination mechanism. As explained in \cite{MHS09:opodis, RMS10:dsn}, the
|
||||
existing BFT consensus (and SMR) algorithms for the partially synchronous
|
||||
system model (for example PBFT~\cite{CL99:osdi}, \cite{DLS88:jacm},
|
||||
\cite{MA06:tdsc}) typically relies on the communication pattern illustrated in
|
||||
Figure~\ref{ch3:fig:coordinator-change} for termination. The
|
||||
Figure~\ref{ch3:fig:coordinator-change} illustrates messages exchanged during
|
||||
the proposer change when processes start a new round\footnote{There is no
|
||||
consistent terminology in the distributed computing terminology on naming
|
||||
sequence of communication steps that corresponds to a logical unit. It is
|
||||
sometimes called a round, phase or a view.}. It guarantees that eventually (ie.
|
||||
after some Global Stabilization Time, GST), there exists a round with a correct
|
||||
proposer that will bring the system into a univalent configuration.
|
||||
Intuitively, in a round in which the proposed value is accepted
|
||||
by all correct processes, and communication between correct processes is
|
||||
timely and reliable, all correct processes decide.
|
||||
|
||||
|
||||
\begin{figure}[tbh!] \def\rdstretch{5} \def\ystretch{3} \centering
|
||||
\begin{rounddiag}{4}{2} \round{1}{~} \rdmessage{1}{1}{$v_1$}
|
||||
\rdmessage{2}{1}{$v_2$} \rdmessage{3}{1}{$v_3$} \rdmessage{4}{1}{$v_4$}
|
||||
\round{2}{~} \rdmessage{1}{1}{$x, [v_{1..4}]$}
|
||||
\rdmessage{1}{2}{$~~~~~~x, [v_{1..4}]$} \rdmessage{1}{3}{$~~~~~~~~x,
|
||||
[v_{1..4}]$} \rdmessage{1}{4}{$~~~~~~~x, [v_{1..4}]$} \end{rounddiag}
|
||||
\vspace{-5mm} \caption{\boldmath Proposer (coordinator) change: $p_1$ is the
|
||||
new proposer.} \label{ch3:fig:coordinator-change} \end{figure}
|
||||
|
||||
To ensure that a proposed value is accepted by all correct
|
||||
processes\footnote{The proposed value is not blindly accepted by correct
|
||||
processes in BFT algorithms. A correct process always verifies if the proposed
|
||||
value is safe to be accepted so that safety properties of consensus are not
|
||||
violated.}
|
||||
a proposer will 1) build the global state by receiving messages from other
|
||||
processes, 2) select the safe value to propose and 3) send the selected value
|
||||
together with the signed messages
|
||||
received in the first step to support it. The
|
||||
value $v_i$ that a correct process sends to the next proposer normally
|
||||
corresponds to a value the process considers as acceptable for a decision:
|
||||
|
||||
\begin{itemize} \item in PBFT~\cite{CL99:osdi} and DLS~\cite{DLS88:jacm} it is
|
||||
not the value itself but a set of $2f+1$ signed messages with the same
|
||||
value id, \item in Fast Byzantine Paxos~\cite{MA06:tdsc} the value
|
||||
itself is being sent. \end{itemize}
|
||||
|
||||
In both cases, using this mechanism in our system model (ie. high
|
||||
number of nodes over gossip based network) would have high communication
|
||||
complexity that increases with the number of processes: in the first case as
|
||||
the message sent depends on the total number of processes, and in the second
|
||||
case as the value (block of transactions) is sent by each process. The set of
|
||||
messages received in the first step are normally piggybacked on the proposal
|
||||
message (in the Figure~\ref{ch3:fig:coordinator-change} denoted with
|
||||
$[v_{1..4}]$) to justify the choice of the selected value $x$. Note that
|
||||
sending this message also does not scale with the number of processes in the
|
||||
system.
|
||||
|
||||
We designed a novel termination mechanism for Tendermint that better suits the
|
||||
system model we consider. It does not require additional communication (neither
|
||||
sending new messages nor piggybacking information on the existing messages) and
|
||||
it is fully based on the communication pattern that is very similar to the
|
||||
normal case in PBFT \cite{CL99:osdi}. Therefore, there is only a single mode of
|
||||
execution in Tendermint, i.e., there is no separation between the normal and
|
||||
the recovery mode, which is the case in other PBFT-like protocols (e.g.,
|
||||
\cite{CL99:osdi}, \cite{Ver09:spinning} or \cite{Cle09:aardvark}). We believe
|
||||
this makes Tendermint simpler to understand and implement correctly.
|
||||
|
||||
Note that the orthogonal approach for reducing message complexity in order to
|
||||
improve
|
||||
scalability and decentralization (number of processes) of BFT consensus
|
||||
algorithms is using advanced cryptography (for example Boneh-Lynn-Shacham (BLS)
|
||||
signatures \cite{BLS2001:crypto}) as done for example in SBFT
|
||||
\cite{Gue2018:sbft}.
|
||||
|
||||
The remainder of the paper is as follows: Section~\ref{sec:definitions} defines
|
||||
the system model and gives the problem definitions. Tendermint
|
||||
consensus algorithm is presented in Section~\ref{sec:tendermint} and the
|
||||
proofs are given in Section~\ref{sec:proof}. We conclude in
|
||||
Section~\ref{sec:conclusion}.
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,168 @@
|
||||
% ---------------------------------------------------------------
|
||||
%
|
||||
% $Id: latex8.sty,v 1.2 1995/09/15 15:31:13 ienne Exp $
|
||||
%
|
||||
% by Paolo.Ienne@di.epfl.ch
|
||||
%
|
||||
% ---------------------------------------------------------------
|
||||
%
|
||||
% no guarantee is given that the format corresponds perfectly to
|
||||
% IEEE 8.5" x 11" Proceedings, but most features should be ok.
|
||||
%
|
||||
% ---------------------------------------------------------------
|
||||
% with LaTeX2e:
|
||||
% =============
|
||||
%
|
||||
% use as
|
||||
% \documentclass[times,10pt,twocolumn]{article}
|
||||
% \usepackage{latex8}
|
||||
% \usepackage{times}
|
||||
%
|
||||
% ---------------------------------------------------------------
|
||||
|
||||
% with LaTeX 2.09:
|
||||
% ================
|
||||
%
|
||||
% use as
|
||||
% \documentstyle[times,art10,twocolumn,latex8]{article}
|
||||
%
|
||||
% ---------------------------------------------------------------
|
||||
% with both versions:
|
||||
% ===================
|
||||
%
|
||||
% specify \pagestyle{empty} to omit page numbers in the final
|
||||
% version
|
||||
%
|
||||
% specify references as
|
||||
% \bibliographystyle{latex8}
|
||||
% \bibliography{...your files...}
|
||||
%
|
||||
% use Section{} and SubSection{} instead of standard section{}
|
||||
% and subsection{} to obtain headings in the form
|
||||
% "1.3. My heading"
|
||||
%
|
||||
% ---------------------------------------------------------------
|
||||
|
||||
\typeout{IEEE 8.5 x 11-Inch Proceedings Style `latex8.sty'.}
|
||||
|
||||
% ten point helvetica bold required for captions
|
||||
% in some sites the name of the helvetica bold font may differ,
|
||||
% change the name here:
|
||||
\font\tenhv = phvb at 10pt
|
||||
%\font\tenhv = phvb7t at 10pt
|
||||
|
||||
% eleven point times bold required for second-order headings
|
||||
% \font\elvbf = cmbx10 scaled 1100
|
||||
\font\elvbf = ptmb scaled 1100
|
||||
|
||||
% set dimensions of columns, gap between columns, and paragraph indent
|
||||
\setlength{\textheight}{8.875in}
|
||||
\setlength{\textwidth}{6.875in}
|
||||
\setlength{\columnsep}{0.3125in}
|
||||
\setlength{\topmargin}{0in}
|
||||
\setlength{\headheight}{0in}
|
||||
\setlength{\headsep}{0in}
|
||||
\setlength{\parindent}{1pc}
|
||||
\setlength{\oddsidemargin}{-.304in}
|
||||
\setlength{\evensidemargin}{-.304in}
|
||||
|
||||
% memento from size10.clo
|
||||
% \normalsize{\@setfontsize\normalsize\@xpt\@xiipt}
|
||||
% \small{\@setfontsize\small\@ixpt{11}}
|
||||
% \footnotesize{\@setfontsize\footnotesize\@viiipt{9.5}}
|
||||
% \scriptsize{\@setfontsize\scriptsize\@viipt\@viiipt}
|
||||
% \tiny{\@setfontsize\tiny\@vpt\@vipt}
|
||||
% \large{\@setfontsize\large\@xiipt{14}}
|
||||
% \Large{\@setfontsize\Large\@xivpt{18}}
|
||||
% \LARGE{\@setfontsize\LARGE\@xviipt{22}}
|
||||
% \huge{\@setfontsize\huge\@xxpt{25}}
|
||||
% \Huge{\@setfontsize\Huge\@xxvpt{30}}
|
||||
|
||||
\def\@maketitle
|
||||
{
|
||||
\newpage
|
||||
\null
|
||||
\vskip .375in
|
||||
\begin{center}
|
||||
{\Large \bf \@title \par}
|
||||
% additional two empty lines at the end of the title
|
||||
\vspace*{24pt}
|
||||
{
|
||||
\large
|
||||
\lineskip .5em
|
||||
\begin{tabular}[t]{c}
|
||||
\@author
|
||||
\end{tabular}
|
||||
\par
|
||||
}
|
||||
% additional small space at the end of the author name
|
||||
\vskip .5em
|
||||
{
|
||||
\large
|
||||
\begin{tabular}[t]{c}
|
||||
\@affiliation
|
||||
\end{tabular}
|
||||
\par
|
||||
\ifx \@empty \@email
|
||||
\else
|
||||
\begin{tabular}{r@{~}l}
|
||||
E-mail: & {\tt \@email}
|
||||
\end{tabular}
|
||||
\par
|
||||
\fi
|
||||
}
|
||||
% additional empty line at the end of the title block
|
||||
\vspace*{12pt}
|
||||
\end{center}
|
||||
}
|
||||
|
||||
\def\abstract
|
||||
{%
|
||||
\centerline{\large\bf Abstract}%
|
||||
\vspace*{12pt}%
|
||||
\it%
|
||||
}
|
||||
|
||||
\def\endabstract
|
||||
{
|
||||
% additional empty line at the end of the abstract
|
||||
\vspace*{12pt}
|
||||
}
|
||||
|
||||
\def\affiliation#1{\gdef\@affiliation{#1}} \gdef\@affiliation{}
|
||||
|
||||
\def\email#1{\gdef\@email{#1}}
|
||||
\gdef\@email{}
|
||||
|
||||
\newlength{\@ctmp}
|
||||
\newlength{\@figindent}
|
||||
\setlength{\@figindent}{1pc}
|
||||
|
||||
\long\def\@makecaption#1#2{
|
||||
\vskip 10pt
|
||||
\setbox\@tempboxa\hbox{\tenhv\noindent #1.~#2}
|
||||
\setlength{\@ctmp}{\hsize}
|
||||
\addtolength{\@ctmp}{-\@figindent}\addtolength{\@ctmp}{-\@figindent}
|
||||
% IF longer than one indented paragraph line
|
||||
\ifdim \wd\@tempboxa >\@ctmp
|
||||
% THEN set as an indented paragraph
|
||||
\begin{list}{}{\leftmargin\@figindent \rightmargin\leftmargin}
|
||||
\item[]\tenhv #1.~#2\par
|
||||
\end{list}
|
||||
\else
|
||||
% ELSE center
|
||||
\hbox to\hsize{\hfil\box\@tempboxa\hfil}
|
||||
\fi}
|
||||
|
||||
% correct heading spacing and type
|
||||
\def\section{\@startsection {section}{1}{\z@}
|
||||
{14pt plus 2pt minus 2pt}{14pt plus 2pt minus 2pt} {\large\bf}}
|
||||
\def\subsection{\@startsection {subsection}{2}{\z@}
|
||||
{13pt plus 2pt minus 2pt}{13pt plus 2pt minus 2pt} {\elvbf}}
|
||||
|
||||
% add the period after section numbers
|
||||
\newcommand{\Section}[1]{\section{\hskip -1em.~#1}}
|
||||
\newcommand{\SubSection}[1]{\subsection{\hskip -1em.~#1}}
|
||||
|
||||
% end of file latex8.sty
|
||||
% ---------------------------------------------------------------
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,153 @@
|
||||
%\documentclass[conference]{IEEEtran}
|
||||
\documentclass[conference,onecolumn,draft,a4paper]{IEEEtran}
|
||||
% Add the compsoc option for Computer Society conferences.
|
||||
%
|
||||
% If IEEEtran.cls has not been installed into the LaTeX system files,
|
||||
% manually specify the path to it like:
|
||||
% \documentclass[conference]{../sty/IEEEtran}
|
||||
|
||||
|
||||
|
||||
% *** GRAPHICS RELATED PACKAGES ***
|
||||
%
|
||||
\ifCLASSINFOpdf
|
||||
\else
|
||||
\fi
|
||||
|
||||
% correct bad hyphenation here
|
||||
\hyphenation{op-tical net-works semi-conduc-tor}
|
||||
|
||||
%\usepackage[caption=false,font=footnotesize]{subfig}
|
||||
\usepackage{tikz}
|
||||
\usetikzlibrary{decorations,shapes,backgrounds,calc}
|
||||
\tikzstyle{msg}=[->,black,>=latex]
|
||||
\tikzstyle{rubber}=[|<->|]
|
||||
\tikzstyle{announce}=[draw=blue,fill=blue,shape=diamond,right,minimum
|
||||
height=2mm,minimum width=1.6667mm,inner sep=0pt]
|
||||
\tikzstyle{decide}=[draw=red,fill=red,shape=isosceles triangle,right,minimum
|
||||
height=2mm,minimum width=1.6667mm,inner sep=0pt,shape border rotate=90]
|
||||
\tikzstyle{cast}=[draw=green!50!black,fill=green!50!black,shape=circle,left,minimum
|
||||
height=2mm,minimum width=1.6667mm,inner sep=0pt]
|
||||
|
||||
|
||||
\usepackage{multirow}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{epstopdf}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{rounddiag}
|
||||
\graphicspath{{../}}
|
||||
|
||||
\usepackage{technote}
|
||||
\usepackage{homodel}
|
||||
\usepackage{enumerate}
|
||||
%%\usepackage{ulem}\normalem
|
||||
|
||||
% to center caption
|
||||
\usepackage{caption}
|
||||
|
||||
\newcommand{\textstretch}{1.4}
|
||||
\newcommand{\algostretch}{1}
|
||||
\newcommand{\eqnstretch}{0.5}
|
||||
|
||||
\newconstruct{\FOREACH}{\textbf{for each}}{\textbf{do}}{\ENDFOREACH}{}
|
||||
|
||||
%\newconstruct{\ON}{\textbf{on}}{\textbf{do}}{\ENDON}{\textbf{end on}}
|
||||
\newcommand\With{\textbf{while}}
|
||||
\newcommand\From{\textbf{from}}
|
||||
\newcommand\Broadcast{\textbf{broadcast}}
|
||||
\newcommand\PBroadcast{send}
|
||||
\newcommand\UpCall{\textbf{UpCall}}
|
||||
\newcommand\DownCall{\textbf{DownCall}}
|
||||
\newcommand \Call{\textbf{Call}}
|
||||
\newident{noop}
|
||||
\newconstruct{\UPON}{\textbf{upon}}{\textbf{do}}{\ENDUPON}{}
|
||||
|
||||
|
||||
|
||||
\newcommand{\abcast}{\mathsf{to\mbox{\sf-}broadcast}}
|
||||
\newcommand{\adeliver}{\mathsf{to\mbox{\sf-}deliver}}
|
||||
|
||||
\newcommand{\ABCAgreement}{\emph{TO-Agreement}}
|
||||
\newcommand{\ABCIntegrity}{\emph{TO-Integrity}}
|
||||
\newcommand{\ABCValidity}{\emph{TO-Validity}}
|
||||
\newcommand{\ABCTotalOrder}{\emph{TO-Order}}
|
||||
\newcommand{\ABCBoundedDelivery}{\emph{TO-Bounded Delivery}}
|
||||
|
||||
|
||||
\newcommand{\tabc}{\mathit{atab\mbox{\sf-}cast}}
|
||||
\newcommand{\anno}{\mathit{atab\mbox{\sf-}announce}}
|
||||
\newcommand{\abort}{\mathit{atab\mbox{\sf-}abort}}
|
||||
\newcommand{\tadel}{\mathit{atab\mbox{\sf-}deliver}}
|
||||
|
||||
\newcommand{\ATABAgreement}{\emph{ATAB-Agreement}}
|
||||
\newcommand{\ATABAbort}{\emph{ATAB-Abort}}
|
||||
\newcommand{\ATABIntegrity}{\emph{ATAB-Integrity}}
|
||||
\newcommand{\ATABValidity}{\emph{ATAB-Validity}}
|
||||
\newcommand{\ATABAnnounce}{\emph{ATAB-Announcement}}
|
||||
\newcommand{\ATABTermination}{\emph{ATAB-Termination}}
|
||||
%\newcommand{\ATABFastAnnounce}{\emph{ATAB-Fast-Announcement}}
|
||||
|
||||
%% Command for observations.
|
||||
\newtheorem{observation}{Observation}
|
||||
|
||||
|
||||
%% HO ALGORITHM DEFINITIONS
|
||||
\newconstruct{\FUNCTION}{\textbf{Function}}{\textbf{:}}{\ENDFUNCTION}{}
|
||||
|
||||
%% Uncomment the following four lines to remove remarks and visible traces of
|
||||
%% modifications in the document
|
||||
%%\renewcommand{\sout}[1]{\relaxx}
|
||||
%%\renewcommand{\uline}[1]{#1}
|
||||
%% \renewcommand{\uwave}[1]{#1}
|
||||
\renewcommand{\note}[2][default]{\relax}
|
||||
|
||||
|
||||
%% The following commands can be used to generate TR or Conference version of the paper
|
||||
\newcommand{\tr}[1]{}
|
||||
\renewcommand{\tr}[1]{#1}
|
||||
\newcommand{\onlypaper}[1]{#1}
|
||||
%\renewcommand{\onlypaper}[1]{}
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%\pagestyle{plain}
|
||||
%\pagestyle{empty}
|
||||
|
||||
%% IEEE tweaks
|
||||
%\setlength{\IEEEilabelindent}{.5\parindent}
|
||||
%\setlength{\IEEEiednormlabelsep}{.5\parindent}
|
||||
|
||||
\begin{document}
|
||||
%
|
||||
% paper title
|
||||
% can use linebreaks \\ within to get better formatting as desired
|
||||
\title{The latest gossip on BFT consensus\vspace{-0.7\baselineskip}}
|
||||
|
||||
|
||||
|
||||
\author{\IEEEauthorblockN{\large Ethan Buchman, Jae Kwon and Zarko Milosevic\\}
|
||||
\IEEEauthorblockN{\large Tendermint}\\
|
||||
%\\\vspace{-0.5\baselineskip}
|
||||
\IEEEauthorblockN{September 24, 2018}
|
||||
}
|
||||
|
||||
% make the title area
|
||||
\maketitle
|
||||
\vspace*{0.5em}
|
||||
|
||||
\begin{abstract}
|
||||
This paper presents Tendermint, a new protocol for ordering events in a distributed network under adversarial conditions. More commonly known as Byzantine Fault Tolerant (BFT) consensus or atomic broadcast, the problem has attracted significant attention in recent years due to the widespread success of blockchain-based digital currencies, such as Bitcoin and Ethereum, which successfully solved the problem in a public setting without a central authority. Tendermint modernizes classic academic work on the subject and simplifies the design of the BFT algorithm by relying on a peer-to-peer gossip protocol among nodes.
|
||||
\end{abstract}
|
||||
|
||||
%\noindent \textbf{Keywords:} Blockchain, Byzantine Fault Tolerance, State Machine %Replication
|
||||
|
||||
\input{intro}
|
||||
\input{definitions}
|
||||
\input{consensus}
|
||||
\input{proof}
|
||||
\input{conclusion}
|
||||
|
||||
\bibliographystyle{IEEEtran}
|
||||
\bibliography{lit}
|
||||
|
||||
%\appendix
|
||||
|
||||
\end{document}
|
||||
@@ -0,0 +1,280 @@
|
||||
\section{Proof of Tendermint consensus algorithm} \label{sec:proof}
|
||||
|
||||
\begin{lemma} \label{lemma:majority-intersection} For all $f\geq 0$, any two
|
||||
sets of processes with voting power at least equal to $2f+1$ have at least one
|
||||
correct process in common. \end{lemma}
|
||||
|
||||
\begin{proof} As the total voting power is equal to $n=3f+1$, we have $2(2f+1)
|
||||
= n+f+1$. This means that the intersection of two sets with the voting
|
||||
power equal to $2f+1$ contains at least $f+1$ voting power in common, \ie,
|
||||
at least one correct process (as the total voting power of faulty processes
|
||||
is $f$). The result follows directly from this. \end{proof}
|
||||
|
||||
\begin{lemma} \label{lemma:locked-decision_value-prevote-v} If $f+1$ correct
|
||||
processes lock value $v$ in round $r_0$ ($lockedValue = v$ and $lockedRound =
|
||||
r_0$), then in all rounds $r > r_0$, they send $\Prevote$ for $id(v)$ or
|
||||
$\nil$. \end{lemma}
|
||||
|
||||
\begin{proof} We prove the result by induction on $r$.
|
||||
|
||||
\emph{Base step $r = r_0 + 1:$} Let's denote with $C$ the set of correct
|
||||
processes with voting power equal to $f+1$. By the rules at
|
||||
line~\ref{line:tab:recvProposal} and line~\ref{line:tab:acceptProposal}, the
|
||||
processes from the set $C$ can't accept $\Proposal$ for any value different
|
||||
from $v$ in round $r$, and therefore can't send a $\li{\Prevote,height_p,
|
||||
r,id(v')}$ message, if $v' \neq v$. Therefore, the Lemma holds for the base
|
||||
step.
|
||||
|
||||
\emph{Induction step from $r_1$ to $r_1+1$:} We assume that no process from the
|
||||
set $C$ has sent $\Prevote$ for values different than $id(v)$ or $\nil$ until
|
||||
round $r_1 + 1$. We now prove that the Lemma also holds for round $r_1 + 1$. As
|
||||
processes from the set $C$ send $\Prevote$ for $id(v)$ or $\nil$ in rounds $r_0
|
||||
\le r \le r_1$, by Lemma~\ref{lemma:majority-intersection} there is no value
|
||||
$v' \neq v$ for which it is possible to receive $2f+1$ $\Prevote$ messages in
|
||||
those rounds (i). Therefore, we have for all processes from the set $C$,
|
||||
$lockedValue = v$ and $lockedRound \ge r_0$. Let's assume by a contradiction
|
||||
that a process $q$ from the set $C$ sends $\Prevote$ in round $r_1 + 1$ for
|
||||
value $id(v')$, where $v' \neq v$. This is possible only by
|
||||
line~\ref{line:tab:prevote-higher-proposal}. Note that this implies that $q$
|
||||
received $2f+1$ $\li{\Prevote,h_q, r,id(v')}$ messages, where $r > r_0$ and $r
|
||||
< r_1 +1$ (see line~\ref{line:tab:cond-prevote-higher-proposal}). A
|
||||
contradiction with (i) and Lemma~\ref{lemma:majority-intersection}.
|
||||
\end{proof}
|
||||
|
||||
\begin{lemma} \label{lemma:agreement} Algorithm~\ref{alg:tendermint} satisfies
|
||||
Agreement. \end{lemma}
|
||||
|
||||
\begin{proof} Let round $r_0$ be the first round of height $h$ such that some
|
||||
correct process $p$ decides $v$. We now prove that if some correct process
|
||||
$q$ decides $v'$ in some round $r \ge r_0$, then $v = v'$.
|
||||
|
||||
In case $r = r_0$, $q$ has received at least $2f+1$
|
||||
$\li{\Precommit,h_p,r_0,id(v')}$ messages at line~\ref{line:tab:onDecideRule},
|
||||
while $p$ has received at least $2f+1$ $\li{\Precommit,h_p,r_0,id(v)}$
|
||||
messages. By Lemma~\ref{lemma:majority-intersection} two sets of messages of
|
||||
voting power $2f+1$ intersect in at least one correct process. As a correct
|
||||
process sends a single $\Precommit$ message in a round, then $v=v'$.
|
||||
|
||||
We prove the case $r > r_0$ by contradiction. By the
|
||||
rule~\ref{line:tab:onDecideRule}, $p$ has received at least $2f+1$ voting-power
|
||||
equivalent of $\li{\Precommit,h_p,r_0,id(v)}$ messages, i.e., at least $f+1$
|
||||
voting-power equivalent correct processes have locked value $v$ in round $r_0$ and have
|
||||
sent those messages (i). Let denote this set of messages with $C$. On the
|
||||
other side, $q$ has received at least $2f+1$ voting power equivalent of
|
||||
$\li{\Precommit,h_q, r,id(v')}$ messages. As the voting power of all faulty
|
||||
processes is at most $f$, some correct process $c$ has sent one of those
|
||||
messages. By the rule at line~\ref{line:tab:recvPrevote}, $c$ has locked value
|
||||
$v'$ in round $r$ before sending $\li{\Precommit,h_q, r,id(v')}$. Therefore $c$
|
||||
has received $2f+1$ $\Prevote$ messages for $id(v')$ in round $r > r_0$ (see
|
||||
line~\ref{line:tab:recvPrevote}). By Lemma~\ref{lemma:majority-intersection}, a
|
||||
process from the set $C$ has sent $\Prevote$ message for $id(v')$ in round $r$.
|
||||
A contradiction with (i) and Lemma~\ref{lemma:locked-decision_value-prevote-v}.
|
||||
\end{proof}
|
||||
|
||||
\begin{lemma} \label{lemma:agreement} Algorithm~\ref{alg:tendermint} satisfies
|
||||
Validity. \end{lemma}
|
||||
|
||||
\begin{proof} Trivially follows from the rule at line
|
||||
\ref{line:tab:validDecisionValue} which ensures that only valid values can be
|
||||
decided. \end{proof}
|
||||
|
||||
\begin{lemma} \label{lemma:round-synchronisation} If we assume that:
|
||||
\begin{enumerate}
|
||||
\item a correct process $p$ is the first correct process to
|
||||
enter a round $r>0$ at time $t > GST$ (for every correct process
|
||||
$c$, $round_c \le r$ at time $t$)
|
||||
\item the proposer of round $r$ is
|
||||
a correct process $q$
|
||||
\item for every correct process $c$,
|
||||
$lockedRound_c \le validRound_q$ at time $t$
|
||||
\item $\timeoutPropose(r)
|
||||
> 2\Delta + \timeoutPrecommit(r-1)$, $\timeoutPrevote(r) > 2\Delta$ and
|
||||
$\timeoutPrecommit(r) > 2\Delta$,
|
||||
\end{enumerate}
|
||||
then all correct processes decide in round $r$ before $t + 4\Delta +
|
||||
\timeoutPrecommit(r-1)$.
|
||||
\end{lemma}
|
||||
|
||||
\begin{proof} As $p$ is the first correct process to enter round $r$, it
|
||||
executed the line~\ref{line:tab:nextRound} after $\timeoutPrecommit(r-1)$
|
||||
expired. Therefore, $p$ received $2f+1$ $\Precommit$ messages in the round
|
||||
$r-1$ before time $t$. By the \emph{Gossip communication} property, all
|
||||
correct processes will receive those messages the latest at time $t +
|
||||
\Delta$. Correct processes that are in rounds $< r-1$ at time $t$ will
|
||||
enter round $r-1$ (see the rule at line~\ref{line:tab:nextRound2}) and
|
||||
trigger $\timeoutPrecommit(r-1)$ (see rule~\ref{line:tab:startTimeoutPrecommit})
|
||||
by time $t+\Delta$. Therefore, all correct processes will start round $r$
|
||||
by time $t+\Delta+\timeoutPrecommit(r-1)$ (i).
|
||||
|
||||
In the worst case, the process $q$ is the last correct process to enter round
|
||||
$r$, so $q$ starts round $r$ and sends $\Proposal$ message for some value $v$
|
||||
at time $t + \Delta + \timeoutPrecommit(r-1)$. Therefore, all correct processes
|
||||
receive the $\Proposal$ message from $q$ the latest by time $t + 2\Delta +
|
||||
\timeoutPrecommit(r-1)$. Therefore, if $\timeoutPropose(r) > 2\Delta +
|
||||
\timeoutPrecommit(r-1)$, all correct processes will receive $\Proposal$ message
|
||||
before $\timeoutPropose(r)$ expires.
|
||||
|
||||
By (3) and the rules at line~\ref{line:tab:recvProposal} and
|
||||
\ref{line:tab:acceptProposal}, all correct processes will accept the
|
||||
$\Proposal$ message for value $v$ and will send a $\Prevote$ message for
|
||||
$id(v)$ by time $t + 2\Delta + \timeoutPrecommit(r-1)$. Note that by the
|
||||
\emph{Gossip communication} property, the $\Prevote$ messages needed to trigger
|
||||
the rule at line~\ref{line:tab:acceptProposal} are received before time $t +
|
||||
\Delta$.
|
||||
|
||||
By time $t + 3\Delta + \timeoutPrecommit(r-1)$, all correct processes will receive
|
||||
$\Proposal$ for $v$ and $2f+1$ corresponding $\Prevote$ messages for $id(v)$.
|
||||
By the rule at line~\ref{line:tab:recvPrevote}, all correct processes will send
|
||||
a $\Precommit$ message (see line~\ref{line:tab:precommit-v}) for $id(v)$ by
|
||||
time $t + 3\Delta + \timeoutPrecommit(r-1)$. Therefore, by time $t + 4\Delta +
|
||||
\timeoutPrecommit(r-1)$, all correct processes will have received the $\Proposal$
|
||||
for $v$ and $2f+1$ $\Precommit$ messages for $id(v)$, so they decide at
|
||||
line~\ref{line:tab:decide} on $v$.
|
||||
|
||||
This scenario holds if every correct process $q$ sends a $\Precommit$ message
|
||||
before $\timeoutPrevote(r)$ expires, and if $\timeoutPrecommit(r)$ does not expire
|
||||
before $t + 4\Delta + \timeoutPrecommit(r-1)$. Let's assume that a correct process
|
||||
$c_1$ is the first correct process to trigger $\timeoutPrevote(r)$ (see the rule
|
||||
at line~\ref{line:tab:recvAny2/3Prevote}) at time $t_1 > t$. This implies that
|
||||
before time $t_1$, $c_1$ received a $\Proposal$ ($step_{c_1}$ must be
|
||||
$\prevote$ by the rule at line~\ref{line:tab:recvAny2/3Prevote}) and a set of
|
||||
$2f+1$ $\Prevote$ messages. By time $t_1 + \Delta$, all correct processes will
|
||||
receive those messages. Note that even if some correct process was in the
|
||||
smaller round before time $t_1$, at time $t_1 + \Delta$ it will start round $r$
|
||||
after receiving those messages (see the rule at
|
||||
line~\ref{line:tab:skipRounds}). Therefore, all correct processes will send
|
||||
their $\Prevote$ message for $id(v)$ by time $t_1 + \Delta$, and all correct
|
||||
processes will receive those messages the by time $t_1 + 2\Delta$. Therefore,
|
||||
as $\timeoutPrevote(r) > 2\Delta$, this ensures that all correct processes receive
|
||||
$\Prevote$ messages from all correct processes before their respective local
|
||||
$\timeoutPrevote(r)$ expire.
|
||||
|
||||
On the other hand, $\timeoutPrecommit(r)$ is triggered in a correct process $c_2$
|
||||
after it receives any set of $2f+1$ $\Precommit$ messages for the first time.
|
||||
Let's denote with $t_2 > t$ the earliest point in time $\timeoutPrecommit(r)$ is
|
||||
triggered in some correct process $c_2$. This implies that $c_2$ has received
|
||||
at least $f+1$ $\Precommit$ messages for $id(v)$ from correct processes, i.e.,
|
||||
those processes have received $\Proposal$ for $v$ and $2f+1$ $\Prevote$
|
||||
messages for $id(v)$ before time $t_2$. By the \emph{Gossip communication}
|
||||
property, all correct processes will receive those messages by time $t_2 +
|
||||
\Delta$, and will send $\Precommit$ messages for $id(v)$. Note that even if
|
||||
some correct processes were at time $t_2$ in a round smaller than $r$, by the
|
||||
rule at line~\ref{line:tab:skipRounds} they will enter round $r$ by time $t_2 +
|
||||
\Delta$. Therefore, by time $t_2 + 2\Delta$, all correct processes will
|
||||
receive $\Proposal$ for $v$ and $2f+1$ $\Precommit$ messages for $id(v)$. So if
|
||||
$\timeoutPrecommit(r) > 2\Delta$, all correct processes will decide before the
|
||||
timeout expires. \end{proof}
|
||||
|
||||
|
||||
\begin{lemma} \label{lemma:validValue} If a correct process $p$ locks a value
|
||||
$v$ at time $t_0 > GST$ in some round $r$ ($lockedValue = v$ and
|
||||
$lockedRound = r$) and $\timeoutPrecommit(r) > 2\Delta$, then all correct
|
||||
processes set $validValue$ to $v$ and $validRound$ to $r$ before starting
|
||||
round $r+1$. \end{lemma}
|
||||
|
||||
\begin{proof} In order to prove this Lemma, we need to prove that if the
|
||||
process $p$ locks a value $v$ at time $t_0$, then no correct process will
|
||||
leave round $r$ before time $t_0 + \Delta$ (unless it has already set
|
||||
$validValue$ to $v$ and $validRound$ to $r$). It is sufficient to prove
|
||||
this, since by the \emph{Gossip communication} property the messages that
|
||||
$p$ received at time $t_0$ and that triggered rule at
|
||||
line~\ref{line:tab:recvPrevote} will be received by time $t_0 + \Delta$ by
|
||||
all correct processes, so all correct processes that are still in round $r$
|
||||
will set $validValue$ to $v$ and $validRound$ to $r$ (by the rule at
|
||||
line~\ref{line:tab:recvPrevote}). To prove this, we need to compute the
|
||||
earliest point in time a correct process could leave round $r$ without
|
||||
updating $validValue$ to $v$ and $validRound$ to $r$ (we denote this time
|
||||
with $t_1$). The Lemma is correct if $t_0 + \Delta < t_1$.
|
||||
|
||||
If the process $p$ locks a value $v$ at time $t_0$, this implies that $p$
|
||||
received the valid $\Proposal$ message for $v$ and $2f+1$
|
||||
$\li{\Prevote,h,r,id(v)}$ at time $t_0$. At least $f+1$ of those messages are
|
||||
sent by correct processes. Let's denote this set of correct processes as $C$. By
|
||||
Lemma~\ref{lemma:majority-intersection} any set of $2f+1$ $\Prevote$ messages
|
||||
in round $r$ contains at least a single message from the set $C$.
|
||||
|
||||
Let's denote as time $t$ the earliest point in time a correct process, $c_1$, triggered
|
||||
$\timeoutPrevote(r)$. This implies that $c_1$ received $2f+1$ $\Prevote$ messages
|
||||
(see the rule at line \ref{line:tab:recvAny2/3Prevote}), where at least one of
|
||||
those messages was sent by a process $c_2$ from the set $C$. Therefore, process
|
||||
$c_2$ had received $\Proposal$ message before time $t$. By the \emph{Gossip
|
||||
communication} property, all correct processes will receive $\Proposal$ and
|
||||
$2f+1$ $\Prevote$ messages for round $r$ by time $t+\Delta$. The latest point
|
||||
in time $p$ will trigger $\timeoutPrevote(r)$ is $t+\Delta$\footnote{Note that
|
||||
even if $p$ was in smaller round at time $t$ it will start round $r$ by time
|
||||
$t+\Delta$.}. So the latest point in time $p$ can lock the value $v$ in
|
||||
round $r$ is $t_0 = t+\Delta+\timeoutPrevote(r)$ (as at this point
|
||||
$\timeoutPrevote(r)$ expires, so a process sends $\Precommit$ $\nil$ and updates
|
||||
$step$ to $\precommit$, see line \ref{line:tab:onTimeoutPrevote}).
|
||||
|
||||
Note that according to the Algorithm \ref{alg:tendermint}, a correct process
|
||||
can not send a $\Precommit$ message before receiving $2f+1$ $\Prevote$
|
||||
messages. Therefore, no correct process can send a $\Precommit$ message in
|
||||
round $r$ before time $t$. If a correct process sends a $\Precommit$ message
|
||||
for $\nil$, it implies that it has waited for the full duration of
|
||||
$\timeoutPrevote(r)$ (see line
|
||||
\ref{line:tab:precommit-nil-onTimeout})\footnote{The other case in which a
|
||||
correct process $\Precommit$ for $\nil$ is after receiving $2f+1$ $Prevote$ for
|
||||
$\nil$ messages, see the line \ref{line:tab:precommit-v-1}. By
|
||||
Lemma~\ref{lemma:majority-intersection}, this is not possible in round $r$.}.
|
||||
Therefore, no correct process can send $\Precommit$ for $\nil$ before time $t +
|
||||
\timeoutPrevote(r)$ (*).
|
||||
|
||||
A correct process $q$ that enters round $r+1$ must wait (i) $\timeoutPrecommit(r)$
|
||||
(see line \ref{line:tab:nextRound}) or (ii) receiving $f+1$ messages from the
|
||||
round $r+1$ (see the line \ref{line:tab:skipRounds}). In the former case, $q$
|
||||
receives $2f+1$ $\Precommit$ messages before starting $\timeoutPrecommit(r)$. If
|
||||
at least a single $\Precommit$ message from a correct process (at least $f+1$
|
||||
voting power equivalent of those messages is sent by correct processes) is for
|
||||
$\nil$, then $q$ cannot start round $r+1$ before time $t_1 = t +
|
||||
\timeoutPrevote(r) + \timeoutPrecommit(r)$ (see (*)). Therefore in this case we have:
|
||||
$t_0 + \Delta < t_1$, i.e., $t+2\Delta+\timeoutPrevote(r) < t + \timeoutPrevote(r) +
|
||||
\timeoutPrecommit(r)$, and this is true whenever $\timeoutPrecommit(r) > 2\Delta$, so
|
||||
Lemma holds in this case.
|
||||
|
||||
If in the set of $2f+1$ $\Precommit$ messages $q$ receives, there is at least a
|
||||
single $\Precommit$ for $id(v)$ message from a correct process $c$, then $q$
|
||||
can start the round $r+1$ the earliest at time $t_1 = t+\timeoutPrecommit(r)$. In
|
||||
this case, by the \emph{Gossip communication} property, all correct processes
|
||||
will receive $\Proposal$ and $2f+1$ $\Prevote$ messages (that $c$ received
|
||||
before time $t$) the latest at time $t+\Delta$. Therefore, $q$ will set
|
||||
$validValue$ to $v$ and $validRound$ to $r$ the latest at time $t+\Delta$. As
|
||||
$t+\Delta < t+\timeoutPrecommit(r)$, whenever $\timeoutPrecommit(r) > \Delta$, the
|
||||
Lemma holds also in this case.
|
||||
|
||||
In case (ii), $q$ received at least a single message from a correct process $c$
|
||||
from the round $r+1$. The earliest point in time $c$ could have started round
|
||||
$r+1$ is $t+\timeoutPrecommit(r)$ in case it received a $\Precommit$ message for
|
||||
$v$ from some correct process in the set of $2f+1$ $\Precommit$ messages it
|
||||
received. The same reasoning as above holds also in this case, so $q$ set
|
||||
$validValue$ to $v$ and $validRound$ to $r$ the latest by time $t+\Delta$. As
|
||||
$t+\Delta < t+\timeoutPrecommit(r)$, whenever $\timeoutPrecommit(r) > \Delta$, the
|
||||
Lemma holds also in this case. \end{proof}
|
||||
|
||||
\begin{lemma} \label{lemma:agreement} Algorithm~\ref{alg:tendermint} satisfies
|
||||
Termination. \end{lemma}
|
||||
|
||||
\begin{proof} Lemma~\ref{lemma:round-synchronisation} defines a scenario in
|
||||
which all correct processes decide. We now prove that within a bounded
|
||||
duration after GST such a scenario will unfold. Let's assume that at time
|
||||
$GST$ the highest round started by a correct process is $r_0$, and that
|
||||
there exists a correct process $p$ such that the following holds: for every
|
||||
correct process $c$, $lockedRound_c \le validRound_p$. Furthermore, we
|
||||
assume that $p$ will be the proposer in some round $r_1 > r$ (this is
|
||||
ensured by the $\coord$ function).
|
||||
|
||||
We have two cases to consider. In the first case, for all rounds $r \ge r_0$
|
||||
and $r < r_1$, no correct process locks a value (set $lockedRound$ to $r$). So
|
||||
in round $r_1$ we have the scenario from the
|
||||
Lemma~\ref{lemma:round-synchronisation}, so all correct processes decides in
|
||||
round $r_1$.
|
||||
|
||||
In the second case, a correct process locks a value $v$ in round $r_2$, where
|
||||
$r_2 \ge r_0$ and $r_2 < r_1$. Let's assume that $r_2$ is the highest round
|
||||
before $r_1$ in which some correct process $q$ locks a value. By Lemma
|
||||
\ref{lemma:validValue} at the end of round $r_2$ the following holds for all
|
||||
correct processes $c$: $validValue_c = lockedValue_q$ and $validRound_c = r_2$.
|
||||
Then in round $r_1$, the conditions for the
|
||||
Lemma~\ref{lemma:round-synchronisation} holds, so all correct processes decide.
|
||||
\end{proof}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
% ROUNDDIAG STYLE
|
||||
% for LaTeX version 2e
|
||||
% by -- 2008 Martin Hutle <martin.hutle@epfl.ch>
|
||||
%
|
||||
% This style file is free software; you can redistribute it and/or
|
||||
% modify it under the terms of the GNU Lesser General Public
|
||||
% License as published by the Free Software Foundation; either
|
||||
% version 2 of the License, or (at your option) any later version.
|
||||
%
|
||||
% This style file is distributed in the hope that it will be useful,
|
||||
% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
% Lesser General Public License for more details.
|
||||
%
|
||||
% You should have received a copy of the GNU Lesser General Public
|
||||
% License along with this style file; if not, write to the
|
||||
% Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
% Boston, MA 02111-1307, USA.
|
||||
%
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{rounddiag}
|
||||
\typeout{Document Style `rounddiag' - provides simple round diagrams}
|
||||
%
|
||||
\RequirePackage{ifthen}
|
||||
\RequirePackage{calc}
|
||||
\RequirePackage{tikz}
|
||||
|
||||
\def\rdstretch{3}
|
||||
|
||||
\tikzstyle{msg}=[->,thick,>=latex]
|
||||
\tikzstyle{rndline}=[dotted]
|
||||
\tikzstyle{procline}=[dotted]
|
||||
|
||||
\newenvironment{rounddiag}[2]{
|
||||
\begin{center}
|
||||
\begin{tikzpicture}
|
||||
\foreach \i in {1,...,#1}{
|
||||
\draw[procline] (0,#1-\i) node[xshift=-1em]{$p_{\i}$} -- (#2*\rdstretch+1,#1-\i);
|
||||
}
|
||||
\foreach \i in {0,...,#2}{
|
||||
\draw[rndline] (\i*\rdstretch+0.5,0) -- (\i*\rdstretch+0.5,#1-1);
|
||||
}
|
||||
\newcommand{\rdat}[2]{
|
||||
(##2*\rdstretch+0.5,#1-##1)
|
||||
}%
|
||||
\newcommand{\round}[2]{%
|
||||
\def\rdround{##1}
|
||||
\ifthenelse{\equal{##2}{}}{}{
|
||||
\node[yshift=-1em] at ({##1*\rdstretch+0.5-0.5*\rdstretch},0) {##2};
|
||||
}
|
||||
}%
|
||||
\newcommand{\rdmessage}[3]{\draw[msg]
|
||||
(\rdround*\rdstretch-\rdstretch+0.5,#1-##1) -- node[yshift=1.2ex]{##3}
|
||||
(\rdround*\rdstretch+0.5,#1-##2);}%
|
||||
\newcommand{\rdalltoall}{%
|
||||
\foreach \i in {1,...,#1}{
|
||||
\foreach \j in {1,...,#1}{
|
||||
{ \rdmessage{\i}{\j}{}}}}}%
|
||||
}{%
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{technote}[2007/11/09]
|
||||
\typeout{Template for quick notes with some useful definitions}
|
||||
|
||||
\RequirePackage{ifthen}
|
||||
\RequirePackage{calc}
|
||||
\RequirePackage{amsmath,amssymb,amsthm}
|
||||
\RequirePackage{epsfig}
|
||||
\RequirePackage{algorithm}
|
||||
\RequirePackage[noend]{algorithmicplus}
|
||||
|
||||
\newboolean{technote@noedit}
|
||||
\setboolean{technote@noedit}{false}
|
||||
\DeclareOption{noedit}{\setboolean{technote@noedit}{true}}
|
||||
|
||||
\newcounter{technote@lang}
|
||||
\setcounter{technote@lang}{0}
|
||||
\DeclareOption{german}{\setcounter{technote@lang}{1}}
|
||||
\DeclareOption{french}{\setcounter{technote@lang}{2}}
|
||||
|
||||
\DeclareOption{fullpage}{
|
||||
\oddsidemargin -10mm % Margin on odd side pages (default=0mm)
|
||||
\evensidemargin -10mm % Margin on even side pages (default=0mm)
|
||||
\topmargin -10mm % Top margin space (default=16mm)
|
||||
\headheight \baselineskip % Height of headers (default=0mm)
|
||||
\headsep \baselineskip % Separation spc btw header and text (d=0mm)
|
||||
\footskip 30pt % Separation spc btw text and footer (d=30pt)
|
||||
\textheight 230mm % Total text height (default=200mm)
|
||||
\textwidth 180mm % Total text width (default=160mm)
|
||||
}
|
||||
|
||||
\renewcommand{\algorithmiccomment}[1]{\hfill/* #1 */}
|
||||
\renewcommand{\algorithmiclnosize}{\scriptsize}
|
||||
|
||||
\newboolean{technote@truenumbers}
|
||||
\setboolean{technote@truenumbers}{false}
|
||||
\DeclareOption{truenumbers}{\setboolean{technote@truenumbers}{true}}
|
||||
|
||||
\ProcessOptions
|
||||
|
||||
\newcommand{\N}{\ifthenelse{\boolean{technote@truenumbers}}%
|
||||
{\mbox{\rm I\hspace{-.5em}N}}%
|
||||
{\mathbb{N}}}
|
||||
|
||||
\newcommand{\R}{\ifthenelse{\boolean{technote@truenumbers}}%
|
||||
{\mbox{\rm I\hspace{-.2em}R}}%
|
||||
{\mathbb{R}}}
|
||||
|
||||
\newcommand{\Z}{\mathbb{Z}}
|
||||
|
||||
\newcommand{\set}[1]{\left\{#1\right\}}
|
||||
\newcommand{\mathsc}[1]{\mbox{\sc #1}}
|
||||
\newcommand{\li}[1]{\langle#1\rangle}
|
||||
\newcommand{\st}{\;s.t.\;}
|
||||
\newcommand{\Real}{\R}
|
||||
\newcommand{\Natural}{\N}
|
||||
\newcommand{\Integer}{\Z}
|
||||
|
||||
% edit commands
|
||||
\newcommand{\newedit}[2]{
|
||||
\newcommand{#1}[2][default]{%
|
||||
\ifthenelse{\boolean{technote@noedit}}{}{
|
||||
\par\vspace{2mm}
|
||||
\noindent
|
||||
\begin{tabular}{|l|}\hline
|
||||
\parbox{\linewidth-\tabcolsep*2}{{\bf #2:}\hfill\ifthenelse{\equal{##1}{default}}{}{##1}}\\\hline
|
||||
\parbox{\linewidth-\tabcolsep*2}{\rule{0pt}{5mm}##2\rule[-2mm]{0pt}{2mm}}\\\hline
|
||||
\end{tabular}
|
||||
\par\vspace{2mm}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\newedit{\note}{Note}
|
||||
\newedit{\comment}{Comment}
|
||||
\newedit{\question}{Question}
|
||||
\newedit{\content}{Content}
|
||||
\newedit{\problem}{Problem}
|
||||
|
||||
\newcommand{\mnote}[1]{\marginpar{\scriptsize\it
|
||||
\begin{minipage}[t]{0.8 in}
|
||||
\raggedright #1
|
||||
\end{minipage}}}
|
||||
|
||||
\newcommand{\Insert}[1]{\underline{#1}\marginpar{$|$}}
|
||||
|
||||
\newcommand{\Delete}[1]{\marginpar{$|$}
|
||||
}
|
||||
|
||||
% lemma, theorem, etc.
|
||||
\newtheorem{lemma}{Lemma}
|
||||
\newtheorem{proposition}{Proposition}
|
||||
\newtheorem{theorem}{Theorem}
|
||||
\newtheorem{corollary}{Corollary}
|
||||
\newtheorem{assumption}{Assumption}
|
||||
\newtheorem{definition}{Definition}
|
||||
|
||||
\gdef\op|{\,|\;}
|
||||
\gdef\op:{\,:\;}
|
||||
\newcommand{\assign}{\leftarrow}
|
||||
\newcommand{\inc}[1]{#1 \assign #1 + 1}
|
||||
\newcommand{\isdef}{:=}
|
||||
|
||||
\newcommand{\ident}[1]{\mathit{#1}}
|
||||
\def\newident#1{\expandafter\def\csname #1\endcsname{\ident{#1}}}
|
||||
|
||||
\newcommand{\eg}{{\it e.g.}}
|
||||
\newcommand{\ie}{{\it i.e.}}
|
||||
\newcommand{\apriori}{{\it apriori}}
|
||||
\newcommand{\etal}{{\it et al.}}
|
||||
|
||||
\newcommand\ps@technote{%
|
||||
\renewcommand\@oddhead{\theheader}%
|
||||
\let\@evenhead\@oddhead
|
||||
\renewcommand\@evenfoot
|
||||
{\hfil\normalfont\textrm{\thepage}\hfil}%
|
||||
\let\@oddfoot\@evenfoot
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
---
|
||||
order: 1
|
||||
---
|
||||
# Byzantine Consensus Algorithm
|
||||
|
||||
## Terms
|
||||
|
||||
- The network is composed of optionally connected _nodes_. Nodes
|
||||
directly connected to a particular node are called _peers_.
|
||||
- The consensus process in deciding the next block (at some _height_
|
||||
`H`) is composed of one or many _rounds_.
|
||||
- `NewHeight`, `Propose`, `Prevote`, `Precommit`, and `Commit`
|
||||
represent state machine states of a round. (aka `RoundStep` or
|
||||
just "step").
|
||||
- A node is said to be _at_ a given height, round, and step, or at
|
||||
`(H,R,S)`, or at `(H,R)` in short to omit the step.
|
||||
- To _prevote_ or _precommit_ something means to broadcast a [prevote
|
||||
vote](https://godoc.org/github.com/tendermint/tendermint/types#Vote)
|
||||
or [first precommit
|
||||
vote](https://godoc.org/github.com/tendermint/tendermint/types#FirstPrecommit)
|
||||
for something.
|
||||
- A vote _at_ `(H,R)` is a vote signed with the bytes for `H` and `R`
|
||||
included in its [sign-bytes](../core/data_structures.md#vote).
|
||||
- _+2/3_ is short for "more than 2/3"
|
||||
- _1/3+_ is short for "1/3 or more"
|
||||
- A set of +2/3 of prevotes for a particular block or `<nil>` at
|
||||
`(H,R)` is called a _proof-of-lock-change_ or _PoLC_ for short.
|
||||
|
||||
## State Machine Overview
|
||||
|
||||
At each height of the blockchain a round-based protocol is run to
|
||||
determine the next block. Each round is composed of three _steps_
|
||||
(`Propose`, `Prevote`, and `Precommit`), along with two special steps
|
||||
`Commit` and `NewHeight`.
|
||||
|
||||
In the optimal scenario, the order of steps is:
|
||||
|
||||
```md
|
||||
NewHeight -> (Propose -> Prevote -> Precommit)+ -> Commit -> NewHeight ->...
|
||||
```
|
||||
|
||||
The sequence `(Propose -> Prevote -> Precommit)` is called a _round_.
|
||||
There may be more than one round required to commit a block at a given
|
||||
height. Examples for why more rounds may be required include:
|
||||
|
||||
- The designated proposer was not online.
|
||||
- The block proposed by the designated proposer was not valid.
|
||||
- The block proposed by the designated proposer did not propagate
|
||||
in time.
|
||||
- The block proposed was valid, but +2/3 of prevotes for the proposed
|
||||
block were not received in time for enough validator nodes by the
|
||||
time they reached the `Precommit` step. Even though +2/3 of prevotes
|
||||
are necessary to progress to the next step, at least one validator
|
||||
may have voted `<nil>` or maliciously voted for something else.
|
||||
- The block proposed was valid, and +2/3 of prevotes were received for
|
||||
enough nodes, but +2/3 of precommits for the proposed block were not
|
||||
received for enough validator nodes.
|
||||
|
||||
Some of these problems are resolved by moving onto the next round &
|
||||
proposer. Others are resolved by increasing certain round timeout
|
||||
parameters over each successive round.
|
||||
|
||||
## State Machine Diagram
|
||||
|
||||
```md
|
||||
+-------------------------------------+
|
||||
v |(Wait til `CommmitTime+timeoutCommit`)
|
||||
+-----------+ +-----+-----+
|
||||
+----------> | Propose +--------------+ | NewHeight |
|
||||
| +-----------+ | +-----------+
|
||||
| | ^
|
||||
|(Else, after timeoutPrecommit) v |
|
||||
+-----+-----+ +-----------+ |
|
||||
| Precommit | <------------------------+ Prevote | |
|
||||
+-----+-----+ +-----------+ |
|
||||
|(When +2/3 Precommits for block found) |
|
||||
v |
|
||||
+--------------------------------------------------------------------+
|
||||
| Commit |
|
||||
| |
|
||||
| * Set CommitTime = now; |
|
||||
| * Wait for block, then stage/save/commit block; |
|
||||
+--------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
# Background Gossip
|
||||
|
||||
A node may not have a corresponding validator private key, but it
|
||||
nevertheless plays an active role in the consensus process by relaying
|
||||
relevant meta-data, proposals, blocks, and votes to its peers. A node
|
||||
that has the private keys of an active validator and is engaged in
|
||||
signing votes is called a _validator-node_. All nodes (not just
|
||||
validator-nodes) have an associated state (the current height, round,
|
||||
and step) and work to make progress.
|
||||
|
||||
Between two nodes there exists a `Connection`, and multiplexed on top of
|
||||
this connection are fairly throttled `Channel`s of information. An
|
||||
epidemic gossip protocol is implemented among some of these channels to
|
||||
bring peers up to speed on the most recent state of consensus. For
|
||||
example,
|
||||
|
||||
- Nodes gossip `PartSet` parts of the current round's proposer's
|
||||
proposed block. A LibSwift inspired algorithm is used to quickly
|
||||
broadcast blocks across the gossip network.
|
||||
- Nodes gossip prevote/precommit votes. A node `NODE_A` that is ahead
|
||||
of `NODE_B` can send `NODE_B` prevotes or precommits for `NODE_B`'s
|
||||
current (or future) round to enable it to progress forward.
|
||||
- Nodes gossip prevotes for the proposed PoLC (proof-of-lock-change)
|
||||
round if one is proposed.
|
||||
- Nodes gossip to nodes lagging in blockchain height with block
|
||||
[commits](https://godoc.org/github.com/tendermint/tendermint/types#Commit)
|
||||
for older blocks.
|
||||
- Nodes opportunistically gossip `ReceivedVote` messages to hint peers what
|
||||
votes it already has.
|
||||
- Nodes broadcast their current state to all neighboring peers. (but
|
||||
is not gossiped further)
|
||||
|
||||
There's more, but let's not get ahead of ourselves here.
|
||||
|
||||
## Proposals
|
||||
|
||||
A proposal is signed and published by the designated proposer at each
|
||||
round. The proposer is chosen by a deterministic and non-choking round
|
||||
robin selection algorithm that selects proposers in proportion to their
|
||||
voting power (see
|
||||
[implementation](https://github.com/tendermint/tendermint/blob/master/types/validator_set.go)).
|
||||
|
||||
A proposal at `(H,R)` is composed of a block and an optional latest
|
||||
`PoLC-Round < R` which is included iff the proposer knows of one. This
|
||||
hints the network to allow nodes to unlock (when safe) to ensure the
|
||||
liveness property.
|
||||
|
||||
## State Machine Spec
|
||||
|
||||
### Propose Step (height:H,round:R)
|
||||
|
||||
Upon entering `Propose`:
|
||||
|
||||
- The designated proposer proposes a block at `(H,R)`.
|
||||
|
||||
The `Propose` step ends:
|
||||
|
||||
- After `timeoutProposeR` after entering `Propose`. --> goto
|
||||
`Prevote(H,R)`
|
||||
- After receiving proposal block and all prevotes at `PoLC-Round`. -->
|
||||
goto `Prevote(H,R)`
|
||||
- After [common exit conditions](#common-exit-conditions)
|
||||
|
||||
### Prevote Step (height:H,round:R)
|
||||
|
||||
Upon entering `Prevote`, each validator broadcasts its prevote vote.
|
||||
|
||||
- First, if the validator is locked on a block since `LastLockRound`
|
||||
but now has a PoLC for something else at round `PoLC-Round` where
|
||||
`LastLockRound < PoLC-Round < R`, then it unlocks.
|
||||
- If the validator is still locked on a block, it prevotes that.
|
||||
- Else, if the proposed block from `Propose(H,R)` is good, it
|
||||
prevotes that.
|
||||
- Else, if the proposal is invalid or wasn't received on time, it
|
||||
prevotes `<nil>`.
|
||||
|
||||
The `Prevote` step ends:
|
||||
|
||||
- After +2/3 prevotes for a particular block or `<nil>`. -->; goto
|
||||
`Precommit(H,R)`
|
||||
- After `timeoutPrevote` after receiving any +2/3 prevotes. --> goto
|
||||
`Precommit(H,R)`
|
||||
- After [common exit conditions](#common-exit-conditions)
|
||||
|
||||
### Precommit Step (height:H,round:R)
|
||||
|
||||
Upon entering `Precommit`, each validator broadcasts its precommit vote.
|
||||
|
||||
- If the validator has a PoLC at `(H,R)` for a particular block `B`, it
|
||||
(re)locks (or changes lock to) and precommits `B` and sets
|
||||
`LastLockRound = R`.
|
||||
- Else, if the validator has a PoLC at `(H,R)` for `<nil>`, it unlocks
|
||||
and precommits `<nil>`.
|
||||
- Else, it keeps the lock unchanged and precommits `<nil>`.
|
||||
|
||||
A precommit for `<nil>` means "I didn’t see a PoLC for this round, but I
|
||||
did get +2/3 prevotes and waited a bit".
|
||||
|
||||
The Precommit step ends:
|
||||
|
||||
- After +2/3 precommits for `<nil>`. --> goto `Propose(H,R+1)`
|
||||
- After `timeoutPrecommit` after receiving any +2/3 precommits. --> goto
|
||||
`Propose(H,R+1)`
|
||||
- After [common exit conditions](#common-exit-conditions)
|
||||
|
||||
### Common exit conditions
|
||||
|
||||
- After +2/3 precommits for a particular block. --> goto
|
||||
`Commit(H)`
|
||||
- After any +2/3 prevotes received at `(H,R+x)`. --> goto
|
||||
`Prevote(H,R+x)`
|
||||
- After any +2/3 precommits received at `(H,R+x)`. --> goto
|
||||
`Precommit(H,R+x)`
|
||||
|
||||
### Commit Step (height:H)
|
||||
|
||||
- Set `CommitTime = now()`
|
||||
- Wait until block is received. --> goto `NewHeight(H+1)`
|
||||
|
||||
### NewHeight Step (height:H)
|
||||
|
||||
- Move `Precommits` to `LastCommit` and increment height.
|
||||
- Set `StartTime = CommitTime+timeoutCommit`
|
||||
- Wait until `StartTime` to receive straggler commits. --> goto
|
||||
`Propose(H,0)`
|
||||
|
||||
## Proofs
|
||||
|
||||
### Proof of Safety
|
||||
|
||||
Assume that at most -1/3 of the voting power of validators is byzantine.
|
||||
If a validator commits block `B` at round `R`, it's because it saw +2/3
|
||||
of precommits at round `R`. This implies that 1/3+ of honest nodes are
|
||||
still locked at round `R' > R`. These locked validators will remain
|
||||
locked until they see a PoLC at `R' > R`, but this won't happen because
|
||||
1/3+ are locked and honest, so at most -2/3 are available to vote for
|
||||
anything other than `B`.
|
||||
|
||||
### Proof of Liveness
|
||||
|
||||
If 1/3+ honest validators are locked on two different blocks from
|
||||
different rounds, a proposers' `PoLC-Round` will eventually cause nodes
|
||||
locked from the earlier round to unlock. Eventually, the designated
|
||||
proposer will be one that is aware of a PoLC at the later round. Also,
|
||||
`timeoutProposalR` increments with round `R`, while the size of a
|
||||
proposal are capped, so eventually the network is able to "fully gossip"
|
||||
the whole proposal (e.g. the block & PoLC).
|
||||
|
||||
### Proof of Fork Accountability
|
||||
|
||||
Define the JSet (justification-vote-set) at height `H` of a validator
|
||||
`V1` to be all the votes signed by the validator at `H` along with
|
||||
justification PoLC prevotes for each lock change. For example, if `V1`
|
||||
signed the following precommits: `Precommit(B1 @ round 0)`,
|
||||
`Precommit(<nil> @ round 1)`, `Precommit(B2 @ round 4)` (note that no
|
||||
precommits were signed for rounds 2 and 3, and that's ok),
|
||||
`Precommit(B1 @ round 0)` must be justified by a PoLC at round 0, and
|
||||
`Precommit(B2 @ round 4)` must be justified by a PoLC at round 4; but
|
||||
the precommit for `<nil>` at round 1 is not a lock-change by definition
|
||||
so the JSet for `V1` need not include any prevotes at round 1, 2, or 3
|
||||
(unless `V1` happened to have prevoted for those rounds).
|
||||
|
||||
Further, define the JSet at height `H` of a set of validators `VSet` to
|
||||
be the union of the JSets for each validator in `VSet`. For a given
|
||||
commit by honest validators at round `R` for block `B` we can construct
|
||||
a JSet to justify the commit for `B` at `R`. We say that a JSet
|
||||
_justifies_ a commit at `(H,R)` if all the committers (validators in the
|
||||
commit-set) are each justified in the JSet with no duplicitous vote
|
||||
signatures (by the committers).
|
||||
|
||||
- **Lemma**: When a fork is detected by the existence of two
|
||||
conflicting [commits](../core/data_structures.md#commit), the
|
||||
union of the JSets for both commits (if they can be compiled) must
|
||||
include double-signing by at least 1/3+ of the validator set.
|
||||
**Proof**: The commit cannot be at the same round, because that
|
||||
would immediately imply double-signing by 1/3+. Take the union of
|
||||
the JSets of both commits. If there is no double-signing by at least
|
||||
1/3+ of the validator set in the union, then no honest validator
|
||||
could have precommitted any different block after the first commit.
|
||||
Yet, +2/3 did. Reductio ad absurdum.
|
||||
|
||||
As a corollary, when there is a fork, an external process can determine
|
||||
the blame by requiring each validator to justify all of its round votes.
|
||||
Either we will find 1/3+ who cannot justify at least one of their votes,
|
||||
and/or, we will find 1/3+ who had double-signed.
|
||||
|
||||
### Alternative algorithm
|
||||
|
||||
Alternatively, we can take the JSet of a commit to be the "full commit".
|
||||
That is, if light clients and validators do not consider a block to be
|
||||
committed unless the JSet of the commit is also known, then we get the
|
||||
desirable property that if there ever is a fork (e.g. there are two
|
||||
conflicting "full commits"), then 1/3+ of the validators are immediately
|
||||
punishable for double-signing.
|
||||
|
||||
There are many ways to ensure that the gossip network efficiently share
|
||||
the JSet of a commit. One solution is to add a new message type that
|
||||
tells peers that this node has (or does not have) a +2/3 majority for B
|
||||
(or) at (H,R), and a bitarray of which votes contributed towards that
|
||||
majority. Peers can react by responding with appropriate votes.
|
||||
|
||||
We will implement such an algorithm for the next iteration of the
|
||||
Tendermint consensus protocol.
|
||||
|
||||
Other potential improvements include adding more data in votes such as
|
||||
the last known PoLC round that caused a lock change, and the last voted
|
||||
round/step (or, we may require that validators not skip any votes). This
|
||||
may make JSet verification/gossip logic easier to implement.
|
||||
|
||||
### Censorship Attacks
|
||||
|
||||
Due to the definition of a block
|
||||
[commit](https://github.com/tendermint/tendermint/blob/master/docs/tendermint-core/validators.md), any 1/3+ coalition of
|
||||
validators can halt the blockchain by not broadcasting their votes. Such
|
||||
a coalition can also censor particular transactions by rejecting blocks
|
||||
that include these transactions, though this would result in a
|
||||
significant proportion of block proposals to be rejected, which would
|
||||
slow down the rate of block commits of the blockchain, reducing its
|
||||
utility and value. The malicious coalition might also broadcast votes in
|
||||
a trickle so as to grind blockchain block commits to a near halt, or
|
||||
engage in any combination of these attacks.
|
||||
|
||||
If a global active adversary were also involved, it can partition the
|
||||
network in such a way that it may appear that the wrong subset of
|
||||
validators were responsible for the slowdown. This is not just a
|
||||
limitation of Tendermint, but rather a limitation of all consensus
|
||||
protocols whose network is potentially controlled by an active
|
||||
adversary.
|
||||
|
||||
### Overcoming Forks and Censorship Attacks
|
||||
|
||||
For these types of attacks, a subset of the validators through external
|
||||
means should coordinate to sign a reorg-proposal that chooses a fork
|
||||
(and any evidence thereof) and the initial subset of validators with
|
||||
their signatures. Validators who sign such a reorg-proposal forego its
|
||||
collateral on all other forks. Clients should verify the signatures on
|
||||
the reorg-proposal, verify any evidence, and make a judgement or prompt
|
||||
the end-user for a decision. For example, a phone wallet app may prompt
|
||||
the user with a security warning, while a refrigerator may accept any
|
||||
reorg-proposal signed by +1/2 of the original validators.
|
||||
|
||||
No non-synchronous Byzantine fault-tolerant algorithm can come to
|
||||
consensus when 1/3+ of validators are dishonest, yet a fork assumes that
|
||||
1/3+ of validators have already been dishonest by double-signing or
|
||||
lock-changing without justification. So, signing the reorg-proposal is a
|
||||
coordination problem that cannot be solved by any non-synchronous
|
||||
protocol (i.e. automatically, and without making assumptions about the
|
||||
reliability of the underlying network). It must be provided by means
|
||||
external to the weakly-synchronous Tendermint consensus algorithm. For
|
||||
now, we leave the problem of reorg-proposal coordination to human
|
||||
coordination via internet media. Validators must take care to ensure
|
||||
that there are no significant network partitions, to avoid situations
|
||||
where two conflicting reorg-proposals are signed.
|
||||
|
||||
Assuming that the external coordination medium and protocol is robust,
|
||||
it follows that forks are less of a concern than [censorship
|
||||
attacks](#censorship-attacks).
|
||||
|
||||
### Canonical vs subjective commit
|
||||
|
||||
We distinguish between "canonical" and "subjective" commits. A subjective commit is what
|
||||
each validator sees locally when they decide to commit a block. The canonical commit is
|
||||
what is included by the proposer of the next block in the `LastCommit` field of
|
||||
the block. This is what makes it canonical and ensures every validator agrees on the canonical commit,
|
||||
even if it is different from the +2/3 votes a validator has seen, which caused the validator to
|
||||
commit the respective block. Each block contains a canonical +2/3 commit for the previous
|
||||
block.
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
order: 2
|
||||
---
|
||||
# Creating a proposal
|
||||
|
||||
A block consists of a header, transactions, votes (the commit),
|
||||
and a list of evidence of malfeasance (ie. signing conflicting votes).
|
||||
|
||||
We include no more than 1/10th of the maximum block size
|
||||
(`ConsensusParams.Block.MaxBytes`) of evidence with each block.
|
||||
|
||||
## Reaping transactions from the mempool
|
||||
|
||||
When we reap transactions from the mempool, we calculate maximum data
|
||||
size by subtracting maximum header size (`MaxHeaderBytes`), the maximum
|
||||
amino overhead for a block (`MaxAminoOverheadForBlock`), the size of
|
||||
the last commit (if present) and evidence (if present). While reaping
|
||||
we account for amino overhead for each transaction.
|
||||
|
||||
```go
|
||||
func MaxDataBytes(maxBytes int64, valsCount, evidenceCount int) int64 {
|
||||
return maxBytes -
|
||||
MaxOverheadForBlock -
|
||||
MaxHeaderBytes -
|
||||
int64(valsCount)*MaxVoteBytes -
|
||||
int64(evidenceCount)*MaxEvidenceBytes
|
||||
}
|
||||
```
|
||||
|
||||
## Validating transactions in the mempool
|
||||
|
||||
Before we accept a transaction in the mempool, we check if it's size is no more
|
||||
than {MaxDataSize}. {MaxDataSize} is calculated using the same formula as
|
||||
above, except we subtract the max number of evidence, {MaxNum} by the maximum size of evidence
|
||||
|
||||
```go
|
||||
func MaxDataBytesUnknownEvidence(maxBytes int64, valsCount int) int64 {
|
||||
return maxBytes -
|
||||
MaxOverheadForBlock -
|
||||
MaxHeaderBytes -
|
||||
(maxNumEvidence * MaxEvidenceBytes)
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,199 @@
|
||||
# Evidence
|
||||
|
||||
Evidence is an important component of Tendermint's security model. Whilst the core
|
||||
consensus protocol provides correctness gaurantees for state machine replication
|
||||
that can tolerate less than 1/3 failures, the evidence system looks to detect and
|
||||
gossip byzantine faults whose combined power is greater than or equal to 1/3. It is worth noting that
|
||||
the evidence system is designed purely to detect possible attacks, gossip them,
|
||||
commit them on chain and inform the application running on top of Tendermint.
|
||||
Evidence in itself does not punish "bad actors", this is left to the discretion
|
||||
of the application. A common form of punishment is slashing where the validators
|
||||
that were caught violating the protocol have all or a portion of their voting
|
||||
power removed. Evidence, given the assumption that 1/3+ of the network is still
|
||||
byzantine, is susceptible to censorship and should therefore be considered added
|
||||
security on a "best effort" basis.
|
||||
|
||||
This document walks through the various forms of evidence, how they are detected,
|
||||
gossiped, verified and committed.
|
||||
|
||||
> NOTE: Evidence here is internal to tendermint and should not be confused with
|
||||
> application evidence
|
||||
|
||||
## Detection
|
||||
|
||||
### Equivocation
|
||||
|
||||
Equivocation is the most fundamental of byzantine faults. Simply put, to prevent
|
||||
replication of state across all nodes, a validator tries to convince some subset
|
||||
of nodes to commit one block whilst convincing another subset to commit a
|
||||
different block. This is achieved by double voting (hence
|
||||
`DuplicateVoteEvidence`). A successful duplicate vote attack requires greater
|
||||
than 1/3 voting power and a (temporary) network partition between the aforementioned
|
||||
subsets. This is because in consensus, votes are gossiped around. When a node
|
||||
observes two conflicting votes from the same peer, it will use the two votes of
|
||||
evidence and begin gossiping this evidence to other nodes. [Verification](#duplicatevoteevidence) is addressed further down.
|
||||
|
||||
```go
|
||||
type DuplicateVoteEvidence struct {
|
||||
VoteA Vote
|
||||
VoteB Vote
|
||||
|
||||
// and abci specific fields
|
||||
}
|
||||
```
|
||||
|
||||
### Light Client Attacks
|
||||
|
||||
Light clients also comply with the 1/3+ security model, however, by using a
|
||||
different, more lightweight verification method they are subject to a
|
||||
different kind of 1/3+ attack whereby the byzantine validators could sign an
|
||||
alternative light block that the light client will think is valid. Detection,
|
||||
explained in greater detail
|
||||
[here](../light-client/detection/detection_003_reviewed.md), involves comparison
|
||||
with multiple other nodes in the hope that at least one is "honest". An "honest"
|
||||
node will return a challenging light block for the light client to validate. If
|
||||
this challenging light block also meets the
|
||||
[validation criteria](../light-client/verification/verification_001_published.md)
|
||||
then the light client sends the "forged" light block to the node.
|
||||
[Verification](#lightclientattackevidence) is addressed further down.
|
||||
|
||||
```go
|
||||
type LightClientAttackEvidence struct {
|
||||
ConflictingBlock LightBlock
|
||||
CommonHeight int64
|
||||
|
||||
// and abci specific fields
|
||||
}
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
If a node receives evidence, it will first try to verify it, then persist it.
|
||||
Evidence of byzantine behavior should only be committed once (uniqueness) and
|
||||
should be committed within a certain period from the point that it occurred
|
||||
(timely). Timelines is defined by the `EvidenceParams`: `MaxAgeNumBlocks` and
|
||||
`MaxAgeDuration`. In Proof of Stake chains where validators are bonded, evidence
|
||||
age should be less than the unbonding period so validators still can be
|
||||
punished. Given these two propoerties the following initial checks are made.
|
||||
|
||||
1. Has the evidence expired? This is done by taking the height of the `Vote`
|
||||
within `DuplicateVoteEvidence` or `CommonHeight` within
|
||||
`LightClientAttakEvidence`. The evidence height is then used to retrieve the
|
||||
header and thus the time of the block that corresponds to the evidence. If
|
||||
`CurrentHeight - MaxAgeNumBlocks > EvidenceHeight` && `CurrentTime -
|
||||
MaxAgeDuration > EvidenceTime`, the evidence is considered expired and
|
||||
ignored.
|
||||
|
||||
2. Has the evidence already been committed? The evidence pool tracks the hash of
|
||||
all committed evidence and uses this to determine uniqueness. If a new
|
||||
evidence has the same hash as a committed one, the new evidence will be
|
||||
ignored.
|
||||
|
||||
### DuplicateVoteEvidence
|
||||
|
||||
Valid `DuplicateVoteEvidence` must adhere to the following rules:
|
||||
|
||||
- Validator Address, Height, Round and Type must be the same for both votes
|
||||
|
||||
- BlockID must be different for both votes (BlockID can be for a nil block)
|
||||
|
||||
- Validator must have been in the validator set at that height
|
||||
|
||||
- Vote signature must be correctly signed. This also uses `ChainID` so we know
|
||||
that the fault occurred on this chain
|
||||
|
||||
### LightClientAttackEvidence
|
||||
|
||||
Valid Light Client Attack Evidence must adhere to the following rules:
|
||||
|
||||
- If the header of the light block is invalid, thus indicating a lunatic attack,
|
||||
the node must check that they can use `verifySkipping` from their header at
|
||||
the common height to the conflicting header
|
||||
|
||||
- If the header is valid, then the validator sets are the same and this is
|
||||
either a form of equivocation or amnesia. We therefore check that 2/3 of the
|
||||
validator set also signed the conflicting header.
|
||||
|
||||
- The nodes own header at the same height as the conflicting header must have a
|
||||
different hash to the conflicting header.
|
||||
|
||||
- If the nodes latest header is less in height to the conflicting header, then
|
||||
the node must check that the conflicting block has a time that is less than
|
||||
this latest header (This is a forward lunatic attack).
|
||||
|
||||
## Gossiping
|
||||
|
||||
If a node verifies evidence it then broadcasts it to all peers, continously sending
|
||||
the same evidence once every 10 seconds until the evidence is seen on chain or
|
||||
expires.
|
||||
|
||||
## Commiting on Chain
|
||||
|
||||
Evidence takes strict priority over regular transactions, thus a block is filled
|
||||
with evidence first and transactions take up the remainder of the space. To
|
||||
mitigate the threat of an already punished node from spamming the network with
|
||||
more evidence, the size of the evidence in a block can be capped by
|
||||
`EvidenceParams.MaxBytes`. Nodes receiving blocks with evidence will validate
|
||||
the evidence before sending `Prevote` and `Precommit` votes. The evidence pool
|
||||
will usually cache verifications so that this process is much quicker.
|
||||
|
||||
## Sending Evidence to the Application
|
||||
|
||||
After evidence is committed, the block is then processed by the block executor
|
||||
which delivers the evidence to the application via `EndBlock`. Evidence is
|
||||
stripped of the actual proof, split up per faulty validator and only the
|
||||
validator, height, time and evidence type is sent.
|
||||
|
||||
```proto
|
||||
enum EvidenceType {
|
||||
UNKNOWN = 0;
|
||||
DUPLICATE_VOTE = 1;
|
||||
LIGHT_CLIENT_ATTACK = 2;
|
||||
}
|
||||
|
||||
message Evidence {
|
||||
EvidenceType type = 1;
|
||||
// The offending validator
|
||||
Validator validator = 2 [(gogoproto.nullable) = false];
|
||||
// The height when the offense occurred
|
||||
int64 height = 3;
|
||||
// The corresponding time where the offense occurred
|
||||
google.protobuf.Timestamp time = 4 [
|
||||
(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
|
||||
// Total voting power of the validator set in case the ABCI application does
|
||||
// not store historical validators.
|
||||
// https://github.com/tendermint/tendermint/issues/4581
|
||||
int64 total_voting_power = 5;
|
||||
}
|
||||
```
|
||||
|
||||
`DuplicateVoteEvidence` and `LightClientAttackEvidence` are self-contained in
|
||||
the sense that the evidence can be used to derive the `abci.Evidence` that is
|
||||
sent to the application. Because of this, extra fields are necessary:
|
||||
|
||||
```go
|
||||
type DuplicateVoteEvidence struct {
|
||||
VoteA *Vote
|
||||
VoteB *Vote
|
||||
|
||||
// abci specific information
|
||||
TotalVotingPower int64
|
||||
ValidatorPower int64
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
type LightClientAttackEvidence struct {
|
||||
ConflictingBlock *LightBlock
|
||||
CommonHeight int64
|
||||
|
||||
// abci specific information
|
||||
ByzantineValidators []*Validator
|
||||
TotalVotingPower int64
|
||||
Timestamp time.Time
|
||||
}
|
||||
```
|
||||
|
||||
These ABCI specific fields don't affect validity of the evidence itself but must
|
||||
be consistent amongst nodes and agreed upon on chain. If evidence with the
|
||||
incorrect abci information is sent, a node will create new evidence from it and
|
||||
replace the ABCI fields with the correct information.
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
order: 1
|
||||
parent:
|
||||
title: Light Client
|
||||
order: false
|
||||
---
|
||||
# Tendermint Light Client Protocol
|
||||
|
||||
Deprecated, please see [light-client](../../light-client/README.md).
|
||||
@@ -0,0 +1,3 @@
|
||||
# Fork accountability
|
||||
|
||||
Deprecated, please see [light-client/accountability](../../light-client/accountability.md).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 119 KiB |
@@ -0,0 +1,3 @@
|
||||
# Detection
|
||||
|
||||
Deprecated, please see [light-client/detection](../../light-client/detection.md).
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user