Merge pull request #2350 from tendermint/release/v0.24.0

Release/v0.24.0
This commit is contained in:
Ethan Buchman 2018-09-07 07:46:44 -04:00 committed by GitHub
commit d419fffe18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
321 changed files with 12279 additions and 6526 deletions

1550
.circleci/codecov.sh Normal file

File diff suppressed because it is too large Load Diff

View File

@ -41,10 +41,10 @@ jobs:
key: v3-pkg-cache
paths:
- /go/pkg
- save_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
paths:
- /go/src/github.com/tendermint/tendermint
# - save_cache:
# key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
# paths:
# - /go/src/github.com/tendermint/tendermint
build_slate:
<<: *defaults
@ -53,8 +53,23 @@ jobs:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
# https://discuss.circleci.com/t/saving-cache-stopped-working-warning-skipping-this-step-disabled-in-configuration/24423/2
# - restore_cache:
# key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: tools
command: |
export PATH="$GOBIN:$PATH"
make get_tools
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run:
name: slate docs
command: |
@ -69,8 +84,22 @@ jobs:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
# - restore_cache:
# key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: tools
command: |
export PATH="$GOBIN:$PATH"
make get_tools
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run:
name: metalinter
command: |
@ -91,8 +120,22 @@ jobs:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
# - restore_cache:
# key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: tools
command: |
export PATH="$GOBIN:$PATH"
make get_tools
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run:
name: Run abci apps tests
command: |
@ -108,8 +151,22 @@ jobs:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
# - restore_cache:
# key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: tools
command: |
export PATH="$GOBIN:$PATH"
make get_tools
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run:
name: Run abci-cli tests
command: |
@ -123,8 +180,22 @@ jobs:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
# - restore_cache:
# key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: tools
command: |
export PATH="$GOBIN:$PATH"
make get_tools
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends bsdmainutils
- run:
name: Run tests
@ -138,8 +209,22 @@ jobs:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
# - restore_cache:
# key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: tools
command: |
export PATH="$GOBIN:$PATH"
make get_tools
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run: mkdir -p /tmp/logs
- run:
name: Run tests
@ -163,12 +248,48 @@ jobs:
at: /tmp/workspace
- restore_cache:
key: v3-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
# - restore_cache:
# key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: tools
command: |
export PATH="$GOBIN:$PATH"
make get_tools
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run:
name: Run tests
command: bash test/persist/test_failure_indices.sh
localnet:
working_directory: /home/circleci/.go_workspace/src/github.com/tendermint/tendermint
machine:
image: circleci/classic:latest
environment:
GOBIN: /home/circleci/.go_workspace/bin
GOPATH: /home/circleci/.go_workspace/
GOOS: linux
GOARCH: amd64
parallelism: 1
steps:
- checkout
- run:
name: run localnet and exit on failure
command: |
set -x
make get_tools
make get_vendor_deps
make build-linux
make localnet-start &
./scripts/localnet-blocks-test.sh 40 5 10 localhost
test_p2p:
environment:
GOBIN: /home/circleci/.go_workspace/bin
@ -186,8 +307,22 @@ jobs:
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
# - restore_cache:
# key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- checkout
- run:
name: tools
command: |
export PATH="$GOBIN:$PATH"
make get_tools
- run:
name: dependencies
command: |
export PATH="$GOBIN:$PATH"
make get_vendor_deps
- run: mkdir -p $GOPATH/src/github.com/tendermint
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
- run:
name: gather
command: |
@ -199,7 +334,7 @@ jobs:
done
- run:
name: upload
command: bash <(curl -s https://codecov.io/bash) -f coverage.txt
command: bash .circleci/codecov.sh -f coverage.txt
workflows:
version: 2
@ -224,6 +359,9 @@ workflows:
- test_persistence:
requires:
- setup_dependencies
- localnet:
requires:
- setup_dependencies
- test_p2p
- upload_coverage:
requires:

2
.github/CODEOWNERS vendored
View File

@ -4,4 +4,4 @@
* @ebuchman @melekes @xla
# Precious documentation
/docs/ @zramsay @jolesbi
/docs/ @zramsay

5
.gitignore vendored
View File

@ -28,9 +28,14 @@ scripts/cutWALUntil/cutWALUntil
libs/pubsub/query/fuzz_test/output
shunit2
.tendermint-lite
addrbook.json
*/vendor
*/.glide
.terraform
terraform.tfstate
terraform.tfstate.backup
terraform.tfstate.d
.vscode

View File

@ -1,5 +1,109 @@
# Changelog
## 0.24.0
*September 6th, 2018*
Special thanks to external contributors with PRs included in this release: ackratos, james-ray, bradyjoestar,
peerlink, Ahmah2009, bluele, b00f.
This release includes breaking upgrades in the block header,
including the long awaited changes for delaying validator set updates by one
block to better support light clients.
It also fixes enforcement on the maximum size of blocks, and includes a BFT
timestamp in each block that can be safely used by applications.
There are also some minor breaking changes to the rpc, config, and ABCI.
See the [UPGRADING.md](UPGRADING.md#v0.24.0) for details on upgrading to the new
version.
From here on, breaking changes will be broken down to better reflect how users
are affected by a change.
A few more breaking changes are in the works - each will come with a clear
Architecture Decision Record (ADR) explaining the change. You can review ADRs
[here](https://github.com/tendermint/tendermint/tree/develop/docs/architecture)
or in the [open Pull Requests](https://github.com/tendermint/tendermint/pulls).
You can also check in on the [issues marked as
breaking](https://github.com/tendermint/tendermint/issues?q=is%3Aopen+is%3Aissue+label%3Abreaking).
BREAKING CHANGES:
* CLI/RPC/Config
- [config] [\#2169](https://github.com/tendermint/tendermint/issues/2169) Replace MaxNumPeers with MaxNumInboundPeers and MaxNumOutboundPeers
- [config] [\#2300](https://github.com/tendermint/tendermint/issues/2300) Reduce default mempool size from 100k to 5k, until ABCI rechecking is implemented.
- [rpc] [\#1815](https://github.com/tendermint/tendermint/issues/1815) `/commit` returns a `signed_header` field instead of everything being top-level
* Apps
- [abci] Added address of the original proposer of the block to Header
- [abci] Change ABCI Header to match Tendermint exactly
- [abci] [\#2159](https://github.com/tendermint/tendermint/issues/2159) Update use of `Validator` (see
[ADR-018](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-018-ABCI-Validators.md)):
- Remove PubKey from `Validator` (so it's just Address and Power)
- Introduce `ValidatorUpdate` (with just PubKey and Power)
- InitChain and EndBlock use ValidatorUpdate
- Update field names and types in BeginBlock
- [state] [\#1815](https://github.com/tendermint/tendermint/issues/1815) Validator set changes are now delayed by one block
- updates returned in ResponseEndBlock for block H will be included in RequestBeginBlock for block H+2
* Go API
- [lite] [\#1815](https://github.com/tendermint/tendermint/issues/1815) Complete refactor of the package
- [node] [\#2212](https://github.com/tendermint/tendermint/issues/2212) NewNode now accepts a `*p2p.NodeKey` (@bradyjoestar)
- [libs/common] [\#2199](https://github.com/tendermint/tendermint/issues/2199) Remove Fmt, in favor of fmt.Sprintf
- [libs/common] SplitAndTrim was deleted
- [libs/common] [\#2274](https://github.com/tendermint/tendermint/issues/2274) Remove unused Math functions like MaxInt, MaxInt64,
MinInt, MinInt64 (@Ahmah2009)
- [libs/clist] Panics if list extends beyond MaxLength
- [crypto] [\#2205](https://github.com/tendermint/tendermint/issues/2205) Rename AminoRoute variables to no longer be prefixed by signature type.
* Blockchain Protocol
- [state] [\#1815](https://github.com/tendermint/tendermint/issues/1815) Validator set changes are now delayed by one block (!)
- Add NextValidatorSet to State, changes on-disk representation of state
- [state] [\#2184](https://github.com/tendermint/tendermint/issues/2184) Enforce ConsensusParams.BlockSize.MaxBytes (See
[ADR-020](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-020-block-size.md)).
- Remove ConsensusParams.BlockSize.MaxTxs
- Introduce maximum sizes for all components of a block, including ChainID
- [types] Updates to the block Header:
- [\#1815](https://github.com/tendermint/tendermint/issues/1815) NextValidatorsHash - hash of the validator set for the next block,
so the current validators actually sign over the hash for the new
validators
- [\#2106](https://github.com/tendermint/tendermint/issues/2106) ProposerAddress - address of the block's original proposer
- [consensus] [\#2203](https://github.com/tendermint/tendermint/issues/2203) Implement BFT time
- Timestamp in block must be monotonic and equal the median of timestamps in block's LastCommit
- [crypto] [\#2239](https://github.com/tendermint/tendermint/issues/2239) Secp256k1 signature changes (See
[ADR-014](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-014-secp-malleability.md)):
- format changed from DER to `r || s`, both little endian encoded as 32 bytes.
- malleability removed by requiring `s` to be in canonical form.
* P2P Protocol
- [p2p] [\#2263](https://github.com/tendermint/tendermint/issues/2263) Update secret connection to use a little endian encoded nonce
- [blockchain] [\#2213](https://github.com/tendermint/tendermint/issues/2213) Fix Amino routes for blockchain reactor messages
(@peerlink)
FEATURES:
- [types] [\#2015](https://github.com/tendermint/tendermint/issues/2015) Allow genesis file to have 0 validators (@b00f)
- Initial validator set can be determined by the app in ResponseInitChain
- [rpc] [\#2161](https://github.com/tendermint/tendermint/issues/2161) New event `ValidatorSetUpdates` for when the validator set changes
- [crypto/multisig] [\#2164](https://github.com/tendermint/tendermint/issues/2164) Introduce multisig pubkey and signature format
- [libs/db] [\#2293](https://github.com/tendermint/tendermint/issues/2293) Allow passing options through when creating instances of leveldb dbs
IMPROVEMENTS:
- [docs] Lint documentation with `write-good` and `stop-words`.
- [docs] [\#2249](https://github.com/tendermint/tendermint/issues/2249) Refactor, deduplicate, and improve the ABCI docs and spec (with thanks to @ttmc).
- [scripts] [\#2196](https://github.com/tendermint/tendermint/issues/2196) Added json2wal tool, which is supposed to help our users restore (@bradyjoestar)
corrupted WAL files and compose test WAL files (@bradyjoestar)
- [mempool] [\#2234](https://github.com/tendermint/tendermint/issues/2234) Now stores txs by hash inside of the cache, to mitigate memory leakage
- [mempool] [\#2166](https://github.com/tendermint/tendermint/issues/2166) Set explicit capacity for map when updating txs (@bluele)
BUG FIXES:
- [config] [\#2284](https://github.com/tendermint/tendermint/issues/2284) Replace `db_path` with `db_dir` from automatically generated configuration files.
- [mempool] [\#2188](https://github.com/tendermint/tendermint/issues/2188) Fix OOM issue from cache map and list getting out of sync
- [state] [\#2051](https://github.com/tendermint/tendermint/issues/2051) KV store index supports searching by `tx.height` (@ackratos)
- [rpc] [\#2327](https://github.com/tendermint/tendermint/issues/2327) `/dial_peers` does not try to dial existing peers
- [node] [\#2323](https://github.com/tendermint/tendermint/issues/2323) Filter empty strings from config lists (@james-ray)
- [abci/client] [\#2236](https://github.com/tendermint/tendermint/issues/2236) Fix closing GRPC connection (@bradyjoestar)
## 0.23.1
*August 22nd, 2018*
@ -646,7 +750,7 @@ BREAKING CHANGES:
- use scripts/wal2json to convert to json for debugging
FEATURES:
- new `certifiers` pkg contains the tendermint light-client library (name subject to change)!
- new `Verifiers` pkg contains the tendermint light-client library (name subject to change)!
- rpc: `/genesis` includes the `app_options` .
- rpc: `/abci_query` takes an additional `height` parameter to support historical queries.
- rpc/client: new ABCIQueryWithOptions supports options like `trusted` (set false to get a proof) and `height` to query a historical height.

22
CHANGELOG_PENDING.md Normal file
View File

@ -0,0 +1,22 @@
# Pending
Special thanks to external contributors with PRs included in this release:
BREAKING CHANGES:
* CLI/RPC/Config
* Apps
* Go API
* Blockchain Protocol
* P2P Protocol
FEATURES:
IMPROVEMENTS:
BUG FIXES:

17
Gopkg.lock generated
View File

@ -357,6 +357,13 @@
pruneopts = "UT"
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
[[projects]]
digest = "1:605b6546f3f43745695298ec2d342d3e952b6d91cdf9f349bea9315f677d759f"
name = "github.com/tendermint/btcd"
packages = ["btcec"]
pruneopts = "UT"
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
[[projects]]
branch = "master"
digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722"
@ -370,12 +377,12 @@
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
[[projects]]
digest = "1:e9113641c839c21d8eaeb2c907c7276af1eddeed988df8322168c56b7e06e0e1"
digest = "1:e0a2a4be1e20c305badc2b0a7a9ab7fef6da500763bec23ab81df3b5f9eec9ee"
name = "github.com/tendermint/go-amino"
packages = ["."]
pruneopts = "UT"
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412"
version = "0.10.1"
revision = "a8328986c1608950fa5d3d1c0472cccc4f8fc02c"
version = "v0.12.0-rc0"
[[projects]]
branch = "master"
@ -398,7 +405,7 @@
"salsa20/salsa",
]
pruneopts = "UT"
revision = "c126467f60eb25f8f27e5a981f32a87e3965053f"
revision = "56440b844dfe139a8ac053f4ecac0b20b79058f4"
[[projects]]
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
@ -504,7 +511,6 @@
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/btcsuite/btcd/btcec",
"github.com/btcsuite/btcutil/base58",
"github.com/btcsuite/btcutil/bech32",
"github.com/ebuchman/fail-test",
@ -536,6 +542,7 @@
"github.com/syndtr/goleveldb/leveldb/errors",
"github.com/syndtr/goleveldb/leveldb/iterator",
"github.com/syndtr/goleveldb/leveldb/opt",
"github.com/tendermint/btcd/btcec",
"github.com/tendermint/ed25519",
"github.com/tendermint/ed25519/extra25519",
"github.com/tendermint/go-amino",

View File

@ -58,7 +58,7 @@
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "=v0.10.1"
version = "v0.12.0-rc0"
[[constraint]]
name = "google.golang.org/grpc"
@ -85,6 +85,10 @@
name = "github.com/btcsuite/btcutil"
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[constraint]]
name = "github.com/tendermint/btcd"
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
# Haven't made a release since 2016.
[[constraint]]
name = "github.com/prometheus/client_golang"

View File

@ -3,7 +3,6 @@ GOTOOLS = \
github.com/golang/dep/cmd/dep \
gopkg.in/alecthomas/gometalinter.v2 \
github.com/gogo/protobuf/protoc-gen-gogo \
github.com/gogo/protobuf/gogoproto \
github.com/square/certstrap
PACKAGES=$(shell go list ./...)
@ -11,6 +10,8 @@ INCLUDE = -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protob
BUILD_TAGS?='tendermint'
BUILD_FLAGS = -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`"
LINT_FLAGS = --exclude '.*\.pb\.go' --vendor --deadline=600s
all: check build test install
check: check_tools get_vendor_deps
@ -37,13 +38,14 @@ protoc_all: protoc_libs protoc_abci protoc_grpc
## If you get the following error,
## "error while loading shared libraries: libprotobuf.so.14: cannot open shared object file: No such file or directory"
## See https://stackoverflow.com/a/25518702
## Note the $< here is substituted for the %.proto
## Note the $@ here is substituted for the %.pb.go
protoc $(INCLUDE) $< --gogo_out=Mgoogle/protobuf/timestamp.proto=github.com/golang/protobuf/ptypes/timestamp,plugins=grpc:.
@echo "--> adding nolint declarations to protobuf generated files"
@awk -i inplace '/^\s*package \w+/ { print "//nolint" }1' $@
########################################
### Build ABCI
# see protobuf section above
protoc_abci: abci/types/types.pb.go
build_abci:
@ -75,7 +77,7 @@ get_tools:
update_tools:
@echo "--> Updating tools"
@go get -u $(GOTOOLS)
go get -u -v $(GOTOOLS)
#Update dependencies
get_vendor_deps:
@ -85,13 +87,15 @@ get_vendor_deps:
#For ABCI and libs
get_protoc:
@# https://github.com/google/protobuf/releases
curl -L https://github.com/google/protobuf/releases/download/v3.4.1/protobuf-cpp-3.4.1.tar.gz | tar xvz && \
cd protobuf-3.4.1 && \
curl -L https://github.com/google/protobuf/releases/download/v3.6.1/protobuf-cpp-3.6.1.tar.gz | tar xvz && \
cd protobuf-3.6.1 && \
DIST_LANG=cpp ./configure && \
make && \
make install && \
make check && \
sudo make install && \
sudo ldconfig && \
cd .. && \
rm -rf protobuf-3.4.1
rm -rf protobuf-3.6.1
draw_deps:
@# requires brew install graphviz or apt-get install graphviz
@ -200,11 +204,11 @@ vagrant_test:
### go tests
test:
@echo "--> Running go test"
@GOCACHE=off go test $(PACKAGES)
@GOCACHE=off go test -p 1 $(PACKAGES)
test_race:
@echo "--> Running go test --race"
@go test -v -race $(PACKAGES)
@GOCACHE=off go test -p 1 -v -race $(PACKAGES)
########################################
@ -215,7 +219,7 @@ fmt:
metalinter:
@echo "--> Running linter"
@gometalinter.v2 --vendor --deadline=600s --disable-all \
@gometalinter.v2 $(LINT_FLAGS) --disable-all \
--enable=deadcode \
--enable=gosimple \
--enable=misspell \
@ -244,7 +248,7 @@ metalinter:
metalinter_all:
@echo "--> Running linter (all)"
gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./...
gometalinter.v2 $(LINT_FLAGS) --enable-all --disable=lll ./...
DESTINATION = ./index.html.md

66
UPGRADING.md Normal file
View File

@ -0,0 +1,66 @@
# Upgrading Tendermint Core
This guide provides steps to be followed when you upgrade your applications to
a newer version of Tendermint Core.
## v0.24.0
New 0.24.0 release contains a lot of changes to the state and types. It's not
compatible to the old versions and there is no straight forward way to update
old data to be compatible with the new version.
To reset the state do:
```
$ tendermint unsafe_reset_all
```
Here we summarize some other notable changes to be mindful of.
### Config changes
`p2p.max_num_peers` was removed in favor of `p2p.max_num_inbound_peers` and
`p2p.max_num_outbound_peers`.
```
# Maximum number of inbound peers
max_num_inbound_peers = 40
# Maximum number of outbound peers to connect to, excluding persistent peers
max_num_outbound_peers = 10
```
As you can see, the default ratio of inbound/outbound peers is 4/1. The reason
is we want it to be easier for new nodes to connect to the network. You can
tweak these parameters to alter the network topology.
### RPC Changes
The result of `/commit` used to contain `header` and `commit` fields at the top level. These are now contained under the `signed_header` field.
### ABCI Changes
The header has been upgraded and contains new fields, but none of the existing
fields were changed, except their order.
The `Validator` type was split into two, one containing an `Address` and one
containing a `PubKey`. When processing `RequestBeginBlock`, use the `Validator`
type, which contains just the `Address`. When returning `ResponseEndBlock`, use
the `ValidatorUpdate` type, which contains just the `PubKey`.
### Validator Set Updates
Validator set updates returned in ResponseEndBlock for height `H` used to take
effect immediately at height `H+1`. Now they will be delayed one block, to take
effect at height `H+2`. Note this means that the change will be seen by the ABCI
app in the `RequestBeginBlock.LastCommitInfo` at block `H+3`. Apps were already
required to maintain a map from validator addresses to pubkeys since v0.23 (when
pubkeys were removed from RequestBeginBlock), but now they may need to track
multiple validator sets at once to accomodate this delay.
### Block Size
The `ConsensusParams.BlockSize.MaxTxs` was removed in favour of
`ConsensusParams.BlockSize.MaxBytes`, which is now enforced. This means blocks
are limitted only by byte-size, not by number of transactions.

View File

@ -17,14 +17,14 @@ The community has provided a number of addtional implementations, see the [Tende
A detailed description of the ABCI methods and message types is contained in:
- [A prose specification](specification.md)
- [A protobuf file](https://github.com/tendermint/abci/blob/master/types/types.proto)
- [A Go interface](https://github.com/tendermint/abci/blob/master/types/application.go).
- [A protobuf file](https://github.com/tendermint/tendermint/blob/master/abci/types/types.proto)
- [A Go interface](https://github.com/tendermint/tendermint/blob/master/abci/types/application.go).
For more background information on ABCI, motivations, and tendermint, please visit [the documentation](http://tendermint.readthedocs.io/en/master/).
For more background information on ABCI, motivations, and tendermint, please visit [the documentation](https://tendermint.com/docs/).
The two guides to focus on are the `Application Development Guide` and `Using ABCI-CLI`.
## Protocl Buffers
## Protocol Buffers
To compile the protobuf file, run:
@ -42,10 +42,13 @@ The `abci-cli` is a simple tool for debugging ABCI servers and running some
example apps. To install it:
```
go get github.com/tendermint/abci
cd $GOPATH/src/github.com/tendermint/abci
mkdir -p $GOPATH/src/github.com/tendermint
cd $GOPATH/src/github.com/tendermint
git clone https://github.com/tendermint/tendermint.git
cd tendermint
make get_tools
make get_vendor_deps
make install
make install_abci
```
## Implementation
@ -91,7 +94,7 @@ Note the length-prefixing used in the socket implementation does not apply for G
The `abci-cli` tool wraps an ABCI client and can be used for probing/testing an ABCI server.
For instance, `abci-cli test` will run a test sequence against a listening server running the Counter application (see below).
It can also be used to run some example applications.
See [the documentation](http://tendermint.readthedocs.io/en/master/) for more details.
See [the documentation](https://tendermint.com/docs/) for more details.
### Examples

View File

@ -22,6 +22,7 @@ type grpcClient struct {
mustConnect bool
client types.ABCIApplicationClient
conn *grpc.ClientConn
mtx sync.Mutex
addr string
@ -60,6 +61,7 @@ RETRY_LOOP:
cli.Logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr)
client := types.NewABCIApplicationClient(conn)
cli.conn = conn
ENSURE_CONNECTED:
for {
@ -78,12 +80,10 @@ RETRY_LOOP:
func (cli *grpcClient) OnStop() {
cli.BaseService.OnStop()
cli.mtx.Lock()
defer cli.mtx.Unlock()
// TODO: how to close conn? its not a net.Conn and grpc doesn't expose a Close()
/*if cli.client.conn != nil {
cli.client.conn.Close()
}*/
if cli.conn != nil {
cli.conn.Close()
}
}
func (cli *grpcClient) StopForError(err error) {

View File

@ -477,11 +477,8 @@ func muxOnCommands(cmd *cobra.Command, pArgs []string) error {
}
func cmdUnimplemented(cmd *cobra.Command, args []string) error {
// TODO: Print out all the sub-commands available
msg := "unimplemented command"
if err := cmd.Help(); err != nil {
msg = err.Error()
}
if len(args) > 0 {
msg += fmt.Sprintf(" args: [%s]", strings.Join(args, " "))
}
@ -489,6 +486,17 @@ func cmdUnimplemented(cmd *cobra.Command, args []string) error {
Code: codeBad,
Log: msg,
})
fmt.Println("Available commands:")
fmt.Printf("%s: %s\n", echoCmd.Use, echoCmd.Short)
fmt.Printf("%s: %s\n", infoCmd.Use, infoCmd.Short)
fmt.Printf("%s: %s\n", checkTxCmd.Use, checkTxCmd.Short)
fmt.Printf("%s: %s\n", deliverTxCmd.Use, deliverTxCmd.Short)
fmt.Printf("%s: %s\n", queryCmd.Use, queryCmd.Short)
fmt.Printf("%s: %s\n", commitCmd.Use, commitCmd.Short)
fmt.Printf("%s: %s\n", setOptionCmd.Use, setOptionCmd.Short)
fmt.Println("Use \"[command] --help\" for more information about a command.")
return nil
}

View File

@ -6,7 +6,6 @@ import (
"github.com/tendermint/tendermint/abci/example/code"
"github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
)
type CounterApplication struct {
@ -22,7 +21,7 @@ func NewCounterApplication(serial bool) *CounterApplication {
}
func (app *CounterApplication) Info(req types.RequestInfo) types.ResponseInfo {
return types.ResponseInfo{Data: cmn.Fmt("{\"hashes\":%v,\"txs\":%v}", app.hashCount, app.txCount)}
return types.ResponseInfo{Data: fmt.Sprintf("{\"hashes\":%v,\"txs\":%v}", app.hashCount, app.txCount)}
}
func (app *CounterApplication) SetOption(req types.RequestSetOption) types.ResponseSetOption {
@ -34,7 +33,7 @@ func (app *CounterApplication) SetOption(req types.RequestSetOption) types.Respo
TODO Panic and have the ABCI server pass an exception.
The client can call SetOptionSync() and get an `error`.
return types.ResponseSetOption{
Error: cmn.Fmt("Unknown key (%s) or value (%s)", key, value),
Error: fmt.Sprintf("Unknown key (%s) or value (%s)", key, value),
}
*/
return types.ResponseSetOption{}
@ -95,10 +94,10 @@ func (app *CounterApplication) Commit() (resp types.ResponseCommit) {
func (app *CounterApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery {
switch reqQuery.Path {
case "hash":
return types.ResponseQuery{Value: []byte(cmn.Fmt("%v", app.hashCount))}
return types.ResponseQuery{Value: []byte(fmt.Sprintf("%v", app.hashCount))}
case "tx":
return types.ResponseQuery{Value: []byte(cmn.Fmt("%v", app.txCount))}
return types.ResponseQuery{Value: []byte(fmt.Sprintf("%v", app.txCount))}
default:
return types.ResponseQuery{Log: cmn.Fmt("Invalid query path. Expected hash or tx, got %v", reqQuery.Path)}
return types.ResponseQuery{Log: fmt.Sprintf("Invalid query path. Expected hash or tx, got %v", reqQuery.Path)}
}
}

View File

@ -39,7 +39,7 @@ func TestGRPC(t *testing.T) {
}
func testStream(t *testing.T, app types.Application) {
numDeliverTxs := 200000
numDeliverTxs := 20000
// Start the listener
server := abciserver.NewSocketServer("unix://test.sock", app)
@ -72,7 +72,7 @@ func testStream(t *testing.T, app types.Application) {
}
if counter == numDeliverTxs {
go func() {
time.Sleep(time.Second * 2) // Wait for a bit to allow counter overflow
time.Sleep(time.Second * 1) // Wait for a bit to allow counter overflow
close(done)
}()
return
@ -148,7 +148,7 @@ func testGRPCSync(t *testing.T, app *types.GRPCApplication) {
t.Log("response", counter)
if counter == numDeliverTxs {
go func() {
time.Sleep(time.Second * 2) // Wait for a bit to allow counter overflow
time.Sleep(time.Second * 1) // Wait for a bit to allow counter overflow
}()
}

View File

@ -7,12 +7,10 @@ import (
// RandVal creates one random validator, with a key derived
// from the input value
func RandVal(i int) types.Validator {
addr := cmn.RandBytes(20)
func RandVal(i int) types.ValidatorUpdate {
pubkey := cmn.RandBytes(32)
power := cmn.RandUint16() + 1
v := types.Ed25519Validator(pubkey, int64(power))
v.Address = addr
v := types.Ed25519ValidatorUpdate(pubkey, int64(power))
return v
}
@ -20,8 +18,8 @@ func RandVal(i int) types.Validator {
// the application. Note that the keys are deterministically
// derived from the index in the array, while the power is
// random (Change this if not desired)
func RandVals(cnt int) []types.Validator {
res := make([]types.Validator, cnt)
func RandVals(cnt int) []types.ValidatorUpdate {
res := make([]types.ValidatorUpdate, cnt)
for i := 0; i < cnt; i++ {
res[i] = RandVal(i)
}

View File

@ -81,8 +81,8 @@ func (app *KVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
app.state.Size += 1
tags := []cmn.KVPair{
{[]byte("app.creator"), []byte("jae")},
{[]byte("app.key"), key},
{Key: []byte("app.creator"), Value: []byte("jae")},
{Key: []byte("app.key"), Value: key},
}
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags}
}

View File

@ -2,6 +2,7 @@ package kvstore
import (
"bytes"
"fmt"
"io/ioutil"
"sort"
"testing"
@ -121,11 +122,11 @@ func TestValUpdates(t *testing.T) {
vals1, vals2 := vals[:nInit], kvstore.Validators()
valsEqual(t, vals1, vals2)
var v1, v2, v3 types.Validator
var v1, v2, v3 types.ValidatorUpdate
// add some validators
v1, v2 = vals[nInit], vals[nInit+1]
diff := []types.Validator{v1, v2}
diff := []types.ValidatorUpdate{v1, v2}
tx1 := MakeValSetChangeTx(v1.PubKey, v1.Power)
tx2 := MakeValSetChangeTx(v2.PubKey, v2.Power)
@ -139,7 +140,7 @@ func TestValUpdates(t *testing.T) {
v1.Power = 0
v2.Power = 0
v3.Power = 0
diff = []types.Validator{v1, v2, v3}
diff = []types.ValidatorUpdate{v1, v2, v3}
tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power)
tx2 = MakeValSetChangeTx(v2.PubKey, v2.Power)
tx3 := MakeValSetChangeTx(v3.PubKey, v3.Power)
@ -157,18 +158,18 @@ func TestValUpdates(t *testing.T) {
} else {
v1.Power = 5
}
diff = []types.Validator{v1}
diff = []types.ValidatorUpdate{v1}
tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power)
makeApplyBlock(t, kvstore, 3, diff, tx1)
vals1 = append([]types.Validator{v1}, vals1[1:]...)
vals1 = append([]types.ValidatorUpdate{v1}, vals1[1:]...)
vals2 = kvstore.Validators()
valsEqual(t, vals1, vals2)
}
func makeApplyBlock(t *testing.T, kvstore types.Application, heightInt int, diff []types.Validator, txs ...[]byte) {
func makeApplyBlock(t *testing.T, kvstore types.Application, heightInt int, diff []types.ValidatorUpdate, txs ...[]byte) {
// make and apply block
height := int64(heightInt)
hash := []byte("foo")
@ -190,12 +191,12 @@ func makeApplyBlock(t *testing.T, kvstore types.Application, heightInt int, diff
}
// order doesn't matter
func valsEqual(t *testing.T, vals1, vals2 []types.Validator) {
func valsEqual(t *testing.T, vals1, vals2 []types.ValidatorUpdate) {
if len(vals1) != len(vals2) {
t.Fatalf("vals dont match in len. got %d, expected %d", len(vals2), len(vals1))
}
sort.Sort(types.Validators(vals1))
sort.Sort(types.Validators(vals2))
sort.Sort(types.ValidatorUpdates(vals1))
sort.Sort(types.ValidatorUpdates(vals2))
for i, v1 := range vals1 {
v2 := vals2[i]
if !bytes.Equal(v1.PubKey.Data, v2.PubKey.Data) ||
@ -207,7 +208,7 @@ func valsEqual(t *testing.T, vals1, vals2 []types.Validator) {
func makeSocketClientServer(app types.Application, name string) (abcicli.Client, cmn.Service, error) {
// Start the listener
socket := cmn.Fmt("unix://%s.sock", name)
socket := fmt.Sprintf("unix://%s.sock", name)
logger := log.TestingLogger()
server := abciserver.NewSocketServer(socket, app)
@ -229,7 +230,7 @@ func makeSocketClientServer(app types.Application, name string) (abcicli.Client,
func makeGRPCClientServer(app types.Application, name string) (abcicli.Client, cmn.Service, error) {
// Start the listener
socket := cmn.Fmt("unix://%s.sock", name)
socket := fmt.Sprintf("unix://%s.sock", name)
logger := log.TestingLogger()
gapp := types.NewGRPCApplication(app)

View File

@ -9,7 +9,6 @@ import (
"github.com/tendermint/tendermint/abci/example/code"
"github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
)
@ -26,7 +25,7 @@ type PersistentKVStoreApplication struct {
app *KVStoreApplication
// validator set
ValUpdates []types.Validator
ValUpdates []types.ValidatorUpdate
logger log.Logger
}
@ -102,7 +101,7 @@ func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) t
// Track the block hash and header information
func (app *PersistentKVStoreApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
// reset valset changes
app.ValUpdates = make([]types.Validator, 0)
app.ValUpdates = make([]types.ValidatorUpdate, 0)
return types.ResponseBeginBlock{}
}
@ -114,11 +113,11 @@ func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) typ
//---------------------------------------------
// update validators
func (app *PersistentKVStoreApplication) Validators() (validators []types.Validator) {
func (app *PersistentKVStoreApplication) Validators() (validators []types.ValidatorUpdate) {
itr := app.app.state.db.Iterator(nil, nil)
for ; itr.Valid(); itr.Next() {
if isValidatorTx(itr.Key()) {
validator := new(types.Validator)
validator := new(types.ValidatorUpdate)
err := types.ReadMessage(bytes.NewBuffer(itr.Value()), validator)
if err != nil {
panic(err)
@ -130,7 +129,7 @@ func (app *PersistentKVStoreApplication) Validators() (validators []types.Valida
}
func MakeValSetChangeTx(pubkey types.PubKey, power int64) []byte {
return []byte(cmn.Fmt("val:%X/%d", pubkey.Data, power))
return []byte(fmt.Sprintf("val:%X/%d", pubkey.Data, power))
}
func isValidatorTx(tx []byte) bool {
@ -168,11 +167,11 @@ func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.Respon
}
// update
return app.updateValidator(types.Ed25519Validator(pubkey, int64(power)))
return app.updateValidator(types.Ed25519ValidatorUpdate(pubkey, int64(power)))
}
// add, update, or remove a validator
func (app *PersistentKVStoreApplication) updateValidator(v types.Validator) types.ResponseDeliverTx {
func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx {
key := []byte("val:" + string(v.PubKey.Data))
if v.Power == 0 {
// remove validator

View File

@ -12,11 +12,11 @@ import (
func InitChain(client abcicli.Client) error {
total := 10
vals := make([]types.Validator, total)
vals := make([]types.ValidatorUpdate, total)
for i := 0; i < total; i++ {
pubkey := cmn.RandBytes(33)
power := cmn.RandInt()
vals[i] = types.Ed25519Validator(pubkey, int64(power))
vals[i] = types.Ed25519ValidatorUpdate(pubkey, int64(power))
}
_, err := client.InitChainSync(types.RequestInitChain{
Validators: vals,

View File

@ -22,7 +22,7 @@ func TestMarshalJSON(t *testing.T) {
Data: []byte("hello"),
GasWanted: 43,
Tags: []cmn.KVPair{
{[]byte("pho"), []byte("bo")},
{Key: []byte("pho"), Value: []byte("bo")},
},
}
b, err = json.Marshal(&r1)
@ -83,7 +83,7 @@ func TestWriteReadMessage2(t *testing.T) {
Log: phrase,
GasWanted: 10,
Tags: []cmn.KVPair{
cmn.KVPair{[]byte("abc"), []byte("def")},
cmn.KVPair{Key: []byte("abc"), Value: []byte("def")},
},
},
// TODO: add the rest

View File

@ -4,8 +4,8 @@ const (
PubKeyEd25519 = "ed25519"
)
func Ed25519Validator(pubkey []byte, power int64) Validator {
return Validator{
func Ed25519ValidatorUpdate(pubkey []byte, power int64) ValidatorUpdate {
return ValidatorUpdate{
// Address:
PubKey: PubKey{
Type: PubKeyEd25519,

File diff suppressed because it is too large Load Diff

View File

@ -60,7 +60,7 @@ message RequestInitChain {
google.protobuf.Timestamp time = 1 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true];
string chain_id = 2;
ConsensusParams consensus_params = 3;
repeated Validator validators = 4 [(gogoproto.nullable)=false];
repeated ValidatorUpdate validators = 4 [(gogoproto.nullable)=false];
bytes app_state_bytes = 5;
}
@ -143,7 +143,7 @@ message ResponseSetOption {
message ResponseInitChain {
ConsensusParams consensus_params = 1;
repeated Validator validators = 2 [(gogoproto.nullable)=false];
repeated ValidatorUpdate validators = 2 [(gogoproto.nullable)=false];
}
message ResponseQuery {
@ -183,7 +183,7 @@ message ResponseDeliverTx {
}
message ResponseEndBlock {
repeated Validator validator_updates = 1 [(gogoproto.nullable)=false];
repeated ValidatorUpdate validator_updates = 1 [(gogoproto.nullable)=false];
ConsensusParams consensus_param_updates = 2;
repeated common.KVPair tags = 3 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"];
}
@ -207,8 +207,7 @@ message ConsensusParams {
// BlockSize contains limits on the block size.
message BlockSize {
int32 max_bytes = 1;
int32 max_txs = 2;
int64 max_gas = 3;
int64 max_gas = 2;
}
// TxSize contains limits on the tx size.
@ -225,42 +224,65 @@ message BlockGossip {
}
message LastCommitInfo {
int32 commit_round = 1;
repeated SigningValidator validators = 2 [(gogoproto.nullable)=false];
int32 round = 1;
repeated VoteInfo votes = 2 [(gogoproto.nullable)=false];
}
//----------------------------------------
// Blockchain Types
// just the minimum the app might need
message Header {
// basics
// basic block info
string chain_id = 1 [(gogoproto.customname)="ChainID"];
int64 height = 2;
google.protobuf.Timestamp time = 3 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true];
// txs
int32 num_txs = 4;
int64 num_txs = 4;
int64 total_txs = 5;
// hashes
bytes last_block_hash = 6;
bytes validators_hash = 7;
bytes app_hash = 8;
// prev block info
BlockID last_block_id = 6 [(gogoproto.nullable)=false];
// consensus
Validator proposer = 9 [(gogoproto.nullable)=false];
// hashes of block data
bytes last_commit_hash = 7; // commit from validators from the last block
bytes data_hash = 8; // transactions
// hashes from the app output from the prev block
bytes validators_hash = 9; // validators for the current block
bytes next_validators_hash = 10; // validators for the next block
bytes consensus_hash = 11; // consensus params for current block
bytes app_hash = 12; // state after txs from the previous block
bytes last_results_hash = 13;// root hash of all results from the txs from the previous block
// consensus info
bytes evidence_hash = 14; // evidence included in the block
bytes proposer_address = 15; // original proposer of the block
}
message BlockID {
bytes hash = 1;
PartSetHeader parts_header = 2 [(gogoproto.nullable)=false];
}
message PartSetHeader {
int32 total = 1;
bytes hash = 2;
}
// Validator
message Validator {
bytes address = 1;
PubKey pub_key = 2 [(gogoproto.nullable)=false];
//PubKey pub_key = 2 [(gogoproto.nullable)=false];
int64 power = 3;
}
// Validator with an extra bool
message SigningValidator {
// ValidatorUpdate
message ValidatorUpdate {
PubKey pub_key = 1 [(gogoproto.nullable)=false];
int64 power = 2;
}
// VoteInfo
message VoteInfo {
Validator validator = 1 [(gogoproto.nullable)=false];
bool signed_last_block = 2;
}

View File

@ -1,31 +0,0 @@
package types
import (
"testing"
asrt "github.com/stretchr/testify/assert"
)
func TestConsensusParams(t *testing.T) {
assert := asrt.New(t)
params := &ConsensusParams{
BlockSize: &BlockSize{MaxGas: 12345},
BlockGossip: &BlockGossip{BlockPartSizeBytes: 54321},
}
var noParams *ConsensusParams // nil
// no error with nil fields
assert.Nil(noParams.GetBlockSize())
assert.EqualValues(noParams.GetBlockSize().GetMaxGas(), 0)
// get values with real fields
assert.NotNil(params.GetBlockSize())
assert.EqualValues(params.GetBlockSize().GetMaxTxs(), 0)
assert.EqualValues(params.GetBlockSize().GetMaxGas(), 12345)
assert.NotNil(params.GetBlockGossip())
assert.EqualValues(params.GetBlockGossip().GetBlockPartSizeBytes(), 54321)
assert.Nil(params.GetTxSize())
assert.EqualValues(params.GetTxSize().GetMaxBytes(), 0)
}

View File

@ -1758,6 +1758,118 @@ func TestHeaderMarshalTo(t *testing.T) {
}
}
func TestBlockIDProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockID(popr, false)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &BlockID{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
littlefuzz := make([]byte, len(dAtA))
copy(littlefuzz, dAtA)
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
if len(littlefuzz) > 0 {
fuzzamount := 100
for i := 0; i < fuzzamount; i++ {
littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256))
littlefuzz = append(littlefuzz, byte(popr.Intn(256)))
}
// shouldn't panic
_ = github_com_gogo_protobuf_proto.Unmarshal(littlefuzz, msg)
}
}
func TestBlockIDMarshalTo(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockID(popr, false)
size := p.Size()
dAtA := make([]byte, size)
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
_, err := p.MarshalTo(dAtA)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &BlockID{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestPartSetHeaderProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedPartSetHeader(popr, false)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &PartSetHeader{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
littlefuzz := make([]byte, len(dAtA))
copy(littlefuzz, dAtA)
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
if len(littlefuzz) > 0 {
fuzzamount := 100
for i := 0; i < fuzzamount; i++ {
littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256))
littlefuzz = append(littlefuzz, byte(popr.Intn(256)))
}
// shouldn't panic
_ = github_com_gogo_protobuf_proto.Unmarshal(littlefuzz, msg)
}
}
func TestPartSetHeaderMarshalTo(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedPartSetHeader(popr, false)
size := p.Size()
dAtA := make([]byte, size)
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
_, err := p.MarshalTo(dAtA)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &PartSetHeader{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestValidatorProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@ -1814,15 +1926,15 @@ func TestValidatorMarshalTo(t *testing.T) {
}
}
func TestSigningValidatorProto(t *testing.T) {
func TestValidatorUpdateProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedSigningValidator(popr, false)
p := NewPopulatedValidatorUpdate(popr, false)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &SigningValidator{}
msg := &ValidatorUpdate{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
@ -1845,10 +1957,10 @@ func TestSigningValidatorProto(t *testing.T) {
}
}
func TestSigningValidatorMarshalTo(t *testing.T) {
func TestValidatorUpdateMarshalTo(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedSigningValidator(popr, false)
p := NewPopulatedValidatorUpdate(popr, false)
size := p.Size()
dAtA := make([]byte, size)
for i := range dAtA {
@ -1858,7 +1970,63 @@ func TestSigningValidatorMarshalTo(t *testing.T) {
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &SigningValidator{}
msg := &ValidatorUpdate{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestVoteInfoProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVoteInfo(popr, false)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &VoteInfo{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
littlefuzz := make([]byte, len(dAtA))
copy(littlefuzz, dAtA)
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
if len(littlefuzz) > 0 {
fuzzamount := 100
for i := 0; i < fuzzamount; i++ {
littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256))
littlefuzz = append(littlefuzz, byte(popr.Intn(256)))
}
// shouldn't panic
_ = github_com_gogo_protobuf_proto.Unmarshal(littlefuzz, msg)
}
}
func TestVoteInfoMarshalTo(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVoteInfo(popr, false)
size := p.Size()
dAtA := make([]byte, size)
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
_, err := p.MarshalTo(dAtA)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &VoteInfo{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
@ -2540,6 +2708,42 @@ func TestHeaderJSON(t *testing.T) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestBlockIDJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockID(popr, true)
marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{}
jsondata, err := marshaler.MarshalToString(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &BlockID{}
err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestPartSetHeaderJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedPartSetHeader(popr, true)
marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{}
jsondata, err := marshaler.MarshalToString(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &PartSetHeader{}
err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestValidatorJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@ -2558,16 +2762,34 @@ func TestValidatorJSON(t *testing.T) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestSigningValidatorJSON(t *testing.T) {
func TestValidatorUpdateJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedSigningValidator(popr, true)
p := NewPopulatedValidatorUpdate(popr, true)
marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{}
jsondata, err := marshaler.MarshalToString(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &SigningValidator{}
msg := &ValidatorUpdate{}
err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestVoteInfoJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVoteInfo(popr, true)
marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{}
jsondata, err := marshaler.MarshalToString(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &VoteInfo{}
err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
@ -3480,6 +3702,62 @@ func TestHeaderProtoCompactText(t *testing.T) {
}
}
func TestBlockIDProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockID(popr, true)
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
msg := &BlockID{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestBlockIDProtoCompactText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockID(popr, true)
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
msg := &BlockID{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestPartSetHeaderProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedPartSetHeader(popr, true)
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
msg := &PartSetHeader{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestPartSetHeaderProtoCompactText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedPartSetHeader(popr, true)
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
msg := &PartSetHeader{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestValidatorProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@ -3508,12 +3786,12 @@ func TestValidatorProtoCompactText(t *testing.T) {
}
}
func TestSigningValidatorProtoText(t *testing.T) {
func TestValidatorUpdateProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedSigningValidator(popr, true)
p := NewPopulatedValidatorUpdate(popr, true)
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
msg := &SigningValidator{}
msg := &ValidatorUpdate{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
@ -3522,12 +3800,40 @@ func TestSigningValidatorProtoText(t *testing.T) {
}
}
func TestSigningValidatorProtoCompactText(t *testing.T) {
func TestValidatorUpdateProtoCompactText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedSigningValidator(popr, true)
p := NewPopulatedValidatorUpdate(popr, true)
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
msg := &SigningValidator{}
msg := &ValidatorUpdate{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestVoteInfoProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVoteInfo(popr, true)
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
msg := &VoteInfo{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestVoteInfoProtoCompactText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVoteInfo(popr, true)
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
msg := &VoteInfo{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
@ -4274,6 +4580,50 @@ func TestHeaderSize(t *testing.T) {
}
}
func TestBlockIDSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockID(popr, true)
size2 := github_com_gogo_protobuf_proto.Size(p)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
size := p.Size()
if len(dAtA) != size {
t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(dAtA))
}
if size2 != size {
t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2)
}
size3 := github_com_gogo_protobuf_proto.Size(p)
if size3 != size {
t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3)
}
}
func TestPartSetHeaderSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedPartSetHeader(popr, true)
size2 := github_com_gogo_protobuf_proto.Size(p)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
size := p.Size()
if len(dAtA) != size {
t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(dAtA))
}
if size2 != size {
t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2)
}
size3 := github_com_gogo_protobuf_proto.Size(p)
if size3 != size {
t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3)
}
}
func TestValidatorSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@ -4296,10 +4646,32 @@ func TestValidatorSize(t *testing.T) {
}
}
func TestSigningValidatorSize(t *testing.T) {
func TestValidatorUpdateSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedSigningValidator(popr, true)
p := NewPopulatedValidatorUpdate(popr, true)
size2 := github_com_gogo_protobuf_proto.Size(p)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
size := p.Size()
if len(dAtA) != size {
t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(dAtA))
}
if size2 != size {
t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2)
}
size3 := github_com_gogo_protobuf_proto.Size(p)
if size3 != size {
t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3)
}
}
func TestVoteInfoSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVoteInfo(popr, true)
size2 := github_com_gogo_protobuf_proto.Size(p)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {

View File

@ -2,58 +2,33 @@ package types
import (
"bytes"
"encoding/json"
"sort"
cmn "github.com/tendermint/tendermint/libs/common"
)
//------------------------------------------------------------------------------
// Validators is a list of validators that implements the Sort interface
type Validators []Validator
// ValidatorUpdates is a list of validators that implements the Sort interface
type ValidatorUpdates []ValidatorUpdate
var _ sort.Interface = (Validators)(nil)
var _ sort.Interface = (ValidatorUpdates)(nil)
// All these methods for Validators:
// All these methods for ValidatorUpdates:
// Len, Less and Swap
// are for Validators to implement sort.Interface
// are for ValidatorUpdates to implement sort.Interface
// which will be used by the sort package.
// See Issue https://github.com/tendermint/abci/issues/212
func (v Validators) Len() int {
func (v ValidatorUpdates) Len() int {
return len(v)
}
// XXX: doesn't distinguish same validator with different power
func (v Validators) Less(i, j int) bool {
func (v ValidatorUpdates) Less(i, j int) bool {
return bytes.Compare(v[i].PubKey.Data, v[j].PubKey.Data) <= 0
}
func (v Validators) Swap(i, j int) {
func (v ValidatorUpdates) Swap(i, j int) {
v1 := v[i]
v[i] = v[j]
v[j] = v1
}
func ValidatorsString(vs Validators) string {
s := make([]validatorPretty, len(vs))
for i, v := range vs {
s[i] = validatorPretty{
Address: v.Address,
PubKey: v.PubKey.Data,
Power: v.Power,
}
}
b, err := json.Marshal(s)
if err != nil {
panic(err.Error())
}
return string(b)
}
type validatorPretty struct {
Address cmn.HexBytes `json:"address"`
PubKey []byte `json:"pub_key"`
Power int64 `json:"power"`
}

View File

@ -1,9 +1,9 @@
package version
// NOTE: we should probably be versioning the ABCI and the abci-cli separately
import (
"github.com/tendermint/tendermint/version"
)
const Maj = "0"
const Min = "12"
const Fix = "0"
// TODO: eliminate this after some version refactor
const Version = "0.12.0"
const Version = version.ABCIVersion

View File

@ -6,8 +6,8 @@ import (
"fmt"
"time"
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
cmn "github.com/tendermint/tendermint/libs/common"
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
)
func main() {

View File

@ -365,10 +365,10 @@ func (pool *BlockPool) debug() string {
nextHeight := pool.height + pool.requestersLen()
for h := pool.height; h < nextHeight; h++ {
if pool.requesters[h] == nil {
str += cmn.Fmt("H(%v):X ", h)
str += fmt.Sprintf("H(%v):X ", h)
} else {
str += cmn.Fmt("H(%v):", h)
str += cmn.Fmt("B?(%v) ", pool.requesters[h].block != nil)
str += fmt.Sprintf("H(%v):", h)
str += fmt.Sprintf("B?(%v) ", pool.requesters[h].block != nil)
}
}
return str

View File

@ -201,7 +201,7 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
// Got a peer status. Unverified.
bcR.pool.SetPeerHeight(src.ID(), msg.Height)
default:
bcR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
bcR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
}
}
@ -321,7 +321,7 @@ FOR_LOOP:
state, err = bcR.blockExec.ApplyBlock(state, firstID, first)
if err != nil {
// TODO This is bad, are we zombie?
cmn.PanicQ(cmn.Fmt("Failed to process committed block (%d:%X): %v",
cmn.PanicQ(fmt.Sprintf("Failed to process committed block (%d:%X): %v",
first.Height, first.Hash(), err))
}
blocksSynced++
@ -356,11 +356,11 @@ type BlockchainMessage interface{}
func RegisterBlockchainMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*BlockchainMessage)(nil), nil)
cdc.RegisterConcrete(&bcBlockRequestMessage{}, "tendermint/mempool/BlockRequest", nil)
cdc.RegisterConcrete(&bcBlockResponseMessage{}, "tendermint/mempool/BlockResponse", nil)
cdc.RegisterConcrete(&bcNoBlockResponseMessage{}, "tendermint/mempool/NoBlockResponse", nil)
cdc.RegisterConcrete(&bcStatusResponseMessage{}, "tendermint/mempool/StatusResponse", nil)
cdc.RegisterConcrete(&bcStatusRequestMessage{}, "tendermint/mempool/StatusRequest", nil)
cdc.RegisterConcrete(&bcBlockRequestMessage{}, "tendermint/blockchain/BlockRequest", nil)
cdc.RegisterConcrete(&bcBlockResponseMessage{}, "tendermint/blockchain/BlockResponse", nil)
cdc.RegisterConcrete(&bcNoBlockResponseMessage{}, "tendermint/blockchain/NoBlockResponse", nil)
cdc.RegisterConcrete(&bcStatusResponseMessage{}, "tendermint/blockchain/StatusResponse", nil)
cdc.RegisterConcrete(&bcStatusRequestMessage{}, "tendermint/blockchain/StatusRequest", nil)
}
func decodeMsg(bz []byte) (msg BlockchainMessage, err error) {
@ -378,7 +378,7 @@ type bcBlockRequestMessage struct {
}
func (m *bcBlockRequestMessage) String() string {
return cmn.Fmt("[bcBlockRequestMessage %v]", m.Height)
return fmt.Sprintf("[bcBlockRequestMessage %v]", m.Height)
}
type bcNoBlockResponseMessage struct {
@ -386,7 +386,7 @@ type bcNoBlockResponseMessage struct {
}
func (brm *bcNoBlockResponseMessage) String() string {
return cmn.Fmt("[bcNoBlockResponseMessage %d]", brm.Height)
return fmt.Sprintf("[bcNoBlockResponseMessage %d]", brm.Height)
}
//-------------------------------------
@ -396,7 +396,7 @@ type bcBlockResponseMessage struct {
}
func (m *bcBlockResponseMessage) String() string {
return cmn.Fmt("[bcBlockResponseMessage %v]", m.Block.Height)
return fmt.Sprintf("[bcBlockResponseMessage %v]", m.Block.Height)
}
//-------------------------------------
@ -406,7 +406,7 @@ type bcStatusRequestMessage struct {
}
func (m *bcStatusRequestMessage) String() string {
return cmn.Fmt("[bcStatusRequestMessage %v]", m.Height)
return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height)
}
//-------------------------------------
@ -416,5 +416,5 @@ type bcStatusResponseMessage struct {
}
func (m *bcStatusResponseMessage) String() string {
return cmn.Fmt("[bcStatusResponseMessage %v]", m.Height)
return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height)
}

View File

@ -156,7 +156,7 @@ func makeTxs(height int64) (txs []types.Tx) {
}
func makeBlock(height int64, state sm.State) *types.Block {
block, _ := state.MakeBlock(height, makeTxs(height), new(types.Commit), nil)
block, _ := state.MakeBlock(height, makeTxs(height), new(types.Commit), nil, state.Validators.GetProposer().Address)
return block
}

View File

@ -148,10 +148,10 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
}
height := block.Height
if g, w := height, bs.Height()+1; g != w {
cmn.PanicSanity(cmn.Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g))
cmn.PanicSanity(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g))
}
if !blockParts.IsComplete() {
cmn.PanicSanity(cmn.Fmt("BlockStore can only save complete block part sets"))
cmn.PanicSanity(fmt.Sprintf("BlockStore can only save complete block part sets"))
}
// Save block meta
@ -188,7 +188,7 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) {
if height != bs.Height()+1 {
cmn.PanicSanity(cmn.Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
cmn.PanicSanity(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
}
partBytes := cdc.MustMarshalBinaryBare(part)
bs.db.Set(calcBlockPartKey(height, index), partBytes)
@ -224,7 +224,7 @@ type BlockStoreStateJSON struct {
func (bsj BlockStoreStateJSON) Save(db dbm.DB) {
bytes, err := cdc.MarshalJSON(bsj)
if err != nil {
cmn.PanicSanity(cmn.Fmt("Could not marshal state bytes: %v", err))
cmn.PanicSanity(fmt.Sprintf("Could not marshal state bytes: %v", err))
}
db.SetSync(blockStoreKey, bytes)
}

View File

@ -6,7 +6,6 @@ import (
"runtime/debug"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -14,6 +13,7 @@ import (
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
func TestLoadBlockStoreStateJSON(t *testing.T) {
@ -49,7 +49,7 @@ func TestNewBlockStore(t *testing.T) {
return nil, nil
})
require.NotNil(t, panicErr, "#%d panicCauser: %q expected a panic", i, tt.data)
assert.Contains(t, panicErr.Error(), tt.wantErr, "#%d data: %q", i, tt.data)
assert.Contains(t, fmt.Sprintf("%#v", panicErr), tt.wantErr, "#%d data: %q", i, tt.data)
}
db.Set(blockStoreKey, nil)
@ -70,7 +70,7 @@ var (
part1 = partSet.GetPart(0)
part2 = partSet.GetPart(1)
seenCommit1 = &types.Commit{Precommits: []*types.Vote{{Height: 10,
Timestamp: time.Now().UTC()}}}
Timestamp: tmtime.Now()}}}
)
// TODO: This test should be simplified ...
@ -91,7 +91,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
block := makeBlock(bs.Height()+1, state)
validPartSet := block.MakePartSet(2)
seenCommit := &types.Commit{Precommits: []*types.Vote{{Height: 10,
Timestamp: time.Now().UTC()}}}
Timestamp: tmtime.Now()}}}
bs.SaveBlock(block, partSet, seenCommit)
require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed")
@ -103,7 +103,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
Height: 1,
NumTxs: 100,
ChainID: "block_test",
Time: time.Now(),
Time: tmtime.Now(),
}
header2 := header1
header2.Height = 4
@ -111,7 +111,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
// End of setup, test data
commitAtH10 := &types.Commit{Precommits: []*types.Vote{{Height: 10,
Timestamp: time.Now().UTC()}}}
Timestamp: tmtime.Now()}}}
tuples := []struct {
block *types.Block
parts *types.PartSet
@ -238,7 +238,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
if subStr := tuple.wantPanic; subStr != "" {
if panicErr == nil {
t.Errorf("#%d: want a non-nil panic", i)
} else if got := panicErr.Error(); !strings.Contains(got, subStr) {
} else if got := fmt.Sprintf("%#v", panicErr); !strings.Contains(got, subStr) {
t.Errorf("#%d:\n\tgotErr: %q\nwant substring: %q", i, got, subStr)
}
continue
@ -335,7 +335,7 @@ func TestBlockFetchAtHeight(t *testing.T) {
partSet := block.MakePartSet(2)
seenCommit := &types.Commit{Precommits: []*types.Vote{{Height: 10,
Timestamp: time.Now().UTC()}}}
Timestamp: tmtime.Now()}}}
bs.SaveBlock(block, partSet, seenCommit)
require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed")

View File

@ -5,8 +5,8 @@ import (
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/p2p"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
)
// GenNodeKeyCmd allows the generation of a node key. It prints node's ID to

View File

@ -1,15 +1,16 @@
package commands
import (
"time"
"fmt"
"github.com/spf13/cobra"
cfg "github.com/tendermint/tendermint/config"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tendermint/libs/common"
tmtime "github.com/tendermint/tendermint/types/time"
)
// InitFilesCmd initialises a fresh Tendermint Core instance.
@ -52,8 +53,8 @@ func initFilesWithConfig(config *cfg.Config) error {
logger.Info("Found genesis file", "path", genFile)
} else {
genDoc := types.GenesisDoc{
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
GenesisTime: time.Now(),
ChainID: fmt.Sprintf("test-chain-%v", cmn.RandStr(6)),
GenesisTime: tmtime.Now(),
ConsensusParams: types.DefaultConsensusParams(),
}
genDoc.Validators = []types.GenesisValidator{{

View File

@ -7,7 +7,6 @@ import (
"github.com/spf13/cobra"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/lite/proxy"
rpcclient "github.com/tendermint/tendermint/rpc/client"
)
@ -66,17 +65,21 @@ func runProxy(cmd *cobra.Command, args []string) error {
}
// First, connect a client
logger.Info("Connecting to source HTTP client...")
node := rpcclient.NewHTTP(nodeAddr, "/websocket")
cert, err := proxy.GetCertifier(chainID, home, nodeAddr)
logger.Info("Constructing Verifier...")
cert, err := proxy.NewVerifier(chainID, home, node, logger)
if err != nil {
return err
return cmn.ErrorWrap(err, "constructing Verifier")
}
cert.SetLogger(logger)
sc := proxy.SecureClient(node, cert)
logger.Info("Starting proxy...")
err = proxy.StartProxy(sc, listenAddr, logger)
if err != nil {
return err
return cmn.ErrorWrap(err, "starting proxy")
}
cmn.TrapSignal(func() {

View File

@ -5,8 +5,8 @@ import (
"github.com/spf13/cobra"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/privval"
)
// ResetAllCmd removes the database of this Tendermint core

View File

@ -6,15 +6,15 @@ import (
"os"
"path/filepath"
"strings"
"time"
"github.com/spf13/cobra"
cfg "github.com/tendermint/tendermint/config"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tendermint/libs/common"
tmtime "github.com/tendermint/tendermint/types/time"
)
var (
@ -76,7 +76,7 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
genVals := make([]types.GenesisValidator, nValidators)
for i := 0; i < nValidators; i++ {
nodeDirName := cmn.Fmt("%s%d", nodeDirPrefix, i)
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
nodeDir := filepath.Join(outputDir, nodeDirName)
config.SetRoot(nodeDir)
@ -98,7 +98,7 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
}
for i := 0; i < nNonValidators; i++ {
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i+nValidators))
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i+nValidators))
config.SetRoot(nodeDir)
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
@ -112,14 +112,14 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
// Generate genesis doc from generated validators
genDoc := &types.GenesisDoc{
GenesisTime: time.Now(),
GenesisTime: tmtime.Now(),
ChainID: "chain-" + cmn.RandStr(6),
Validators: genVals,
}
// Write genesis file.
for i := 0; i < nValidators+nNonValidators; i++ {
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
if err := genDoc.SaveAs(filepath.Join(nodeDir, config.BaseConfig.Genesis)); err != nil {
_ = os.RemoveAll(outputDir)
return err
@ -159,7 +159,7 @@ func hostnameOrIP(i int) string {
func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error {
persistentPeers := make([]string, nValidators+nNonValidators)
for i := 0; i < nValidators+nNonValidators; i++ {
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
config.SetRoot(nodeDir)
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
if err != nil {
@ -170,7 +170,7 @@ func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error {
persistentPeersList := strings.Join(persistentPeers, ",")
for i := 0; i < nValidators+nNonValidators; i++ {
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
config.SetRoot(nodeDir)
config.P2P.PersistentPeers = persistentPeersList
config.P2P.AddrBookStrict = false

View File

@ -94,7 +94,6 @@ func (cfg *Config) SetRoot(root string) *Config {
// BaseConfig defines the base configuration for a Tendermint node
type BaseConfig struct {
// chainID is unexposed and immutable but here for convenience
chainID string
@ -102,49 +101,49 @@ type BaseConfig struct {
// This should be set in viper so it can unmarshal into this struct
RootDir string `mapstructure:"home"`
// Path to the JSON file containing the initial validator set and other meta data
Genesis string `mapstructure:"genesis_file"`
// Path to the JSON file containing the private key to use as a validator in the consensus protocol
PrivValidator string `mapstructure:"priv_validator_file"`
// A JSON file containing the private key to use for p2p authenticated encryption
NodeKey string `mapstructure:"node_key_file"`
// A custom human readable name for this node
Moniker string `mapstructure:"moniker"`
// TCP or UNIX socket address for Tendermint to listen on for
// connections from an external PrivValidator process
PrivValidatorListenAddr string `mapstructure:"priv_validator_laddr"`
// TCP or UNIX socket address of the ABCI application,
// or the name of an ABCI application compiled in with the Tendermint binary
ProxyApp string `mapstructure:"proxy_app"`
// Mechanism to connect to the ABCI application: socket | grpc
ABCI string `mapstructure:"abci"`
// Output level for logging
LogLevel string `mapstructure:"log_level"`
// TCP or UNIX socket address for the profiling server to listen on
ProfListenAddress string `mapstructure:"prof_laddr"`
// A custom human readable name for this node
Moniker string `mapstructure:"moniker"`
// If this node is many blocks behind the tip of the chain, FastSync
// allows them to catchup quickly by downloading blocks in parallel
// and verifying their commits
FastSync bool `mapstructure:"fast_sync"`
// If true, query the ABCI app on connecting to a new peer
// so the app can decide if we should keep the connection or not
FilterPeers bool `mapstructure:"filter_peers"` // false
// Database backend: leveldb | memdb
DBBackend string `mapstructure:"db_backend"`
// Database directory
DBPath string `mapstructure:"db_dir"`
// Output level for logging
LogLevel string `mapstructure:"log_level"`
// Path to the JSON file containing the initial validator set and other meta data
Genesis string `mapstructure:"genesis_file"`
// Path to the JSON file containing the private key to use as a validator in the consensus protocol
PrivValidator string `mapstructure:"priv_validator_file"`
// TCP or UNIX socket address for Tendermint to listen on for
// connections from an external PrivValidator process
PrivValidatorListenAddr string `mapstructure:"priv_validator_laddr"`
// A JSON file containing the private key to use for p2p authenticated encryption
NodeKey string `mapstructure:"node_key_file"`
// Mechanism to connect to the ABCI application: socket | grpc
ABCI string `mapstructure:"abci"`
// TCP or UNIX socket address for the profiling server to listen on
ProfListenAddress string `mapstructure:"prof_laddr"`
// If true, query the ABCI app on connecting to a new peer
// so the app can decide if we should keep the connection or not
FilterPeers bool `mapstructure:"filter_peers"` // false
}
// DefaultBaseConfig returns a default base configuration for a Tendermint node
@ -239,6 +238,8 @@ type RPCConfig struct {
// If you want to accept more significant number than the default, make sure
// you increase your OS limits.
// 0 - unlimited.
// Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
// 1024 - 40 - 10 - 50 = 924 = ~900
MaxOpenConnections int `mapstructure:"max_open_connections"`
}
@ -248,11 +249,9 @@ func DefaultRPCConfig() *RPCConfig {
ListenAddress: "tcp://0.0.0.0:26657",
GRPCListenAddress: "",
GRPCMaxOpenConnections: 900, // no ipv4
GRPCMaxOpenConnections: 900,
Unsafe: false,
// should be < {ulimit -Sn} - {MaxNumPeers} - {N of wal, db and other open files}
// 1024 - 50 - 50 = 924 = ~900
Unsafe: false,
MaxOpenConnections: 900,
}
}
@ -293,10 +292,14 @@ type P2PConfig struct {
AddrBook string `mapstructure:"addr_book_file"`
// Set true for strict address routability rules
// Set false for private or local networks
AddrBookStrict bool `mapstructure:"addr_book_strict"`
// Maximum number of peers to connect to
MaxNumPeers int `mapstructure:"max_num_peers"`
// Maximum number of inbound peers
MaxNumInboundPeers int `mapstructure:"max_num_inbound_peers"`
// Maximum number of outbound peers to connect to, excluding persistent peers
MaxNumOutboundPeers int `mapstructure:"max_num_outbound_peers"`
// Time to wait before flushing messages out on the connection, in ms
FlushThrottleTimeout int `mapstructure:"flush_throttle_timeout"`
@ -346,7 +349,8 @@ func DefaultP2PConfig() *P2PConfig {
UPNP: false,
AddrBook: defaultAddrBookPath,
AddrBookStrict: true,
MaxNumPeers: 50,
MaxNumInboundPeers: 40,
MaxNumOutboundPeers: 10,
FlushThrottleTimeout: 100,
MaxPacketMsgPayloadSize: 1024, // 1 kB
SendRate: 5120000, // 5 mB/s
@ -417,8 +421,10 @@ func DefaultMempoolConfig() *MempoolConfig {
RecheckEmpty: true,
Broadcast: true,
WalPath: filepath.Join(defaultDataDir, "mempool.wal"),
Size: 100000,
CacheSize: 100000,
// Each signature verification takes .5ms, size reduced until we implement
// ABCI Recheck
Size: 5000,
CacheSize: 10000,
}
}
@ -463,6 +469,9 @@ type ConsensusConfig struct {
// Reactor sleep duration parameters are in milliseconds
PeerGossipSleepDuration int `mapstructure:"peer_gossip_sleep_duration"`
PeerQueryMaj23SleepDuration int `mapstructure:"peer_query_maj23_sleep_duration"`
// Block time parameters in milliseconds. Corresponds to the minimum time increment between consecutive blocks.
BlockTimeIota int `mapstructure:"blocktime_iota"`
}
// DefaultConsensusConfig returns a default configuration for the consensus service
@ -481,6 +490,7 @@ func DefaultConsensusConfig() *ConsensusConfig {
CreateEmptyBlocksInterval: 0,
PeerGossipSleepDuration: 100,
PeerQueryMaj23SleepDuration: 2000,
BlockTimeIota: 1000,
}
}
@ -497,9 +507,17 @@ func TestConsensusConfig() *ConsensusConfig {
cfg.SkipTimeoutCommit = true
cfg.PeerGossipSleepDuration = 5
cfg.PeerQueryMaj23SleepDuration = 250
cfg.BlockTimeIota = 10
return cfg
}
// MinValidVoteTime returns the minimum acceptable block time.
// See the [BFT time spec](https://godoc.org/github.com/tendermint/tendermint/docs/spec/consensus/bft-time.md).
func (cfg *ConsensusConfig) MinValidVoteTime(lastBlockTime time.Time) time.Time {
return lastBlockTime.
Add(time.Duration(cfg.BlockTimeIota) * time.Millisecond)
}
// WaitForTxs returns true if the consensus should wait for transactions before entering the propose step
func (cfg *ConsensusConfig) WaitForTxs() bool {
return !cfg.CreateEmptyBlocks || cfg.CreateEmptyBlocksInterval > 0
@ -556,8 +574,8 @@ func (cfg *ConsensusConfig) SetWalFile(walFile string) {
//-----------------------------------------------------------------------------
// TxIndexConfig
// TxIndexConfig defines the configuration for the transaction
// indexer, including tags to index.
// TxIndexConfig defines the configuration for the transaction indexer,
// including tags to index.
type TxIndexConfig struct {
// What indexer to use for transactions
//
@ -566,16 +584,21 @@ type TxIndexConfig struct {
// 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
Indexer string `mapstructure:"indexer"`
// Comma-separated list of tags to index (by default the only tag is tx hash)
// Comma-separated list of tags to index (by default the only tag is "tx.hash")
//
// You can also index transactions by height by adding "tx.height" tag here.
//
// It's recommended to index only a subset of tags due to possible memory
// bloat. This is, of course, depends on the indexer's DB and the volume of
// transactions.
IndexTags string `mapstructure:"index_tags"`
// When set to true, tells indexer to index all tags. Note this may be not
// desirable (see the comment above). IndexTags has a precedence over
// IndexAllTags (i.e. when given both, IndexTags will be indexed).
// When set to true, tells indexer to index all tags (predefined tags:
// "tx.hash", "tx.height" and all tags from DeliverTx responses).
//
// Note this may be not desirable (see the comment above). IndexTags has a
// precedence over IndexAllTags (i.e. when given both, IndexTags will be
// indexed).
IndexAllTags bool `mapstructure:"index_all_tags"`
}

View File

@ -81,7 +81,7 @@ fast_sync = {{ .BaseConfig.FastSync }}
db_backend = "{{ .BaseConfig.DBBackend }}"
# Database directory
db_path = "{{ js .BaseConfig.DBPath }}"
db_dir = "{{ js .BaseConfig.DBPath }}"
# Output level for logging, including package level options
log_level = "{{ .BaseConfig.LogLevel }}"
@ -94,6 +94,10 @@ genesis_file = "{{ js .BaseConfig.Genesis }}"
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
priv_validator_file = "{{ js .BaseConfig.PrivValidator }}"
# TCP or UNIX socket address for Tendermint to listen on for
# connections from an external PrivValidator process
priv_validator_laddr = "{{ .BaseConfig.PrivValidatorListenAddr }}"
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
node_key_file = "{{ js .BaseConfig.NodeKey}}"
@ -124,6 +128,8 @@ grpc_laddr = "{{ .RPC.GRPCListenAddress }}"
# If you want to accept more significant number than the default, make sure
# you increase your OS limits.
# 0 - unlimited.
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
# 1024 - 40 - 10 - 50 = 924 = ~900
grpc_max_open_connections = {{ .RPC.GRPCMaxOpenConnections }}
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
@ -134,6 +140,8 @@ unsafe = {{ .RPC.Unsafe }}
# If you want to accept more significant number than the default, make sure
# you increase your OS limits.
# 0 - unlimited.
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
# 1024 - 40 - 10 - 50 = 924 = ~900
max_open_connections = {{ .RPC.MaxOpenConnections }}
##### peer to peer configuration options #####
@ -161,13 +169,17 @@ upnp = {{ .P2P.UPNP }}
addr_book_file = "{{ js .P2P.AddrBook }}"
# Set true for strict address routability rules
# Set false for private or local networks
addr_book_strict = {{ .P2P.AddrBookStrict }}
# Time to wait before flushing messages out on the connection, in ms
flush_throttle_timeout = {{ .P2P.FlushThrottleTimeout }}
# Maximum number of peers to connect to
max_num_peers = {{ .P2P.MaxNumPeers }}
# Maximum number of inbound peers
max_num_inbound_peers = {{ .P2P.MaxNumInboundPeers }}
# Maximum number of outbound peers to connect to, excluding persistent peers
max_num_outbound_peers = {{ .P2P.MaxNumOutboundPeers }}
# Maximum size of a message packet payload, in bytes
max_packet_msg_payload_size = {{ .P2P.MaxPacketMsgPayloadSize }}
@ -239,16 +251,21 @@ peer_query_maj23_sleep_duration = {{ .Consensus.PeerQueryMaj23SleepDuration }}
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
indexer = "{{ .TxIndex.Indexer }}"
# Comma-separated list of tags to index (by default the only tag is tx hash)
# Comma-separated list of tags to index (by default the only tag is "tx.hash")
#
# You can also index transactions by height by adding "tx.height" tag here.
#
# It's recommended to index only a subset of tags due to possible memory
# bloat. This is, of course, depends on the indexer's DB and the volume of
# transactions.
index_tags = "{{ .TxIndex.IndexTags }}"
# When set to true, tells indexer to index all tags. Note this may be not
# desirable (see the comment above). IndexTags has a precedence over
# IndexAllTags (i.e. when given both, IndexTags will be indexed).
# When set to true, tells indexer to index all tags (predefined tags:
# "tx.hash", "tx.height" and all tags from DeliverTx responses).
#
# Note this may be not desirable (see the comment above). IndexTags has a
# precedence over IndexAllTags (i.e. when given both, IndexTags will be
# indexed).
index_all_tags = {{ .TxIndex.IndexAllTags }}
##### instrumentation configuration options #####

View File

@ -2,14 +2,15 @@ package consensus
import (
"context"
"fmt"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tendermint/libs/common"
)
func init() {
@ -156,8 +157,8 @@ func TestByzantine(t *testing.T) {
case <-done:
case <-tick.C:
for i, reactor := range reactors {
t.Log(cmn.Fmt("Consensus Reactor %v", i))
t.Log(cmn.Fmt("%v", reactor))
t.Log(fmt.Sprintf("Consensus Reactor %v", i))
t.Log(fmt.Sprintf("%v", reactor))
}
t.Fatalf("Timed out waiting for all validators to commit first block")
}

View File

@ -25,6 +25,7 @@ import (
"github.com/tendermint/tendermint/privval"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/tendermint/tendermint/abci/example/counter"
"github.com/tendermint/tendermint/abci/example/kvstore"
@ -75,7 +76,7 @@ func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartS
ValidatorAddress: vs.PrivValidator.GetAddress(),
Height: vs.Height,
Round: vs.Round,
Timestamp: time.Now().UTC(),
Timestamp: tmtime.Now(),
Type: voteType,
BlockID: types.BlockID{hash, header},
}
@ -348,13 +349,13 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou
for i := 0; i < nValidators; i++ {
stateDB := dbm.NewMemDB() // each state needs its own db
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
thisConfig := ResetConfig(cmn.Fmt("%s_%d", testName, i))
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
for _, opt := range configOpts {
opt(thisConfig)
}
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
app := appFunc()
vals := types.TM2PB.Validators(state.Validators)
vals := types.TM2PB.ValidatorUpdates(state.Validators)
app.InitChain(abci.RequestInitChain{Validators: vals})
css[i] = newConsensusStateWithConfig(thisConfig, state, privVals[i], app)
@ -372,7 +373,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
for i := 0; i < nPeers; i++ {
stateDB := dbm.NewMemDB() // each state needs its own db
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
thisConfig := ResetConfig(cmn.Fmt("%s_%d", testName, i))
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
var privVal types.PrivValidator
if i < nValidators {
@ -386,7 +387,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
}
app := appFunc()
vals := types.TM2PB.Validators(state.Validators)
vals := types.TM2PB.ValidatorUpdates(state.Validators)
app.InitChain(abci.RequestInitChain{Validators: vals})
css[i] = newConsensusStateWithConfig(thisConfig, state, privVal, app)
@ -423,7 +424,7 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
sort.Sort(types.PrivValidatorsByAddress(privValidators))
return &types.GenesisDoc{
GenesisTime: time.Now(),
GenesisTime: tmtime.Now(),
ChainID: config.ChainID(),
Validators: validators,
}, privValidators

View File

@ -10,7 +10,6 @@ import (
"github.com/tendermint/tendermint/abci/example/code"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
)
@ -89,7 +88,7 @@ func deliverTxsRange(cs *ConsensusState, start, end int) {
binary.BigEndian.PutUint64(txBytes, uint64(i))
err := cs.mempool.CheckTx(txBytes, nil)
if err != nil {
panic(cmn.Fmt("Error after CheckTx: %v", err))
panic(fmt.Sprintf("Error after CheckTx: %v", err))
}
}
}
@ -100,7 +99,7 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) {
height, round := cs.Height, cs.Round
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
NTxs := 10000
NTxs := 3000
go deliverTxsRange(cs, 0, NTxs)
startTestRound(cs, height, round)
@ -126,7 +125,7 @@ func TestMempoolRmBadTx(t *testing.T) {
binary.BigEndian.PutUint64(txBytes, uint64(0))
resDeliver := app.DeliverTx(txBytes)
assert.False(t, resDeliver.IsErr(), cmn.Fmt("expected no error. got %v", resDeliver))
assert.False(t, resDeliver.IsErr(), fmt.Sprintf("expected no error. got %v", resDeliver))
resCommit := app.Commit()
assert.True(t, len(resCommit.Data) > 0)
@ -149,7 +148,7 @@ func TestMempoolRmBadTx(t *testing.T) {
// check for the tx
for {
txs := cs.mempool.Reap(1)
txs := cs.mempool.ReapMaxBytes(len(txBytes))
if len(txs) == 0 {
emptyMempoolCh <- struct{}{}
return
@ -190,7 +189,7 @@ func NewCounterApplication() *CounterApplication {
}
func (app *CounterApplication) Info(req abci.RequestInfo) abci.ResponseInfo {
return abci.ResponseInfo{Data: cmn.Fmt("txs:%v", app.txCount)}
return abci.ResponseInfo{Data: fmt.Sprintf("txs:%v", app.txCount)}
}
func (app *CounterApplication) DeliverTx(tx []byte) abci.ResponseDeliverTx {

View File

@ -17,6 +17,7 @@ import (
"github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
const (
@ -241,7 +242,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
"height", hb.Height, "round", hb.Round, "sequence", hb.Sequence,
"valIdx", hb.ValidatorIndex, "valAddr", hb.ValidatorAddress)
default:
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
}
case DataChannel:
@ -262,7 +263,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
}
conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()}
default:
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
}
case VoteChannel:
@ -287,7 +288,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
default:
// don't punish (leave room for soft upgrades)
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
}
case VoteSetBitsChannel:
@ -319,11 +320,11 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
}
default:
// don't punish (leave room for soft upgrades)
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
}
default:
conR.Logger.Error(cmn.Fmt("Unknown chId %X", chID))
conR.Logger.Error(fmt.Sprintf("Unknown chId %X", chID))
}
if err != nil {
@ -482,7 +483,7 @@ OUTER_LOOP:
if prs.ProposalBlockParts == nil {
blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height)
if blockMeta == nil {
cmn.PanicCrisis(cmn.Fmt("Failed to load block %d when blockStore is at %d",
cmn.PanicCrisis(fmt.Sprintf("Failed to load block %d when blockStore is at %d",
prs.Height, conR.conS.blockStore.Height()))
}
ps.InitProposalBlockParts(blockMeta.BlockID.PartsHeader)
@ -1034,7 +1035,7 @@ func (ps *PeerState) ensureCatchupCommitRound(height int64, round int, numValida
NOTE: This is wrong, 'round' could change.
e.g. if orig round is not the same as block LastCommit round.
if ps.CatchupCommitRound != -1 && ps.CatchupCommitRound != round {
cmn.PanicSanity(cmn.Fmt("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round))
cmn.PanicSanity(fmt.Sprintf("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round))
}
*/
if ps.PRS.CatchupCommitRound == round {
@ -1138,7 +1139,7 @@ func (ps *PeerState) SetHasVote(vote *types.Vote) {
}
func (ps *PeerState) setHasVote(height int64, round int, type_ byte, index int) {
logger := ps.logger.With("peerH/R", cmn.Fmt("%d/%d", ps.PRS.Height, ps.PRS.Round), "H/R", cmn.Fmt("%d/%d", height, round))
logger := ps.logger.With("peerH/R", fmt.Sprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), "H/R", fmt.Sprintf("%d/%d", height, round))
logger.Debug("setHasVote", "type", type_, "index", index)
// NOTE: some may be nil BitArrays -> no side effects.
@ -1165,7 +1166,7 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) {
psCatchupCommitRound := ps.PRS.CatchupCommitRound
psCatchupCommit := ps.PRS.CatchupCommit
startTime := time.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second)
startTime := tmtime.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second)
ps.PRS.Height = msg.Height
ps.PRS.Round = msg.Round
ps.PRS.Step = msg.Step

View File

@ -20,6 +20,7 @@ import (
"github.com/tendermint/tendermint/libs/log"
mempl "github.com/tendermint/tendermint/mempool"
sm "github.com/tendermint/tendermint/state"
tmtime "github.com/tendermint/tendermint/types/time"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/p2p"
@ -115,10 +116,10 @@ func TestReactorWithEvidence(t *testing.T) {
for i := 0; i < nValidators; i++ {
stateDB := dbm.NewMemDB() // each state needs its own db
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
thisConfig := ResetConfig(cmn.Fmt("%s_%d", testName, i))
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
app := appFunc()
vals := types.TM2PB.Validators(state.Validators)
vals := types.TM2PB.ValidatorUpdates(state.Validators)
app.InitChain(abci.RequestInitChain{Validators: vals})
pv := privVals[i]
@ -194,7 +195,8 @@ func newMockEvidencePool(val []byte) *mockEvidencePool {
}
}
func (m *mockEvidencePool) PendingEvidence() []types.Evidence {
// NOTE: maxBytes is ignored
func (m *mockEvidencePool) PendingEvidence(maxBytes int) []types.Evidence {
if m.height > 0 {
return m.ev
}
@ -207,7 +209,7 @@ func (m *mockEvidencePool) Update(block *types.Block, state sm.State) {
panic("block has no evidence")
}
}
m.height += 1
m.height++
}
//------------------------------------
@ -295,14 +297,14 @@ func TestReactorRecordsBlockParts(t *testing.T) {
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
}
// Test we record votes from other peers
// Test we record votes from other peers.
func TestReactorRecordsVotes(t *testing.T) {
// create dummy peer
// Create dummy peer.
peer := p2pdummy.NewPeer()
ps := NewPeerState(peer).SetLogger(log.TestingLogger())
peer.Set(types.PeerStateKey, ps)
// create reactor
// Create reactor.
css := randConsensusNet(1, "consensus_reactor_records_votes_test", newMockTickerFunc(true), newPersistentKVStore)
reactor := NewConsensusReactor(css[0], false) // so we dont start the consensus states
reactor.SetEventBus(css[0].eventBus)
@ -320,7 +322,7 @@ func TestReactorRecordsVotes(t *testing.T) {
ValidatorAddress: val.Address,
Height: 2,
Round: 0,
Timestamp: time.Now().UTC(),
Timestamp: tmtime.Now(),
Type: types.VoteTypePrevote,
BlockID: types.BlockID{},
}
@ -540,7 +542,7 @@ func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{}
err := validateBlock(newBlock, activeVals)
assert.Nil(t, err)
for _, tx := range txs {
css[j].mempool.CheckTx(tx, nil)
err := css[j].mempool.CheckTx(tx, nil)
assert.Nil(t, err)
}
}, css)

View File

@ -264,15 +264,15 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
stateBlockHeight := state.LastBlockHeight
h.logger.Info("ABCI Replay Blocks", "appHeight", appBlockHeight, "storeHeight", storeBlockHeight, "stateHeight", stateBlockHeight)
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain.
if appBlockHeight == 0 {
validators := types.TM2PB.Validators(state.Validators)
nextVals := types.TM2PB.ValidatorUpdates(state.NextValidators) // state.Validators would work too.
csParams := types.TM2PB.ConsensusParams(h.genDoc.ConsensusParams)
req := abci.RequestInitChain{
Time: h.genDoc.GenesisTime,
ChainId: h.genDoc.ChainID,
ConsensusParams: csParams,
Validators: validators,
Validators: nextVals,
AppStateBytes: h.genDoc.AppState,
}
res, err := proxyApp.Consensus().InitChainSync(req)
@ -280,11 +280,9 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
return nil, err
}
// if the app returned validators
// or consensus params, update the state
// with the them
// If the app returned validators or consensus params, update the state.
if len(res.Validators) > 0 {
vals, err := types.PB2TM.Validators(res.Validators)
vals, err := types.PB2TM.ValidatorUpdates(res.Validators)
if err != nil {
return nil, err
}
@ -296,7 +294,7 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
sm.SaveState(h.stateDB, state)
}
// First handle edge cases and constraints on the storeBlockHeight
// First handle edge cases and constraints on the storeBlockHeight.
if storeBlockHeight == 0 {
return appHash, checkAppHash(state, appHash)
@ -306,11 +304,11 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
} else if storeBlockHeight < stateBlockHeight {
// the state should never be ahead of the store (this is under tendermint's control)
cmn.PanicSanity(cmn.Fmt("StateBlockHeight (%d) > StoreBlockHeight (%d)", stateBlockHeight, storeBlockHeight))
cmn.PanicSanity(fmt.Sprintf("StateBlockHeight (%d) > StoreBlockHeight (%d)", stateBlockHeight, storeBlockHeight))
} else if storeBlockHeight > stateBlockHeight+1 {
// store should be at most one ahead of the state (this is under tendermint's control)
cmn.PanicSanity(cmn.Fmt("StoreBlockHeight (%d) > StateBlockHeight + 1 (%d)", storeBlockHeight, stateBlockHeight+1))
cmn.PanicSanity(fmt.Sprintf("StoreBlockHeight (%d) > StateBlockHeight + 1 (%d)", storeBlockHeight, stateBlockHeight+1))
}
var err error

View File

@ -13,12 +13,12 @@ import (
bc "github.com/tendermint/tendermint/blockchain"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
const (
@ -34,7 +34,7 @@ func RunReplayFile(config cfg.BaseConfig, csConfig *cfg.ConsensusConfig, console
consensusState := newConsensusStateForReplay(config, csConfig)
if err := consensusState.ReplayFile(csConfig.WalFile(), console); err != nil {
cmn.Exit(cmn.Fmt("Error during consensus replay: %v", err))
cmn.Exit(fmt.Sprintf("Error during consensus replay: %v", err))
}
}
@ -302,12 +302,12 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
NewHandshaker(stateDB, state, blockStore, gdoc))
err = proxyApp.Start()
if err != nil {
cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err))
cmn.Exit(fmt.Sprintf("Error starting proxy app conns: %v", err))
}
eventBus := types.NewEventBus()
if err := eventBus.Start(); err != nil {
cmn.Exit(cmn.Fmt("Failed to start event bus: %v", err))
cmn.Exit(fmt.Sprintf("Failed to start event bus: %v", err))
}
mempool, evpool := sm.MockMempool{}, sm.MockEvidencePool{}

View File

@ -3,7 +3,6 @@ package consensus
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/ioutil"
@ -20,15 +19,14 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
crypto "github.com/tendermint/tendermint/crypto"
auto "github.com/tendermint/tendermint/libs/autofile"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/libs/log"
)
var consensusReplayConfig *cfg.Config
@ -418,7 +416,7 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
}
defer proxyApp.Stop()
validators := types.TM2PB.Validators(state.Validators)
validators := types.TM2PB.ValidatorUpdates(state.Validators)
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{
Validators: validators,
}); err != nil {
@ -455,7 +453,7 @@ func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, c
}
defer proxyApp.Stop()
validators := types.TM2PB.Validators(state.Validators)
validators := types.TM2PB.ValidatorUpdates(state.Validators)
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{
Validators: validators,
}); err != nil {
@ -494,7 +492,7 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
return nil, nil, err
}
if !found {
return nil, nil, errors.New(cmn.Fmt("WAL does not contain height %d.", 1))
return nil, nil, fmt.Errorf("WAL does not contain height %d.", 1)
}
defer gr.Close() // nolint: errcheck
@ -531,11 +529,11 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
panic(err)
}
if block.Height != height+1 {
panic(cmn.Fmt("read bad block from wal. got height %d, expected %d", block.Height, height+1))
panic(fmt.Sprintf("read bad block from wal. got height %d, expected %d", block.Height, height+1))
}
commitHeight := thisBlockCommit.Precommits[0].Height
if commitHeight != height+1 {
panic(cmn.Fmt("commit doesnt match. got height %d, expected %d", commitHeight, height+1))
panic(fmt.Sprintf("commit doesnt match. got height %d, expected %d", commitHeight, height+1))
}
blocks = append(blocks, block)
commits = append(commits, thisBlockCommit)
@ -564,11 +562,11 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
panic(err)
}
if block.Height != height+1 {
panic(cmn.Fmt("read bad block from wal. got height %d, expected %d", block.Height, height+1))
panic(fmt.Sprintf("read bad block from wal. got height %d, expected %d", block.Height, height+1))
}
commitHeight := thisBlockCommit.Precommits[0].Height
if commitHeight != height+1 {
panic(cmn.Fmt("commit doesnt match. got height %d, expected %d", commitHeight, height+1))
panic(fmt.Sprintf("commit doesnt match. got height %d, expected %d", commitHeight, height+1))
}
blocks = append(blocks, block)
commits = append(commits, thisBlockCommit)
@ -641,7 +639,7 @@ func (bs *mockBlockStore) LoadSeenCommit(height int64) *types.Commit {
func TestInitChainUpdateValidators(t *testing.T) {
val, _ := types.RandValidator(true, 10)
vals := types.NewValidatorSet([]*types.Validator{val})
app := &initChainApp{vals: types.TM2PB.Validators(vals)}
app := &initChainApp{vals: types.TM2PB.ValidatorUpdates(vals)}
clientCreator := proxy.NewLocalClientCreator(app)
config := ResetConfig("proxy_test_")
@ -668,7 +666,7 @@ func TestInitChainUpdateValidators(t *testing.T) {
assert.Equal(t, newValAddr, expectValAddr)
}
func newInitChainApp(vals []abci.Validator) *initChainApp {
func newInitChainApp(vals []abci.ValidatorUpdate) *initChainApp {
return &initChainApp{
vals: vals,
}
@ -677,7 +675,7 @@ func newInitChainApp(vals []abci.Validator) *initChainApp {
// returns the vals on InitChain
type initChainApp struct {
abci.BaseApplication
vals []abci.Validator
vals []abci.ValidatorUpdate
}
func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {

View File

@ -12,6 +12,7 @@ import (
fail "github.com/ebuchman/fail-test"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
tmtime "github.com/tendermint/tendermint/types/time"
cfg "github.com/tendermint/tendermint/config"
cstypes "github.com/tendermint/tendermint/consensus/types"
@ -74,7 +75,6 @@ type ConsensusState struct {
privValidator types.PrivValidator // for signing votes
// services for creating and executing blocks
// TODO: encapsulate all of this in one "BlockManager"
blockExec *sm.BlockExecutor
blockStore sm.BlockStore
mempool sm.Mempool
@ -154,6 +154,7 @@ func NewConsensusState(
cs.setProposal = cs.defaultSetProposal
cs.updateToState(state)
// Don't call scheduleRound0 yet.
// We do that upon Start().
cs.reconstructLastCommit(state)
@ -187,7 +188,7 @@ func WithMetrics(metrics *Metrics) CSOption {
// String returns a string.
func (cs *ConsensusState) String() string {
// better not to access shared variables
return cmn.Fmt("ConsensusState") //(H:%v R:%v S:%v", cs.Height, cs.Round, cs.Step)
return fmt.Sprintf("ConsensusState") //(H:%v R:%v S:%v", cs.Height, cs.Round, cs.Step)
}
// GetState returns a copy of the chain state.
@ -197,6 +198,15 @@ func (cs *ConsensusState) GetState() sm.State {
return cs.state.Copy()
}
// GetLastHeight returns the last height committed.
// If there were no blocks, returns 0.
func (cs *ConsensusState) GetLastHeight() int64 {
cs.mtx.Lock()
defer cs.mtx.Unlock()
return cs.RoundState.Height - 1
}
// GetRoundState returns a shallow copy of the internal consensus state.
func (cs *ConsensusState) GetRoundState() *cstypes.RoundState {
cs.mtx.RLock()
@ -413,8 +423,8 @@ func (cs *ConsensusState) updateRoundStep(round int, step cstypes.RoundStepType)
// enterNewRound(height, 0) at cs.StartTime.
func (cs *ConsensusState) scheduleRound0(rs *cstypes.RoundState) {
//cs.Logger.Info("scheduleRound0", "now", time.Now(), "startTime", cs.StartTime)
sleepDuration := rs.StartTime.Sub(time.Now()) // nolint: gotype, gosimple
//cs.Logger.Info("scheduleRound0", "now", tmtime.Now(), "startTime", cs.StartTime)
sleepDuration := rs.StartTime.Sub(tmtime.Now()) // nolint: gotype, gosimple
cs.scheduleTimeout(sleepDuration, rs.Height, 0, cstypes.RoundStepNewHeight)
}
@ -451,7 +461,7 @@ func (cs *ConsensusState) reconstructLastCommit(state sm.State) {
}
added, err := lastPrecommits.AddVote(precommit)
if !added || err != nil {
cmn.PanicCrisis(cmn.Fmt("Failed to reconstruct LastCommit: %v", err))
cmn.PanicCrisis(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err))
}
}
if !lastPrecommits.HasTwoThirdsMajority() {
@ -464,13 +474,13 @@ func (cs *ConsensusState) reconstructLastCommit(state sm.State) {
// The round becomes 0 and cs.Step becomes cstypes.RoundStepNewHeight.
func (cs *ConsensusState) updateToState(state sm.State) {
if cs.CommitRound > -1 && 0 < cs.Height && cs.Height != state.LastBlockHeight {
cmn.PanicSanity(cmn.Fmt("updateToState() expected state height of %v but found %v",
cmn.PanicSanity(fmt.Sprintf("updateToState() expected state height of %v but found %v",
cs.Height, state.LastBlockHeight))
}
if !cs.state.IsEmpty() && cs.state.LastBlockHeight+1 != cs.Height {
// This might happen when someone else is mutating cs.state.
// Someone forgot to pass in state.Copy() somewhere?!
cmn.PanicSanity(cmn.Fmt("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v",
cmn.PanicSanity(fmt.Sprintf("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v",
cs.state.LastBlockHeight+1, cs.Height))
}
@ -507,7 +517,7 @@ func (cs *ConsensusState) updateToState(state sm.State) {
// to be gathered for the first block.
// And alternative solution that relies on clocks:
// cs.StartTime = state.LastBlockTime.Add(timeoutCommit)
cs.StartTime = cs.config.Commit(time.Now())
cs.StartTime = cs.config.Commit(tmtime.Now())
} else {
cs.StartTime = cs.config.Commit(cs.CommitTime)
}
@ -690,7 +700,7 @@ func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) {
cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent())
cs.enterNewRound(ti.Height, ti.Round+1)
default:
panic(cmn.Fmt("Invalid timeout step: %v", ti.Step))
panic(fmt.Sprintf("Invalid timeout step: %v", ti.Step))
}
}
@ -716,15 +726,15 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != cstypes.RoundStepNewHeight) {
logger.Debug(cmn.Fmt("enterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Debug(fmt.Sprintf("enterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
if now := time.Now(); cs.StartTime.After(now) {
if now := tmtime.Now(); cs.StartTime.After(now) {
logger.Info("Need to set a buffer and log message here for sanity.", "startTime", cs.StartTime, "now", now)
}
logger.Info(cmn.Fmt("enterNewRound(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Info(fmt.Sprintf("enterNewRound(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
// Increment validators if necessary
validators := cs.Validators
@ -811,10 +821,10 @@ func (cs *ConsensusState) enterPropose(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPropose <= cs.Step) {
logger.Debug(cmn.Fmt("enterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Debug(fmt.Sprintf("enterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
logger.Info(cmn.Fmt("enterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Info(fmt.Sprintf("enterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
defer func() {
// Done enterPropose:
@ -894,7 +904,7 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
cs.sendInternalMessage(msgInfo{&BlockPartMessage{cs.Height, cs.Round, part}, ""})
}
cs.Logger.Info("Signed proposal", "height", height, "round", round, "proposal", proposal)
cs.Logger.Debug(cmn.Fmt("Signed proposal block: %v", block))
cs.Logger.Debug(fmt.Sprintf("Signed proposal block: %v", block))
} else {
if !cs.replayMode {
cs.Logger.Error("enterPropose: Error signing proposal", "height", height, "round", round, "err", err)
@ -938,13 +948,25 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
return
}
maxBytes := cs.state.ConsensusParams.BlockSize.MaxBytes
// bound evidence to 1/10th of the block
evidence := cs.evpool.PendingEvidence(maxBytes / 10)
// Mempool validated transactions
txs := cs.mempool.Reap(cs.state.ConsensusParams.BlockSize.MaxTxs)
evidence := cs.evpool.PendingEvidence()
block, parts := cs.state.MakeBlock(cs.Height, txs, commit, evidence)
txs := cs.mempool.ReapMaxBytes(maxDataBytes(maxBytes, cs.state.Validators.Size(), len(evidence)))
proposerAddr := cs.privValidator.GetAddress()
block, parts := cs.state.MakeBlock(cs.Height, txs, commit, evidence, proposerAddr)
return block, parts
}
func maxDataBytes(maxBytes, valsCount, evidenceCount int) int {
return maxBytes -
types.MaxAminoOverheadForBlock -
types.MaxHeaderBytes -
(valsCount * types.MaxVoteBytes) -
(evidenceCount * types.MaxEvidenceBytes)
}
// Enter: `timeoutPropose` after entering Propose.
// Enter: proposal block and POL is ready.
// Enter: any +2/3 prevotes for future round.
@ -952,7 +974,7 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
// Otherwise vote nil.
func (cs *ConsensusState) enterPrevote(height int64, round int) {
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevote <= cs.Step) {
cs.Logger.Debug(cmn.Fmt("enterPrevote(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
cs.Logger.Debug(fmt.Sprintf("enterPrevote(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
@ -970,7 +992,7 @@ func (cs *ConsensusState) enterPrevote(height int64, round int) {
// TODO: catchup event?
}
cs.Logger.Info(cmn.Fmt("enterPrevote(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
cs.Logger.Info(fmt.Sprintf("enterPrevote(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
// Sign and broadcast vote as necessary
cs.doPrevote(height, round)
@ -1016,13 +1038,13 @@ func (cs *ConsensusState) enterPrevoteWait(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevoteWait <= cs.Step) {
logger.Debug(cmn.Fmt("enterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Debug(fmt.Sprintf("enterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
if !cs.Votes.Prevotes(round).HasTwoThirdsAny() {
cmn.PanicSanity(cmn.Fmt("enterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round))
cmn.PanicSanity(fmt.Sprintf("enterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round))
}
logger.Info(cmn.Fmt("enterPrevoteWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Info(fmt.Sprintf("enterPrevoteWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
defer func() {
// Done enterPrevoteWait:
@ -1044,11 +1066,11 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommit <= cs.Step) {
logger.Debug(cmn.Fmt("enterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Debug(fmt.Sprintf("enterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
logger.Info(cmn.Fmt("enterPrecommit(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Info(fmt.Sprintf("enterPrecommit(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
defer func() {
// Done enterPrecommit:
@ -1076,7 +1098,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
// the latest POLRound should be this round.
polRound, _ := cs.Votes.POLInfo()
if polRound < round {
cmn.PanicSanity(cmn.Fmt("This POLRound should be %v but got %", round, polRound))
cmn.PanicSanity(fmt.Sprintf("This POLRound should be %v but got %v", round, polRound))
}
// +2/3 prevoted nil. Unlock and precommit nil.
@ -1110,7 +1132,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
logger.Info("enterPrecommit: +2/3 prevoted proposal block. Locking", "hash", blockID.Hash)
// Validate the block.
if err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock); err != nil {
cmn.PanicConsensus(cmn.Fmt("enterPrecommit: +2/3 prevoted for an invalid block: %v", err))
cmn.PanicConsensus(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err))
}
cs.LockedRound = round
cs.LockedBlock = cs.ProposalBlock
@ -1140,13 +1162,13 @@ func (cs *ConsensusState) enterPrecommitWait(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommitWait <= cs.Step) {
logger.Debug(cmn.Fmt("enterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Debug(fmt.Sprintf("enterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
return
}
if !cs.Votes.Precommits(round).HasTwoThirdsAny() {
cmn.PanicSanity(cmn.Fmt("enterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round))
cmn.PanicSanity(fmt.Sprintf("enterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round))
}
logger.Info(cmn.Fmt("enterPrecommitWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
logger.Info(fmt.Sprintf("enterPrecommitWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
defer func() {
// Done enterPrecommitWait:
@ -1164,17 +1186,17 @@ func (cs *ConsensusState) enterCommit(height int64, commitRound int) {
logger := cs.Logger.With("height", height, "commitRound", commitRound)
if cs.Height != height || cstypes.RoundStepCommit <= cs.Step {
logger.Debug(cmn.Fmt("enterCommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
logger.Debug(fmt.Sprintf("enterCommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
return
}
logger.Info(cmn.Fmt("enterCommit(%v/%v). Current: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
logger.Info(fmt.Sprintf("enterCommit(%v/%v). Current: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
defer func() {
// Done enterCommit:
// keep cs.Round the same, commitRound points to the right Precommits set.
cs.updateRoundStep(cs.Round, cstypes.RoundStepCommit)
cs.CommitRound = commitRound
cs.CommitTime = time.Now()
cs.CommitTime = tmtime.Now()
cs.newStep()
// Maybe finalize immediately.
@ -1214,7 +1236,7 @@ func (cs *ConsensusState) tryFinalizeCommit(height int64) {
logger := cs.Logger.With("height", height)
if cs.Height != height {
cmn.PanicSanity(cmn.Fmt("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height))
cmn.PanicSanity(fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height))
}
blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority()
@ -1236,7 +1258,7 @@ func (cs *ConsensusState) tryFinalizeCommit(height int64) {
// Increment height and goto cstypes.RoundStepNewHeight
func (cs *ConsensusState) finalizeCommit(height int64) {
if cs.Height != height || cs.Step != cstypes.RoundStepCommit {
cs.Logger.Debug(cmn.Fmt("finalizeCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step))
cs.Logger.Debug(fmt.Sprintf("finalizeCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step))
return
}
@ -1244,21 +1266,21 @@ func (cs *ConsensusState) finalizeCommit(height int64) {
block, blockParts := cs.ProposalBlock, cs.ProposalBlockParts
if !ok {
cmn.PanicSanity(cmn.Fmt("Cannot finalizeCommit, commit does not have two thirds majority"))
cmn.PanicSanity(fmt.Sprintf("Cannot finalizeCommit, commit does not have two thirds majority"))
}
if !blockParts.HasHeader(blockID.PartsHeader) {
cmn.PanicSanity(cmn.Fmt("Expected ProposalBlockParts header to be commit header"))
cmn.PanicSanity(fmt.Sprintf("Expected ProposalBlockParts header to be commit header"))
}
if !block.HashesTo(blockID.Hash) {
cmn.PanicSanity(cmn.Fmt("Cannot finalizeCommit, ProposalBlock does not hash to commit hash"))
cmn.PanicSanity(fmt.Sprintf("Cannot finalizeCommit, ProposalBlock does not hash to commit hash"))
}
if err := cs.blockExec.ValidateBlock(cs.state, block); err != nil {
cmn.PanicConsensus(cmn.Fmt("+2/3 committed an invalid block: %v", err))
cmn.PanicConsensus(fmt.Sprintf("+2/3 committed an invalid block: %v", err))
}
cs.Logger.Info(cmn.Fmt("Finalizing commit of block with %d txs", block.NumTxs),
cs.Logger.Info(fmt.Sprintf("Finalizing commit of block with %d txs", block.NumTxs),
"height", block.Height, "hash", block.Hash(), "root", block.AppHash)
cs.Logger.Info(cmn.Fmt("%v", block))
cs.Logger.Info(fmt.Sprintf("%v", block))
fail.Fail() // XXX
@ -1429,7 +1451,11 @@ func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p
}
if added && cs.ProposalBlockParts.IsComplete() {
// Added and completed!
_, err = cdc.UnmarshalBinaryReader(cs.ProposalBlockParts.GetReader(), &cs.ProposalBlock, int64(cs.state.ConsensusParams.BlockSize.MaxBytes))
_, err = cdc.UnmarshalBinaryReader(
cs.ProposalBlockParts.GetReader(),
&cs.ProposalBlock,
int64(cs.state.ConsensusParams.BlockSize.MaxBytes),
)
if err != nil {
return true, err
}
@ -1510,7 +1536,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
return added, err
}
cs.Logger.Info(cmn.Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
cs.Logger.Info(fmt.Sprintf("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
cs.eventBus.PublishEventVote(types.EventDataVote{vote})
cs.evsw.FireEvent(types.EventVote, vote)
@ -1626,7 +1652,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
cs.enterPrecommitWait(height, vote.Round)
}
default:
panic(cmn.Fmt("Unexpected vote type %X", vote.Type)) // go-wire should prevent this.
panic(fmt.Sprintf("Unexpected vote type %X", vote.Type)) // go-wire should prevent this.
}
return
@ -1635,12 +1661,13 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
addr := cs.privValidator.GetAddress()
valIndex, _ := cs.Validators.GetByAddress(addr)
vote := &types.Vote{
ValidatorAddress: addr,
ValidatorIndex: valIndex,
Height: cs.Height,
Round: cs.Round,
Timestamp: time.Now().UTC(),
Timestamp: cs.voteTime(),
Type: type_,
BlockID: types.BlockID{hash, header},
}
@ -1648,6 +1675,23 @@ func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSet
return vote, err
}
func (cs *ConsensusState) voteTime() time.Time {
now := tmtime.Now()
minVoteTime := now
// TODO: We should remove next line in case we don't vote for v in case cs.ProposalBlock == nil,
// even if cs.LockedBlock != nil. See https://github.com/tendermint/spec.
if cs.LockedBlock != nil {
minVoteTime = cs.config.MinValidVoteTime(cs.LockedBlock.Time)
} else if cs.ProposalBlock != nil {
minVoteTime = cs.config.MinValidVoteTime(cs.ProposalBlock.Time)
}
if now.After(minVoteTime) {
return now
}
return minVoteTime
}
// sign the vote and publish on internalMsgQueue
func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.PartSetHeader) *types.Vote {
// if we don't have a key or we're not in the validator set, do nothing

View File

@ -8,10 +8,9 @@ import (
"time"
cstypes "github.com/tendermint/tendermint/consensus/types"
"github.com/tendermint/tendermint/libs/log"
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
)
func init() {
@ -84,7 +83,7 @@ func TestStateProposerSelection0(t *testing.T) {
prop = cs1.GetRoundState().Validators.GetProposer()
if !bytes.Equal(prop.Address, vss[1].GetAddress()) {
panic(cmn.Fmt("expected proposer to be validator %d. Got %X", 1, prop.Address))
panic(fmt.Sprintf("expected proposer to be validator %d. Got %X", 1, prop.Address))
}
}
@ -104,8 +103,9 @@ func TestStateProposerSelection2(t *testing.T) {
// everyone just votes nil. we get a new proposer each round
for i := 0; i < len(vss); i++ {
prop := cs1.GetRoundState().Validators.GetProposer()
if !bytes.Equal(prop.Address, vss[(i+2)%len(vss)].GetAddress()) {
panic(cmn.Fmt("expected proposer to be validator %d. Got %X", (i+2)%len(vss), prop.Address))
correctProposer := vss[(i+2)%len(vss)].GetAddress()
if !bytes.Equal(prop.Address, correctProposer) {
panic(fmt.Sprintf("expected RoundState.Validators.GetProposer() to be validator %d. Got %X", (i+2)%len(vss), prop.Address))
}
rs := cs1.GetRoundState()
@ -444,7 +444,7 @@ func TestStateLockNoPOL(t *testing.T) {
// now we're on a new round and are the proposer
if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) {
panic(cmn.Fmt("Expected proposal block to be locked block. Got %v, Expected %v", rs.ProposalBlock, rs.LockedBlock))
panic(fmt.Sprintf("Expected proposal block to be locked block. Got %v, Expected %v", rs.ProposalBlock, rs.LockedBlock))
}
<-voteCh // prevote

View File

@ -6,9 +6,9 @@ import (
"strings"
"sync"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tendermint/libs/common"
)
type RoundVoteSet struct {
@ -169,7 +169,7 @@ func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {
case types.VoteTypePrecommit:
return rvs.Precommits
default:
cmn.PanicSanity(cmn.Fmt("Unexpected vote type %X", type_))
cmn.PanicSanity(fmt.Sprintf("Unexpected vote type %X", type_))
return nil
}
}
@ -219,7 +219,7 @@ func (hvs *HeightVoteSet) StringIndented(indent string) string {
voteSetString = roundVoteSet.Precommits.StringShort()
vsStrings = append(vsStrings, voteSetString)
}
return cmn.Fmt(`HeightVoteSet{H:%v R:0~%v
return fmt.Sprintf(`HeightVoteSet{H:%v R:0~%v
%s %v
%s}`,
hvs.height, hvs.round,

View File

@ -1,12 +1,12 @@
package types
import (
"fmt"
"testing"
"time"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tendermint/libs/common"
tmtime "github.com/tendermint/tendermint/types/time"
)
var config *cfg.Config // NOTE: must be reset for each _test.go file
@ -55,14 +55,14 @@ func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivVali
ValidatorIndex: valIndex,
Height: height,
Round: round,
Timestamp: time.Now().UTC(),
Timestamp: tmtime.Now(),
Type: types.VoteTypePrecommit,
BlockID: types.BlockID{[]byte("fakehash"), types.PartSetHeader{}},
}
chainID := config.ChainID()
err := privVal.SignVote(chainID, vote)
if err != nil {
panic(cmn.Fmt("Error signing vote: %v", err))
panic(fmt.Sprintf("Error signing vote: %v", err))
return nil
}
return vote

View File

@ -4,8 +4,8 @@ import (
"fmt"
"time"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
)
//-----------------------------------------------------------------------------

View File

@ -5,8 +5,8 @@ import (
"fmt"
"time"
"github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
)
//-----------------------------------------------------------------------------

View File

@ -2,12 +2,12 @@ package types
import (
"testing"
"time"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
func BenchmarkRoundStateDeepCopy(b *testing.B) {
@ -23,11 +23,11 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) {
Hash: cmn.RandBytes(20),
},
}
sig := make([]byte, ed25519.SignatureEd25519Size)
sig := make([]byte, ed25519.SignatureSize)
for i := 0; i < nval; i++ {
precommits[i] = &types.Vote{
ValidatorAddress: types.Address(cmn.RandBytes(20)),
Timestamp: time.Now(),
Timestamp: tmtime.Now(),
BlockID: blockID,
Signature: sig,
}
@ -40,7 +40,7 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) {
block := &types.Block{
Header: types.Header{
ChainID: cmn.RandStr(12),
Time: time.Now(),
Time: tmtime.Now(),
LastBlockID: blockID,
LastCommitHash: cmn.RandBytes(20),
DataHash: cmn.RandBytes(20),
@ -62,7 +62,7 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) {
parts := block.MakePartSet(4096)
// Random Proposal
proposal := &types.Proposal{
Timestamp: time.Now(),
Timestamp: tmtime.Now(),
BlockPartsHeader: types.PartSetHeader{
Hash: cmn.RandBytes(20),
},
@ -73,8 +73,8 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) {
// TODO: hvs :=
rs := &RoundState{
StartTime: time.Now(),
CommitTime: time.Now(),
StartTime: tmtime.Now(),
CommitTime: tmtime.Now(),
Validators: vset,
Proposal: proposal,
ProposalBlock: block,

View File

@ -1,8 +1,6 @@
package consensus
import (
cmn "github.com/tendermint/tendermint/libs/common"
)
import "fmt"
// kind of arbitrary
var Spec = "1" // async
@ -10,4 +8,4 @@ var Major = "0" //
var Minor = "2" // replay refactor
var Revision = "2" // validation -> commit
var Version = cmn.Fmt("v%s/%s.%s.%s", Spec, Major, Minor, Revision)
var Version = fmt.Sprintf("v%s/%s.%s.%s", Spec, Major, Minor, Revision)

View File

@ -11,9 +11,10 @@ import (
"github.com/pkg/errors"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/types"
auto "github.com/tendermint/tendermint/libs/autofile"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
)
const (
@ -119,8 +120,8 @@ func (wal *baseWAL) Write(msg WALMessage) {
}
// Write the wal message
if err := wal.enc.Encode(&TimedWALMessage{time.Now(), msg}); err != nil {
panic(cmn.Fmt("Error writing msg to consensus wal: %v \n\nMessage: %v", err, msg))
if err := wal.enc.Encode(&TimedWALMessage{tmtime.Now(), msg}); err != nil {
panic(fmt.Sprintf("Error writing msg to consensus wal: %v \n\nMessage: %v", err, msg))
}
}
@ -134,7 +135,7 @@ func (wal *baseWAL) WriteSync(msg WALMessage) {
wal.Write(msg)
if err := wal.group.Flush(); err != nil {
panic(cmn.Fmt("Error flushing consensus wal buf to file. Error: %v \n", err))
panic(fmt.Sprintf("Error flushing consensus wal buf to file. Error: %v \n", err))
}
}

View File

@ -13,14 +13,14 @@ import (
"github.com/tendermint/tendermint/abci/example/kvstore"
bc "github.com/tendermint/tendermint/blockchain"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
auto "github.com/tendermint/tendermint/libs/autofile"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/privval"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
// WALWithNBlocks generates a consensus WAL. It does this by spining up a

View File

@ -3,20 +3,21 @@ package consensus
import (
"bytes"
"crypto/rand"
"fmt"
// "sync"
"testing"
"time"
"github.com/tendermint/tendermint/consensus/types"
tmtypes "github.com/tendermint/tendermint/types"
cmn "github.com/tendermint/tendermint/libs/common"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestWALEncoderDecoder(t *testing.T) {
now := time.Now()
now := tmtime.Now()
msgs := []TimedWALMessage{
TimedWALMessage{Time: now, Msg: EndHeightMessage{0}},
TimedWALMessage{Time: now, Msg: timeoutInfo{Duration: time.Second, Height: 1, Round: 1, Step: types.RoundStepPropose}},
@ -54,8 +55,8 @@ func TestWALSearchForEndHeight(t *testing.T) {
h := int64(3)
gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{})
assert.NoError(t, err, cmn.Fmt("expected not to err on height %d", h))
assert.True(t, found, cmn.Fmt("expected to find end height for %d", h))
assert.NoError(t, err, fmt.Sprintf("expected not to err on height %d", h))
assert.True(t, found, fmt.Sprintf("expected to find end height for %d", h))
assert.NotNil(t, gr, "expected group not to be nil")
defer gr.Close()
@ -64,7 +65,7 @@ func TestWALSearchForEndHeight(t *testing.T) {
assert.NoError(t, err, "expected to decode a message")
rs, ok := msg.Msg.(tmtypes.EventDataRoundState)
assert.True(t, ok, "expected message of type EventDataRoundState")
assert.Equal(t, rs.Height, h+1, cmn.Fmt("wrong height"))
assert.Equal(t, rs.Height, h+1, fmt.Sprintf("wrong height"))
}
/*
@ -93,7 +94,7 @@ func benchmarkWalDecode(b *testing.B, n int) {
enc := NewWALEncoder(buf)
data := nBytes(n)
enc.Encode(&TimedWALMessage{Msg: data, Time: time.Now().Round(time.Second)})
enc.Encode(&TimedWALMessage{Msg: data, Time: time.Now().Round(time.Second).UTC()})
encoded := buf.Bytes()

View File

@ -18,11 +18,11 @@ import (
var _ crypto.PrivKey = PrivKeyEd25519{}
const (
Ed25519PrivKeyAminoRoute = "tendermint/PrivKeyEd25519"
Ed25519PubKeyAminoRoute = "tendermint/PubKeyEd25519"
PrivKeyAminoRoute = "tendermint/PrivKeyEd25519"
PubKeyAminoRoute = "tendermint/PubKeyEd25519"
// Size of an Edwards25519 signature. Namely the size of a compressed
// Edwards25519 point, and a field element. Both of which are 32 bytes.
SignatureEd25519Size = 64
SignatureSize = 64
)
var cdc = amino.NewCodec()
@ -30,11 +30,11 @@ var cdc = amino.NewCodec()
func init() {
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
cdc.RegisterConcrete(PubKeyEd25519{},
Ed25519PubKeyAminoRoute, nil)
PubKeyAminoRoute, nil)
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
cdc.RegisterConcrete(PrivKeyEd25519{},
Ed25519PrivKeyAminoRoute, nil)
PrivKeyAminoRoute, nil)
}
// PrivKeyEd25519 implements crypto.PrivKey.
@ -158,10 +158,10 @@ func (pubKey PubKeyEd25519) Bytes() []byte {
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ []byte) bool {
// make sure we use the same algorithm to sign
if len(sig_) != SignatureEd25519Size {
if len(sig_) != SignatureSize {
return false
}
sig := new([SignatureEd25519Size]byte)
sig := new([SignatureSize]byte)
copy(sig[:], sig_)
pubKeyBytes := [PubKeyEd25519Size]byte(pubKey)
return ed25519.Verify(&pubKeyBytes, msg, sig)

View File

@ -2,8 +2,10 @@ package cryptoAmino
import (
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/multisig"
"github.com/tendermint/tendermint/crypto/secp256k1"
)
@ -24,15 +26,17 @@ func RegisterAmino(cdc *amino.Codec) {
// These are all written here instead of
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
"tendermint/PubKeyEd25519", nil)
ed25519.PubKeyAminoRoute, nil)
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
"tendermint/PubKeySecp256k1", nil)
secp256k1.PubKeyAminoRoute, nil)
cdc.RegisterConcrete(multisig.PubKeyMultisigThreshold{},
multisig.PubKeyMultisigThresholdAminoRoute, nil)
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
cdc.RegisterConcrete(ed25519.PrivKeyEd25519{},
"tendermint/PrivKeyEd25519", nil)
ed25519.PrivKeyAminoRoute, nil)
cdc.RegisterConcrete(secp256k1.PrivKeySecp256k1{},
"tendermint/PrivKeySecp256k1", nil)
secp256k1.PrivKeyAminoRoute, nil)
}
func PrivKeyFromBytes(privKeyBytes []byte) (privKey crypto.PrivKey, err error) {

View File

@ -53,24 +53,27 @@ func ExamplePrintRegisteredTypes() {
//| ---- | ---- | ------ | ----- | ------ |
//| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | |
//| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | |
//| PubKeyMultisigThreshold | tendermint/PubKeyMultisigThreshold | 0x22C1F7E2 | variable | |
//| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | |
//| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | |
}
func TestKeyEncodings(t *testing.T) {
cases := []struct {
privKey crypto.PrivKey
privSize, pubSize int // binary sizes
privKey crypto.PrivKey
privSize, pubSize, sigSize int // binary sizes
}{
{
privKey: ed25519.GenPrivKey(),
privSize: 69,
pubSize: 37,
sigSize: 65,
},
{
privKey: secp256k1.GenPrivKey(),
privSize: 37,
pubSize: 38,
sigSize: 65,
},
}
@ -87,7 +90,7 @@ func TestKeyEncodings(t *testing.T) {
var sig1, sig2 []byte
sig1, err := tc.privKey.Sign([]byte("something"))
assert.NoError(t, err, "tc #%d", tcIndex)
checkAminoBinary(t, sig1, &sig2, -1) // Signature size changes for Secp anyways.
checkAminoBinary(t, sig1, &sig2, tc.sigSize)
assert.EqualValues(t, sig1, sig2, "tc #%d", tcIndex)
// Check (de/en)codings of PubKeys.

View File

@ -6,8 +6,9 @@ import (
cmn "github.com/tendermint/tendermint/libs/common"
. "github.com/tendermint/tendermint/libs/test"
"github.com/tendermint/tendermint/crypto/tmhash"
"testing"
"github.com/tendermint/tendermint/crypto/tmhash"
)
type testItem []byte

View File

@ -0,0 +1,233 @@
package bitarray
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"regexp"
"strings"
)
// CompactBitArray is an implementation of a space efficient bit array.
// This is used to ensure that the encoded data takes up a minimal amount of
// space after amino encoding.
// This is not thread safe, and is not intended for concurrent usage.
type CompactBitArray struct {
ExtraBitsStored byte `json:"extra_bits"` // The number of extra bits in elems.
Elems []byte `json:"bits"`
}
// NewCompactBitArray returns a new compact bit array.
// It returns nil if the number of bits is zero.
func NewCompactBitArray(bits int) *CompactBitArray {
if bits <= 0 {
return nil
}
return &CompactBitArray{
ExtraBitsStored: byte(bits % 8),
Elems: make([]byte, (bits+7)/8),
}
}
// Size returns the number of bits in the bitarray
func (bA *CompactBitArray) Size() int {
if bA == nil {
return 0
} else if bA.ExtraBitsStored == byte(0) {
return len(bA.Elems) * 8
}
// num_bits = 8*num_full_bytes + overflow_in_last_byte
// num_full_bytes = (len(bA.Elems)-1)
return (len(bA.Elems)-1)*8 + int(bA.ExtraBitsStored)
}
// GetIndex returns the bit at index i within the bit array.
// The behavior is undefined if i >= bA.Size()
func (bA *CompactBitArray) GetIndex(i int) bool {
if bA == nil {
return false
}
if i >= bA.Size() {
return false
}
return bA.Elems[i>>3]&(uint8(1)<<uint8(7-(i%8))) > 0
}
// SetIndex sets the bit at index i within the bit array.
// The behavior is undefined if i >= bA.Size()
func (bA *CompactBitArray) SetIndex(i int, v bool) bool {
if bA == nil {
return false
}
if i >= bA.Size() {
return false
}
if v {
bA.Elems[i>>3] |= (uint8(1) << uint8(7-(i%8)))
} else {
bA.Elems[i>>3] &= ^(uint8(1) << uint8(7-(i%8)))
}
return true
}
// NumTrueBitsBefore returns the number of bits set to true before the
// given index. e.g. if bA = _XX__XX, NumOfTrueBitsBefore(4) = 2, since
// there are two bits set to true before index 4.
func (bA *CompactBitArray) NumTrueBitsBefore(index int) int {
numTrueValues := 0
for i := 0; i < index; i++ {
if bA.GetIndex(i) {
numTrueValues++
}
}
return numTrueValues
}
// Copy returns a copy of the provided bit array.
func (bA *CompactBitArray) Copy() *CompactBitArray {
if bA == nil {
return nil
}
c := make([]byte, len(bA.Elems))
copy(c, bA.Elems)
return &CompactBitArray{
ExtraBitsStored: bA.ExtraBitsStored,
Elems: c,
}
}
// String returns a string representation of CompactBitArray: BA{<bit-string>},
// where <bit-string> is a sequence of 'x' (1) and '_' (0).
// The <bit-string> includes spaces and newlines to help people.
// For a simple sequence of 'x' and '_' characters with no spaces or newlines,
// see the MarshalJSON() method.
// Example: "BA{_x_}" or "nil-BitArray" for nil.
func (bA *CompactBitArray) String() string {
return bA.StringIndented("")
}
// StringIndented returns the same thing as String(), but applies the indent
// at every 10th bit, and twice at every 50th bit.
func (bA *CompactBitArray) StringIndented(indent string) string {
if bA == nil {
return "nil-BitArray"
}
lines := []string{}
bits := ""
size := bA.Size()
for i := 0; i < size; i++ {
if bA.GetIndex(i) {
bits += "x"
} else {
bits += "_"
}
if i%100 == 99 {
lines = append(lines, bits)
bits = ""
}
if i%10 == 9 {
bits += indent
}
if i%50 == 49 {
bits += indent
}
}
if len(bits) > 0 {
lines = append(lines, bits)
}
return fmt.Sprintf("BA{%v:%v}", size, strings.Join(lines, indent))
}
// MarshalJSON implements json.Marshaler interface by marshaling bit array
// using a custom format: a string of '-' or 'x' where 'x' denotes the 1 bit.
func (bA *CompactBitArray) MarshalJSON() ([]byte, error) {
if bA == nil {
return []byte("null"), nil
}
bits := `"`
size := bA.Size()
for i := 0; i < size; i++ {
if bA.GetIndex(i) {
bits += `x`
} else {
bits += `_`
}
}
bits += `"`
return []byte(bits), nil
}
var bitArrayJSONRegexp = regexp.MustCompile(`\A"([_x]*)"\z`)
// UnmarshalJSON implements json.Unmarshaler interface by unmarshaling a custom
// JSON description.
func (bA *CompactBitArray) UnmarshalJSON(bz []byte) error {
b := string(bz)
if b == "null" {
// This is required e.g. for encoding/json when decoding
// into a pointer with pre-allocated BitArray.
bA.ExtraBitsStored = 0
bA.Elems = nil
return nil
}
// Validate 'b'.
match := bitArrayJSONRegexp.FindStringSubmatch(b)
if match == nil {
return fmt.Errorf("BitArray in JSON should be a string of format %q but got %s", bitArrayJSONRegexp.String(), b)
}
bits := match[1]
// Construct new CompactBitArray and copy over.
numBits := len(bits)
bA2 := NewCompactBitArray(numBits)
for i := 0; i < numBits; i++ {
if bits[i] == 'x' {
bA2.SetIndex(i, true)
}
}
*bA = *bA2
return nil
}
// CompactMarshal is a space efficient encoding for CompactBitArray.
// It is not amino compatible.
func (bA *CompactBitArray) CompactMarshal() []byte {
size := bA.Size()
if size <= 0 {
return []byte("null")
}
bz := make([]byte, 0, size/8)
// length prefix number of bits, not number of bytes. This difference
// takes 3-4 bits in encoding, as opposed to instead encoding the number of
// bytes (saving 3-4 bits) and including the offset as a full byte.
bz = appendUvarint(bz, uint64(size))
bz = append(bz, bA.Elems...)
return bz
}
// CompactUnmarshal is a space efficient decoding for CompactBitArray.
// It is not amino compatible.
func CompactUnmarshal(bz []byte) (*CompactBitArray, error) {
if len(bz) < 2 {
return nil, errors.New("compact bit array: invalid compact unmarshal size")
} else if bytes.Equal(bz, []byte("null")) {
return NewCompactBitArray(0), nil
}
size, n := binary.Uvarint(bz)
bz = bz[n:]
if len(bz) != int(size+7)/8 {
return nil, errors.New("compact bit array: invalid compact unmarshal size")
}
bA := &CompactBitArray{byte(int(size % 8)), bz}
return bA, nil
}
func appendUvarint(b []byte, x uint64) []byte {
var a [binary.MaxVarintLen64]byte
n := binary.PutUvarint(a[:], x)
return append(b, a[:n]...)
}

View File

@ -0,0 +1,196 @@
package bitarray
import (
"encoding/json"
"math/rand"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
cmn "github.com/tendermint/tendermint/libs/common"
)
func randCompactBitArray(bits int) (*CompactBitArray, []byte) {
numBytes := (bits + 7) / 8
src := cmn.RandBytes((bits + 7) / 8)
bA := NewCompactBitArray(bits)
for i := 0; i < numBytes-1; i++ {
for j := uint8(0); j < 8; j++ {
bA.SetIndex(i*8+int(j), src[i]&(uint8(1)<<(8-j)) > 0)
}
}
// Set remaining bits
for i := uint8(0); i < 8-uint8(bA.ExtraBitsStored); i++ {
bA.SetIndex(numBytes*8+int(i), src[numBytes-1]&(uint8(1)<<(8-i)) > 0)
}
return bA, src
}
func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) {
bitList := []int{-127, -128, -1 << 31}
for _, bits := range bitList {
bA := NewCompactBitArray(bits)
require.Nil(t, bA)
}
}
func TestJSONMarshalUnmarshal(t *testing.T) {
bA1 := NewCompactBitArray(0)
bA2 := NewCompactBitArray(1)
bA3 := NewCompactBitArray(1)
bA3.SetIndex(0, true)
bA4 := NewCompactBitArray(5)
bA4.SetIndex(0, true)
bA4.SetIndex(1, true)
bA5 := NewCompactBitArray(9)
bA5.SetIndex(0, true)
bA5.SetIndex(1, true)
bA5.SetIndex(8, true)
bA6 := NewCompactBitArray(16)
bA6.SetIndex(0, true)
bA6.SetIndex(1, true)
bA6.SetIndex(8, false)
bA6.SetIndex(15, true)
testCases := []struct {
bA *CompactBitArray
marshalledBA string
}{
{nil, `null`},
{bA1, `null`},
{bA2, `"_"`},
{bA3, `"x"`},
{bA4, `"xx___"`},
{bA5, `"xx______x"`},
{bA6, `"xx_____________x"`},
}
for _, tc := range testCases {
t.Run(tc.bA.String(), func(t *testing.T) {
bz, err := json.Marshal(tc.bA)
require.NoError(t, err)
assert.Equal(t, tc.marshalledBA, string(bz))
var unmarshalledBA *CompactBitArray
err = json.Unmarshal(bz, &unmarshalledBA)
require.NoError(t, err)
if tc.bA == nil {
require.Nil(t, unmarshalledBA)
} else {
require.NotNil(t, unmarshalledBA)
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems)
if assert.EqualValues(t, tc.bA.String(), unmarshalledBA.String()) {
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems)
}
}
})
}
}
func TestCompactMarshalUnmarshal(t *testing.T) {
bA1 := NewCompactBitArray(0)
bA2 := NewCompactBitArray(1)
bA3 := NewCompactBitArray(1)
bA3.SetIndex(0, true)
bA4 := NewCompactBitArray(5)
bA4.SetIndex(0, true)
bA4.SetIndex(1, true)
bA5 := NewCompactBitArray(9)
bA5.SetIndex(0, true)
bA5.SetIndex(1, true)
bA5.SetIndex(8, true)
bA6 := NewCompactBitArray(16)
bA6.SetIndex(0, true)
bA6.SetIndex(1, true)
bA6.SetIndex(8, false)
bA6.SetIndex(15, true)
testCases := []struct {
bA *CompactBitArray
marshalledBA []byte
}{
{nil, []byte("null")},
{bA1, []byte("null")},
{bA2, []byte{byte(1), byte(0)}},
{bA3, []byte{byte(1), byte(128)}},
{bA4, []byte{byte(5), byte(192)}},
{bA5, []byte{byte(9), byte(192), byte(128)}},
{bA6, []byte{byte(16), byte(192), byte(1)}},
}
for _, tc := range testCases {
t.Run(tc.bA.String(), func(t *testing.T) {
bz := tc.bA.CompactMarshal()
assert.Equal(t, tc.marshalledBA, bz)
unmarshalledBA, err := CompactUnmarshal(bz)
require.NoError(t, err)
if tc.bA == nil {
require.Nil(t, unmarshalledBA)
} else {
require.NotNil(t, unmarshalledBA)
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems)
if assert.EqualValues(t, tc.bA.String(), unmarshalledBA.String()) {
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems)
}
}
})
}
}
func TestCompactBitArrayNumOfTrueBitsBefore(t *testing.T) {
testCases := []struct {
marshalledBA string
bAIndex []int
trueValueIndex []int
}{
{`"_____"`, []int{0, 1, 2, 3, 4}, []int{0, 0, 0, 0, 0}},
{`"x"`, []int{0}, []int{0}},
{`"_x"`, []int{1}, []int{0}},
{`"x___xxxx"`, []int{0, 4, 5, 6, 7}, []int{0, 1, 2, 3, 4}},
{`"__x_xx_x__x_x___"`, []int{2, 4, 5, 7, 10, 12}, []int{0, 1, 2, 3, 4, 5}},
{`"______________xx"`, []int{14, 15}, []int{0, 1}},
}
for tcIndex, tc := range testCases {
t.Run(tc.marshalledBA, func(t *testing.T) {
var bA *CompactBitArray
err := json.Unmarshal([]byte(tc.marshalledBA), &bA)
require.NoError(t, err)
for i := 0; i < len(tc.bAIndex); i++ {
require.Equal(t, tc.trueValueIndex[i], bA.NumTrueBitsBefore(tc.bAIndex[i]), "tc %d, i %d", tcIndex, i)
}
})
}
}
func TestCompactBitArrayGetSetIndex(t *testing.T) {
r := rand.New(rand.NewSource(100))
numTests := 10
numBitsPerArr := 100
for i := 0; i < numTests; i++ {
bits := r.Intn(1000)
bA, _ := randCompactBitArray(bits)
for j := 0; j < numBitsPerArr; j++ {
copy := bA.Copy()
index := r.Intn(bits)
val := (r.Int63() % 2) == 0
bA.SetIndex(index, val)
require.Equal(t, val, bA.GetIndex(index), "bA.SetIndex(%d, %v) failed on bit array: %s", index, val, copy)
}
}
}

View File

@ -0,0 +1,70 @@
package multisig
import (
"errors"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/multisig/bitarray"
)
// Multisignature is used to represent the signature object used in the multisigs.
// Sigs is a list of signatures, sorted by corresponding index.
type Multisignature struct {
BitArray *bitarray.CompactBitArray
Sigs [][]byte
}
// NewMultisig returns a new Multisignature of size n.
func NewMultisig(n int) *Multisignature {
// Default the signature list to have a capacity of two, since we can
// expect that most multisigs will require multiple signers.
return &Multisignature{bitarray.NewCompactBitArray(n), make([][]byte, 0, 2)}
}
// GetIndex returns the index of pk in keys. Returns -1 if not found
func getIndex(pk crypto.PubKey, keys []crypto.PubKey) int {
for i := 0; i < len(keys); i++ {
if pk.Equals(keys[i]) {
return i
}
}
return -1
}
// AddSignature adds a signature to the multisig, at the corresponding index.
// If the signature already exists, replace it.
func (mSig *Multisignature) AddSignature(sig []byte, index int) {
newSigIndex := mSig.BitArray.NumTrueBitsBefore(index)
// Signature already exists, just replace the value there
if mSig.BitArray.GetIndex(index) {
mSig.Sigs[newSigIndex] = sig
return
}
mSig.BitArray.SetIndex(index, true)
// Optimization if the index is the greatest index
if newSigIndex == len(mSig.Sigs) {
mSig.Sigs = append(mSig.Sigs, sig)
return
}
// Expand slice by one with a dummy element, move all elements after i
// over by one, then place the new signature in that gap.
mSig.Sigs = append(mSig.Sigs, make([]byte, 0))
copy(mSig.Sigs[newSigIndex+1:], mSig.Sigs[newSigIndex:])
mSig.Sigs[newSigIndex] = sig
}
// AddSignatureFromPubKey adds a signature to the multisig,
// at the index in keys corresponding to the provided pubkey.
func (mSig *Multisignature) AddSignatureFromPubKey(sig []byte, pubkey crypto.PubKey, keys []crypto.PubKey) error {
index := getIndex(pubkey, keys)
if index == -1 {
return errors.New("provided key didn't exist in pubkeys")
}
mSig.AddSignature(sig, index)
return nil
}
// Marshal the multisignature with amino
func (mSig *Multisignature) Marshal() []byte {
return cdc.MustMarshalBinaryBare(mSig)
}

View File

@ -0,0 +1,92 @@
package multisig
import (
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
)
// PubKeyMultisigThreshold implements a K of N threshold multisig.
type PubKeyMultisigThreshold struct {
K uint `json:"threshold"`
PubKeys []crypto.PubKey `json:"pubkeys"`
}
var _ crypto.PubKey = &PubKeyMultisigThreshold{}
// NewPubKeyMultisigThreshold returns a new PubKeyMultisigThreshold.
// Panics if len(pubkeys) < k or 0 >= k.
func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey {
if k <= 0 {
panic("threshold k of n multisignature: k <= 0")
}
if len(pubkeys) < k {
panic("threshold k of n multisignature: len(pubkeys) < k")
}
return &PubKeyMultisigThreshold{uint(k), pubkeys}
}
// VerifyBytes expects sig to be an amino encoded version of a MultiSignature.
// Returns true iff the multisignature contains k or more signatures
// for the correct corresponding keys,
// and all signatures are valid. (Not just k of the signatures)
// The multisig uses a bitarray, so multiple signatures for the same key is not
// a concern.
func (pk *PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) bool {
var sig *Multisignature
err := cdc.UnmarshalBinaryBare(marshalledSig, &sig)
if err != nil {
return false
}
size := sig.BitArray.Size()
// ensure bit array is the correct size
if len(pk.PubKeys) != size {
return false
}
// ensure size of signature list
if len(sig.Sigs) < int(pk.K) || len(sig.Sigs) > size {
return false
}
// ensure at least k signatures are set
if sig.BitArray.NumTrueBitsBefore(size) < int(pk.K) {
return false
}
// index in the list of signatures which we are concerned with.
sigIndex := 0
for i := 0; i < size; i++ {
if sig.BitArray.GetIndex(i) {
if !pk.PubKeys[i].VerifyBytes(msg, sig.Sigs[sigIndex]) {
return false
}
sigIndex++
}
}
return true
}
// Bytes returns the amino encoded version of the PubKeyMultisigThreshold
func (pk *PubKeyMultisigThreshold) Bytes() []byte {
return cdc.MustMarshalBinaryBare(pk)
}
// Address returns tmhash(PubKeyMultisigThreshold.Bytes())
func (pk *PubKeyMultisigThreshold) Address() crypto.Address {
return crypto.Address(tmhash.Sum(pk.Bytes()))
}
// Equals returns true iff pk and other both have the same number of keys, and
// all constituent keys are the same, and in the same order.
func (pk *PubKeyMultisigThreshold) Equals(other crypto.PubKey) bool {
otherKey, sameType := other.(*PubKeyMultisigThreshold)
if !sameType {
return false
}
if pk.K != otherKey.K || len(pk.PubKeys) != len(otherKey.PubKeys) {
return false
}
for i := 0; i < len(pk.PubKeys); i++ {
if !pk.PubKeys[i].Equals(otherKey.PubKeys[i]) {
return false
}
}
return true
}

View File

@ -0,0 +1,112 @@
package multisig
import (
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
)
// This tests multisig functionality, but it expects the first k signatures to be valid
// TODO: Adapt it to give more flexibility about first k signatures being valid
func TestThresholdMultisigValidCases(t *testing.T) {
pkSet1, sigSet1 := generatePubKeysAndSignatures(5, []byte{1, 2, 3, 4})
cases := []struct {
msg []byte
k int
pubkeys []crypto.PubKey
signingIndices []int
// signatures should be the same size as signingIndices.
signatures [][]byte
passAfterKSignatures []bool
}{
{
msg: []byte{1, 2, 3, 4},
k: 2,
pubkeys: pkSet1,
signingIndices: []int{0, 3, 1},
signatures: sigSet1,
passAfterKSignatures: []bool{false},
},
}
for tcIndex, tc := range cases {
multisigKey := NewPubKeyMultisigThreshold(tc.k, tc.pubkeys)
multisignature := NewMultisig(len(tc.pubkeys))
for i := 0; i < tc.k-1; i++ {
signingIndex := tc.signingIndices[i]
multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys)
require.False(t, multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()),
"multisig passed when i < k, tc %d, i %d", tcIndex, i)
multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys)
require.Equal(t, i+1, len(multisignature.Sigs),
"adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex)
}
require.False(t, multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()),
"multisig passed with k - 1 sigs, tc %d", tcIndex)
multisignature.AddSignatureFromPubKey(tc.signatures[tc.signingIndices[tc.k]], tc.pubkeys[tc.signingIndices[tc.k]], tc.pubkeys)
require.True(t, multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()),
"multisig failed after k good signatures, tc %d", tcIndex)
for i := tc.k + 1; i < len(tc.signingIndices); i++ {
signingIndex := tc.signingIndices[i]
multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys)
require.Equal(t, tc.passAfterKSignatures[i-tc.k-1],
multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()),
"multisig didn't verify as expected after k sigs, tc %d, i %d", tcIndex, i)
multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys)
require.Equal(t, i+1, len(multisignature.Sigs),
"adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex)
}
}
}
// TODO: Fully replace this test with table driven tests
func TestThresholdMultisigDuplicateSignatures(t *testing.T) {
msg := []byte{1, 2, 3, 4, 5}
pubkeys, sigs := generatePubKeysAndSignatures(5, msg)
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys)
multisignature := NewMultisig(5)
require.False(t, multisigKey.VerifyBytes(msg, multisignature.Marshal()))
multisignature.AddSignatureFromPubKey(sigs[0], pubkeys[0], pubkeys)
// Add second signature manually
multisignature.Sigs = append(multisignature.Sigs, sigs[0])
require.False(t, multisigKey.VerifyBytes(msg, multisignature.Marshal()))
}
// TODO: Fully replace this test with table driven tests
func TestMultiSigPubKeyEquality(t *testing.T) {
msg := []byte{1, 2, 3, 4}
pubkeys, _ := generatePubKeysAndSignatures(5, msg)
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys)
var unmarshalledMultisig *PubKeyMultisigThreshold
cdc.MustUnmarshalBinaryBare(multisigKey.Bytes(), &unmarshalledMultisig)
require.True(t, multisigKey.Equals(unmarshalledMultisig))
// Ensure that reordering pubkeys is treated as a different pubkey
pubkeysCpy := make([]crypto.PubKey, 5)
copy(pubkeysCpy, pubkeys)
pubkeysCpy[4] = pubkeys[3]
pubkeysCpy[3] = pubkeys[4]
multisigKey2 := NewPubKeyMultisigThreshold(2, pubkeysCpy)
require.False(t, multisigKey.Equals(multisigKey2))
}
func generatePubKeysAndSignatures(n int, msg []byte) (pubkeys []crypto.PubKey, signatures [][]byte) {
pubkeys = make([]crypto.PubKey, n)
signatures = make([][]byte, n)
for i := 0; i < n; i++ {
var privkey crypto.PrivKey
if rand.Int63()%2 == 0 {
privkey = ed25519.GenPrivKey()
} else {
privkey = secp256k1.GenPrivKey()
}
pubkeys[i] = privkey.PubKey()
signatures[i], _ = privkey.Sign(msg)
}
return
}

26
crypto/multisig/wire.go Normal file
View File

@ -0,0 +1,26 @@
package multisig
import (
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
)
// TODO: Figure out API for others to either add their own pubkey types, or
// to make verify / marshal accept a cdc.
const (
PubKeyMultisigThresholdAminoRoute = "tendermint/PubKeyMultisigThreshold"
)
var cdc = amino.NewCodec()
func init() {
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
cdc.RegisterConcrete(PubKeyMultisigThreshold{},
PubKeyMultisigThresholdAminoRoute, nil)
cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
ed25519.PubKeyAminoRoute, nil)
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
secp256k1.PubKeyAminoRoute, nil)
}

View File

@ -7,7 +7,7 @@ import (
"fmt"
"io"
secp256k1 "github.com/btcsuite/btcd/btcec"
secp256k1 "github.com/tendermint/btcd/btcec"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto"
"golang.org/x/crypto/ripemd160"
@ -15,8 +15,8 @@ import (
//-------------------------------------
const (
Secp256k1PrivKeyAminoRoute = "tendermint/PrivKeySecp256k1"
Secp256k1PubKeyAminoRoute = "tendermint/PubKeySecp256k1"
PrivKeyAminoRoute = "tendermint/PrivKeySecp256k1"
PubKeyAminoRoute = "tendermint/PubKeySecp256k1"
)
var cdc = amino.NewCodec()
@ -24,11 +24,11 @@ var cdc = amino.NewCodec()
func init() {
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
cdc.RegisterConcrete(PubKeySecp256k1{},
Secp256k1PubKeyAminoRoute, nil)
PubKeyAminoRoute, nil)
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
cdc.RegisterConcrete(PrivKeySecp256k1{},
Secp256k1PrivKeyAminoRoute, nil)
PrivKeyAminoRoute, nil)
}
//-------------------------------------
@ -141,10 +141,12 @@ func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig []byte) bool {
if err != nil {
return false
}
parsedSig, err := secp256k1.ParseDERSignature(sig[:], secp256k1.S256())
parsedSig, err := secp256k1.ParseSignature(sig[:], secp256k1.S256())
if err != nil {
return false
}
// Underlying library ensures that this signature is in canonical form, to
// prevent Secp256k1 malleability from altering the sign of the s term.
return parsedSig.Verify(crypto.Sha256(msg), pub)
}

View File

@ -11,7 +11,7 @@ import (
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/secp256k1"
underlyingSecp256k1 "github.com/btcsuite/btcd/btcec"
underlyingSecp256k1 "github.com/tendermint/btcd/btcec"
)
type keyData struct {

View File

@ -2,6 +2,7 @@ package xsalsa20symmetric
import (
"errors"
"fmt"
"github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common"
@ -18,7 +19,7 @@ const secretLen = 32
// NOTE: call crypto.MixEntropy() first.
func EncryptSymmetric(plaintext []byte, secret []byte) (ciphertext []byte) {
if len(secret) != secretLen {
cmn.PanicSanity(cmn.Fmt("Secret must be 32 bytes long, got len %v", len(secret)))
cmn.PanicSanity(fmt.Sprintf("Secret must be 32 bytes long, got len %v", len(secret)))
}
nonce := crypto.CRandBytes(nonceLen)
nonceArr := [nonceLen]byte{}
@ -35,7 +36,7 @@ func EncryptSymmetric(plaintext []byte, secret []byte) (ciphertext []byte) {
// The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext.
func DecryptSymmetric(ciphertext []byte, secret []byte) (plaintext []byte, err error) {
if len(secret) != secretLen {
cmn.PanicSanity(cmn.Fmt("Secret must be 32 bytes long, got len %v", len(secret)))
cmn.PanicSanity(fmt.Sprintf("Secret must be 32 bytes long, got len %v", len(secret)))
}
if len(ciphertext) <= secretbox.Overhead+nonceLen {
return nil, errors.New("Ciphertext is too short")

9
docs/.textlintrc.json Normal file
View File

@ -0,0 +1,9 @@
{
"rules": {
"stop-words": {
"severity": "warning",
"defaultWords": false,
"words": "stop-words.txt"
}
}
}

View File

@ -8,14 +8,10 @@ and built using [VuePress](https://vuepress.vuejs.org/) from the tendermint webs
- https://github.com/tendermint/tendermint.com
which has a [configuration file](https://github.com/tendermint/tendermint.com/blob/develop/docs/.vuepress/config.js) for displaying
the Table of Contents that lists all the documentation.
Under the hood, Jenkins listens for changes (on develop or master) in ./docs then rebuilds
either the staging or production site depending on which branch the changes were made.
Under the hood, Jenkins listens for changes in ./docs then pushes a `docs-staging` branch to the tendermint.com repo with the latest documentation. That branch must be manually PR'd to `develop` then `master` for staging then production. This process should happen in synchrony with a release.
To update the Table of Contents (layout of the documentation sidebar), edit the
`config.js` in this directory, while the `README.md` is the landing page for the
website documentation.
The `README.md` in this directory is the landing page for
website documentation and the following folders are intentionally
ommitted:
- `architecture/` ==> contains Architecture Design Records
- `spec/` ==> contains the detailed specification

View File

@ -1,23 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python -msphinx
SPHINXPROJ = Tendermint
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
install:
@pip install -r requirements.txt
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -11,17 +11,17 @@ replicates it on many machines. In other words, a blockchain.
Tendermint requires an application running over the Application Blockchain
Interface (ABCI) - and comes packaged with an example application to do so.
Follow the [installation instructions](./introduction/install) to get up and running
quickly. For more details on [using tendermint](./tendermint-core/using-tendermint) see that
Follow the [installation instructions](./introduction/install.md) to get up and running
quickly. For more details on [using tendermint](./tendermint-core/using-tendermint.md) see that
and the following sections.
## Networks
Testnets can be setup manually on one or more machines, or automatically on one
or more machine, using a variety of methods described in the [deploy testnets
section](./networks/deploy-testnets).
section](./networks/deploy-testnets.md).
## Application Development
The first step to building application on Tendermint is to [install
ABCI-CLI](./app-dev/getting-started) and play with the example applications.
ABCI-CLI](./app-dev/getting-started.md) and play with the example applications.

View File

@ -36,7 +36,7 @@ Available Commands:
console Start an interactive abci console for multiple commands
counter ABCI demo example
deliver_tx Deliver a new tx to the application
kvstore ABCI demo example
kvstore ABCI demo example
echo Have the application echo a message
help Help about any command
info Get some info about the application
@ -140,7 +140,7 @@ response.
The server may be generic for a particular language, and we provide a
[reference implementation in
Golang](https://github.com/tendermint/tendermint/tree/develop/abci/server). See the
[list of other ABCI implementations](./ecosystem.html) for servers in
[list of other ABCI implementations](./ecosystem.md) for servers in
other languages.
The handler is specific to the application, and may be arbitrary, so

View File

@ -1,5 +1,9 @@
# ABCI Specification
### XXX
DEPRECATED: Moved [here](../spec/abci/abci.md)
## Message Types
ABCI requests/responses are defined as simple Protobuf messages in [this
@ -111,14 +115,21 @@ See below for more details on the message types and how they are used.
- `Time (google.protobuf.Timestamp)`: Genesis time.
- `ChainID (string)`: ID of the blockchain.
- `ConsensusParams (ConsensusParams)`: Initial consensus-critical parameters.
- `Validators ([]Validator)`: Initial genesis validators.
- `Validators ([]ValidatorUpdate)`: Initial genesis validators.
- `AppStateBytes ([]byte)`: Serialized initial application state. Amino-encoded JSON bytes.
- **Response**:
- `ConsensusParams (ConsensusParams)`: Initial
consensus-critical parameters.
- `Validators ([]Validator)`: Initial validator set.
- `Validators ([]ValidatorUpdate)`: Initial validator set (if non empty).
- **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, the initial validator set will be the
ResponseInitChain.Validators (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
@ -161,15 +172,17 @@ See below for more details on the message types and how they are used.
- `Hash ([]byte)`: The block's hash. This can be derived from the
block header.
- `Header (struct{})`: The block header.
- `LastCommitInfo (LastCommitInfo)`: Info about the last commit.
- `LastCommitInfo (LastCommitInfo)`: Info about the last commit, including the
round, and the list of validators and which ones signed the last block.
- `ByzantineValidators ([]Evidence)`: List of evidence of
validators that acted maliciously
validators that acted maliciously.
- **Response**:
- `Tags ([]cmn.KVPair)`: Key-Value tags for filtering and indexing
- **Usage**:
- Signals the beginning of a new block. Called prior to
any DeliverTxs.
- The header is expected to at least contain the Height.
- 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. NOTE validators here do not
include pubkeys.
@ -237,7 +250,7 @@ See below for more details on the message types and how they are used.
- **Request**:
- `Height (int64)`: Height of the block just executed.
- **Response**:
- `ValidatorUpdates ([]Validator)`: Changes to validator set (set
- `ValidatorUpdates ([]ValidatorUpdate)`: Changes to validator set (set
voting power to 0 to remove).
- `ConsensusParamUpdates (ConsensusParams)`: Changes to
consensus-critical time, size, and other parameters.
@ -245,8 +258,11 @@ See below for more details on the message types and how they are used.
- **Usage**:
- Signals the end of a block.
- Called prior to each Commit, after all transactions.
- Validator set and consensus params are updated with the result.
- Validator pubkeys are expected to be go-wire encoded.
- Validator updates returned for block H:
- apply to the NextValidatorsHash of block H+1
- apply to the ValidatorsHash (and thus the validator set) for block H+2
- apply to the RequestBeginBlock.LastCommitInfo (ie. the last validator set) for block H+3
- Consensus params returned for block H apply for block H+1
### Commit
@ -271,12 +287,17 @@ See below for more details on the message types and how they are used.
- `NumTxs (int32)`: Number of transactions in the block
- `TotalTxs (int64)`: Total number of transactions in the blockchain until
now
- `LastBlockHash ([]byte)`: Hash of the previous (parent) block
- `LastBlockID (BlockID)`: Hash of the previous (parent) block
- `LastCommitHash ([]byte)`: Hash of the previous block's commit
- `ValidatorsHash ([]byte)`: Hash of the validator set for this block
- `NextValidatorsHash ([]byte)`: Hash of the validator set for the next block
- `ConsensusHash ([]byte)`: Hash of the consensus parameters for this block
- `AppHash ([]byte)`: Data returned by the last call to `Commit` - typically the
Merkle root of the application state after executing the previous block's
transactions
- `Proposer (Validator)`: Original proposer for the block
- `LastResultsHash ([]byte)`: Hash of the ABCI results returned by the last block
- `EvidenceHash ([]byte)`: Hash of the evidence included in this block
- `ProposerAddress ([]byte)`: Original proposer for the block
- **Usage**:
- Provided in RequestBeginBlock
- Provides important context about the current state of the blockchain -
@ -288,16 +309,27 @@ See below for more details on the message types and how they are used.
- **Fields**:
- `Address ([]byte)`: Address of the validator (hash of the public key)
- `Power (int64)`: Voting power of the validator
- **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**:
- `PubKey (PubKey)`: Public key of the validator
- `Power (int64)`: Voting power of the validator
- **Usage**:
- Provides all identifying information about the validator
- Validator identified by PubKey
- Used to tell Tendermint to update the validator set
### SigningValidator
### VoteInfo
- **Fields**:
- `Validator (Validator)`: A validator
- `SignedLastBlock (bool)`: Indicated whether or not the validator signed
- `SignedLastBlock (bool)`: Indicates whether or not the validator signed
the last block
- **Usage**:
- Indicates whether a validator signed the last block, allowing for rewards
@ -330,6 +362,6 @@ See below for more details on the message types and how they are used.
### LastCommitInfo
- **Fields**:
- `CommitRound (int32)`: Commit round.
- `Validators ([]SigningValidator)`: List of validators in the current
validator set and whether or not they signed a vote.
- `Round (int32)`: Commit round.
- `Votes ([]VoteInfo)`: List of validators addresses in the last validator set
with their voting power and whether or not they signed a vote.

View File

@ -46,6 +46,5 @@ See the following for more extensive documentation:
- [Interchain Standard for the Light-Client REST API](https://github.com/cosmos/cosmos-sdk/pull/1028)
- [Tendermint RPC Docs](https://tendermint.github.io/slate/)
- [Tendermint in Production](https://github.com/tendermint/tendermint/pull/1618)
- [Tendermint Basics](https://tendermint.readthedocs.io/en/master/using-tendermint.html)
- [ABCI spec](https://github.com/tendermint/tendermint/blob/develop/abci/docs/abci-spec.md)
- [Tendermint in Production](../tendermint-core/running-in-production.md)
- [ABCI spec](./abci-spec.md)

View File

@ -1,5 +1,10 @@
# Application Development Guide
## XXX
This page is undergoing deprecation. All content is being moved to the new [home
of the ABCI specification](../spec/abci/README.md).
## ABCI Design
The purpose of ABCI is to provide a clean interface between state
@ -502,7 +507,7 @@ In go:
```
func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
return types.ResponseInfo{Data: fmt.Sprintf("{\"size\":%v}", app.state.Size())}
}
```

View File

@ -5,24 +5,21 @@
"url": "https://github.com/cosmos/cosmos-sdk",
"language": "Go",
"author": "Cosmos",
"description":
"A prototypical account based crypto currency state machine supporting plugins"
"description": "A prototypical account based crypto currency state machine supporting plugins"
},
{
"name": "cb-ledger",
"url": "https://github.com/block-finance/cpp-abci",
"language": "C++",
"author": "Block Finance",
"description":
"Custodian Bank Ledger, integrating central banking with the blockchains of tomorrow"
"description": "Custodian Bank Ledger, integrating central banking with the blockchains of tomorrow"
},
{
"name": "Clearchain",
"url": "https://github.com/tendermint/clearchain",
"language": "Go",
"author": "FXCLR",
"description":
"Application to manage a distributed ledger for money transfers that support multi-currency accounts"
"description": "Application to manage a distributed ledger for money transfers that support multi-currency accounts"
},
{
"name": "Ethermint",
@ -43,8 +40,7 @@
"url": "https://github.com/hyperledger/burrow",
"language": "Go",
"author": "Monax Industries",
"description":
"Ethereum Virtual Machine augmented with native permissioning scheme and global key-value store"
"description": "Ethereum Virtual Machine augmented with native permissioning scheme and global key-value store"
},
{
"name": "Merkle AVL Tree",
@ -72,8 +68,7 @@
"url": "https://github.com/trusch/passchain",
"language": "Go",
"author": "trusch",
"description":
"Tool to securely store and share passwords, tokens and other short secrets"
"description": "Tool to securely store and share passwords, tokens and other short secrets"
},
{
"name": "Passwerk",
@ -87,8 +82,7 @@
"url": "https://github.com/davebryson/py-tendermint",
"language": "Python",
"author": "Dave Bryson",
"description":
"A Python microframework for building blockchain applications with Tendermint"
"description": "A Python microframework for building blockchain applications with Tendermint"
},
{
"name": "Stratumn SDK",
@ -102,16 +96,14 @@
"url": "https://github.com/keppel/lotion",
"language": "Javascript",
"author": "Judd Keppel",
"description":
"A Javascript microframework for building blockchain applications with Tendermint"
"description": "A Javascript microframework for building blockchain applications with Tendermint"
},
{
"name": "Tendermint Blockchain Chat App",
"url": "https://github.com/SaifRehman/tendermint-chat-app/",
"language": "Javascript",
"author": "Saif Rehman",
"description":
"This is a minimal chat application based on Tendermint using Lotion.js in 30 lines of code!. It also includes web/mobile application built using Ionic 3."
"description": "This is a minimal chat application based on Tendermint using Lotion.js in 30 lines of code!. It also includes web/mobile application built using Ionic 3."
},
{
"name": "BigchainDB",
@ -131,7 +123,7 @@
"abciServers": [
{
"name": "abci",
"url": "https://github.com/tendermint/abci",
"url": "https://github.com/tendermint/tendermint/tree/master/abci",
"language": "Go",
"author": "Tendermint"
},
@ -184,16 +176,14 @@
"url": "https://github.com/tendermint/tools",
"technology": "Docker and Kubernetes",
"author": "Tendermint",
"description":
"Deploy a Tendermint test network using Google's kubernetes"
"description": "Deploy a Tendermint test network using Google's kubernetes"
},
{
"name": "terraforce",
"url": "https://github.com/tendermint/tools",
"technology": "Terraform",
"author": "Tendermint",
"description":
"Terraform + our custom terraforce tool; deploy a production Tendermint network with load balancing over multiple AWS availability zones"
"description": "Terraform + our custom terraforce tool; deploy a production Tendermint network with load balancing over multiple AWS availability zones"
},
{
"name": "ansible-tendermint",

View File

@ -16,16 +16,21 @@ Let's take a look at the `[tx_index]` config section:
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
indexer = "kv"
# Comma-separated list of tags to index (by default the only tag is tx hash)
# Comma-separated list of tags to index (by default the only tag is "tx.hash")
#
# You can also index transactions by height by adding "tx.height" tag here.
#
# It's recommended to index only a subset of tags due to possible memory
# bloat. This is, of course, depends on the indexer's DB and the volume of
# transactions.
index_tags = ""
# When set to true, tells indexer to index all tags. Note this may be not
# desirable (see the comment above). IndexTags has a precedence over
# IndexAllTags (i.e. when given both, IndexTags will be indexed).
# When set to true, tells indexer to index all tags (predefined tags:
# "tx.hash", "tx.height" and all tags from DeliverTx responses).
#
# Note this may be not desirable (see the comment above). IndexTags has a
# precedence over IndexAllTags (i.e. when given both, IndexTags will be
# indexed).
index_all_tags = false
```
@ -59,7 +64,6 @@ all tags, set `index_all_tags=true`
Note, there are a few predefined tags:
- `tm.event` (event type)
- `tx.hash` (transaction's hash)
- `tx.height` (height of the block transaction was committed in)

View File

@ -26,3 +26,39 @@ more information on query syntax and other options.
You can also use tags, given you had included them into DeliverTx
response, to query transaction results. See [Indexing
transactions](./indexing-transactions.md) for details.
### ValidatorSetUpdates
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://tendermint.com/docs/app-dev/abci-spec.html#endblock) in
the ABCI spec).
Response:
```
{
"jsonrpc": "2.0",
"id": "0#event",
"result": {
"query": "tm.event='ValidatorSetUpdates'",
"data": {
"type": "tendermint/event/ValidatorSetUpdates",
"value": {
"validator_updates": [
{
"address": "09EAD022FD25DE3A02E64B0FE9610B1417183EE4",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "ww0z4WaZ0Xg+YI10w43wTWbBmM3dpVza4mmSQYsd0ck="
},
"voting_power": "10",
"accum": "0"
}
]
}
}
}
}
```

View File

@ -7,8 +7,7 @@ a subset of transactions** (rather than all of them) using `/subscribe?event=X`.
example, I want to subscribe for all transactions associated with a particular
account. Same for fetching. The user may want to **fetch transactions based on
some filter** (rather than fetching all the blocks). For example, I want to get
all transactions for a particular account in the last two weeks (`tx's block
time >= '2017-06-05'`).
all transactions for a particular account in the last two weeks (`tx's block time >= '2017-06-05'`).
Now you can't even subscribe to "all txs" in Tendermint.

View File

@ -3,11 +3,11 @@
## Context
Right now, we can query the present validator set, but there is no history.
If you were offline for a long time, there is no way to reconstruct past validators. This is needed for the light client and we agreed needs enhancement of the API.
If you were offline for a long time, there is no way to reconstruct past validators. This is needed for the light client and we agreed needs enhancement of the API.
## Decision
For every block, store a new structure that contains either the latest validator set,
For every block, store a new structure that contains either the latest validator set,
or the height of the last block for which the validator set changed. Note this is not
the height of the block which returned the validator set change itself, but the next block,
ie. the first block it comes into effect for.
@ -19,7 +19,7 @@ are updated frequently - for instance by only saving the diffs, rather than the
An alternative approach suggested keeping the validator set, or diffs of it, in a merkle IAVL tree.
While it might afford cheaper proofs that a validator set has not changed, it would be more complex,
and likely less efficient.
and likely less efficient.
## Status

View File

@ -7,7 +7,7 @@ Since they may be need to be different in different networks, and potentially to
networks, we seek to initialize them in a genesis file, and expose them through the ABCI.
While we have some specific parameters now, like maximum block and transaction size, we expect to have more in the future,
such as a period over which evidence is valid, or the frequency of checkpoints.
such as a period over which evidence is valid, or the frequency of checkpoints.
## Decision
@ -45,7 +45,7 @@ type BlockGossip struct {
The `ConsensusParams` can evolve over time by adding new structs that cover different aspects of the consensus rules.
The `BlockPartSizeBytes` and the `BlockSize.MaxBytes` are enforced to be greater than 0.
The `BlockPartSizeBytes` and the `BlockSize.MaxBytes` are enforced to be greater than 0.
The former because we need a part size, the latter so that we always have at least some sanity check over the size of blocks.
### ABCI
@ -53,14 +53,14 @@ The former because we need a part size, the latter so that we always have at lea
#### InitChain
InitChain currently takes the initial validator set. It should be extended to also take parts of the ConsensusParams.
There is some case to be made for it to take the entire Genesis, except there may be things in the genesis,
There is some case to be made for it to take the entire Genesis, except there may be things in the genesis,
like the BlockPartSize, that the app shouldn't really know about.
#### EndBlock
The EndBlock response includes a `ConsensusParams`, which includes BlockSize and TxSize, but not BlockGossip.
Other param struct can be added to `ConsensusParams` in the future.
The `0` value is used to denote no change.
The `0` value is used to denote no change.
Any other value will update that parameter in the `State.ConsensusParams`, to be applied for the next block.
Tendermint should have hard-coded upper limits as sanity checks.
@ -83,4 +83,3 @@ Proposed.
### Neutral
- The TxSize, which checks validity, may be in conflict with the config's `max_block_size_tx`, which determines proposal sizes

View File

@ -8,13 +8,13 @@ The proposed trust metric will allow Tendermint to maintain local trust rankings
The Tendermint Core project developers would like to improve Tendermint security and reliability by keeping track of the level of trustworthiness peers have demonstrated within the peer-to-peer network. This way, undesirable outcomes from peers will not immediately result in them being dropped from the network (potentially causing drastic changes to take place). Instead, peers behavior can be monitored with appropriate metrics and be removed from the network once Tendermint Core is certain the peer is a threat. For example, when the PEXReactor makes a request for peers network addresses from a already known peer, and the returned network addresses are unreachable, this untrustworthy behavior should be tracked. Returning a few bad network addresses probably shouldnt cause a peer to be dropped, while excessive amounts of this behavior does qualify the peer being dropped.
Trust metrics can be circumvented by malicious nodes through the use of strategic oscillation techniques, which adapts the malicious nodes behavior pattern in order to maximize its goals. For instance, if the malicious node learns that the time interval of the Tendermint trust metric is *X* hours, then it could wait *X* hours in-between malicious activities. We could try to combat this issue by increasing the interval length, yet this will make the system less adaptive to recent events.
Trust metrics can be circumvented by malicious nodes through the use of strategic oscillation techniques, which adapts the malicious nodes behavior pattern in order to maximize its goals. For instance, if the malicious node learns that the time interval of the Tendermint trust metric is _X_ hours, then it could wait _X_ hours in-between malicious activities. We could try to combat this issue by increasing the interval length, yet this will make the system less adaptive to recent events.
Instead, having shorter intervals, but keeping a history of interval values, will give our metric the flexibility needed in order to keep the network stable, while also making it resilient against a strategic malicious node in the Tendermint peer-to-peer network. Also, the metric can access trust data over a rather long period of time while not greatly increasing its history size by aggregating older history values over a larger number of intervals, and at the same time, maintain great precision for the recent intervals. This approach is referred to as fading memories, and closely resembles the way human beings remember their experiences. The trade-off to using history data is that the interval values should be preserved in-between executions of the node.
### References
S. Mudhakar, L. Xiong, and L. Liu, “TrustGuard: Countering Vulnerabilities in Reputation Management for Decentralized Overlay Networks,” in *Proceedings of the 14th international conference on World Wide Web, pp. 422-431*, May 2005.
S. Mudhakar, L. Xiong, and L. Liu, “TrustGuard: Countering Vulnerabilities in Reputation Management for Decentralized Overlay Networks,” in _Proceedings of the 14th international conference on World Wide Web, pp. 422-431_, May 2005.
## Decision
@ -26,25 +26,23 @@ The three subsections below will cover the process being considered for calculat
The proposed trust metric will count good and bad events relevant to the object, and calculate the percent of counters that are good over an interval with a predefined duration. This is the procedure that will continue for the life of the trust metric. When the trust metric is queried for the current **trust value**, a resilient equation will be utilized to perform the calculation.
The equation being proposed resembles a Proportional-Integral-Derivative (PID) controller used in control systems. The proportional component allows us to be sensitive to the value of the most recent interval, while the integral component allows us to incorporate trust values stored in the history data, and the derivative component allows us to give weight to sudden changes in the behavior of a peer. We compute the trust value of a peer in interval i based on its current trust ranking, its trust rating history prior to interval *i* (over the past *maxH* number of intervals) and its trust ranking fluctuation. We will break up the equation into the three components.
The equation being proposed resembles a Proportional-Integral-Derivative (PID) controller used in control systems. The proportional component allows us to be sensitive to the value of the most recent interval, while the integral component allows us to incorporate trust values stored in the history data, and the derivative component allows us to give weight to sudden changes in the behavior of a peer. We compute the trust value of a peer in interval i based on its current trust ranking, its trust rating history prior to interval _i_ (over the past _maxH_ number of intervals) and its trust ranking fluctuation. We will break up the equation into the three components.
```math
(1) Proportional Value = a * R[i]
```
where *R*[*i*] denotes the raw trust value at time interval *i* (where *i* == 0 being current time) and *a* is the weight applied to the contribution of the current reports. The next component of our equation uses a weighted sum over the last *maxH* intervals to calculate the history value for time *i*:
where _R_[*i*] denotes the raw trust value at time interval _i_ (where _i_ == 0 being current time) and _a_ is the weight applied to the contribution of the current reports. The next component of our equation uses a weighted sum over the last _maxH_ intervals to calculate the history value for time _i_:
`H[i] = ` ![formula1](img/formula1.png "Weighted Sum Formula")
`H[i] =` ![formula1](img/formula1.png "Weighted Sum Formula")
The weights can be chosen either optimistically or pessimistically. An optimistic weight creates larger weights for newer history data values, while the the pessimistic weight creates larger weights for time intervals with lower scores. The default weights used during the calculation of the history value are optimistic and calculated as *Wk* = 0.8^*k*, for time interval *k*. With the history value available, we can now finish calculating the integral value:
The weights can be chosen either optimistically or pessimistically. An optimistic weight creates larger weights for newer history data values, while the the pessimistic weight creates larger weights for time intervals with lower scores. The default weights used during the calculation of the history value are optimistic and calculated as _Wk_ = 0.8^_k_, for time interval _k_. With the history value available, we can now finish calculating the integral value:
```math
(2) Integral Value = b * H[i]
```
Where *H*[*i*] denotes the history value at time interval *i* and *b* is the weight applied to the contribution of past performance for the object being measured. The derivative component will be calculated as follows:
Where _H_[*i*] denotes the history value at time interval _i_ and _b_ is the weight applied to the contribution of past performance for the object being measured. The derivative component will be calculated as follows:
```math
D[i] = R[i] H[i]
@ -52,25 +50,25 @@ D[i] = R[i] H[i]
(3) Derivative Value = c(D[i]) * D[i]
```
Where the value of *c* is selected based on the *D*[*i*] value relative to zero. The default selection process makes *c* equal to 0 unless *D*[*i*] is a negative value, in which case c is equal to 1. The result is that the maximum penalty is applied when current behavior is lower than previously experienced behavior. If the current behavior is better than the previously experienced behavior, then the Derivative Value has no impact on the trust value. With the three components brought together, our trust value equation is calculated as follows:
Where the value of _c_ is selected based on the _D_[*i*] value relative to zero. The default selection process makes _c_ equal to 0 unless _D_[*i*] is a negative value, in which case c is equal to 1. The result is that the maximum penalty is applied when current behavior is lower than previously experienced behavior. If the current behavior is better than the previously experienced behavior, then the Derivative Value has no impact on the trust value. With the three components brought together, our trust value equation is calculated as follows:
```math
TrustValue[i] = a * R[i] + b * H[i] + c(D[i]) * D[i]
```
As a performance optimization that will keep the amount of raw interval data being saved to a reasonable size of *m*, while allowing us to represent 2^*m* - 1 history intervals, we can employ the fading memories technique that will trade space and time complexity for the precision of the history data values by summarizing larger quantities of less recent values. While our equation above attempts to access up to *maxH* (which can be 2^*m* - 1), we will map those requests down to *m* values using equation 4 below:
As a performance optimization that will keep the amount of raw interval data being saved to a reasonable size of _m_, while allowing us to represent 2^_m_ - 1 history intervals, we can employ the fading memories technique that will trade space and time complexity for the precision of the history data values by summarizing larger quantities of less recent values. While our equation above attempts to access up to _maxH_ (which can be 2^_m_ - 1), we will map those requests down to _m_ values using equation 4 below:
```math
(4) j = index, where index > 0
```
Where *j* is one of *(0, 1, 2, … , m 1)* indices used to access history interval data. Now we can access the raw intervals using the following calculations:
Where _j_ is one of _(0, 1, 2, … , m 1)_ indices used to access history interval data. Now we can access the raw intervals using the following calculations:
```math
R[0] = raw data for current time interval
```
`R[j] = ` ![formula2](img/formula2.png "Fading Memories Formula")
`R[j] =` ![formula2](img/formula2.png "Fading Memories Formula")
### Trust Metric Store
@ -84,9 +82,7 @@ When the node is shutting down, the trust metric store will save history data fo
Each trust metric allows for the recording of positive/negative events, querying the current trust value/score, and the stopping/pausing of tracking over time intervals. This can be seen below:
```go
// TrustMetric - keeps track of peer reliability
type TrustMetric struct {
// Private elements.
@ -123,13 +119,11 @@ tm.BadEvents(1)
score := tm.TrustScore()
tm.Stop()
```
Some of the trust metric parameters can be configured. The weight values should probably be left alone in more cases, yet the time durations for the tracking window and individual time interval should be considered.
```go
// TrustMetricConfig - Configures the weight functions and time intervals for the metric
type TrustMetricConfig struct {
// Determines the percentage given to current behavior
@ -165,23 +159,21 @@ config := TrustMetricConfig{
tm := NewMetricWithConfig(config)
tm.BadEvents(10)
tm.Pause()
tm.Pause()
tm.GoodEvents(1) // becomes active again
```
A trust metric store should be created with a DB that has persistent storage so it can save history data across node executions. All trust metrics instantiated by the store will be created with the provided TrustMetricConfig configuration.
A trust metric store should be created with a DB that has persistent storage so it can save history data across node executions. All trust metrics instantiated by the store will be created with the provided TrustMetricConfig configuration.
When you attempt to fetch the trust metric for a peer, and an entry does not exist in the trust metric store, a new metric is automatically created and the entry made within the store.
In additional to the fetching method, GetPeerTrustMetric, the trust metric store provides a method to call when a peer has disconnected from the node. This is so the metric can be paused (history data will not be saved) for periods of time when the node is not having direct experiences with the peer.
```go
// TrustMetricStore - Manages all trust metrics for peers
type TrustMetricStore struct {
cmn.BaseService
// Private elements
}
@ -214,7 +206,6 @@ tm := tms.GetPeerTrustMetric(key)
tm.BadEvents(1)
tms.PeerDisconnected(key)
```
## Status

View File

@ -17,11 +17,13 @@ For example, when the PEXReactor makes a request for peers network addresses fro
The trust metric implementation allows a developer to obtain a peer's trust metric from a trust metric store, and track good and bad events relevant to a peer's behavior, and at any time, the peer's metric can be queried for a current trust value. The current trust value is calculated with a formula that utilizes current behavior, previous behavior, and change between the two. Current behavior is calculated as the percentage of good behavior within a time interval. The time interval is short; probably set between 30 seconds and 5 minutes. On the other hand, the historic data can estimate a peer's behavior over days worth of tracking. At the end of a time interval, the current behavior becomes part of the historic data, and a new time interval begins with the good and bad counters reset to zero.
These are some important things to keep in mind regarding how the trust metrics handle time intervals and scoring:
- Each new time interval begins with a perfect score
- Bad events quickly bring the score down and good events cause the score to slowly rise
- When the time interval is over, the percentage of good events becomes historic data.
Some useful information about the inner workings of the trust metric:
- When a trust metric is first instantiated, a timer (ticker) periodically fires in order to handle transitions between trust metric time intervals
- If a peer is disconnected from a node, the timer should be paused, since the node is no longer connected to that peer
- The ability to pause the metric is supported with the store **PeerDisconnected** method and the metric **Pause** method
@ -76,6 +78,7 @@ Peer quality is tracked in the connection and across the reactors by storing the
thread safe Data store.
Peer behaviour is then defined as one of the following:
- Fatal - something outright malicious that causes us to disconnect the peer and ban it from the address book for some amount of time
- Bad - Any kind of timeout, messages that don't unmarshal, fail other validity checks, or messages we didn't ask for or aren't expecting (usually worth one bad event)
- Neutral - Unknown channels/message types/version upgrades (no good or bad events recorded)

View File

@ -11,18 +11,18 @@ implementations:
The SocketPV address can be provided via flags at the command line - doing so
will cause Tendermint to ignore any "priv_validator.json" file and to listen on
the given address for incoming connections from an external priv_validator
process. It will halt any operation until at least one external process
process. It will halt any operation until at least one external process
succesfully connected.
The external priv_validator process will dial the address to connect to
Tendermint, and then Tendermint will send requests on the ensuing connection to
sign votes and proposals. Thus the external process initiates the connection,
but the Tendermint process makes all requests. In a later stage we're going to
sign votes and proposals. Thus the external process initiates the connection,
but the Tendermint process makes all requests. In a later stage we're going to
support multiple validators for fault tolerance. To prevent double signing they
need to be synced, which is deferred to an external solution (see #1185).
In addition, Tendermint will provide implementations that can be run in that
external process. These include:
external process. These include:
- FilePV will encrypt the private key, and the user must enter password to
decrypt key when process is started.

View File

@ -8,7 +8,7 @@
## Context
The ABCI was first introduced in late 2015. It's purpose is to be:
The ABCI was first introduced in late 2015. It's purpose is to be:
- a generic interface between state machines and their replication engines
- agnostic to the language the state machine is written in
@ -66,8 +66,8 @@ possible.
### Validators
To change the validator set, applications can return a list of validator updates
with ResponseEndBlock. In these updates, the public key *must* be included,
because Tendermint requires the public key to verify validator signatures. This
with ResponseEndBlock. In these updates, the public key _must_ be included,
because Tendermint requires the public key to verify validator signatures. This
means ABCI developers have to work with PubKeys. That said, it would also be
convenient to work with address information, and for it to be simple to do so.
@ -80,7 +80,7 @@ in commits.
### InitChain
Tendermint passes in a list of validators here, and nothing else. It would
Tendermint passes in a list of validators here, and nothing else. It would
benefit the application to be able to control the initial validator set. For
instance the genesis file could include application-based information about the
initial validator set that the application could process to determine the
@ -120,7 +120,6 @@ v1 will:
That said, an Amino v2 will be worked on to improve the performance of the
format and its useability in cryptographic applications.
### PubKey
Encoding schemes infect software. As a generic middleware, ABCI aims to have
@ -143,7 +142,6 @@ where `type` can be:
- "ed225519", with `data = <raw 32-byte pubkey>`
- "secp256k1", with `data = <33-byte OpenSSL compressed pubkey>`
As we want to retain flexibility here, and since ideally, PubKey would be an
interface type, we do not use `enum` or `oneof`.

View File

@ -66,13 +66,10 @@ Make the following changes:
- More modern and standard cryptographic functions with wider adoption and hardware acceleration
### Negative
- Exact authenticated encryption construction isn't already provided in a well-used library
### Neutral
## References

View File

@ -15,11 +15,11 @@ https://github.com/tendermint/tendermint/issues/986.
A few solutions were considered:
1. [Prometheus](https://prometheus.io)
a) Prometheus API
b) [go-kit metrics package](https://github.com/go-kit/kit/tree/master/metrics) as an interface plus Prometheus
c) [telegraf](https://github.com/influxdata/telegraf)
d) new service, which will listen to events emitted by pubsub and report metrics
5. [OpenCensus](https://opencensus.io/go/index.html)
a) Prometheus API
b) [go-kit metrics package](https://github.com/go-kit/kit/tree/master/metrics) as an interface plus Prometheus
c) [telegraf](https://github.com/influxdata/telegraf)
d) new service, which will listen to events emitted by pubsub and report metrics
2. [OpenCensus](https://opencensus.io/go/index.html)
### 1. Prometheus
@ -70,30 +70,30 @@ will need to write interfaces ourselves.
### List of metrics
| | Name | Type | Description |
| - | --------------------------------------- | ------- | ----------------------------------------------------------------------------- |
| A | consensus_height | Gauge | |
| A | consensus_validators | Gauge | Number of validators who signed |
| A | consensus_validators_power | Gauge | Total voting power of all validators |
| A | consensus_missing_validators | Gauge | Number of validators who did not sign |
| A | consensus_missing_validators_power | Gauge | Total voting power of the missing validators |
| A | consensus_byzantine_validators | Gauge | Number of validators who tried to double sign |
| A | consensus_byzantine_validators_power | Gauge | Total voting power of the byzantine validators |
| A | consensus_block_interval | Timing | Time between this and last block (Block.Header.Time) |
| | consensus_block_time | Timing | Time to create a block (from creating a proposal to commit) |
| | consensus_time_between_blocks | Timing | Time between committing last block and (receiving proposal creating proposal) |
| A | consensus_rounds | Gauge | Number of rounds |
| | consensus_prevotes | Gauge | |
| | consensus_precommits | Gauge | |
| | consensus_prevotes_total_power | Gauge | |
| | consensus_precommits_total_power | Gauge | |
| A | consensus_num_txs | Gauge | |
| A | mempool_size | Gauge | |
| A | consensus_total_txs | Gauge | |
| A | consensus_block_size | Gauge | In bytes |
| A | p2p_peers | Gauge | Number of peers node's connected to |
| | Name | Type | Description |
| --- | ------------------------------------ | ------ | ----------------------------------------------------------------------------- |
| A | consensus_height | Gauge | |
| A | consensus_validators | Gauge | Number of validators who signed |
| A | consensus_validators_power | Gauge | Total voting power of all validators |
| A | consensus_missing_validators | Gauge | Number of validators who did not sign |
| A | consensus_missing_validators_power | Gauge | Total voting power of the missing validators |
| A | consensus_byzantine_validators | Gauge | Number of validators who tried to double sign |
| A | consensus_byzantine_validators_power | Gauge | Total voting power of the byzantine validators |
| A | consensus_block_interval | Timing | Time between this and last block (Block.Header.Time) |
| | consensus_block_time | Timing | Time to create a block (from creating a proposal to commit) |
| | consensus_time_between_blocks | Timing | Time between committing last block and (receiving proposal creating proposal) |
| A | consensus_rounds | Gauge | Number of rounds |
| | consensus_prevotes | Gauge | |
| | consensus_precommits | Gauge | |
| | consensus_prevotes_total_power | Gauge | |
| | consensus_precommits_total_power | Gauge | |
| A | consensus_num_txs | Gauge | |
| A | mempool_size | Gauge | |
| A | consensus_total_txs | Gauge | |
| A | consensus_block_size | Gauge | In bytes |
| A | p2p_peers | Gauge | Number of peers node's connected to |
`A` - will be implemented in the fist place.
`A` - will be implemented in the fist place.
**Proposed solution**

View File

@ -0,0 +1,183 @@
# ADR 012: ABCI `ProposeTx` Method
## Changelog
25-06-2018: Initial draft based on [#1776](https://github.com/tendermint/tendermint/issues/1776)
## Context
[#1776](https://github.com/tendermint/tendermint/issues/1776) was
opened in relation to implementation of a Plasma child chain using Tendermint
Core as consensus/replication engine.
Due to the requirements of [Minimal Viable Plasma (MVP)](https://ethresear.ch/t/minimal-viable-plasma/426) and [Plasma Cash](https://ethresear.ch/t/plasma-cash-plasma-with-much-less-per-user-data-checking/1298), it is necessary for ABCI apps to have a mechanism to handle the following cases (more may emerge in the near future):
1. `deposit` transactions on the Root Chain, which must consist of a block
with a single transaction, where there are no inputs and only one output
made in favour of the depositor. In this case, a `block` consists of
a transaction with the following shape:
```
[0, 0, 0, 0, #input1 - zeroed out
0, 0, 0, 0, #input2 - zeroed out
<depositor_address>, <amount>, #output1 - in favour of depositor
0, 0, #output2 - zeroed out
<fee>,
]
```
`exit` transactions may also be treated in a similar manner, wherein the
input is the UTXO being exited on the Root Chain, and the output belongs to
a reserved "burn" address, e.g., `0x0`. In such cases, it is favourable for
the containing block to only hold a single transaction that may receive
special treatment.
2. Other "internal" transactions on the child chain, which may be initiated
unilaterally. The most basic example of is a coinbase transaction
implementing validator node incentives, but may also be app-specific. In
these cases, it may be favourable for such transactions to
be ordered in a specific manner, e.g., coinbase transactions will always be
at index 0. In general, such strategies increase the determinism and
predictability of blockchain applications.
While it is possible to deal with the cases enumerated above using the
existing ABCI, currently available result in suboptimal workarounds. Two are
explained in greater detail below.
### Solution 1: App state-based Plasma chain
In this work around, the app maintains a `PlasmaStore` with a corresponding
`Keeper`. The PlasmaStore is responsible for maintaing a second, separate
blockchain that complies with the MVP specification, including `deposit`
blocks and other "internal" transactions. These "virtual" blocks are then broadcasted
to the Root Chain.
This naive approach is, however, fundamentally flawed, as it by definition
diverges from the canonical chain maintained by Tendermint. This is further
exacerbated if the business logic for generating such transactions is
potentially non-deterministic, as this should not even be done in
`Begin/EndBlock`, which may, as a result, break consensus guarantees.
Additinoally, this has serious implications for "watchers" - independent third parties,
or even an auxilliary blockchain, responsible for ensuring that blocks recorded
on the Root Chain are consistent with the Plasma chain's. Since, in this case,
the Plasma chain is inconsistent with the canonical one maintained by Tendermint
Core, it seems that there exists no compact means of verifying the legitimacy of
the Plasma chain without replaying every state transition from genesis (!).
### Solution 2: Broadcast to Tendermint Core from ABCI app
This approach is inspired by `tendermint`, in which Ethereum transactions are
relayed to Tendermint Core. It requires the app to maintain a client connection
to the consensus engine.
Whenever an "internal" transaction needs to be created, the proposer of the
current block broadcasts the transaction or transactions to Tendermint as
needed in order to ensure that the Tendermint chain and Plasma chain are
completely consistent.
This allows "internal" transactions to pass through the full consensus
process, and can be validated in methods like `CheckTx`, i.e., signed by the
proposer, is the semantically correct, etc. Note that this involves informing
the ABCI app of the block proposer, which was temporarily hacked in as a means
of conducting this experiment, although this should not be necessary when the
current proposer is passed to `BeginBlock`.
It is much easier to relay these transactions directly to the Root
Chain smart contract and/or maintain a "compressed" auxiliary chain comprised
of Plasma-friendly blocks that 100% reflect the canonical (Tendermint)
blockchain. Unfortunately, this approach not idiomatic (i.e., utilises the
Tendermint consensus engine in unintended ways). Additionally, it does not
allow the application developer to:
- Control the _ordering_ of transactions in the proposed block (e.g., index 0,
or 0 to `n` for coinbase transactions)
- Control the _number_ of transactions in the block (e.g., when a `deposit`
block is required)
Since determinism is of utmost importance in blockchain engineering, this approach,
while more viable, should also not be considered as fit for production.
## Decision
### `ProposeTx`
In order to address the difficulties described above, the ABCI interface must
expose an additional method, tentatively named `ProposeTx`.
It should have the following signature:
```
ProposeTx(RequestProposeTx) ResponseProposeTx
```
Where `RequestProposeTx` and `ResponseProposeTx` are `message`s with the
following shapes:
```
message RequestProposeTx {
int64 next_block_height = 1; // height of the block the proposed tx would be part of
Validator proposer = 2; // the proposer details
}
message ResponseProposeTx {
int64 num_tx = 1; // the number of tx to include in proposed block
repeated bytes txs = 2; // ordered transaction data to include in block
bool exclusive = 3; // whether the block should include other transactions (from `mempool`)
}
```
`ProposeTx` would be called by before `mempool.Reap` at this
[line](https://github.com/tendermint/tendermint/blob/master/consensus/state.go#L906).
Depending on whether `exclusive` is `true` or `false`, the proposed
transactions are then pushed on top of the transactions received from
`mempool.Reap`.
### `DeliverTx`
Since the list of `tx` received from `ProposeTx` are _not_ passed through `CheckTx`,
it is probably a good idea to provide a means of differentiatiating "internal" transactions
from user-generated ones, in case the app developer needs/wants to take extra measures to
ensure validity of the proposed transactions.
Therefore, the `RequestDeliverTx` message should be changed to provide an additional flag, like so:
```
message RequestDeliverTx {
bytes tx = 1;
bool internal = 2;
}
```
Alternatively, an additional method `DeliverProposeTx` may be added as an accompanient to
`ProposeTx`. However, it is not clear at this stage if this additional overhead is necessary
to preserve consensus guarantees given that a simple flag may suffice for now.
## Status
Pending
## Consequences
### Positive
- Tendermint ABCI apps will be able to function as minimally viable Plasma chains.
- It will thereby become possible to add an extension to `cosmos-sdk` to enable
ABCI apps to support both IBC and Plasma, maximising interop.
- ABCI apps will have great control and flexibility in managing blockchain state,
without having to resort to non-deterministic hacks and/or unsafe workarounds
### Negative
- Maintenance overhead of exposing additional ABCI method
- Potential security issues that may have been overlooked and must now be tested extensively
### Neutral
- ABCI developers must deal with increased (albeit nominal) API surface area.
## References
- [#1776 Plasma and "Internal" Transactions in ABCI Apps](https://github.com/tendermint/tendermint/issues/1776)
- [Minimal Viable Plasma](https://ethresear.ch/t/minimal-viable-plasma/426)
- [Plasma Cash: Plasma with much less per-user data checking](https://ethresear.ch/t/plasma-cash-plasma-with-much-less-per-user-data-checking/1298)

View File

@ -9,8 +9,9 @@ handling. An artifact is the dependency of the Switch on
`[config.P2PConfig`](https://github.com/tendermint/tendermint/blob/05a76fb517f50da27b4bfcdc7b4cf185fc61eff6/config/config.go#L272-L339).
Addresses:
* [#2046](https://github.com/tendermint/tendermint/issues/2046)
* [#2047](https://github.com/tendermint/tendermint/issues/2047)
- [#2046](https://github.com/tendermint/tendermint/issues/2046)
- [#2047](https://github.com/tendermint/tendermint/issues/2047)
First iteraton in [#2067](https://github.com/tendermint/tendermint/issues/2067)
@ -29,15 +30,14 @@ transport implementation is responsible to filter establishing peers specific
to its domain, for the default multiplexed implementation the following will
apply:
* connections from our own node
* handshake fails
* upgrade to secret connection fails
* prevent duplicate ip
* prevent duplicate id
* nodeinfo incompatibility
- connections from our own node
- handshake fails
- upgrade to secret connection fails
- prevent duplicate ip
- prevent duplicate id
- nodeinfo incompatibility
``` go
```go
// PeerTransport proxies incoming and outgoing peer connections.
type PeerTransport interface {
// Accept returns a newly connected Peer.
@ -75,7 +75,7 @@ func NewMTransport(
nodeAddr NetAddress,
nodeInfo NodeInfo,
nodeKey NodeKey,
) *multiplexTransport
) *multiplexTransport
```
### Switch
@ -84,7 +84,7 @@ From now the Switch will depend on a fully setup `PeerTransport` to
retrieve/reach out to its peers. As the more low-level concerns are pushed to
the transport, we can omit passing the `config.P2PConfig` to the Switch.
``` go
```go
func NewSwitch(transport PeerTransport, opts ...SwitchOption) *Switch
```
@ -96,17 +96,17 @@ In Review.
### Positive
* free Switch from transport concerns - simpler implementation
* pluggable transport implementation - simpler test setup
* remove Switch dependency on P2PConfig - easier to test
- free Switch from transport concerns - simpler implementation
- pluggable transport implementation - simpler test setup
- remove Switch dependency on P2PConfig - easier to test
### Negative
* more setup for tests which depend on Switches
- more setup for tests which depend on Switches
### Neutral
* multiplexed will be the default implementation
- multiplexed will be the default implementation
[0] These guards could be potentially extended to be pluggable much like
middlewares to express different concerns required by differentally configured

View File

@ -14,22 +14,23 @@ to easily swap these out.
### How do we encrypt with AEAD's
AEAD's typically require a nonce in addition to the key.
AEAD's typically require a nonce in addition to the key.
For the purposes we require symmetric cryptography for,
we need encryption to be stateless.
Because of this we use random nonces.
Because of this we use random nonces.
(Thus the AEAD must support random nonces)
We currently construct a random nonce, and encrypt the data with it.
We currently construct a random nonce, and encrypt the data with it.
The returned value is `nonce || encrypted data`.
The limitation of this is that does not provide a way to identify
which algorithm was used in encryption.
Consequently decryption with multiple algoritms is sub-optimal.
Consequently decryption with multiple algoritms is sub-optimal.
(You have to try them all)
## Decision
We should create the following two methods in a new `crypto/encoding/symmetric` package:
We should create the following two methods in a new `crypto/encoding/symmetric` package:
```golang
func Encrypt(aead cipher.AEAD, plaintext []byte) (ciphertext []byte, err error)
func Decrypt(key []byte, ciphertext []byte) (plaintext []byte, err error)
@ -37,18 +38,19 @@ func Register(aead cipher.AEAD, algo_name string, NewAead func(key []byte) (ciph
```
This allows you to specify the algorithm in encryption, but not have to specify
it in decryption.
it in decryption.
This is intended for ease of use in downstream applications, in addition to people
looking at the file directly.
One downside is that for the encrypt function you must have already initialized an AEAD,
but I don't really see this as an issue.
but I don't really see this as an issue.
If there is no error in encryption, Encrypt will return `algo_name || nonce || aead_ciphertext`.
If there is no error in encryption, Encrypt will return `algo_name || nonce || aead_ciphertext`.
`algo_name` should be length prefixed, using standard varuint encoding.
This will be binary data, but thats not a problem considering the nonce and ciphertext are also binary.
This solution requires a mapping from aead type to name.
We can achieve this via reflection.
This solution requires a mapping from aead type to name.
We can achieve this via reflection.
```golang
func getType(myvar interface{}) string {
if t := reflect.TypeOf(myvar); t.Kind() == reflect.Ptr {
@ -58,7 +60,8 @@ func getType(myvar interface{}) string {
}
}
```
Then we maintain a map from the name returned from `getType(aead)` to `algo_name`.
Then we maintain a map from the name returned from `getType(aead)` to `algo_name`.
In decryption, we read the `algo_name`, and then instantiate a new AEAD with the key.
Then we call the AEAD's decrypt method on the provided nonce/ciphertext.
@ -81,13 +84,16 @@ Proposed.
## Consequences
### Positive
* Allows us to support new AEAD's, in a way that makes decryption easier
* Allows downstream users to add their own AEAD
- Allows us to support new AEAD's, in a way that makes decryption easier
- Allows downstream users to add their own AEAD
### Negative
* We will have to break all private keys stored on disk.
They can be recovered using seed words, and upgrade scripts are simple.
- We will have to break all private keys stored on disk.
They can be recovered using seed words, and upgrade scripts are simple.
### Neutral
* Caller has to instantiate the AEAD with the private key.
However it forces them to be aware of what signing algorithm they are using, which is a positive.
- Caller has to instantiate the AEAD with the private key.
However it forces them to be aware of what signing algorithm they are using, which is a positive.

View File

@ -22,21 +22,21 @@ Removing this second layer of signature malleability concerns could ease downstr
### ECDSA context
Secp256k1 is ECDSA over a particular curve.
The signature is of the form `(r, s)`, where `s` is a field element.
The signature is of the form `(r, s)`, where `s` is a field element.
(The particular field is the `Z_n`, where the elliptic curve has order `n`)
However `(r, -s)` is also another valid solution.
Note that anyone can negate a group element, and therefore can get this second signature.
## Decision
We can just distinguish a canonical form for the ECDSA signatures.
We can just distinguish a canonical form for the ECDSA signatures.
Then we require that all ECDSA signatures be in the form which we defined as canonical.
We reject signatures in non-canonical form.
A canonical form is rather easy to define and check.
A canonical form is rather easy to define and check.
It would just be the smaller of the two values for `s`, defined lexicographically.
This is a simple check, instead of checking if `s < n`, instead check `s <= (n - 1)/2`.
An example of another cryptosystem using this
An example of another cryptosystem using this
is the parity definition here https://github.com/zkcrypto/pairing/pull/30#issuecomment-372910663.
This is the same solution Ethereum has chosen for solving secp malleability.
@ -47,15 +47,17 @@ Fork https://github.com/btcsuite/btcd, and just update the [parse sig method](ht
## Status
Proposed.
Implemented
## Consequences
### Positive
* Lets us maintain the ability to expect a tx hash to appear in the blockchain.
- Lets us maintain the ability to expect a tx hash to appear in the blockchain.
### Negative
* More work in all future implementations (Though this is a very simple check)
* Requires us to maintain another fork
- More work in all future implementations (Though this is a very simple check)
- Requires us to maintain another fork
### Neutral

View File

@ -1,8 +1,8 @@
# ADR 015: Crypto encoding
# ADR 015: Crypto encoding
## Context
We must standardize our method for encoding public keys and signatures on chain.
We must standardize our method for encoding public keys and signatures on chain.
Currently we amino encode the public keys and signatures.
The reason we are using amino here is primarily due to ease of support in
parsing for other languages.
@ -54,9 +54,11 @@ When placed in state, signatures will still be amino encoded, but it will be the
primitive type `[]byte` getting encoded.
#### Ed25519
Use the canonical representation for signatures.
#### Secp256k1
There isn't a clear canonical representation here.
Signatures have two elements `r,s`.
These bytes are encoded as `r || s`, where `r` and `s` are both exactly
@ -65,16 +67,18 @@ This is basically Ethereum's encoding, but without the leading recovery bit.
## Status
Proposed. The signature section seems to be agreed upon for the most part.
Needs decision on Enum types.
Implemented
## Consequences
### Positive
* More space efficient signatures
- More space efficient signatures
### Negative
* We have an amino dependency for cryptography.
- We have an amino dependency for cryptography.
### Neutral
* No change to public keys
- No change to public keys

View File

@ -0,0 +1,301 @@
# ADR 016: Protocol Versions
## TODO
- How to / should we version the authenticated encryption handshake itself (ie.
upfront protocol negotiation for the P2PVersion)
## Changelog
- 03-08-2018: Updates from discussion with Jae:
- ProtocolVersion contains Block/AppVersion, not Current/Next
- signal upgrades to Tendermint using EndBlock fields
- dont restrict peer compatibilty by version to simplify syncing old nodes
- 28-07-2018: Updates from review
- split into two ADRs - one for protocol, one for chains
- include signalling for upgrades in header
- 16-07-2018: Initial draft - was originally joint ADR for protocol and chain
versions
## Context
The Software Version is covered by SemVer and described elsewhere.
It is not relevant to the protocol description, suffice to say that if any protocol version
changes, the software version changes, but not necessarily vice versa.
Software version shoudl be included in NodeInfo for convenience/diagnostics.
We are also interested in versioning across different blockchains in a
meaningful way, for instance to differentiate branches of a contentious
hard-fork. We leave that for a later ADR.
Here we focus on protocol versions.
## Requirements
We need to version components of the blockchain that may be independently upgraded.
We need to do it in a way that is scalable and maintainable - we can't just litter
the code with conditionals.
We can consider the complete version of the protocol to contain the following sub-versions:
BlockVersion, P2PVersion, AppVersion. These versions reflect the major sub-components
of the software that are likely to evolve together, at different rates, and in different ways,
as described below.
The BlockVersion defines the core of the blockchain data structures and
should change infrequently.
The P2PVersion defines how peers connect and communicate with eachother - it's
not part of the blockchain data structures, but defines the protocols used to build the
blockchain. It may change gradually.
The AppVersion determines how we compute app specific information, like the
AppHash and the Results.
All of these versions may change over the life of a blockchain, and we need to
be able to help new nodes sync up across version changes. This means we must be willing
to connect to peers with older version.
### BlockVersion
- All tendermint hashed data-structures (headers, votes, txs, responses, etc.).
- Note the semantic meaning of a transaction may change according to the AppVersion, but the way txs are merklized into the header is part of the BlockVersion
- It should be the least frequent/likely to change.
- Tendermint should be stabilizing - it's just Atomic Broadcast.
- We can start considering for Tendermint v2.0 in a year
- It's easy to determine the version of a block from its serialized form
### P2PVersion
- All p2p and reactor messaging (messages, detectable behaviour)
- Will change gradually as reactors evolve to improve performance and support new features - eg proposed new message types BatchTx in the mempool and HasBlockPart in the consensus
- It's easy to determine the version of a peer from its first serialized message/s
- New versions must be compatible with at least one old version to allow gradual upgrades
### AppVersion
- The ABCI state machine (txs, begin/endblock behaviour, commit hashing)
- Behaviour and message types will change abruptly in the course of the life of a chain
- Need to minimize complexity of the code for supporting different AppVersions at different heights
- Ideally, each version of the software supports only a _single_ AppVersion at one time
- this means we checkout different versions of the software at different heights instead of littering the code
with conditionals
- minimize the number of data migrations required across AppVersion (ie. most AppVersion should be able to read the same state from disk as previous AppVersion).
## Ideal
Each component of the software is independently versioned in a modular way and its easy to mix and match and upgrade.
Good luck pal ;)
## Proposal
Each of BlockVersion, AppVersion, P2PVersion is a monotonically increasing int64.
To use these versions, we need to update the block Header, the p2p NodeInfo, and the ABCI.
### Header
Block Header should include a `Version` struct as its first field like:
```
type Version struct {
CurrentVersion ProtocolVersion
ChainID string
NextVersion ProtocolVersion
}
type ProtocolVersion struct {
BlockVersion int64
AppVersion int64
}
```
Note this effectively makes BlockVersion the first field in the block Header.
Since we have settled on a proto3 header, the ability to read the BlockVersion out of the serialized header is unanimous.
Using a Version struct gives us more flexibility to add fields without breaking
the header.
The ProtocolVersion struct includes both the Block and App versions - it should
serve as a complete description of the consensus-critical protocol.
Using the `NextVersion` field, proposer's can signal their readiness to upgrade
to a new Block and/or App version.
### NodeInfo
NodeInfo should include a Version struct as its first field like:
```
type Version struct {
P2PVersion int64
ChainID string
BlockVersion int64
AppVersion int64
SoftwareVersion string
}
```
Note this effectively makes P2PVersion the first field in the NodeInfo, so it
should be easy to read this out of the serialized header if need be to facilitate an upgrade.
The SoftwareVersion here should include the name of the software client and
it's SemVer version - this is for convenience only. Eg.
`tendermint-core/v0.22.8`.
The other versions and ChainID will determine peer compatibility (described below).
### ABCI
Since the ABCI is responsible for keeping Tendermint and the App in sync, we
need to communicate version information through it.
On startup, we use Info to perform a basic handshake. It should include all the
version information.
We also need to be able to update versions in the life of a blockchain. The
natural place to do this is EndBlock.
#### Info
RequestInfo should add support for protocol versions like:
```
message RequestInfo {
string software_version
int64 block_version
int64 p2p_version
}
```
Similarly, ResponseInfo should return the versions:
```
message ResponseInfo {
string data
string software_version
int64 app_version
int64 last_block_height
bytes last_block_app_hash
}
```
#### EndBlock
Updating the version could be done either with new fields or by using the
existing `tags`. Since we're trying to communicate information that will be
included in Tendermint block Headers, it should be native to the ABCI, and not
something embedded through some scheme in the tags.
ResponseEndBlock will include a new field `version_updates`:
```
message ResponseEndBlock {
repeated Validator validator_updates
ConsensusParams consensus_param_updates
repeated common.KVPair tags
VersionUpdates version_updates
}
message VersionUpdates {
ProtocolVersion current_version
ProtocolVersion next_version
}
message ProtocolVersion {
int64 block_version
int64 app_version
}
```
Tendermint will use the information in VersionUpdates for the next block it
proposes.
### BlockVersion
BlockVersion is included in both the Header and the NodeInfo.
Changing BlockVersion should happen quite infrequently and ideally only for extreme emergency.
Note Ethereum has not had to make an upgrade like this (everything has been at state machine level, AFAIK).
### P2PVersion
P2PVersion is not included in the block Header, just the NodeInfo.
P2PVersion is the first field in the NodeInfo. NodeInfo is also proto3 so this is easy to read out.
Note we need the peer/reactor protocols to take the versions of peers into account when sending messages:
- don't send messages they don't understand
- don't send messages they don't expect
Doing this will be specific to the upgrades being made.
Note we also include the list of reactor channels in the NodeInfo and already don't send messages for channels the peer doesn't understand.
If upgrades always use new channels, this simplifies the development cost of backwards compatibility.
Note NodeInfo is only exchanged after the authenticated encryption handshake to ensure that it's private.
Doing any version exchange before encrypting could be considered information leakage, though I'm not sure
how much that matters compared to being able to upgrade the protocol.
XXX: if needed, can we change the meaning of the first byte of the first message to encode a handshake version?
this is the first byte of a 32-byte ed25519 pubkey.
### AppVersion
AppVersion is also included in the block Header and the NodeInfo.
AppVersion essentially defines how the AppHash and Results are computed.
### Peer Compatibility
Restricting peer compatibility based on version is complicated by the need to
help old peers, possibly on older versions, sync the blockchain.
We might be tempted to say that we only connect to peers with the same
AppVersion and BlockVersion (since these define the consensus critical
computations), and a select list of P2PVersions (ie. those compatible with
ours), but then we'd need to make accomodations for connecting to peers with the
right Block/AppVersion for the height they're on.
For now, we will connect to peers with any version and restrict compatibility
solely based on the ChainID. We leave more restrictive rules on peer
compatibiltiy to a future proposal.
### Future Changes
It may be valuable to support an `/unsafe_stop?height=_` endpoint to tell Tendermint to shutdown at a given height.
This could be use by an external manager process that oversees upgrades by
checking out and installing new software versions and restarting the process. It
would subscribe to the relevant upgrade event (needs to be implemented) and call `/unsafe_stop` at
the correct height (of course only after getting approval from its user!)
## Consequences
### Positive
- Make tendermint and application versions native to the ABCI to more clearly
communicate about them
- Distinguish clearly between protocol versions and software version to
facilitate implementations in other languages
- Versions included in key data structures in easy to discern way
- Allows proposers to signal for upgrades and apps to decide when to actually change the
version (and start signalling for a new version)
### Neutral
- Unclear how to version the initial P2P handshake itself
- Versions aren't being used (yet) to restrict peer compatibility
- Signalling for a new version happens through the proposer and must be
tallied/tracked in the app.
### Negative
- Adds more fields to the ABCI
- Implies that a single codebase must be able to handle multiple versions

View File

@ -0,0 +1,99 @@
# ADR 017: Chain Versions
## TODO
- clarify how to handle slashing when ChainID changes
## Changelog
- 28-07-2018: Updates from review
- split into two ADRs - one for protocol, one for chains
- 16-07-2018: Initial draft - was originally joint ADR for protocol and chain
versions
## Context
Software and Protocol versions are covered in a separate ADR.
Here we focus on chain versions.
## Requirements
We need to version blockchains across protocols, networks, forks, etc.
We need chain identifiers and descriptions so we can talk about a multitude of chains,
and especially the differences between them, in a meaningful way.
### Networks
We need to support many independent networks running the same version of the software,
even possibly starting from the same initial state.
They must have distinct identifiers so that peers know which one they are joining and so
validators and users can prevent replay attacks.
Call this the `NetworkName` (note we currently call this `ChainID` in the software. In this
ADR, ChainID has a different meaning).
It represents both the application being run and the community or intention
of running it.
Peers only connect to other peers with the same NetworkName.
### Forks
We need to support existing networks upgrading and forking, wherein they may do any of:
- revert back to some height, continue with the same versions but new blocks
- arbitrarily mutate state at some height, continue with the same versions (eg. Dao Fork)
- change the AppVersion at some height
Note because of Tendermint's voting power threshold rules, a chain can only be extended under the "original" rules and under the new rules
if 1/3 or more is double signing, which is expressly prohibited, and is supposed to result in their punishment on both chains. Since they can censor
the punishment, the chain is expected to be hardforked to remove the validators. Thus, if both branches are to continue after a fork,
they will each require a new identifier, and the old chain identifier will be retired (ie. only useful for syncing history, not for new blocks)..
TODO: explain how to handle slashing when chain id changed!
We need a consistent way to describe forks.
## Proposal
### ChainDescription
ChainDescription is a complete immutable description of a blockchain. It takes the following form:
```
ChainDescription = <NetworkName>/<BlockVersion>/<AppVersion>/<StateHash>/<ValHash>/<ConsensusParamsHash>
```
Here, StateHash is the merkle root of the initial state, ValHash is the merkle root of the initial Tendermint validator set,
and ConsensusParamsHash is the merkle root of the initial Tendermint consensus parameters.
The `genesis.json` file must contain enough information to compute this value. It need not contain the StateHash or ValHash itself,
but contain the state from which they can be computed with the given protocol versions.
NOTE: consider splitting NetworkName into NetworkName and AppName - this allows
folks to independently use the same application for different networks (ie we
could imagine multiple communities of validators wanting to put up a Hub using
the same app but having a distinct network name. Arguably not needed if
differences will come via different initial state / validators).
#### ChainID
Define `ChainID = TMHASH(ChainDescriptor)`. It's the unique ID of a blockchain.
It should be Bech32 encoded when handled by users, eg. with `cosmoschain` prefix.
#### Forks and Uprades
When a chain forks or upgrades but continues the same history, it takes a new ChainDescription as follows:
```
ChainDescription = <ChainID>/x/<Height>/<ForkDescription>
```
Where
- ChainID is the ChainID from the previous ChainDescription (ie. its hash)
- `x` denotes that a change occured
- `Height` is the height the change occured
- ForkDescription has the same form as ChainDescription but for the fork
- this allows forks to specify new versions for tendermint or the app, as well as arbitrary changes to the state or validator set

Some files were not shown because too many files have changed in this diff Show More